diff --git a/.clang-format b/.clang-format index a8054814d54..c3a9fa31eb5 100644 --- a/.clang-format +++ b/.clang-format @@ -3,9 +3,11 @@ AllowShortFunctionsOnASingleLine: Inline AlwaysBreakTemplateDeclarations: Yes BasedOnStyle: LLVM BreakConstructorInitializers: BeforeComma -ColumnLimit: 100 +ColumnLimit: 0 ConstructorInitializerAllOnOneLineOrOnePerLine: true IndentWidth: 4 +IndentAccessModifiers: false +AccessModifierOffset: -4 PointerAlignment: Left IncludeCategories: - Regex: '^"\.\./' diff --git a/.gitignore b/.gitignore index db676afddf5..707a2517dee 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ cmake-build-debug/ .cquery_cache/ .cxx/ .cache/ +build_pvs_studio/ # Dependencies node_modules @@ -26,11 +27,18 @@ lib/protobuf # Generated files jni/cpp/generated jni/java/wallet/core/jni +jni/java/wallet/core/proto swift/Sources/Generated swift/wallet-core/ -src/Generated +src/Generated/*.cpp include/TrustWalletCore/TWHRP.h include/TrustWalletCore/TW*Proto.h +include/TrustWalletCore/TWDerivation.h +include/TrustWalletCore/TWEthereumChainID.h + +# Wasm +emsdk/ +wasm-build/ # Code coverage files coverage.info diff --git a/CMakeLists.txt b/CMakeLists.txt index 7434634139b..0886e36252f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,21 +1,12 @@ -cmake_minimum_required(VERSION 3.8 FATAL_ERROR) +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. -project(TrustWalletCore) - -include(GNUInstallDirs) +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) -# Configure warnings -if(NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) - set(TW_CXX_WARNINGS "-Wshorten-64-to-32") -endif() -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TW_CXX_WARNINGS}") -set(CMAKE_EXPORT_COMPILE_COMMANDS 1) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) -set(CMAKE_CXX_VISIBILITY_PRESET hidden) - -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version" FORCE) +project(TrustWalletCore) if((NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC"))) message(FATAL_ERROR "You should use clang or msvc compiler") @@ -23,35 +14,30 @@ endif() if ("$ENV{PREFIX}" STREQUAL "") set(PREFIX "${CMAKE_SOURCE_DIR}/build/local") -else() +else () set(PREFIX "$ENV{PREFIX}") -endif() +endif () -# Configure CCache if available -find_program(CCACHE_FOUND ccache) -if(CCACHE_FOUND) - set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) - set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) -endif(CCACHE_FOUND) +include(GNUInstallDirs) +include(cmake/StandardSettings.cmake) +include(cmake/CompilerWarnings.cmake) +include(cmake/StaticAnalyzers.cmake) +include(cmake/FindHostPackage.cmake) -include_directories(${PREFIX}/include) -link_directories(${PREFIX}/lib) +add_library(${PROJECT_NAME}_INTERFACE INTERFACE) +target_include_directories(${PROJECT_NAME}_INTERFACE INTERFACE ${PREFIX}/include) +target_link_directories(${PROJECT_NAME}_INTERFACE INTERFACE ${PREFIX}/lib) +set_project_warnings(${PROJECT_NAME}_INTERFACE) if(WIN32) add_definitions(-D_CRT_SECURE_NO_WARNINGS) # Disable strcpy warnings endif() - add_subdirectory(trezor-crypto) -macro(find_host_package) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) - find_package(${ARGN}) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endmacro(find_host_package) +if (TW_COMPILE_WASM) + message(STATUS "Wasm build enabled") + add_subdirectory(wasm) +endif () find_host_package(Boost REQUIRED) @@ -75,41 +61,16 @@ if(WIN32) endif() endif() -option(CODE_COVERAGE "Enable coverage reporting" OFF) -if(CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -fprofile-arcs -ftest-coverage") - set(CMAKE_EXE_LINKER_FLAGS "-fprofile-arcs -ftest-coverage") -endif() -option(CLANG_TIDY "Enable static code analysis with (clang-tidy)" OFF) -if(CLANG_TIDY) - find_program(CLANG_TIDY_BIN NAMES clang-tidy-11) - if(CLANG_TIDY_BIN) - set(CMAKE_CXX_CLANG_TIDY clang-tidy-11;) - message("clang-tidy ${CMAKE_CXX_CLANG_TIDY} ${CLANG_TIDY_BIN}") - else() - message(FATAL_ERROR "Could not find clang-tidy") - endif() -endif() -option(CLANG_ASAN "Enable ASAN dynamic address sanitizer" OFF) -if(CLANG_ASAN) - # https://clang.llvm.org/docs/AddressSanitizer.html - # https://github.com/trustwallet/wallet-core/issues/1170 - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer") - set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer") - message("CLANG_ASAN on, ${CMAKE_CXX_FLAGS_DEBUG}") -endif() - -# Source files -if(${ANDROID}) +# Source files ** +if (${ANDROID}) message("Configuring for JNI") file(GLOB_RECURSE sources src/*.c src/*.cc src/*.cpp src/*.h jni/cpp/*.c jni/cpp/*.cpp jni/cpp/*.h jni/cpp/*.c) add_library(TrustWalletCore SHARED ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) - find_library(log-lib log) - target_link_libraries(TrustWalletCore PRIVATE TrezorCrypto ${Protobuf_LIBRARIES} ${log-lib} Boost::boost) -else() + target_link_libraries(TrustWalletCore PUBLIC ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto ${Protobuf_LIBRARIES} ${log-lib} Boost::boost) +else () file(GLOB_RECURSE sources src/*.c src/*.cc src/*.cpp src/*.h) if(WIN32) file(GLOB_RECURSE headers include/TrustWalletCore/*.h) @@ -121,39 +82,62 @@ else() add_library(TrustWalletCore SHARED ${sources} ${headers} ${PROTO_SRCS} ${PROTO_HDRS}) target_compile_definitions(TrustWalletCore PRIVATE TW_EXPORT_LIBRARY) endif() - else() + else() message("Configuring standalone") add_library(TrustWalletCore ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) endif() - target_link_libraries(TrustWalletCore PRIVATE TrezorCrypto ${Protobuf_LIBRARIES} Boost::boost) -endif() +target_link_libraries(TrustWalletCore PUBLIC ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto ${Protobuf_LIBRARIES} Boost::boost) +endif () + +if (TW_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_enable_coverage(TrustWalletCore) +endif () + if(NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) target_compile_options(TrustWalletCore PRIVATE "-Wall") endif() -set_target_properties(TrustWalletCore - PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON -) +if (TW_CLANG_ASAN) + target_enable_asan(TrustWalletCore) +endif () # Define headers for this library. PUBLIC headers are used for compiling the # library, and will be added to consumers' build paths. target_include_directories(TrustWalletCore - PUBLIC + PUBLIC $ $ - PRIVATE + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/jni/cpp ${CMAKE_CURRENT_SOURCE_DIR}/build/local/include -) + ) -if(NOT ANDROID AND NOT IOS_PLATFORM AND NOT (WIN32 AND NOT TW_STATIC_LIBRARY)) +if (TW_UNIT_TESTS AND NOT (WIN32 AND NOT TW_STATIC_LIBRARY)) add_subdirectory(tests) +endif () + +if (TW_BUILD_EXAMPLES AND NOT (WIN32 AND NOT TW_STATIC_LIBRARY)) add_subdirectory(walletconsole/lib) add_subdirectory(walletconsole) +endif () + +if (TW_ENABLE_PVS_STUDIO) + tw_add_pvs_studio_target(TrustWalletCore) +endif () + +if (TW_ENABLE_CLANG_TIDY) + tw_add_clang_tidy_target(TrustWalletCore) +endif () + +if (NOT ANDROID AND TW_UNITY_BUILD) + set_target_properties(TrustWalletCore PROPERTIES UNITY_BUILD ON) + file(GLOB_RECURSE PROTOBUF_SOURCE_FILES CONFIGURE_DEPENDS src/Cosmos/Protobuf/*.pb.cc src/proto/*.pb.cc) + foreach(file ${PROTOBUF_SOURCE_FILES}) + set_property(SOURCE ${file} PROPERTY SKIP_UNITY_BUILD_INCLUSION ON) + endforeach() + message(STATUS "Unity build activated") endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/swift/cpp.xcconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/swift/cpp.xcconfig @ONLY) @@ -167,8 +151,10 @@ install(TARGETS TrustWalletCore ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/WalletCore - FILES_MATCHING PATTERN "*.h") +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/WalletCore + FILES_MATCHING PATTERN "*.h" +) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - diff --git a/Dockerfile b/Dockerfile index d78df6195dc..fae3ef89269 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive @@ -17,9 +17,10 @@ RUN apt-get update \ # Add latest cmake/boost SHELL ["/bin/bash", "-o", "pipefail", "-c"] RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc | apt-key add - \ - && apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' \ - && apt-add-repository -y ppa:mhier/libboost-latest + && apt-add-repository 'deb https://apt.kitware.com/ubuntu/ bionic main' + +RUN wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb && dpkg -i ./libssl1.1_1.1.0g-2ubuntu4_amd64.deb # Install required packages for dev RUN apt-get update \ && apt-get install -y \ @@ -27,16 +28,16 @@ RUN apt-get update \ libtool autoconf pkg-config \ ninja-build \ ruby-full \ - clang-10 \ - llvm-10 \ + clang-14 \ + llvm-14 \ libc++-dev libc++abi-dev \ - cmake \ - libboost1.74-dev \ + cmake \ + libboost-all-dev \ ccache \ && apt-get clean && rm -rf /var/lib/apt/lists/* -ENV CC=/usr/bin/clang-10 -ENV CXX=/usr/bin/clang++-10 +ENV CC=/usr/bin/clang-14 +ENV CXX=/usr/bin/clang++-14 # ↑ Setup build environment # ↓ Build and compile wallet core @@ -47,9 +48,25 @@ WORKDIR /wallet-core # Install dependencies RUN tools/install-dependencies -# Build: generate, cmake, and make +# Build: generate, cmake, and make lib RUN tools/generate-files \ && cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug \ - && make -Cbuild -j12 + && make -Cbuild -j12 TrustWalletCore + +# Build unit tester +RUN make -Cbuild -j12 tests + +# Download and Install Go +ENV GO_VERSION=1.16.12 +ENV GO_ARCH=amd64 +RUN wget "https://golang.org/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" \ + && tar -xf "go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" \ + && chown -R root:root ./go \ + && mv -v ./go /usr/local \ + && ls /usr/local/go \ + && /usr/local/go/bin/go version \ + && rm "go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" + +# Building GoLang sample app: cd samples/go && /usr/local/go/bin/go build -o main && ./main CMD ["/bin/bash"] diff --git a/Package.swift b/Package.swift index 114042ae861..7f6216bde07 100644 --- a/Package.swift +++ b/Package.swift @@ -1,27 +1,24 @@ // swift-tools-version:5.3 - import PackageDescription let package = Package( name: "WalletCore", platforms: [.iOS(.v13)], products: [ - .library( - name: "WalletCore", targets: ["WalletCore", "SwiftProtobuf"] - ) - ], - dependencies: [ + .library(name: "WalletCore", targets: ["WalletCore"]), + .library(name: "SwiftProtobuf", targets: ["SwiftProtobuf"]) ], + dependencies: [], targets: [ .binaryTarget( name: "WalletCore", - url: "https://github.com/trustwallet/wallet-core/releases/download/2.6.31/WalletCore.xcframework.zip", - checksum: "29d88807485f88992e00a5b7ed1ddf53ca57dacb89ba5e5368dc931102600879" + url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.6/WalletCore.xcframework.zip", + checksum: "a3df0c2b30fc59ede0a2600266fc19b8c0cf655dbef3fb832488c8ddedcb6b93" ), .binaryTarget( name: "SwiftProtobuf", - url: "https://github.com/trustwallet/wallet-core/releases/download/2.6.31/SwiftProtobuf.xcframework.zip", - checksum: "d1035aa8a32f2483305bbe9cd3d5774bf6c1a65ce5a37213c9ee651c78873a55" + url: "https://github.com/trustwallet/wallet-core/releases/download/3.0.6/SwiftProtobuf.xcframework.zip", + checksum: "61fa8483d4bd43f1898db6997eff0279426f15f9e518e12db0d762ec5f927a9b" ) ] ) diff --git a/README.md b/README.md index 1065ca92e6b..36b798cb3cd 100644 --- a/README.md +++ b/README.md @@ -4,18 +4,19 @@ Trust Wallet Core is an open source, cross-platform, mobile-focused library implementing low-level cryptographic wallet functionality for a high number of blockchains. It is a core part of the popular [Trust Wallet](https://trustwallet.com), and some other projects. Most of the code is C++ with a set of strict C interfaces, and idiomatic interfaces for supported languages: -Swift for iOS and Java for Android. +Swift for iOS and Java (Kotlin) for Android. ![iOS CI](https://github.com/trustwallet/wallet-core/workflows/iOS%20CI/badge.svg) ![Android CI](https://github.com/trustwallet/wallet-core/workflows/Android%20CI/badge.svg) ![Linux CI](https://github.com/trustwallet/wallet-core/workflows/Linux%20CI/badge.svg) +![Wasm CI](https://github.com/trustwallet/wallet-core/workflows/Wasm%20CI/badge.svg) ![Docker CI](https://github.com/trustwallet/wallet-core/workflows/Docker%20CI/badge.svg) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/trustwallet/wallet-core) ![GitHub](https://img.shields.io/github/license/TrustWallet/wallet-core.svg) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/trustwallet/wallet-core) +![SPM](https://img.shields.io/badge/SPM-ready-blue) ![Cocoapods](https://img.shields.io/cocoapods/v/TrustWalletCore.svg) -![Cocoapods platforms](https://img.shields.io/cocoapods/p/TrustWalletCore.svg) # Documentation @@ -23,7 +24,7 @@ For comprehensive documentation, see [developer.trustwallet.com](https://develop # Supported Blockchains -Wallet Core supports more than **50** blockchains: Bitcoin, Ethereum, Binance Chain, and most major blockchain platforms. +Wallet Core supports more than **60** blockchains: Bitcoin, Ethereum, BNB, Cosmos, Solana, and most major blockchain platforms. The full list is [here](docs/registry.md). # Building @@ -37,33 +38,59 @@ If you want to use wallet core in your project follow these instructions. ## Android -Future Android releases will be hosted on [GitHub packages](https://github.com/trustwallet/wallet-core/packages/700258), please checkout [this guide](https://docs.github.com/en/packages/guides/configuring-gradle-for-use-with-github-packages#installing-a-package) for more details. +Android releases are hosted on [GitHub packages](https://github.com/trustwallet/wallet-core/packages/700258), you need to add GitHub access token to install it. Please checkout [this installation guide](https://developer.trustwallet.com/wallet-core/integration-guide/android-guide#adding-library-dependency) or `build.gradle` from our [android sample](https://github.com/trustwallet/wallet-core/blob/master/samples/android/build.gradle) -Add this dependency to build.gradle and run `gradle install` +Don't forget replacing the version in the code with latest: ![GitHub release (latest by date)](https://img.shields.io/github/v/release/trustwallet/wallet-core) -```groovy -plugins { - id 'maven' -} +## iOS + +We currently support Swift Package Manager and CocoaPods (will discontinue in the future). + +### SPM -dependencies { - implementation 'com.trustwallet:wallet-core:x.y.z' -} +Download latest `Package.swift` from [GitHub Releases](https://github.com/trustwallet/wallet-core/releases) and put it in a local `WalletCore` folder. + +Add this line to the `dependencies` parameter in your `Package.swift`: + +```swift +.package(name: "WalletCore", path: "../WalletCore"), ``` -Replace x.y.z with latest version: -![GitHub release (latest by date)](https://img.shields.io/github/v/release/trustwallet/wallet-core) -## iOS +Or add remote url + `master` branch, it points to recent (not always latest) binary release. + +```swift +.package(name: "WalletCore", url: "https://github.com/trustwallet/wallet-core", .branchItem("master")), +``` -We currently support only CocoaPods. Add this line to your Podfile and run `pod install`: +Then add libraries to target's `dependencies`: + +```swift +.product(name: "WalletCore", package: "WalletCore"), +.product(name: "SwiftProtobuf", package: "WalletCore"), +``` + +### CocoaPods + +Add this line to your Podfile and run `pod install`: ```ruby pod 'TrustWalletCore' ``` +## NPM (beta) + +```js +npm install @trustwallet/wallet-core +``` + +## Go (beta) + +Please check out the [Go integration sample](https://github.com/trustwallet/wallet-core/tree/master/samples/go). + + # Projects -Projects using Trust Wallet Core. Add yours too! +Projects using Trust Wallet Core. Add yours too! [Trust Wallet](https://trustwallet.com) @@ -72,7 +99,15 @@ Projects using Trust Wallet Core. Add yours too! | [crypto.com](https://crypto.com) | [Alice](https://www.alicedapp.com/) | [Frontier](https://frontier.xyz/) +| [Tokenary](https://tokenary.io/) + +# Community + +There are a few community-maintained projects that extend Wallet Core to some additional platforms and languages. Note this is not an endorsement, please do your own research before using them: +- Flutter binding https://github.com/weishirongzhen/flutter_trust_wallet_core +- Python binding https://github.com/phuang/wallet-core-python +- Wallet Core on Windows https://github.com/kaetemi/wallet-core-windows # Contributing diff --git a/WalletCore.podspec b/WalletCore.podspec index 828e43ca136..42fe7431537 100644 --- a/WalletCore.podspec +++ b/WalletCore.podspec @@ -29,7 +29,7 @@ Pod::Spec.new do |s| end s.subspec 'Core' do |ss| - protobuf_source_dir = 'build/local/src/protobuf/protobuf-3.14.0' + protobuf_source_dir = 'build/local/src/protobuf/protobuf-3.19.2' include_dir = 'build/local/include' ss.source_files = 'src/**/*.{c,cc,cpp,h}', @@ -57,11 +57,15 @@ Pod::Spec.new do |s| "#{protobuf_source_dir}/src/google/protobuf/extension_set_heavy.cc", "#{protobuf_source_dir}/src/google/protobuf/field_mask.pb.cc", "#{protobuf_source_dir}/src/google/protobuf/generated_enum_util.cc", + "#{protobuf_source_dir}/src/google/protobuf/generated_message_bases.cc", "#{protobuf_source_dir}/src/google/protobuf/generated_message_reflection.cc", "#{protobuf_source_dir}/src/google/protobuf/generated_message_table_driven.cc", "#{protobuf_source_dir}/src/google/protobuf/generated_message_table_driven_lite.cc", + "#{protobuf_source_dir}/src/google/protobuf/generated_message_tctable_full.cc", + "#{protobuf_source_dir}/src/google/protobuf/generated_message_tctable_lite.cc", "#{protobuf_source_dir}/src/google/protobuf/generated_message_util.cc", "#{protobuf_source_dir}/src/google/protobuf/implicit_weak_message.cc", + "#{protobuf_source_dir}/src/google/protobuf/inlined_string_field.cc", "#{protobuf_source_dir}/src/google/protobuf/io/coded_stream.cc", "#{protobuf_source_dir}/src/google/protobuf/io/gzip_stream.cc", "#{protobuf_source_dir}/src/google/protobuf/io/io_win32.cc", @@ -78,6 +82,7 @@ Pod::Spec.new do |s| "#{protobuf_source_dir}/src/google/protobuf/parse_context.cc", "#{protobuf_source_dir}/src/google/protobuf/reflection_ops.cc", "#{protobuf_source_dir}/src/google/protobuf/repeated_field.cc", + "#{protobuf_source_dir}/src/google/protobuf/repeated_ptr_field.cc", "#{protobuf_source_dir}/src/google/protobuf/service.cc", "#{protobuf_source_dir}/src/google/protobuf/source_context.pb.cc", "#{protobuf_source_dir}/src/google/protobuf/struct.pb.cc", @@ -111,7 +116,6 @@ Pod::Spec.new do |s| "#{protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectsource.cc", "#{protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectwriter.cc", "#{protobuf_source_dir}/src/google/protobuf/util/internal/type_info.cc", - "#{protobuf_source_dir}/src/google/protobuf/util/internal/type_info_test_helper.cc", "#{protobuf_source_dir}/src/google/protobuf/util/internal/utility.cc", "#{protobuf_source_dir}/src/google/protobuf/util/json_util.cc", "#{protobuf_source_dir}/src/google/protobuf/util/message_differencer.cc", @@ -120,6 +124,7 @@ Pod::Spec.new do |s| "#{protobuf_source_dir}/src/google/protobuf/wire_format.cc", "#{protobuf_source_dir}/src/google/protobuf/wire_format_lite.cc", "#{protobuf_source_dir}/src/google/protobuf/wrappers.pb.cc" + ss.exclude_files = 'trezor-crypto/include/TrezorCrypto/base58.h', 'trezor-crypto/crypto/monero', diff --git a/android/app/build.gradle b/android/app/build.gradle index 22d53172334..f38c9acc235 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -2,12 +2,11 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' android { - compileSdkVersion 30 - ndkVersion '21.2.6472646' + compileSdkVersion 32 + ndkVersion '23.1.7779620' defaultConfig { applicationId "com.trustwallet.core.app" minSdkVersion 23 - targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -21,7 +20,7 @@ android { minifyEnabled false // limit platforms built for testing ndk { - abiFilters 'x86' + abiFilters 'x86', 'arm64-v8a' } } } @@ -43,7 +42,7 @@ dependencies { androidTestImplementation 'androidx.test:runner:1.3.0' androidTestImplementation 'android.arch.core:core-testing:1.1.1' - implementation 'io.grpc:grpc-protobuf:1.34.0' + implementation 'com.google.protobuf:protobuf-javalite:3.21.2' } repositories { mavenCentral() diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 87dd5ae2cc9..98f69f55d1b 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -40,8 +40,10 @@ class CoinAddressDerivationTests { CALLISTO -> assertEquals("0x3E6FFC80745E6669135a76F4A7ce6BCF02436e04", address) DASH -> assertEquals("XqHiz8EXYbTAtBEYs4pWTHh7ipEDQcNQeT", address) DIGIBYTE -> assertEquals("dgb1qtjgmerfqwdffyf8ghcrkgy52cghsqptynmyswu", address) - ETHEREUM, SMARTCHAIN, POLYGON, OPTIMISM, ARBITRUM, ECOCHAIN, AVALANCHECCHAIN, XDAI, - FANTOM, RONIN -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) + ETHEREUM, SMARTCHAIN, POLYGON, OPTIMISM, ZKSYNC, ARBITRUM, ECOCHAIN, AVALANCHECCHAIN, XDAI, + FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, + AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) + RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) ETHEREUMCLASSIC -> assertEquals("0x078bA3228F3E6C08bEEac9A005de0b7e7089aD1c", address) GOCHAIN -> assertEquals("0x5940ce4A14210d4Ccd0ac206CE92F21828016aC2", address) GROESTLCOIN -> assertEquals("grs1qexwmshts5pdpeqglkl39zyl6693tmfwp0cue4j", address) @@ -57,7 +59,7 @@ class CoinAddressDerivationTests { VECHAIN -> assertEquals("0x1a553275dF34195eAf23942CB7328AcF9d48c160", address) WANCHAIN -> assertEquals("0xD5ca90b928279FE5D06144136a25DeD90127aC15", address) ZCASH -> assertEquals("t1YYnByMzdGhQv3W3rnjHMrJs6HH4Y231gy", address) - ZCOIN -> assertEquals("aEd5XFChyXobvEics2ppAqgK3Bgusjxtik", address) + FIRO -> assertEquals("aEd5XFChyXobvEics2ppAqgK3Bgusjxtik", address) NIMIQ -> assertEquals("NQ76 7AVR EHDA N05U X7SY XB14 XJU7 8ERV GM6H", address) STELLAR -> assertEquals("GA3H6I4C5XUBYGVB66KXR27JV5KS3APSTKRUWOIXZ5MVWZKVTLXWKZ2P", address) AION -> assertEquals("0xa0629f34c9ea4757ad0b275628d4d02e3db6c9009ba2ceeba76a5b55fb2ca42e", address) @@ -79,7 +81,7 @@ class CoinAddressDerivationTests { RAVENCOIN -> assertEquals("RHoCwPc2FCQqwToYnSiAb3SrCET4zEHsbS", address) WAVES -> assertEquals("3P63vkaHhyE9pPv9EfsjwGKqmZYcCRHys4n", address) AETERNITY -> assertEquals("ak_QDHJSfvHG9sDHBobaWt2TAGhuhipYjEqZEH34bWugpJfJc3GN", address) - TERRA -> assertEquals("terra1rh402g98t7sly8trzqw5cyracntlep6qe3smug", address) + TERRA, TERRAV2 -> assertEquals("terra1rh402g98t7sly8trzqw5cyracntlep6qe3smug", address) MONACOIN -> assertEquals("M9xFZzZdZhCDxpx42cM8bQHnLwaeX1aNja", address) FIO -> assertEquals("FIO7MN1LuSfFgrbVHmrt9cVa2FYAs857Ppr9dzvEXoD1miKSxm3n3", address) HARMONY -> assertEquals("one12fk20wmvgypdkn59n4hq8e3aa5899xfx4vsu09", address) @@ -88,7 +90,7 @@ class CoinAddressDerivationTests { KUSAMA -> assertEquals("G9xV2EatmrjRC1FLPexc3ddqNRRzCsAdURU8RFiAAJX6ppY", address) POLKADOT -> assertEquals("13nN6BGAoJwd7Nw1XxeBCx5YcBXuYnL94Mh7i3xBprqVSsFk", address) KAVA -> assertEquals("kava1drpa0x9ptz0fql3frv562rcrhj2nstuz3pas87", address) - CARDANO -> assertEquals("addr1snpa4z7ntyfszv7ckquprdw75w4qjqh0qmya9jtkpxxlzxghlqyvv7l0yjamh8fxraw06p3ua8sj2g2gv98v4849s43t9g2999kquuu5egnprk", address) + CARDANO -> assertEquals("addr1qyr8jjfnypp95eq74aqzn7ss687ehxclgj7mu6gratmg3mul2040vt35dypp042awzsjk5xm3zr3zm5qh7454uwdv08s84ray2", address) NEO -> assertEquals("AT6w7PJvwPcSqHvtbNBY2aHPDv12eW5Uuf", address) FILECOIN -> assertEquals("f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori", address) ELROND -> assertEquals("erd1jfcy8aeru6vlx4fe6h3pc3vlpe2cnnur5zetxdhp879yagq7vqvs8na4f8", address) @@ -98,6 +100,11 @@ class CoinAddressDerivationTests { THORCHAIN -> assertEquals("thor1c8jd7ad9pcw4k3wkuqlkz4auv95mldr2kyhc65", address) BLUZELLE -> assertEquals("bluzelle1xccvees6ev4wm2r49rc6ptulsdxa8x8jfpmund", address) CRYPTOORG -> assertEquals("cro16fdf785ejm00jf9a24d23pzqzjh2h05klxjwu8", address) - CELO -> assertEquals("0xea1ac53e7Ccb5b47cdE341C118615Ef1862e3CF5", address) + OSMOSIS -> assertEquals("osmo142j9u5eaduzd7faumygud6ruhdwme98qclefqp", address) + ECASH -> assertEquals("ecash:qpelrdn7a0hcucjlf9ascz3lkxv7r3rffgzn6x5377", address) + NATIVEEVMOS -> assertEquals("evmos13u6g7vqgw074mgmf2ze2cadzvkz9snlwstd20d", address) + NERVOS -> assertEquals("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3", address) + EVERSCALE -> assertEquals("0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04", address) + APTOS -> assertEquals("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt index 7d4fe6bda0a..f8fc2f3a5bf 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/TestCoinType.kt @@ -3,14 +3,16 @@ package com.trustwallet.core.app.blockchains import wallet.core.jni.CoinType import wallet.core.jni.Curve import wallet.core.jni.Purpose +import wallet.core.jni.Derivation + import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse import org.junit.Test + class TestCoinType { init { - System.loadLibrary("TrustWalletCore"); + System.loadLibrary("TrustWalletCore") } @Test @@ -43,4 +45,14 @@ class TestCoinType { fun testCoinCurve() { assertEquals(Curve.SECP256K1, CoinType.BITCOIN.curve()) } + + @Test + fun testDerivationPath() { + var res = CoinType.createFromValue(CoinType.BITCOIN.value()).derivationPath().toString() + assertEquals(res, "m/84'/0'/0'/0/0") + res = CoinType.createFromValue(CoinType.BITCOIN.value()).derivationPathWithDerivation(Derivation.BITCOINLEGACY).toString() + assertEquals(res, "m/44'/0'/0'/0/0") + res = CoinType.createFromValue(CoinType.SOLANA.value()).derivationPathWithDerivation(Derivation.SOLANASOLANA).toString() + assertEquals(res, "m/44'/501'/0'/0'") + } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt new file mode 100644 index 00000000000..180617ff5b1 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosAddress.kt @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.aptos + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestAptosAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val any = AnyAddress("0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108", CoinType.APTOS) + assertEquals(any.coin(), CoinType.APTOS) + assertEquals(any.description(), "0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108") + + assertFalse(AnyAddress.isValid("0xMQqpqMQgCBuiPkoXfgZZsJvuzCeI1zc00z6vHJj4", CoinType.APTOS)) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt new file mode 100644 index 00000000000..6d52a4c9d1d --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/aptos/TestAptosSigner.kt @@ -0,0 +1,58 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.aptos + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHexBytesInByteString +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.CoinType +import wallet.core.jni.proto.Aptos + +class TestAptosSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun AptosTransactionSigning() { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet + val key = + "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec".toHexBytesInByteString() + + val transfer = Aptos.TransferMessage.newBuilder().setAmount(1000) + .setTo("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30").build() + val signingInput = Aptos.SigningInput.newBuilder().setChainId(33) + .setSender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30") + .setSequenceNumber(99) + .setGasUnitPrice(100) + .setMaxGasAmount(3296766) + .setExpirationTimestampSecs(3664390082) + .setTransfer(transfer) + .setPrivateKey(key) + .build() + + val result = AnySigner.sign(signingInput, CoinType.APTOS, Aptos.SigningOutput.parser()) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.rawTxn.toByteArray())), + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021" + ) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.authenticator.signature.toByteArray())), + "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01" + ) + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.encoded.toByteArray())), + "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01" + ) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainSigner.kt index 0e25638c1de..662773bba9c 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bandchain/TestBandChainSigner.kt @@ -31,7 +31,7 @@ class TestBandChainSigner { val from = AnyAddress(publicKey, BANDCHAIN).description() val txAmount = Cosmos.Amount.newBuilder().apply { - amount = 1 + amount = "1" denom = "uband" }.build() @@ -46,7 +46,7 @@ class TestBandChainSigner { }.build() val feeAmount = Cosmos.Amount.newBuilder().apply { - amount = 200 + amount = "200" denom = "uband" }.build() diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleSigner.kt index 033ee1eb707..ba4d7e99238 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/bluzelle/TestBluzelleSigner.kt @@ -36,7 +36,7 @@ class TestBluzelleSigner { val from = AnyAddress(publicKey, BLUZELLE).description() val txAmount = Cosmos.Amount.newBuilder().apply { - amount = 1 + amount = "1" denom = "ubnt" }.build() @@ -51,7 +51,7 @@ class TestBluzelleSigner { }.build() val feeAmount = Cosmos.Amount.newBuilder().apply { - amount = 1000 + amount = "1000" denom = "ubnt" }.build() diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoAddress.kt index c71dbe93ab6..6ac0f1c4d63 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoAddress.kt @@ -20,12 +20,12 @@ class TestCardanoAddress { @Test fun testAddress() { - val key = PrivateKey("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4".toHexByteArray()) - val pubkey = key.publicKeyEd25519Extended + val key = PrivateKey("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4639aadd8b6499ae39b78018b79255fbd8f585cbda9cbb9e907a72af86afb7a05d41a57c2dec9a6a19d6bf3b1fa784f334f3a0048d25ccb7b78a7b44066f9ba7bed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a".toHexByteArray()) + val pubkey = key.publicKeyEd25519Cardano val address = AnyAddress(pubkey, CoinType.CARDANO) - val expected = AnyAddress("addr1s3tl64970vuthz2j0qkz7kd2ya5j3fxuhdnv333vu38e6c37e4dq80ek4raf7hs3adag2tzpuxz7895a2x8xde5f8jqa8lrjyuqfj5k50pm668", CoinType.CARDANO) + val expected = AnyAddress("addr1qx4z6twzknkkux0hhp0kq6hvdfutczp56g56y5em8r8mgvxalp7nkkk25vuspleke2zltaetmlwrfxv7t049cq9jmwjswmfw6t", CoinType.CARDANO) - assertEquals(pubkey.data().toHex(), "0x57fd54be7b38bb8952782c2f59aa276928a4dcbb66c8c62ce44f9d623ecd5a03bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4") + assertEquals(pubkey.data().toHex(), "0x57fd54be7b38bb8952782c2f59aa276928a4dcbb66c8c62ce44f9d623ecd5a03bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d483e4b09a4ba73725625c346870e109bd335831f2ba0b72b09bfb44040f1016caed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a") assertEquals(address.description(), expected.description()) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoSigning.kt new file mode 100644 index 00000000000..e76aa67b785 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cardano/TestCardanoSigning.kt @@ -0,0 +1,264 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.cardano + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.* +import wallet.core.jni.Cardano.getStakingAddress +import wallet.core.jni.CoinType.CARDANO +import wallet.core.jni.proto.Cardano +import wallet.core.jni.proto.Cardano.SigningInput +import wallet.core.jni.proto.Cardano.SigningOutput +import wallet.core.jni.proto.Common.SigningError + + +class TestCardanoSigning { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testSignTransfer1() { + val message = Cardano.Transfer.newBuilder() + .setToAddress("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5") + .setChangeAddress("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23") + .setAmount(7_000_000) + .build() + val input = Cardano.SigningInput.newBuilder() + .setTransferMessage(message) + .setTtl(53333333) + + val privKey = Numeric.hexStringToByteArray("089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e") + input.addPrivateKey(ByteString.copyFrom(privKey)) + + val outpoint1 = Cardano.OutPoint.newBuilder() + .setTxHash(ByteString.copyFrom(Numeric.hexStringToByteArray("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"))) + .setOutputIndex(1) + .build() + val utxo1 = Cardano.TxInput.newBuilder() + .setOutPoint(outpoint1) + .setAddress("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23") + .setAmount(1_500_000) + .build() + input.addUtxos(utxo1) + + val outpoint2 = Cardano.OutPoint.newBuilder() + .setTxHash(ByteString.copyFrom(Numeric.hexStringToByteArray("554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0"))) + .setOutputIndex(0) + .build() + val utxo2 = Cardano.TxInput.newBuilder() + .setOutPoint(outpoint2) + .setAddress("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23") + .setAmount(6_500_000) + .build() + input.addUtxos(utxo2) + + val output = AnySigner.sign(input.build(), CARDANO, SigningOutput.parser()) + assertEquals(output.error, SigningError.OK) + + val encoded = output.encoded + assertEquals(Numeric.toHexString(encoded.toByteArray()), + "0x83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a006acfc082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a000ca96c021a000298d4031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058407cf591599852b5f5e007fdc241062405c47e519266c0d884b0767c1d4f5eacce00db035998e53ed10ca4ba5ce4aac8693798089717ce6cf4415f345cc764200ef6"); + + val txid = output.txId + assertEquals(Numeric.toHexString(txid.toByteArray()), "0x9b5b15e133cd73ccaa85307d2986aebc846505118a2eb4e6111e6b4b67d1f389"); + } + + @Test + fun testSignTransferToken1() { + val toToken = Cardano.TokenAmount.newBuilder() + .setPolicyId("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77") + .setAssetName("SUNDAE") + .setAmount(ByteString.copyFrom(Numeric.hexStringToByteArray("01312d00"))) // 20000000 + .build() + val toTokenBundle = Cardano.TokenBundle.newBuilder() + .addToken(toToken) + .build() + + // check min ADA amount, set it + val minAmount = wallet.core.jni.Cardano.minAdaAmount(toTokenBundle.toByteArray()) + assertEquals(minAmount, 1_444_443) + + val message = Cardano.Transfer.newBuilder() + .setToAddress("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5") + .setChangeAddress("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq") + .setAmount(minAmount) + .setUseMaxAmount(false) + .setTokenAmount(toTokenBundle) + .build() + val input = Cardano.SigningInput.newBuilder() + .setTransferMessage(message) + .setTtl(53333333) + + val privKey = Numeric.hexStringToByteArray("089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e") + input.addPrivateKey(ByteString.copyFrom(privKey)) + + val outpoint1 = Cardano.OutPoint.newBuilder() + .setTxHash(ByteString.copyFrom(Numeric.hexStringToByteArray("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"))) + .setOutputIndex(1) + .build() + val utxo1 = Cardano.TxInput.newBuilder() + .setOutPoint(outpoint1) + .setAddress("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23") + .setAmount(8_051_373) + val token3 = Cardano.TokenAmount.newBuilder() + .setPolicyId("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77") + .setAssetName("CUBY") + .setAmount(ByteString.copyFrom(Numeric.hexStringToByteArray("2dc6c0"))) // 3000000 + .build() + utxo1.addTokenAmount(token3) + input.addUtxos(utxo1.build()) + + val outpoint2 = Cardano.OutPoint.newBuilder() + .setTxHash(ByteString.copyFrom(Numeric.hexStringToByteArray("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"))) + .setOutputIndex(2) + .build() + val utxo2 = Cardano.TxInput.newBuilder() + .setOutPoint(outpoint2) + .setAddress("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23") + .setAmount(2_000_000) + val token1 = Cardano.TokenAmount.newBuilder() + .setPolicyId("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77") + .setAssetName("SUNDAE") + .setAmount(ByteString.copyFrom(Numeric.hexStringToByteArray("04d3e8d9"))) // 80996569 + .build() + utxo2.addTokenAmount(token1) + val token2 = Cardano.TokenAmount.newBuilder() + .setPolicyId("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77") + .setAssetName("CUBY") + .setAmount(ByteString.copyFrom(Numeric.hexStringToByteArray("1e8480"))) // 2000000 + .build() + utxo2.addTokenAmount(token2) + input.addUtxos(utxo2.build()) + + val output = AnySigner.sign(input.build(), CARDANO, SigningOutput.parser()) + assertEquals(output.error, SigningError.OK) + + val encoded = output.encoded + assertEquals(Numeric.toHexString(encoded.toByteArray()), + "0x83a40082825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76702018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd5821a00160a5ba1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a01312d00825839018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468821a0080aac9a1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a244435542591a004c4b404653554e4441451a03a2bbd9021a0002aa09031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840d90dcfbd190cbe59c42094e59eeb49b3de9d80a85b786cc311f932c5c9302d1c8c6c577b22aa70ff7955c139c700ea918f8cb425c3ba43a27980e1d238e4e908f6"); + + val txid = output.txId + assertEquals(Numeric.toHexString(txid.toByteArray()), "0x201c537693b005b64a0f0528e366ec67a84be0119ed4363b547f141f2a7770c2"); + } + + @Test + fun testSignStakingRegisterAndDelegate() { + val ownAddress = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23" + val stakingAddress = getStakingAddress(ownAddress) + val poolIdNufi = Numeric.hexStringToByteArray("7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6") + + val message = Cardano.Transfer.newBuilder() + .setToAddress(ownAddress) + .setChangeAddress(ownAddress) + .setAmount(4_000_000) // not relevant if we use MaxAmount + .setUseMaxAmount(true) + .build() + // Register staking key, 2 ADA desposit + val register = Cardano.RegisterStakingKey.newBuilder() + .setStakingAddress(stakingAddress) + .setDepositAmount(2_000_000) + // Delegate + val delegate = Cardano.Delegate.newBuilder() + .setStakingAddress(stakingAddress) + .setPoolId(ByteString.copyFrom(poolIdNufi)) + .setDepositAmount(0) + val input = Cardano.SigningInput.newBuilder() + .setTransferMessage(message) + .setRegisterStakingKey(register) + .setDelegate(delegate) + .setTtl(69885081) + + val privKey = Numeric.hexStringToByteArray("089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e") + input.addPrivateKey(ByteString.copyFrom(privKey)) + + val outpoint1 = Cardano.OutPoint.newBuilder() + .setTxHash(ByteString.copyFrom(Numeric.hexStringToByteArray("9b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e"))) + .setOutputIndex(0) + .build() + val utxo1 = Cardano.TxInput.newBuilder() + .setOutPoint(outpoint1) + .setAddress(ownAddress) + .setAmount(4_000_000) + .build() + input.addUtxos(utxo1) + + val outpoint2 = Cardano.OutPoint.newBuilder() + .setTxHash(ByteString.copyFrom(Numeric.hexStringToByteArray("9b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e"))) + .setOutputIndex(1) + .build() + val utxo2 = Cardano.TxInput.newBuilder() + .setOutPoint(outpoint2) + .setAddress(ownAddress) + .setAmount(26651312) + .build() + input.addUtxos(utxo2) + + val output = AnySigner.sign(input.build(), CARDANO, SigningOutput.parser()) + assertEquals(output.error, SigningError.OK) + + val encoded = output.encoded + assertEquals(Numeric.toHexString(encoded.toByteArray()), + "0x83a500828258209b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e008258209b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e01018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a01b27ef5021a0002b03b031a042a5c99048282008200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b83028200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b581c7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6a100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840677c901704be027d9a1734e8aa06f0700009476fa252baaae0de280331746a320a61456d842d948ea5c0e204fc36f3bd04c88ca7ee3d657d5a38014243c37c07825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e0693258401fa21bdc62b85ca217bf08cbacdeba2fadaf33dc09ee3af9cc25b40f24822a1a42cfbc03585cc31a370ef75aaec4d25db6edcf329e40a4e725ec8718c94f220af6"); + + val txid = output.txId + assertEquals(Numeric.toHexString(txid.toByteArray()), "0x96a781fd6481b6a7fd3926da110265e8c44b53947b81daa84da5b148825d02aa"); + } + + @Test + fun testSignStakingWithdraw() { + val ownAddress = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23" + val stakingAddress = getStakingAddress(ownAddress) + + val message = Cardano.Transfer.newBuilder() + .setToAddress(ownAddress) + .setChangeAddress(ownAddress) + .setAmount(6_000_000) // not relevant if we use MaxAmount + .setUseMaxAmount(true) + .build() + // Withdraw available amount + val withdraw = Cardano.Withdraw.newBuilder() + .setStakingAddress(stakingAddress) + .setWithdrawAmount(3468) + val input = Cardano.SigningInput.newBuilder() + .setTransferMessage(message) + .setWithdraw(withdraw) + .setTtl(71678326) + + val privKey = Numeric.hexStringToByteArray("089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e") + input.addPrivateKey(ByteString.copyFrom(privKey)) + + val outpoint1 = Cardano.OutPoint.newBuilder() + .setTxHash(ByteString.copyFrom(Numeric.hexStringToByteArray("7dfd2c579794314b1f84efc9db932a098e440ccefb874945591f1d4e85a9152a"))) + .setOutputIndex(0) + .build() + val utxo1 = Cardano.TxInput.newBuilder() + .setOutPoint(outpoint1) + .setAddress(ownAddress) + .setAmount(6_305_913) + .build() + input.addUtxos(utxo1) + + val output = AnySigner.sign(input.build(), CARDANO, SigningOutput.parser()) + assertEquals(output.error, SigningError.OK) + + val encoded = output.encoded + assertEquals(Numeric.toHexString(encoded.toByteArray()), + "0x83a500818258207dfd2c579794314b1f84efc9db932a098e440ccefb874945591f1d4e85a9152a00018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a005da6ff021a00029f06031a0445b97605a1581de1df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b190d8ca100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058401ebaca2876fd17122404912a2558a98109cdf0f990a938d2917fa2c3b8c4e55e18a2cbabfa82fff03fa0d7ab8b88ca01ed18e42af3bfc4cda7f423a3aa30c00b825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e069325840777f04fa8f083fe562aecf78898aaaaac36e2cc6ca962f6ffb01e84a421cae1860496db79b2c5fb2879524c3d5121060b9ea1e693336230c6e5338e14c4c3303f6"); + + val txid = output.txId + assertEquals(Numeric.toHexString(txid.toByteArray()), "0x6dcf3956232953fc25b8355fb1ded1e912b5802090fd21434d789087d6329683"); + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cosmos/TestCosmosTransactions.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cosmos/TestCosmosTransactions.kt index ff0ed3a94c9..7267138e1da 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cosmos/TestCosmosTransactions.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cosmos/TestCosmosTransactions.kt @@ -1,14 +1,16 @@ -package com.trustwalval.core.app.blockchains.cosmos +package com.trustwallet.core.app.blockchains.cosmos import android.util.Log import com.google.protobuf.ByteString import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHex import org.junit.Assert.assertEquals import org.junit.Test import wallet.core.jni.* import wallet.core.jni.CoinType.COSMOS import wallet.core.jni.proto.Cosmos import wallet.core.jni.proto.Cosmos.SigningOutput +import wallet.core.jni.proto.Cosmos.SigningMode import wallet.core.java.AnySigner class TestCosmosTransactions { @@ -17,6 +19,104 @@ class TestCosmosTransactions { System.loadLibrary("TrustWalletCore") } + @Test + fun testAuthStakingTransaction() { + val key = + PrivateKey("c7764249cdf77f8f1d840fa8af431579e5e41cf1af937e1e23afa22f3f4f0ccc".toHexByteArray()) + + val stakeAuth = Cosmos.Message.StakeAuthorization.newBuilder().apply { + allowList = Cosmos.Message.StakeAuthorization.Validators.newBuilder().apply { + addAddress("cosmosvaloper1gjtvly9lel6zskvwtvlg5vhwpu9c9waw7sxzwx") + }.build() + authorizationType = Cosmos.Message.AuthorizationType.DELEGATE + }.build() + + val authStakingMsg = Cosmos.Message.AuthGrant.newBuilder().apply { + grantee = "cosmos1fs7lu28hx5m9akm7rp0c2422cn8r2f7gurujhf" + granter = "cosmos13k0q0l7lg2kr32kvt7ly236ppldy8v9dzwh3gd" + grantStake = stakeAuth + expiration = 1692309600 + }.build() + + val message = Cosmos.Message.newBuilder().apply { + authGrant = authStakingMsg + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "2418" + denom = "uatom" + }.build() + + val cosmosFee = Cosmos.Fee.newBuilder().apply { + gas = 96681 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf + accountNumber = 1290826 + chainId = "cosmoshub-4" + memo = "" + sequence = 5 + fee = cosmosFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, COSMOS, SigningOutput.parser()) + + assertEquals( + output.serialized, + "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjoSNgo0Y29zbW9zdmFsb3BlcjFnanR2bHk5bGVsNnpza3Z3dHZsZzV2aHdwdTljOXdhdzdzeHp3eCABEgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQIFyfuijGKf87Hz61ZqxasfLI1PZnNge4RDq/tRyB/tZI6p80iGRqHecoV6+84EQkc9GTlNRQOSlApRCsivT9XI=\"}" + ) + assertEquals(output.error, "") + } + + @Test + fun testRemoveAuthStakingTransaction() { + val key = + PrivateKey("c7764249cdf77f8f1d840fa8af431579e5e41cf1af937e1e23afa22f3f4f0ccc".toHexByteArray()) + + val removeAuthStakingMsg = Cosmos.Message.AuthRevoke.newBuilder().apply { + grantee = "cosmos1fs7lu28hx5m9akm7rp0c2422cn8r2f7gurujhf" + granter = "cosmos13k0q0l7lg2kr32kvt7ly236ppldy8v9dzwh3gd" + msgTypeUrl = "/cosmos.staking.v1beta1.MsgDelegate" + }.build() + + val message = Cosmos.Message.newBuilder().apply { + authRevoke = removeAuthStakingMsg + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "2194" + denom = "uatom" + }.build() + + val cosmosFee = Cosmos.Fee.newBuilder().apply { + gas = 87735 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf + accountNumber = 1290826 + chainId = "cosmoshub-4" + memo = "" + sequence = 4 + fee = cosmosFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, COSMOS, SigningOutput.parser()) + + assertEquals( + output.serialized, + "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CqoBCqcBCh8vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnUmV2b2tlEoMBCi1jb3Ntb3MxM2swcTBsN2xnMmtyMzJrdnQ3bHkyMzZwcGxkeTh2OWR6d2gzZ2QSLWNvc21vczFmczdsdTI4aHg1bTlha203cnAwYzI0MjJjbjhyMmY3Z3VydWpoZhojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAQSEwoNCgV1YXRvbRIEMjE5NBC3rQUaQI7K+W7MMBoD6FbFZxRBqs9VTjErztjWTy57+fvrLaTCIZ+eBs7CuaKqfUZdSN8otjubSHVTQID3k9DpPAX0yDo=\"}" + ) + assertEquals(output.error, "") + } + @Test fun testSigningTransaction() { val key = @@ -25,7 +125,7 @@ class TestCosmosTransactions { val from = AnyAddress(publicKey, COSMOS).description() val txAmount = Cosmos.Amount.newBuilder().apply { - amount = 1 + amount = "1" denom = "muon" }.build() @@ -40,7 +140,7 @@ class TestCosmosTransactions { }.build() val feeAmount = Cosmos.Amount.newBuilder().apply { - amount = 200 + amount = "200" denom = "muon" }.build() @@ -50,6 +150,7 @@ class TestCosmosTransactions { }.build() val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf accountNumber = 1037 chainId = "gaia-13003" memo = "" @@ -60,11 +161,12 @@ class TestCosmosTransactions { }.build() val output = AnySigner.sign(signingInput, COSMOS, SigningOutput.parser()) - val jsonPayload = output.json - - val expectedJsonPayload = """{"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}}""" - assertEquals(expectedJsonPayload, jsonPayload) + assertEquals( + output.serialized, + "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATESZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAgSEQoLCgRtdW9uEgMyMDAQwJoMGkD54fQAFlekIAnE62hZYl0uQelh/HLv0oQpCciY5Dn8H1SZFuTsrGdu41PH1Uxa4woptCELi/8Ov9yzdeEFAC9H\"}" + ) + assertEquals(output.error, "") } @Test @@ -93,8 +195,12 @@ class TestCosmosTransactions { }] } """ - val key = "c9b0a273831931aa4a5f8d1a570d5021dda91d3319bd3819becdaabfb7b44e3b".toHexByteArray() + val key = + "c9b0a273831931aa4a5f8d1a570d5021dda91d3319bd3819becdaabfb7b44e3b".toHexByteArray() val result = AnySigner.signJSON(json, key, COSMOS.value()) - assertEquals(result, """{"mode":"block","tx":{"fee":{"amount":[{"amount":"5000","denom":"uatom"}],"gas":"200000"},"memo":"Testing","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"995000","denom":"uatom"}],"from_address":"cosmos1ufwv9ymhqaal6xz47n0jhzm2wf4empfqvjy575","to_address":"cosmos135qla4294zxarqhhgxsx0sw56yssa3z0f78pm0"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6EsukEXB53GhohQVeDpxtkeH8KQIayd/Co/ApYRYkTm"},"signature":"ULEpUqNzoAnYEx2x22F3ANAiPXquAU9+mqLWoAA/ZOUGTMsdb6vryzsW6AKX2Kqj1pGNdrTcQ58Z09JPyjpgEA=="}]}}""") + assertEquals( + result, + """{"mode":"block","tx":{"fee":{"amount":[{"amount":"5000","denom":"uatom"}],"gas":"200000"},"memo":"Testing","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"995000","denom":"uatom"}],"from_address":"cosmos1ufwv9ymhqaal6xz47n0jhzm2wf4empfqvjy575","to_address":"cosmos135qla4294zxarqhhgxsx0sw56yssa3z0f78pm0"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6EsukEXB53GhohQVeDpxtkeH8KQIayd/Co/ApYRYkTm"},"signature":"ULEpUqNzoAnYEx2x22F3ANAiPXquAU9+mqLWoAA/ZOUGTMsdb6vryzsW6AKX2Kqj1pGNdrTcQ58Z09JPyjpgEA=="}]}}""" + ) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgSigner.kt index 56707c5969c..98c568d9bef 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/cryptoorg/TestCryptoorgSigner.kt @@ -10,6 +10,7 @@ import com.google.protobuf.ByteString import com.trustwallet.core.app.utils.Numeric import com.trustwallet.core.app.utils.toHexByteArray import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHex import com.trustwallet.core.app.utils.toHexBytesInByteString import org.junit.Assert.assertEquals import org.junit.Test @@ -17,6 +18,7 @@ import wallet.core.java.AnySigner import wallet.core.jni.CoinType.CRYPTOORG import wallet.core.jni.proto.Cosmos import wallet.core.jni.proto.Cosmos.SigningOutput +import wallet.core.jni.proto.Cosmos.SigningMode import wallet.core.jni.* class TestCryptoorgSigner { @@ -32,7 +34,7 @@ class TestCryptoorgSigner { val from = AnyAddress(publicKey, CRYPTOORG).description() val txAmount = Cosmos.Amount.newBuilder().apply { - amount = 100000000 + amount = "50000000" denom = "basecro" }.build() @@ -47,7 +49,7 @@ class TestCryptoorgSigner { }.build() val feeAmount = Cosmos.Amount.newBuilder().apply { - amount = 5000 + amount = "5000" denom = "basecro" }.build() @@ -57,19 +59,20 @@ class TestCryptoorgSigner { }.build() val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf accountNumber = 125798 chainId = "crypto-org-chain-mainnet-1" memo = "" - sequence = 0 + sequence = 2 fee = cosmosFee privateKey = ByteString.copyFrom(key.data()) addAllMessages(listOf(message)) }.build() val output = AnySigner.sign(signingInput, CRYPTOORG, SigningOutput.parser()) - val jsonPayload = output.json - val expectedJsonPayload = """{"mode":"block","tx":{"fee":{"amount":[{"amount":"5000","denom":"basecro"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"100000000","denom":"basecro"}],"from_address":"cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0","to_address":"cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A4gxsGFiPn6L5Z2IjHEISkXI0IkwfL9exV3GLB171Wvj"},"signature":"5+5rSFFg0FE9cTklQWQHNktBDJsz7UCnMSgF0t0+gYcrIhEWUyTtibXaHZQbKAAaciJ1BkHXYREjU55VswByVg=="}]}}""" - assertEquals(expectedJsonPayload, jsonPayload) + // https://crypto.org/explorer/tx/BCB213B0A121F0CF11BECCF52475F1C8328D6070F3CFDA9E14C42E6DB30E847E + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CpABCo0BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm0KKmNybzFjdHd0Y3dwZ2tza3k5ODhkaHRoNmpzbHh2ZXVtZ3UwZDQ1emdmMBIqY3JvMXhwYWh5NmM3d2xkeGFjdjZsZDk5aDQzNW1odmZuc3VwMjR2Y3VzGhMKB2Jhc2Vjcm8SCDUwMDAwMDAwEmkKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOIMbBhYj5+i+WdiIxxCEpFyNCJMHy/XsVdxiwde9Vr4xIECgIIARgCEhUKDwoHYmFzZWNybxIENTAwMBDAmgwaQAcxK9xk6r69gmz+1UWaCnYxNuXPXZdp59YcqKPJE5d6fp+IICTBOwd2rs8MiApcf8kNSrbZ6oECxcGQAdxF0SI=\"}") + assertEquals(output.error, "") } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt index 05c17837ddc..e8304e3deeb 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt @@ -18,9 +18,9 @@ class TestElrondAddress { System.loadLibrary("TrustWalletCore") } - private val aliceBech32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" - private var aliceSeedHex = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf" - private var alicePubKeyHex = "0xfd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293" + private var aliceBech32 = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + private var alicePubKeyHex = "0x0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1" + private var aliceSeedHex = "0x413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9" @Test fun testAddressFromPrivateKey() { diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt index ac37e8f520e..a99a949bdb4 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt @@ -21,39 +21,128 @@ class TestElrondSigner { System.loadLibrary("TrustWalletCore") } - val aliceBech32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" - var aliceSeedHex = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf" - var alicePubKeyHex = "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293" - - val bobBech32 = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" - var bobSeedHex = "e3a3a3d1ac40d42d8fd4c569a9749b65a1250dd3d10b6f4e438297662ea4850e" - var bobPubKeyHex = "c70cf50b238372fffaf7b7c5723b06b57859d424a2da621bcc1b2f317543aa36" - + private var aliceBech32 = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + private var aliceSeedHex = "0x413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9" + private var bobBech32 = "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" + @Test - fun signTransaction() { - val transaction = Elrond.TransactionMessage.newBuilder() - .setNonce(0) - .setValue("0") + fun signGenericAction() { + val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) + + val accounts = Elrond.Accounts.newBuilder() + .setSenderNonce(7) .setSender(aliceBech32) .setReceiver(bobBech32) + .build() + + val genericAction = Elrond.GenericAction.newBuilder() + .setAccounts(accounts) + .setValue("0") + .setData("foo") + .setVersion(1) + .build() + + val signingInput = Elrond.SigningInput.newBuilder() + .setGenericAction(genericAction) .setGasPrice(1000000000) .setGasLimit(50000) - .setData("foo") .setChainId("1") - .setVersion(1) + .setPrivateKey(privateKey) + .build() + + val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val expectedSignature = "e8647dae8b16e034d518a1a860c6a6c38d16192d0f1362833e62424f424e5da660770dff45f4b951d9cc58bfb9d14559c977d443449bfc4b8783ff9c84065700" + + assertEquals(expectedSignature, output.signature) + assertEquals("""{"nonce":7,"value":"0","receiver":"$bobBech32","sender":"$aliceBech32","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"$expectedSignature"}""", output.encoded) + } + + @Test + fun signEGLDTransfer() { + val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) + + val accounts = Elrond.Accounts.newBuilder() + .setSenderNonce(7) + .setSender(aliceBech32) + .setReceiver(bobBech32) + .build() + + val transfer = Elrond.EGLDTransfer.newBuilder() + .setAccounts(accounts) + .setAmount("1000000000000000000") + .build() + + val signingInput = Elrond.SigningInput.newBuilder() + .setEgldTransfer(transfer) + .setChainId("1") + .setPrivateKey(privateKey) + .build() + + val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val expectedSignature = "7e1c4c63b88ea72dcf7855a54463b1a424eb357ac3feb4345221e512ce07c7a50afb6d7aec6f480b554e32cf2037082f3bc17263d1394af1f3ef240be53c930b" + + assertEquals(expectedSignature, output.signature) + assertEquals("""{"nonce":7,"value":"1000000000000000000","receiver":"$bobBech32","sender":"$aliceBech32","gasPrice":1000000000,"gasLimit":50000,"chainID":"1","version":1,"signature":"$expectedSignature"}""", output.encoded) + } + + @Test + fun signESDTTransfer() { + val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) + + val accounts = Elrond.Accounts.newBuilder() + .setSenderNonce(7) + .setSender(aliceBech32) + .setReceiver(bobBech32) + .build() + + val transfer = Elrond.ESDTTransfer.newBuilder() + .setAccounts(accounts) + .setTokenIdentifier("MYTOKEN-1234") + .setAmount("10000000000000") + .build() + + val signingInput = Elrond.SigningInput.newBuilder() + .setEsdtTransfer(transfer) + .setChainId("1") + .setPrivateKey(privateKey) .build() - + + val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val expectedSignature = "9add6d9ac3f1a1fddb07b934e8a73cad3b8c232bdf29d723c1b38ad619905f03e864299d06eb3fe3bbb48a9f1d9b7f14e21dc5eaffe0c87f5718ad0c4198bb0c" + val expectedData = "RVNEVFRyYW5zZmVyQDRkNTk1NDRmNGI0NTRlMmQzMTMyMzMzNEAwOTE4NGU3MmEwMDA=" + + assertEquals(expectedSignature, output.signature) + assertEquals("""{"nonce":7,"value":"0","receiver":"$bobBech32","sender":"$aliceBech32","gasPrice":1000000000,"gasLimit":425000,"data":"$expectedData","chainID":"1","version":1,"signature":"$expectedSignature"}""", output.encoded) + } + + @Test + fun signESDTNFTTransfer() { val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) + val accounts = Elrond.Accounts.newBuilder() + .setSenderNonce(7) + .setSender(aliceBech32) + .setReceiver(bobBech32) + .build() + + val transfer = Elrond.ESDTNFTTransfer.newBuilder() + .setAccounts(accounts) + .setTokenCollection("LKMEX-aab910") + .setTokenNonce(4) + .setAmount("184300000000000000") + .build() + val signingInput = Elrond.SigningInput.newBuilder() + .setEsdtnftTransfer(transfer) + .setChainId("1") .setPrivateKey(privateKey) - .setTransaction(transaction) .build() val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) - val expectedSignature = "b5fddb8c16fa7f6123cb32edc854f1e760a3eb62c6dc420b5a4c0473c58befd45b621b31a448c5b59e21428f2bc128c80d0ee1caa4f2bf05a12be857ad451b00" + val expectedSignature = "cc935685d5b31525e059a16a832cba98dee751983a5a93de4198f6553a2c55f5f1e0b4300fe9077376fa754546da0b0f6697e66462101a209aafd0fc775ab60a" + val expectedData = "RVNEVE5GVFRyYW5zZmVyQDRjNGI0ZDQ1NTgyZDYxNjE2MjM5MzEzMEAwNEAwMjhlYzNkZmEwMWFjMDAwQDgwNDlkNjM5ZTVhNjk4MGQxY2QyMzkyYWJjY2U0MTAyOWNkYTc0YTE1NjM1MjNhMjAyZjA5NjQxY2MyNjE4Zjg=" assertEquals(expectedSignature, output.signature) - assertEquals("""{"nonce":0,"value":"0","receiver":"$bobBech32","sender":"$aliceBech32","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"$expectedSignature"}""", output.encoded) + assertEquals("""{"nonce":7,"value":"0","receiver":"$aliceBech32","sender":"$aliceBech32","gasPrice":1000000000,"gasLimit":937500,"data":"$expectedData","chainID":"1","version":1,"signature":"$expectedSignature"}""", output.encoded) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleAddress.kt new file mode 100644 index 00000000000..b09cfaff057 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleAddress.kt @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.everscale + +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestEverscaleAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("5b59e0372d19b6355c73fa8cc708fa3301ae2ec21bb6277e8b79d386ccb7846f".toHexByteArray()) + val pubkey = key.publicKeyEd25519 + val address = AnyAddress(pubkey, CoinType.EVERSCALE) + val expected = AnyAddress("0:269fee242eb410786abe1777a14785c8bbeb1e34100c7570e17698b36ad66fb0", CoinType.EVERSCALE) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleSigner.kt new file mode 100644 index 00000000000..cb1d7266f92 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/everscale/TestEverscaleSigner.kt @@ -0,0 +1,48 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.everscale + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHexBytesInByteString +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.CoinType.EVERSCALE +import wallet.core.jni.proto.Everscale +import wallet.core.jni.proto.Everscale.SigningOutput + +class TestEverscaleSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testSign() { + val transferMessage = Everscale.Transfer.newBuilder().apply { + bounce = false + behavior = Everscale.MessageBehavior.SimpleTransfer + amount = 100000000 + expiredAt = 1680770631 + to = "0:db18a67f4626f15ac0537a18445937f685f9b30184f0d7b28be4bdeb92d2fd90" + encodedContractData = "te6ccgEBAQEAKgAAUAAAAAFLqS2KOWKN+7Y5OSiKhKisiw6t/h2ovvR3WbapyAtrdctwupw=" + }.build() + val signingInput = Everscale.SigningInput.newBuilder().apply { + transfer = transferMessage + privateKey = ByteString.copyFrom("542bd4288352f1c6b270046f153d406aec054a0a06000ab9b36b5c6dd3050ad4".toHexByteArray()) + }.build() + + val output = AnySigner.sign(signingInput, EVERSCALE, SigningOutput.parser()) + + // Link to the external message: https://everscan.io/messages/73807b0a3ca2d8564c023dccd5b9da222a270f68338c6fc2c064dda376a2c59d + val expectedString = "te6ccgICAAIAAQAAAKoAAAHfiAG+Ilaz1wTyTEauoymMGl6o+NGqhszIlHS8BXAmXniYrAImASIQKH2jIwoA65IGC6aua4gAA4fFo/Nuxgb3sIRELhZnSXIS7IsE2E4D+8hk3EWGVZX+ICqlN/ka9DvXduhaXUlsUyF0MjgAAAAIHAABAGhCAG2MUz+jE3itYCm9DCIsm/tC/NmAwnhr2UXyXvXJaX7IIC+vCAAAAAAAAAAAAAAAAAAA" + assertEquals(output.encoded, expectedString) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt new file mode 100644 index 00000000000..d7db293adf0 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/juno/TestJunoAddress.kt @@ -0,0 +1,35 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.juno + +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert +import org.junit.Test +import wallet.core.jni.* + +class TestJunoAddress { + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAnyAddressValidation() { + val addr = "juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94" + val anyAddr = AnyAddress(addr, CoinType.COSMOS, "juno") + assert(AnyAddress.isValidBech32(anyAddr.description(), CoinType.COSMOS, "juno")) + assert(!AnyAddress.isValidBech32(anyAddr.description(), CoinType.BITCOIN, "juno")) + assert(!AnyAddress.isValid(anyAddr.description(), CoinType.BITCOIN)) + assert(!AnyAddress.isValid(anyAddr.description(), CoinType.COSMOS)) + } + + @Test + fun testAnyAddressFromPubkey() { + val pubKey = PublicKey("02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc".toHexByteArray(), PublicKeyType.SECP256K1) + val anyAddr = AnyAddress(pubKey, CoinType.COSMOS, "juno") + Assert.assertEquals(anyAddr.description(), "juno1cj2vfjec3c3luf9fx9vddnglhh9gawmncn4k5n"); + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kava/TestKavaTransactions.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kava/TestKavaTransactions.kt index 06b3c7b686a..b715150a080 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kava/TestKavaTransactions.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kava/TestKavaTransactions.kt @@ -1,4 +1,4 @@ -package com.trustwalval.core.app.blockchains.cosmos +package com.trustwallet.core.app.blockchains.cosmos import android.util.Log import com.google.protobuf.ByteString @@ -40,7 +40,7 @@ class TestKavaTransactions { }.build() val feeAmount = Cosmos.Amount.newBuilder().apply { - amount = 100 + amount = "100" denom = "ukava" }.build() diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kcc/TestKuCoinCommunityChainAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kcc/TestKuCoinCommunityChainAddress.kt new file mode 100644 index 00000000000..0d2fe4a7446 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/kcc/TestKuCoinCommunityChainAddress.kt @@ -0,0 +1,31 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.kcc + +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestKuCoinCommunityChainAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("33b85056aabab539bcb68540735ecf054e38bc58b29b751530e2b54ecb4ca564".toHexByteArray()) + val pubkey = key.getPublicKeySecp256k1(false) + val address = AnyAddress(pubkey, CoinType.KUCOINCOMMUNITYCHAIN) + val expected = AnyAddress("0xE5cA667d795685E9915E5F4b4254ca832eEB398B", CoinType.KUCOINCOMMUNITYCHAIN) + + assertEquals(pubkey.data().toHex(), "0x0413bde18e3329af54d51a24f424fe09a8d7d42c324c07e10e53a6e139cbee80e6288142dec2ed46f7b81dccbb28d6168cdc7b208928730cbeeb911f8db6a707bb") + assertEquals(address.description(), expected.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/near/TestNEARSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/near/TestNEARSigner.kt index 3680868be56..8e5021abd4b 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/near/TestNEARSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/near/TestNEARSigner.kt @@ -26,9 +26,9 @@ class TestNEARSigner { signerId = "test.near" nonce = 1 receiverId = "whatever.near" - addActionsBuilder().apply { + addActions(NEAR.Action.newBuilder().apply { transfer = transferAction - } + }) blockHash = ByteString.copyFrom(Base58.decodeNoCheck("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM")) privateKey = ByteString.copyFrom(Base58.decodeNoCheck("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv").sliceArray(0..31)) }.build() diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosAddress.kt new file mode 100644 index 00000000000..64f1c492ada --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosAddress.kt @@ -0,0 +1,29 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.nervos + +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestNervosAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb".toHexByteArray()) + val pubkey = key.getPublicKeySecp256k1(true) + val address = AnyAddress(pubkey, CoinType.NERVOS) + val expected = AnyAddress("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras980hksatlslfaktks7epf25", CoinType.NERVOS) + + assertEquals(address.description(), expected.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosSigner.kt new file mode 100644 index 00000000000..3d0a149e5ae --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/nervos/TestNervosSigner.kt @@ -0,0 +1,69 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.nervos + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytesInByteString +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* +import wallet.core.jni.CoinType.NERVOS +import wallet.core.jni.proto.Nervos.* +import wallet.core.java.AnySigner + +class TestNervosSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testSigning() { + val key = PrivateKey("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb".toHexByteArray()) + + val lockScript = Script.newBuilder().apply { + codeHash = "9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8".toHexBytesInByteString() + hashType = "type" + args = "c4b50c5c8d074f063ec0a77ded0eaff0fa7b65da".toHexBytesInByteString() + }.build() + + val signingInput = SigningInput.newBuilder().apply { + addPrivateKey(ByteString.copyFrom(key.data())) + byteFee = 1 + nativeTransfer = NativeTransfer.newBuilder().apply { + toAddress = "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3" + changeAddress = "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78yze6eyfyvd537z66ur22c9mmrgz82ama" + amount = 10000000000 + }.build() + addAllCell(listOf( + Cell.newBuilder().apply { + capacity = 100000000000 + outPoint = OutPoint.newBuilder().apply { + txHash = "71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3".toHexBytesInByteString() + index = 1 + }.build() + lock = lockScript + }.build(), + Cell.newBuilder().apply { + capacity = 20000000000 + outPoint = OutPoint.newBuilder().apply { + txHash = "71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3".toHexBytesInByteString() + index = 0 + }.build() + lock = lockScript + }.build() + )) + }.build() + + val output = AnySigner.sign(signingInput, NERVOS, SigningOutput.parser()) + + assertEquals(output.transactionJson, "{\"cell_deps\":[{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x0\",\"tx_hash\":\"0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c\"}}],\"header_deps\":[],\"inputs\":[{\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":\"0x71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3\"},\"since\":\"0x0\"}],\"outputs\":[{\"capacity\":\"0x2540be400\",\"lock\":{\"args\":\"0xab201f55b02f53b385f79b34dfad548e549b48fc\",\"code_hash\":\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":\"type\"},\"type\":null},{\"capacity\":\"0x2540be230\",\"lock\":{\"args\":\"0xb0d65be39059d6489231b48f85ad706a560bbd8d\",\"code_hash\":\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":\"type\"},\"type\":null}],\"outputs_data\":[\"0x\",\"0x\"],\"version\":\"0x0\",\"witnesses\":[\"0x55000000100000005500000055000000410000002a9ef2ad7829e5ea0c7a32735d29a0cb2ec20434f6fd5bf6e29cda56b28e08140156191cbbf80313d3c9cae4b74607acce7b28eb21d52ef058ed8491cdde70b700\"]}") + assertEquals(output.transactionId, "0xf2c32afde7e72011985583873bc16c0a3c01fc01fc161eb4b914fcf84c53cdf8") + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisSigner.kt index b0839d0b5ba..f9962ebb13c 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/oasis/TestOasisSigner.kt @@ -8,16 +8,12 @@ package com.trustwallet.core.app.blockchains.oasis import com.google.protobuf.ByteString import com.trustwallet.core.app.utils.Numeric -import com.trustwallet.core.app.utils.toHexByteArray -import com.trustwallet.core.app.utils.toHexBytes -import com.trustwallet.core.app.utils.toHexBytesInByteString -import junit.framework.Assert.assertEquals import org.junit.Test +import org.junit.Assert.assertEquals import wallet.core.java.AnySigner import wallet.core.jni.CoinType.OASIS import wallet.core.jni.proto.Oasis import wallet.core.jni.proto.Oasis.SigningOutput -import java.math.BigInteger class TestOasisSigner { @@ -49,7 +45,7 @@ class TestOasisSigner { val output = AnySigner.sign(signingInput.build(), OASIS, SigningOutput.parser()) assertEquals( - "0xa273756e747275737465645f7261775f76616c7565585ea4656e6f6e636500666d6574686f64707374616b696e672e5472616e7366657263666565a2636761730066616d6f756e74410064626f6479a262746f5500c73cc001463434915ba3f39751beb7c0905b45eb66616d6f756e744400989680697369676e6174757265a26a7075626c69635f6b6579582093d8f8a455f50527976a8aa87ebde38d5606efa86cb985d3fb466aff37000e3b697369676e61747572655840e331ce731ed819106586152b13cd98ecf3248a880bdc71174ee3d83f6d5f3f8ee8fc34c19b22032f2f1e3e06d382720125d7a517fba9295c813228cc2b63170b", + "0xa2697369676e6174757265a2697369676e617475726558406e51c18c9b2015c9b49414b3307336597f51ff331873d214ce2db81c9651a34d99529ccaa294a39ccd01c6b0bc2c2239d87c624e5ba4840cf99ac8f9283e240c6a7075626c69635f6b6579582093d8f8a455f50527976a8aa87ebde38d5606efa86cb985d3fb466aff37000e3b73756e747275737465645f7261775f76616c7565585ea463666565a2636761730066616d6f756e74410064626f6479a262746f5500c73cc001463434915ba3f39751beb7c0905b45eb66616d6f756e744400989680656e6f6e636500666d6574686f64707374616b696e672e5472616e73666572", Numeric.toHexString(output.encoded.toByteArray()) ) } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisAddress.kt new file mode 100644 index 00000000000..1d881bbdef1 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisAddress.kt @@ -0,0 +1,31 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.osmosis + +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestOsmosisAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af".toHexByteArray()) + val pubkey = key.getPublicKeySecp256k1(true) + val address = AnyAddress(pubkey, CoinType.OSMOSIS) + val expected = AnyAddress("osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5", CoinType.OSMOSIS) + + assertEquals(pubkey.data().toHex(), "0x02ecef5ce437a302c67f95468de4b31f36e911f467d7e6a52b41c1e13e1d563649") + assertEquals(address.description(), expected.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisSigner.kt new file mode 100644 index 00000000000..97f075947ba --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/osmosis/TestOsmosisSigner.kt @@ -0,0 +1,78 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.osmosis + +import android.util.Log +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.Numeric +import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytes +import com.trustwallet.core.app.utils.toHexBytesInByteString +import com.trustwallet.core.app.utils.toHex +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* +import wallet.core.jni.CoinType.OSMOSIS +import wallet.core.jni.proto.Cosmos +import wallet.core.jni.proto.Cosmos.SigningOutput +import wallet.core.jni.proto.Cosmos.SigningMode +import wallet.core.java.AnySigner + +class TestOsmosisSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun OsmosisTransactionSigning() { + val key = PrivateKey("8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(true) + val from = AnyAddress(publicKey, OSMOSIS).description() + + val txAmount = Cosmos.Amount.newBuilder().apply { + amount = "99800" + denom = "uosmo" + }.build() + + val sendCoinsMsg = Cosmos.Message.Send.newBuilder().apply { + fromAddress = from + toAddress = "osmo18s0hdnsllgcclweu9aymw4ngktr2k0rkvn7jmn" + addAllAmounts(listOf(txAmount)) + }.build() + + val message = Cosmos.Message.newBuilder().apply { + sendCoinsMessage = sendCoinsMsg + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "200" + denom = "uosmo" + }.build() + + val osmosisFee = Cosmos.Fee.newBuilder().apply { + gas = 200000 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf + accountNumber = 124703 + chainId = "osmosis-1" + memo = "" + sequence = 0 + fee = osmosisFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, OSMOSIS, SigningOutput.parser()) + + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"Co0BCooBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmoKK29zbW8xbWt5Njljbjhla3R3eTA4NDV2ZWM5dXBzZHBoa3R4dDBlbjk3ZjUSK29zbW8xOHMwaGRuc2xsZ2NjbHdldTlheW13NG5na3RyMmswcmt2bjdqbW4aDgoFdW9zbW8SBTk5ODAwEmQKTgpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQLs71zkN6MCxn+VRo3ksx826RH0Z9fmpStBweE+HVY2SRIECgIIARISCgwKBXVvc21vEgMyMDAQwJoMGkAMY//Md5GRUR4lVZhk558hFS3kii9QZYoYKfg4+ac/xgNeyoiEweVDhcmEvlH1orVwjLUOnYs4ly2a/yIurYVj\"}") + assertEquals(output.error, "") + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt index 74d9826c091..2e5a09f2885 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/polkadot/TestPolkadotSigner.kt @@ -84,4 +84,35 @@ class TestPolkadotSigner { val expected = "0x6103840036092fac541e0e5feda19e537c679b487566d7101141c203ac8322c27e5f076a00a8b1f859d788f11a958e98b731358f89cf3fdd41a667ea992522e8d4f46915f4c03a1896f2ac54bdc5f16e2ce8a2a3bf233d02aad8192332afd2113ed6688e0d0010001a02080700007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b540201070508002c2a55b5ffdca266bd0207df97565b03255f70783ca1a349be5ed9f44589c36000d44533a4d21fd9d6f5d57c8cd05c61a6f23f9131cec8ae386b6b437db399ec3d" assertEquals(encoded, expected) } + + @Test + fun PolkadotTransactionSignChillAndUnbond() { + val call = Polkadot.Staking.ChillAndUnbond.newBuilder().apply { + value = "0x1766444D00".toHexBytesInByteString() // 10.05 DOT + } + + val input = Polkadot.SigningInput.newBuilder().apply { + genesisHash = genesisHashStr + blockHash = "0x35ba668bb19453e8da6334cadcef2a27c8d4141bfc8b49e78e853c3d73e1ecd0".toHexBytesInByteString() + nonce = 6 + specVersion = 9200 + network = Polkadot.Network.POLKADOT + transactionVersion = 12 + privateKey = "298fcced2b497ed48367261d8340f647b3fca2d9415d57c2e3c5ef90482a2266".toHexBytesInByteString() + era = Polkadot.Era.newBuilder().apply { + blockNumber = 10541373 + period = 64 + }.build() + stakingCall = Polkadot.Staking.newBuilder().apply { + chillAndUnbond = call.build() + }.build() + } + + val output = AnySigner.sign(input.build(), POLKADOT, SigningOutput.parser()) + val encoded = Numeric.toHexString(output.encoded.toByteArray()) + + // https://polkadot.subscan.io/extrinsic/10541383-2 + val expected = "0xd10184008361bd08ddca5fda28b5e2aa84dc2621de566e23e089e555a42194c3eaf2da7900c891ba102db672e378945d74cf7f399226a76b43cab502436971599255451597fc2599902e4b62c7ce85ecc3f653c693fef3232be620984b5bb5bcecbbd7b209d50318001a02080706070207004d446617" + assertEquals(encoded, expected) + } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt new file mode 100644 index 00000000000..852245d5a05 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/smartbitcoincash/TestSmartBitcoinCashAddress.kt @@ -0,0 +1,31 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +package com.trustwallet.core.app.blockchains.smartbitcoincash + +import com.trustwallet.core.app.utils.toHex +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* + +class TestSmartBitcoinCashAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testAddress() { + val key = PrivateKey("ab4accc9310d90a61fc354d8f353bca4a2b3c0590685d3eb82d0216af3badddc".toHexByteArray()) + val pubkey = key.getPublicKeySecp256k1(false) + val address = AnyAddress(pubkey, CoinType.SMARTBITCOINCASH) + val expected = AnyAddress("0xA3Dcd899C0f3832DFDFed9479a9d828c6A4EB2A7", CoinType.SMARTBITCOINCASH) + + assertEquals(pubkey.data().toHex(), "0x0448a9ffac8022f1c7eb5253746e24d11d9b6b2737c0aecd48335feabb95a179916b1f3a97bed6740a85a2d11c663d38566acfb08af48a47ce0c835c65c9b23d0d") + assertEquals(address.description(), expected.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stellar/TestStellarAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stellar/TestStellarAddress.kt index ddd89799276..9d11510cf1f 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stellar/TestStellarAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stellar/TestStellarAddress.kt @@ -18,7 +18,7 @@ class TestAddress { val pubkey = key.publicKeyEd25519 val address = AnyAddress(pubkey, CoinType.STELLAR) - assertEquals(pubkey.data().toHex(), "0x09A966BCAACC103E38896BAAE3F8C2F06C21FD47DD4F864FF0D33F9819DF5CA2".toLowerCase()) + assertEquals(pubkey.data().toHex(), "0x09A966BCAACC103E38896BAAE3F8C2F06C21FD47DD4F864FF0D33F9819DF5CA2".lowercase()) assertEquals(address.description(), "GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI") } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stellar/TestStellarSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stellar/TestStellarSigner.kt index a0a35c869a5..a76ea84550a 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stellar/TestStellarSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/stellar/TestStellarSigner.kt @@ -31,7 +31,6 @@ class TestStellarTransactionSigner { sequence = 2 passphrase = StellarPassphrase.STELLAR.toString() opPayment = operation.build() - memoVoid = memoVoidBuilder.build() privateKey = ByteString.copyFrom(PrivateKey("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722".toHexByteArray()).data()) } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/terra/TestTerraClassicTxs.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/terra/TestTerraClassicTxs.kt new file mode 100644 index 00000000000..2c67c665dd8 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/terra/TestTerraClassicTxs.kt @@ -0,0 +1,209 @@ +package com.trustwallet.core.app.blockchains.terra + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.* +import wallet.core.jni.CoinType.TERRA +import wallet.core.jni.proto.Cosmos +import wallet.core.jni.proto.Cosmos.SigningOutput +import wallet.core.jni.proto.Cosmos.SigningMode +import wallet.core.java.AnySigner + +class TestTerraClassicTxs { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testSigningTransaction() { + val key = + PrivateKey("1037f828ca313f4c9e120316e8e9ff25e17f07fe66ba557d5bc5e2eeb7cba8f6".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(true) + val from = AnyAddress(publicKey, TERRA).description() + + val txAmount = Cosmos.Amount.newBuilder().apply { + amount = "1000000" + denom = "uluna" + }.build() + + val sendCoinsMsg = Cosmos.Message.Send.newBuilder().apply { + fromAddress = from + toAddress = "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms" + addAllAmounts(listOf(txAmount)) + typePrefix = "bank/MsgSend" + }.build() + + val message = Cosmos.Message.newBuilder().apply { + sendCoinsMessage = sendCoinsMsg + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "3000" + denom = "uluna" + }.build() + + val cosmosFee = Cosmos.Fee.newBuilder().apply { + gas = 200000 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + accountNumber = 158 + chainId = "soju-0013" + memo = "" + sequence = 0 + fee = cosmosFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, TERRA, SigningOutput.parser()) + val jsonPayload = output.json + + val expectedJsonPayload = """{"mode":"block","tx":{"fee":{"amount":[{"amount":"3000","denom":"uluna"}],"gas":"200000"},"memo":"","msg":[{"type":"bank/MsgSend","value":{"amount":[{"amount":"1000000","denom":"uluna"}],"from_address":"terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe","to_address":"terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk"},"signature":"KPdiVsKpY12JG/VKEJVa/FpMKclxlS0qNNG6VOAypj10R5vY5UX5IgRJET1zNYnH0wvcXxfNXV+s8jtwN2UXiQ=="}]}}""" + assertEquals(expectedJsonPayload, jsonPayload) + + } + + @Test + fun testSigningWasmTerraTransferTxProtobuf() { + val key = + PrivateKey("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(true) + val from = AnyAddress(publicKey, TERRA).description() + + val wasmTransferMessage = Cosmos.Message.WasmTerraExecuteContractTransfer.newBuilder().apply { + senderAddress = from + contractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76" // ANC + amount = ByteString.copyFrom("0x3D090".toHexByteArray()) // 250000 + recipientAddress = "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp" + }.build() + + val message = Cosmos.Message.newBuilder().apply { + wasmTerraExecuteContractTransferMessage = wasmTransferMessage + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "3000" + denom = "uluna" + }.build() + + val cosmosFee = Cosmos.Fee.newBuilder().apply { + gas = 200000 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf + accountNumber = 3407705 + chainId = "columbus-5" + memo = "" + sequence = 3 + fee = cosmosFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, TERRA, SigningOutput.parser()) + + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CucBCuQBCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBK5AQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2Glt7InRyYW5zZmVyIjp7ImFtb3VudCI6IjI1MDAwMCIsInJlY2lwaWVudCI6InRlcnJhMWpsZ2FxeTludm4yaGY1dDJzcmE5eWN6OHM3N3duZjlsMGttZ2NwIn19EmcKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQNwZjrHsPmJKW/rXOWfukpQ1+lOHOJW3/IlFFnKLNmsABIECgIIARgDEhMKDQoFdWx1bmESBDMwMDAQwJoMGkAaprIEMLPH2HmFdwFGoaipb2GIyhXt6ombz+WMnG2mORBI6gFt0M+IymYgzZz6w1SW52R922yafDnn7yXfutRw\"}") + assertEquals(output.error, "") + } + + @Test + fun testSigningWasmTerraGenericProtobuf() { + val key = + PrivateKey("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(true) + val from = AnyAddress(publicKey, TERRA).description() + + val wasmGenericMessage = Cosmos.Message.WasmTerraExecuteContractGeneric.newBuilder().apply { + senderAddress = from + contractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76" // ANC + executeMsg = """{"transfer": { "amount": "250000", "recipient": "terra1d7048csap4wzcv5zm7z6tdqem2agyp9647vdyj" } }""" + }.build() + + val message = Cosmos.Message.newBuilder().apply { + wasmTerraExecuteContractGeneric = wasmGenericMessage + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "3000" + denom = "uluna" + }.build() + + val cosmosFee = Cosmos.Fee.newBuilder().apply { + gas = 200000 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf + accountNumber = 3407705 + chainId = "columbus-5" + memo = "" + sequence = 7 + fee = cosmosFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, TERRA, SigningOutput.parser()) + + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"Cu4BCusBCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBLAAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2GmJ7InRyYW5zZmVyIjogeyAiYW1vdW50IjogIjI1MDAwMCIsICJyZWNpcGllbnQiOiAidGVycmExZDcwNDhjc2FwNHd6Y3Y1em03ejZ0ZHFlbTJhZ3lwOTY0N3ZkeWoiIH0gfRJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYBxITCg0KBXVsdW5hEgQzMDAwEMCaDBpAkPsS7xlSng2LMc9KiD1soN5NLaDcUh8I9okPmsdJN3le1B7yxRGNB4aQfhaRl/8Z0r5vitRT0AWuxDasd8wcFw==\"}") + assertEquals(output.error, "") + } + + @Test + fun testSigningWasmTerraGenericWithCoinsProtobuf() { + val key = + PrivateKey("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(true) + val from = AnyAddress(publicKey, TERRA).description() + + val coins = Cosmos.Amount.newBuilder().apply { + amount = "1000" + denom = "uusd" + }.build() + + val wasmGenericMessage = Cosmos.Message.WasmTerraExecuteContractGeneric.newBuilder().apply { + senderAddress = from + contractAddress = "terra1sepfj7s0aeg5967uxnfk4thzlerrsktkpelm5s" // ANC Market + executeMsg = """{ "deposit_stable": {} }""" + addCoins(coins) + }.build() + + val message = Cosmos.Message.newBuilder().apply { + wasmTerraExecuteContractGeneric = wasmGenericMessage + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "7000" + denom = "uluna" + }.build() + + val cosmosFee = Cosmos.Fee.newBuilder().apply { + gas = 600000 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf + accountNumber = 3407705 + chainId = "columbus-5" + memo = "" + sequence = 9 + fee = cosmosFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, TERRA, SigningOutput.parser()) + + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CrIBCq8BCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBKEAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMXNlcGZqN3MwYWVnNTk2N3V4bmZrNHRoemxlcnJza3RrcGVsbTVzGhh7ICJkZXBvc2l0X3N0YWJsZSI6IHt9IH0qDAoEdXVzZBIEMTAwMBJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYCRITCg0KBXVsdW5hEgQ3MDAwEMDPJBpAGyi7f1ioY8XV6pjFq1s86Om4++CIUnd3rLHif2iopCcYvX0mLkTlQ6NUERg8nWTYgXcj6fOTO/ptgPuAtv0NWg==\"}") + assertEquals(output.error, "") + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/terra/TestTerraTransactions.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/terra/TestTerraTransactions.kt index 0f1dbf2dac6..6ee4574001a 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/terra/TestTerraTransactions.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/terra/TestTerraTransactions.kt @@ -1,15 +1,15 @@ -package com.trustwalval.core.app.blockchains.terra +package com.trustwallet.core.app.blockchains.terra -import android.util.Log import com.google.protobuf.ByteString import com.trustwallet.core.app.utils.toHexByteArray import org.junit.Assert.assertEquals import org.junit.Test -import wallet.core.java.AnySigner import wallet.core.jni.* -import wallet.core.jni.CoinType.TERRA +import wallet.core.jni.CoinType.TERRAV2 import wallet.core.jni.proto.Cosmos import wallet.core.jni.proto.Cosmos.SigningOutput +import wallet.core.jni.proto.Cosmos.SigningMode +import wallet.core.java.AnySigner class TestTerraTransactions { @@ -20,20 +20,19 @@ class TestTerraTransactions { @Test fun testSigningTransaction() { val key = - PrivateKey("1037f828ca313f4c9e120316e8e9ff25e17f07fe66ba557d5bc5e2eeb7cba8f6".toHexByteArray()) + PrivateKey("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005".toHexByteArray()) val publicKey = key.getPublicKeySecp256k1(true) - val from = AnyAddress(publicKey, TERRA).description() + val from = AnyAddress(publicKey, TERRAV2).description() val txAmount = Cosmos.Amount.newBuilder().apply { - amount = 1000000 + amount = "1000000" denom = "uluna" }.build() val sendCoinsMsg = Cosmos.Message.Send.newBuilder().apply { fromAddress = from - toAddress = "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms" + toAddress = "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp" addAllAmounts(listOf(txAmount)) - typePrefix = "bank/MsgSend" }.build() val message = Cosmos.Message.newBuilder().apply { @@ -41,7 +40,7 @@ class TestTerraTransactions { }.build() val feeAmount = Cosmos.Amount.newBuilder().apply { - amount = 3000 + amount = "30000" denom = "uluna" }.build() @@ -51,20 +50,65 @@ class TestTerraTransactions { }.build() val signingInput = Cosmos.SigningInput.newBuilder().apply { - accountNumber = 158 - chainId = "soju-0013" + signingMode = SigningMode.Protobuf + accountNumber = 1037 + chainId = "phoenix-1" memo = "" - sequence = 0 + sequence = 1 fee = cosmosFee privateKey = ByteString.copyFrom(key.data()) addAllMessages(listOf(message)) }.build() - val output = AnySigner.sign(signingInput, TERRA, SigningOutput.parser()) + val output = AnySigner.sign(signingInput, TERRAV2, SigningOutput.parser()) val jsonPayload = output.json - val expectedJsonPayload = """{"mode":"block","tx":{"fee":{"amount":[{"amount":"3000","denom":"uluna"}],"gas":"200000"},"memo":"","msg":[{"type":"bank/MsgSend","value":{"amount":[{"amount":"1000000","denom":"uluna"}],"from_address":"terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe","to_address":"terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk"},"signature":"KPdiVsKpY12JG/VKEJVa/FpMKclxlS0qNNG6VOAypj10R5vY5UX5IgRJET1zNYnH0wvcXxfNXV+s8jtwN2UXiQ=="}]}}""" - assertEquals(expectedJsonPayload, jsonPayload) + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CpEBCo4BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm4KLHRlcnJhMWhzazZqcnl5cWpmaHA1ZGhjNTV0YzlqdGNreWd4MGVwMzdoZGQyEix0ZXJyYTFqbGdhcXk5bnZuMmhmNXQyc3JhOXljejhzNzd3bmY5bDBrbWdjcBoQCgV1bHVuYRIHMTAwMDAwMBJoClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYARIUCg4KBXVsdW5hEgUzMDAwMBDAmgwaQPh0C3rjzdixIUiyPx3FlWAxzbKILNAcSRVeQnaTl1vsI5DEfYa2oYlUBLqyilcMCcU/iaJLhex30No2ak0Zn1Q=\"}") + assertEquals(output.error, "") + } + + @Test + fun testSigningWasmTerraTransferTx() { + val key = + PrivateKey("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616".toHexByteArray()) + val publicKey = key.getPublicKeySecp256k1(true) + val from = AnyAddress(publicKey, TERRAV2).description() + + val wasmTransferMessage = Cosmos.Message.WasmExecuteContractTransfer.newBuilder().apply { + senderAddress = from + contractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76" + amount = ByteString.copyFrom("0x3D090".toHexByteArray()) // 250000 + recipientAddress = "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp" + }.build() + + val message = Cosmos.Message.newBuilder().apply { + wasmExecuteContractTransferMessage = wasmTransferMessage + }.build() + + val feeAmount = Cosmos.Amount.newBuilder().apply { + amount = "3000" + denom = "uluna" + }.build() + + val cosmosFee = Cosmos.Fee.newBuilder().apply { + gas = 200000 + addAllAmounts(listOf(feeAmount)) + }.build() + + val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf + accountNumber = 3407705 + chainId = "phoenix-1" + memo = "" + sequence = 3 + fee = cosmosFee + privateKey = ByteString.copyFrom(key.data()) + addAllMessages(listOf(message)) + }.build() + + val output = AnySigner.sign(signingInput, TERRAV2, SigningOutput.parser()) + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CuUBCuIBCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QSuQEKLHRlcnJhMTh3dWtwODRkcTIyN3d1NG1naDBqbTZuOW5sbmo2cnM4MnBwOXdmEix0ZXJyYTE0ejU2bDBmcDJsc2Y4Nnp5M2h0eTJ6NDdlemtobnRodHI5eXE3NhpbeyJ0cmFuc2ZlciI6eyJhbW91bnQiOiIyNTAwMDAiLCJyZWNpcGllbnQiOiJ0ZXJyYTFqbGdhcXk5bnZuMmhmNXQyc3JhOXljejhzNzd3bmY5bDBrbWdjcCJ9fRJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYAxITCg0KBXVsdW5hEgQzMDAwEMCaDBpAiBGbQaj+jsXE6/FssD3fC77QOxpli9GqsPea+KoNyMIEgVj89Hii+oU1bAEQS4qV0SaE2V6RNy24uCcFTIRbcQ==\"}") + assertEquals(output.error, "") } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/tezos/TestTezosSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/tezos/TestTezosSigner.kt index f84b3edb766..f14a6a6c689 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/tezos/TestTezosSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/tezos/TestTezosSigner.kt @@ -1,10 +1,13 @@ -package com.trustwallet.core.app.blockchains.waves +package com.trustwallet.core.app.blockchains.tezos +import com.trustwallet.core.app.utils.Numeric import com.trustwallet.core.app.utils.toHexByteArray +import com.trustwallet.core.app.utils.toHexBytesInByteString import org.junit.Assert.* import org.junit.Test import wallet.core.jni.CoinType.TEZOS import wallet.core.java.AnySigner +import wallet.core.jni.proto.Tezos.* class TestTezosTransactionSigner { @@ -12,6 +15,115 @@ class TestTezosTransactionSigner { System.loadLibrary("TrustWalletCore") } + @Test + fun testSigningFA2() { + val key = + "363265a0b3f06661001cab8b4f3ca8fd97ae70608184979cf7300836f57ec2d6".toHexBytesInByteString() + + val transferInfos = Txs.newBuilder() + .setAmount("10") + .setTokenId("0") + .setTo("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP") + .build() + + val txObj = TxObject.newBuilder() + .setFrom("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP") + .addTxs(transferInfos) + .build() + + val fa12 = FA2Parameters.newBuilder() + .setEntrypoint("transfer") + .addTxsObject(txObj) + .build() + + val parameters = OperationParameters.newBuilder() + .setFa2Parameters(fa12) + .build() + + val transactionData = TransactionOperationData.newBuilder() + .setAmount(0) + .setDestination("KT1DYk1XDzHredJq1EyNkDindiWDqZyekXGj") + .setParameters(parameters) + .build() + + val transaction = Operation.newBuilder() + .setSource("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP") + .setFee(100000) + .setCounter(2993173) + .setGasLimit(100000) + .setStorageLimit(0) + .setKind(Operation.OperationKind.TRANSACTION) + .setTransactionOperationData(transactionData) + .build(); + + val operationList = OperationList.newBuilder() + .setBranch("BKvEAX9HXfJZWYfTQbR1C7B3ADoKY6a1aKVRF7qQqvc9hS8Rr3m") + .addOperations(transaction) + .build(); + + val signingInput = SigningInput.newBuilder() + .setPrivateKey(key) + .setOperationList(operationList) + .build() + + val result = AnySigner.sign(signingInput, TEZOS, SigningOutput.parser()) + + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.encoded.toByteArray())), + "1b1f9345dc9f77bd24b09034d1d2f9a28f02ac837f49db54b8d68341f53dc4b76c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0695d8b601a08d0600000136767f88850bae28bfb9f46b73c5e87ede4de12700ffff087472616e7366657200000066020000006107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b5550020000003107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070000000a552d24710d6c59383286700c6c2917b25a6c1fa8b587e593c289dd47704278796792f1e522c1623845ec991e292b0935445e6994850bd03f035a006c5ed93806" + ) + } + + @Test + fun testSigningFA12() { + val key = + "363265a0b3f06661001cab8b4f3ca8fd97ae70608184979cf7300836f57ec2d6".toHexBytesInByteString() + + val fa12 = FA12Parameters.newBuilder() + .setEntrypoint("transfer") + .setFrom("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP") + .setTo("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP") + .setValue("123") + .build() + + val parameters = OperationParameters.newBuilder() + .setFa12Parameters(fa12) + .build() + + val transactionData = TransactionOperationData.newBuilder() + .setAmount(0) + .setDestination("KT1EwXFWoG9bYebmF4pYw72aGjwEnBWefgW5") + .setParameters(parameters) + .build() + + val transaction = Operation.newBuilder() + .setSource("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP") + .setFee(100000) + .setCounter(2993172) + .setGasLimit(100000) + .setStorageLimit(0) + .setKind(Operation.OperationKind.TRANSACTION) + .setTransactionOperationData(transactionData) + .build(); + + val operationList = OperationList.newBuilder() + .setBranch("BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp") + .addOperations(transaction) + .build(); + + val signingInput = SigningInput.newBuilder() + .setPrivateKey(key) + .setOperationList(operationList) + .build() + + val result = AnySigner.sign(signingInput, TEZOS, SigningOutput.parser()) + + assertEquals( + Numeric.cleanHexPrefix(Numeric.toHexString(result.encoded.toByteArray())), + "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0694d8b601a08d0600000145bd8a65cc48159d8ea60a55df735b7c5ad45f0e00ffff087472616e736665720000005907070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb012914d768155fba2df319a81136e8e3e573b9cadb1676834490c90212615d271da029b6b0531e290e9063bcdb40bea43627af048b18e036f02be2b6b22fc8b307" + ) + } + @Test fun testSigningJSON() { val json = """ @@ -43,9 +155,13 @@ class TestTezosTransactionSigner { } } """ - val key = "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f".toHexByteArray() + val key = + "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f".toHexByteArray() val result = AnySigner.signJSON(json, key, TEZOS.value()) assertTrue(AnySigner.supportsJSON(TEZOS.value())) - assertEquals(result, "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e6c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80993f001f44e810201000081faa75f741ef614b0e35fcc8c90dfa3b0b957210001b86398d5b9be737dca8e4106ea18d70e69b75e92f892fb283546a99152b8d7794b919c0fbf1c31de386069a60014491c0e7505adef5781cead1cfe6608030b") + assertEquals( + result, + "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e6c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80993f001f44e810201000081faa75f741ef614b0e35fcc8c90dfa3b0b957210001b86398d5b9be737dca8e4106ea18d70e69b75e92f892fb283546a99152b8d7794b919c0fbf1c31de386069a60014491c0e7505adef5781cead1cfe6608030b" + ) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainSigner.kt index 41021609528..2314586d13e 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORChainSigner.kt @@ -18,6 +18,7 @@ import wallet.core.java.AnySigner import wallet.core.jni.CoinType.THORCHAIN import wallet.core.jni.proto.Cosmos import wallet.core.jni.proto.Cosmos.SigningOutput +import wallet.core.jni.proto.Cosmos.SigningMode import wallet.core.jni.* class TestTHORChainSigner { @@ -31,47 +32,49 @@ class TestTHORChainSigner { val key = PrivateKey("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e".toHexByteArray()) val publicKey = key.getPublicKeySecp256k1(true) - val from = AnyAddress(publicKey, THORCHAIN).description() + val from = AnyAddress(publicKey, THORCHAIN).data() + val to = AnyAddress("thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn", THORCHAIN).data() val txAmount = Cosmos.Amount.newBuilder().apply { - amount = 2000000 + amount = "38000000" denom = "rune" }.build() - val sendCoinsMsg = Cosmos.Message.Send.newBuilder().apply { - fromAddress = from - toAddress = "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn" + val sendCoinsMsg = Cosmos.Message.THORChainSend.newBuilder().apply { + fromAddress = ByteString.copyFrom(from) + toAddress = ByteString.copyFrom(to) addAllAmounts(listOf(txAmount)) }.build() val message = Cosmos.Message.newBuilder().apply { - sendCoinsMessage = sendCoinsMsg + thorchainSendMessage = sendCoinsMsg }.build() val feeAmount = Cosmos.Amount.newBuilder().apply { - amount = 2000000 + amount = "200" denom = "rune" }.build() val cosmosFee = Cosmos.Fee.newBuilder().apply { - gas = 200000 + gas = 2500000 addAllAmounts(listOf(feeAmount)) }.build() val signingInput = Cosmos.SigningInput.newBuilder().apply { + signingMode = SigningMode.Protobuf + chainId = "thorchain-mainnet-v1" accountNumber = 593 - chainId = "thorchain" + sequence = 21 memo = "" - sequence = 2 fee = cosmosFee privateKey = ByteString.copyFrom(key.data()) addAllMessages(listOf(message)) }.build() val output = AnySigner.sign(signingInput, THORCHAIN, SigningOutput.parser()) - val jsonPayload = output.json - val expectedJsonPayload = """{"mode":"block","tx":{"fee":{"amount":[{"amount":"2000000","denom":"rune"}],"gas":"200000"},"memo":"","msg":[{"type":"thorchain/MsgSend","value":{"amount":[{"amount":"2000000","denom":"rune"}],"from_address":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","to_address":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"SsagpldYbikqjq0sw4IM8oo3l2FE4iPi1kPxJgQ6PhdT/RRmg4BEAq3weB+hTOp4SfFXI/r+wms7tKkZci6SbA=="}]}}""" - assertEquals(expectedJsonPayload, jsonPayload) + assertEquals(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"ClIKUAoOL3R5cGVzLk1zZ1NlbmQSPgoUFSLnZ9tusZcIsAOAKb+9YHvJvQ4SFMqGRZ+wBVHH30JUDF54aRksgzrbGhAKBHJ1bmUSCDM4MDAwMDAwEmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQPtmX45bPQpL1/OWkK7pBWZzNXZbjExVKfJ6nBJ3jF8dxIECgIIARgVEhIKCwoEcnVuZRIDMjAwEKDLmAEaQKZtS3ATa26OOGvqdKm14ZbHeNfkPtIajXi5MkZ5XaX2SWOeX+YnCPZ9TxF9Jj5cVIo71m55xq4hVL3yDbRe89g=\"}") + assertEquals(output.error, "") + assertEquals(output.json, "") } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt new file mode 100644 index 00000000000..2d1d3caf77e --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/thorchain/TestTHORSwapSigning.kt @@ -0,0 +1,71 @@ +package com.trustwallet.core.app.blockchains.thorchainswap + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexByteArray +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import wallet.core.jni.PrivateKey +import wallet.core.java.AnySigner +import wallet.core.jni.CoinType.ETHEREUM +import wallet.core.jni.proto.Ethereum.SigningOutput +import wallet.core.jni.proto.THORChainSwap +import wallet.core.jni.THORChainSwap.buildSwap +import com.trustwallet.core.app.utils.Numeric + +class TestTHORChainSwap { + + init { + System.loadLibrary("TrustWalletCore") + } + + @Test + fun testSwapEthBnb() { + // prepare swap input + val input = THORChainSwap.SwapInput.newBuilder() + input.apply { + fromChain = THORChainSwap.Chain.ETH + fromAddress = "0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7" + toAsset = THORChainSwap.Asset.newBuilder().apply { + chain = THORChainSwap.Chain.BNB + symbol = "BNB" + tokenId = "" + }.build() + toAddress = "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx" + vaultAddress = "0x1091c4De6a3cF09CdA00AbDAeD42c7c3B69C83EC" + routerAddress = "0x42A5Ed456650a09Dc10EBc6361A7480fDd61f27B" + fromAmount = "50000000000000000" + toAmountLimit = "600003" + } + + // serialize input + val inputSerialized = input.build().toByteArray() + assertEquals(Numeric.toHexString(inputSerialized), "0x0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a1135303030303030303030303030303030304206363030303033") + + // invoke swap + val outputData = buildSwap(inputSerialized) + assertEquals(outputData.count(), 311) + + // parse result in proto + val outputProto = THORChainSwap.SwapOutput.newBuilder().mergeFrom(outputData) + assertEquals(outputProto.fromChain, THORChainSwap.Chain.ETH) + assertEquals(outputProto.toChain, THORChainSwap.Chain.BNB) + assertEquals(outputProto.error.code, THORChainSwap.ErrorCode.OK) + assertTrue(outputProto.hasEthereum()) + val txInput = outputProto.ethereum + + // set few fields before signing + val txInputFull = txInput.toBuilder().apply { + chainId = ByteString.copyFrom("0x01".toHexByteArray()) + nonce = ByteString.copyFrom("0x03".toHexByteArray()) + gasPrice = ByteString.copyFrom("0x06FC23AC00".toHexByteArray()) + gasLimit = ByteString.copyFrom("0x013880".toHexByteArray()) + privateKey = ByteString.copyFrom(PrivateKey("0x4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904".toHexByteArray()).data()) + }.build() + + // sign and encode resulting input + val output = AnySigner.sign(txInputFull, ETHEREUM, SigningOutput.parser()) + + assertEquals(Numeric.toHexString(output.encoded.toByteArray()), "0xf90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000025a06ae104be3201baca38315352f81fac70ca4dd47339981914e64e91149813e780a066a3f0b2c44ddf5a96a38481274f623f552a593d723237d6742185f4885c0064") + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/waves/TestWavesAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/waves/TestWavesAddress.kt index d0669b9feca..ef8b4217e6c 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/waves/TestWavesAddress.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/waves/TestWavesAddress.kt @@ -18,7 +18,7 @@ class TestAddress { val pubkey = key.publicKeyCurve25519 val address = AnyAddress(pubkey, CoinType.WAVES) - assertEquals(pubkey.data().toHex(), "0x559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d".toLowerCase()) + assertEquals(pubkey.data().toHex(), "0x559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d".lowercase()) assertEquals(address.description(), "3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds") } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestBase32.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestBase32.kt new file mode 100644 index 00000000000..d564eff45d8 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestBase32.kt @@ -0,0 +1,35 @@ +package com.trustwallet.core.app.utils + +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.Base32 + +class TestBase32 { + init { + System.loadLibrary("TrustWalletCore"); + } + + @Test + fun testEncode() { + assertEquals(Base32.encode("HelloWorld".toByteArray()), "JBSWY3DPK5XXE3DE") + } + + @Test + fun testEncodeWithAlphabet() { + assertEquals(Base32.encodeWithAlphabet("7uoq6tp427uzv7fztkbsnn64iwotfrristwpryy".toByteArray(), "abcdefghijklmnopqrstuvwxyz234567"), "g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i") + } + + @Test + fun testDecode() { + var decoded = Base32.decode("JBSWY3DPK5XXE3DE") + + assertEquals(String(decoded, Charsets.UTF_8), "HelloWorld") + } + + @Test + fun testDecodeWithAlphabet() { + var decoded = Base32.decodeWithAlphabet("g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i", "abcdefghijklmnopqrstuvwxyz234567") + + assertEquals(String(decoded, Charsets.UTF_8), "7uoq6tp427uzv7fztkbsnn64iwotfrristwpryy") + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestBase64.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestBase64.kt new file mode 100644 index 00000000000..b69851da1b8 --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestBase64.kt @@ -0,0 +1,34 @@ +package com.trustwallet.core.app.utils + +import org.junit.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.Base64 + +class TestBase64 { + init { + System.loadLibrary("TrustWalletCore"); + } + + @Test + fun testEncode() { + assertEquals(Base64.encode("HelloWorld".toByteArray()), "SGVsbG9Xb3JsZA==") + } + + @Test + fun testDecode() { + val decoded = Base64.decode("SGVsbG9Xb3JsZA==") + assertEquals(String(decoded, Charsets.UTF_8), "HelloWorld") + } + + @Test + fun testEncodeUrl() { + assertEquals(Base64.encodeUrl("+\\?ab".toByteArray()), "K1w_YWI=") + } + + @Test + fun testDecodeUrl() { + val decoded = Base64.decodeUrl("K1w_YWI=") + assertEquals(String(decoded, Charsets.UTF_8), "+\\?ab") + } +} + diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestData.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestData.kt index 439c74f99ab..4df94d6b558 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestData.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestData.kt @@ -15,25 +15,22 @@ class TestData { assertEquals(Numeric.toHexString(data), "0x01020304") } + @Test fun testUsingExtensions() { - { - val data = "01020304".toHexBytes() - assertEquals(data.toHex(), "0x01020304") - } - { // with prefix - val data = "0x01020304".toHexBytes() - assertEquals(data.toHex(), "0x01020304") - } + val data = "01020304".toHexBytes() + assertEquals(data.toHex(), "0x01020304") + + // with prefix + val data2 = "0x01020304".toHexBytes() + assertEquals(data2.toHex(), "0x01020304") } + @Test fun testOddLength() { - { - val data = "0x0".toHexBytes() - assertEquals(data.toHex(), "0x00") - } - { - val data = "0x28fa6ae00".toHexBytes() - assertEquals(data.toHex(), "0x28fa6ae00") - } + val data = "0x0".toHexBytes() + assertEquals(data.toHex(), "0x00") + + val data2 = "0x28fa6ae00".toHexBytes() + assertEquals(data2.toHex(), "0x028fa6ae00") } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt index c6d069ae45b..abdbc8050ab 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/utils/TestPrivateKey.kt @@ -69,10 +69,10 @@ class TestPrivateKey { @Test fun testGetSharedKey() { val privateKeyData = "9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0".toHexBytes() - val privateKey = PrivateKey(privateKeyData)!! + val privateKey = PrivateKey(privateKeyData) val publicKeyData = "02a18a98316b5f52596e75bfa5ca9fa9912edd0c989b86b73d41bb64c9c6adb992".toHexBytes() - val publicKey = PublicKey(publicKeyData, PublicKeyType.SECP256K1)!! + val publicKey = PublicKey(publicKeyData, PublicKeyType.SECP256K1) val derivedData = privateKey.getSharedKey(publicKey, Curve.SECP256K1) assertNotNull(derivedData) @@ -83,10 +83,10 @@ class TestPrivateKey { @Test fun testGetSharedKeyWycherproof() { val privateKeyData = "f4b7ff7cccc98813a69fae3df222bfe3f4e28f764bf91b4a10d8096ce446b254".toHexBytes() - val privateKey = PrivateKey(privateKeyData)!! + val privateKey = PrivateKey(privateKeyData) val publicKeyData = "02d8096af8a11e0b80037e1ee68246b5dcbb0aeb1cf1244fd767db80f3fa27da2b".toHexBytes() - val publicKey = PublicKey(publicKeyData, PublicKeyType.SECP256K1)!! + val publicKey = PublicKey(publicKeyData, PublicKeyType.SECP256K1) val derivedData = privateKey.getSharedKey(publicKey, Curve.SECP256K1) assertNotNull(derivedData) @@ -97,11 +97,11 @@ class TestPrivateKey { @Test fun testGetSharedKeyBidirectional() { val privateKeyData1 = "9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0".toHexBytes() - val privateKey1 = PrivateKey(privateKeyData1)!! + val privateKey1 = PrivateKey(privateKeyData1) val publicKey1 = privateKey1.getPublicKeySecp256k1(true) val privateKeyData2 = "ef2cf705af8714b35c0855030f358f2bee356ff3579cea2607b2025d80133c3a".toHexBytes() - val privateKey2 = PrivateKey(privateKeyData2)!! + val privateKey2 = PrivateKey(privateKeyData2) val publicKey2 = privateKey2.getPublicKeySecp256k1(true) val derivedData1 = privateKey1.getSharedKey(publicKey2, Curve.SECP256K1) @@ -116,10 +116,10 @@ class TestPrivateKey { @Test fun testGetSharedKeyError() { val privateKeyData = "9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0".toHexBytes() - val privateKey = PrivateKey(privateKeyData)!! + val privateKey = PrivateKey(privateKeyData) val publicKeyData = "02a18a98316b5f52596e75bfa5ca9fa9912edd0c989b86b73d41bb64c9c6adb992".toHexBytes() - val publicKey = PublicKey(publicKeyData, PublicKeyType.SECP256K1)!! + val publicKey = PublicKey(publicKeyData, PublicKeyType.SECP256K1) val derivedData = privateKey.getSharedKey(publicKey, Curve.ED25519) assertNull(derivedData) diff --git a/android/build.gradle b/android/build.gradle index 4ee76b45799..b1ca946f2d1 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.4.21' + ext.kotlin_version = '1.6.10' repositories { google() mavenCentral() @@ -10,7 +10,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:4.2.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 493d6573668..4cf5f4720c8 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Mar 16 12:35:47 JST 2021 +#Wed Jan 19 18:01:57 JST 2022 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.2-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/android/trustwalletcore/build.gradle b/android/trustwalletcore/build.gradle index f1c8028a4d1..bf97bfd2412 100644 --- a/android/trustwalletcore/build.gradle +++ b/android/trustwalletcore/build.gradle @@ -3,11 +3,10 @@ apply plugin: 'maven-publish' group='com.github.trustwallet' android { - compileSdkVersion 28 - ndkVersion '21.2.6472646' + compileSdkVersion 32 + ndkVersion '23.1.7779620' defaultConfig { minSdkVersion 23 - targetSdkVersion 29 versionCode 1 versionName "1.0" externalNativeBuild { @@ -30,7 +29,7 @@ android { minifyEnabled false // limit platforms built for testing ndk { - abiFilters 'x86' + abiFilters 'x86', 'arm64-v8a' } } } @@ -41,16 +40,14 @@ android { externalNativeBuild { cmake { - version "3.10.2" + version "3.18.1" path "../../CMakeLists.txt" } } } dependencies { - implementation 'io.grpc:grpc-protobuf:1.34.0' + implementation 'com.google.protobuf:protobuf-javalite:3.21.2' } apply from: 'maven-push.gradle' - - diff --git a/bootstrap.sh b/bootstrap.sh index e5c948496d8..1dba588dd30 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,27 +1,12 @@ #!/usr/bin/env bash +# +# Initializes the workspace with dependencies, then performs full build # Fail if any commands fails set -e -echo "#### Initializing... ####" +echo "#### Initializing workspace with dependencies ... ####" tools/install-dependencies -echo "#### Generating files... ####" -tools/generate-files - -echo "#### Building... ####" -cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug -make -Cbuild -j12 tests TrezorCryptoTests - -if [ -x "$(command -v clang-tidy)" ]; then - echo "#### Linting... ####" - tools/lint -fi - -echo "#### Testing... ####" -export CK_TIMEOUT_MULTIPLIER=4 -build/trezor-crypto/crypto/tests/TrezorCryptoTests - -ROOT="`dirname \"$0\"`" -TESTS_ROOT="`(cd \"$ROOT/tests\" && pwd)`" -build/tests/tests "$TESTS_ROOT" +echo "#### Building and running tests ... ####" +tools/build-and-test diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake new file mode 100644 index 00000000000..d7d54fd984e --- /dev/null +++ b/cmake/CompilerWarnings.cmake @@ -0,0 +1,95 @@ +macro(target_enable_asan target) + message("-- ASAN Enabled, Configuring...") + target_compile_options(${target} PUBLIC + $<$,$>:-fsanitize=address -fno-omit-frame-pointer> + $<$,$>:-fsanitize=address -fno-omit-frame-pointer>) + target_link_options(${target} PUBLIC + $<$,$>:-fsanitize=address -fno-omit-frame-pointer> + $<$,$>:-fsanitize=address -fno-omit-frame-pointer>) +endmacro() + +macro(target_enable_coverage target) + message(STATUS "Code coverage ON") + # This option is used to compile and link code instrumented for coverage analysis. + # The option is a synonym for -fprofile-arcs -ftest-coverage (when compiling) and -lgcov (when linking). + # See the documentation for those options for more details. + # https://gcc.gnu.org/onlinedocs/gcc-9.3.0/gcc/Instrumentation-Options.html + if (TW_IDE_CLION) + message(STATUS "Code coverage for Clion ON") + target_compile_options(${target} PUBLIC + $<$,$>:-fprofile-instr-generate -fcoverage-mapping> + $<$,$>:-fprofile-instr-generate -fcoverage-mapping> + $<$,$>:-fprofile-instr-generate -fcoverage-mapping>) + target_link_options(${target} PUBLIC + $<$,$>:-fprofile-instr-generate -fcoverage-mapping> + $<$,$>:-fprofile-instr-generate -fcoverage-mapping> + $<$,$>:-fprofile-instr-generate -fcoverage-mapping>) + else() + target_compile_options(${target} PUBLIC + $<$,$>:--coverage> + $<$,$>:--coverage>) + target_link_options(${target} PUBLIC + $<$,$>:--coverage> + $<$,$>:--coverage>) + endif () +endmacro() + +add_library(tw_error_settings INTERFACE) +add_library(tw::error_settings ALIAS tw_error_settings) + +add_library(tw_defaults_features INTERFACE) +add_library(tw::defaults_features ALIAS tw_defaults_features) + +add_library(tw_optimize_settings INTERFACE) +add_library(tw::optimize_settings ALIAS tw_optimize_settings) + +if(NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) + target_compile_options( + tw_error_settings + INTERFACE + -Wall + -Wextra # reasonable and standard + -Wfatal-errors # short error report + -Wshadow # warn the user if a variable declaration shadows one from a + -Wshorten-64-to-32 + -Wno-nullability-completeness + # parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a + # non-virtual destructor. This helps catch hard to track down memory errors + -Wcast-align # warn for potential performance problem casts + #-Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual + # function + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output + ) +endif () + + + + +if (TW_WARNINGS_AS_ERRORS) + target_compile_options( + tw_error_settings + INTERFACE + -Werror + ) +endif () + +target_compile_features(tw_defaults_features INTERFACE cxx_std_20) + +target_compile_options(tw_optimize_settings INTERFACE + $<$,$>:-O0 -g> + $<$,$>:-O0 -g> + $<$,$>:-O2> + $<$,$>:-O2> + ) + +function(set_project_warnings project_name) + target_link_libraries(${project_name} INTERFACE tw::error_settings tw::defaults_features tw::optimize_settings) + + if (NOT TARGET ${project_name}) + message(AUTHOR_WARNING "${project_name} is not a target, thus no compiler warning were added.") + endif () +endfunction() diff --git a/cmake/FindHostPackage.cmake b/cmake/FindHostPackage.cmake new file mode 100644 index 00000000000..188a2ee7e9d --- /dev/null +++ b/cmake/FindHostPackage.cmake @@ -0,0 +1,9 @@ +macro(find_host_package) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) + find_package(${ARGN}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endmacro(find_host_package) diff --git a/cmake/PVS-Studio.cmake b/cmake/PVS-Studio.cmake new file mode 100644 index 00000000000..d19bc3b105e --- /dev/null +++ b/cmake/PVS-Studio.cmake @@ -0,0 +1,615 @@ +# 2006-2008 (c) Viva64.com Team +# 2008-2018 (c) OOO "Program Verification Systems" +# +# Version 12 + +cmake_minimum_required(VERSION 3.0.0) +cmake_policy(SET CMP0054 NEW) + +if (PVS_STUDIO_AS_SCRIPT) + # This code runs at build time. + # It executes pvs-studio-analyzer and propagates its return value. + + set(in_cl_params FALSE) + set(additional_args) + + foreach (arg ${PVS_STUDIO_COMMAND}) + if (NOT in_cl_params) + if ("${arg}" STREQUAL "--cl-params") + set(in_cl_params TRUE) + endif () + else () + # A workaround for macOS frameworks (e.g. QtWidgets.framework) + # You can test this workaround on this project: https://github.com/easyaspi314/MidiEditor/tree/gba + if (APPLE AND "${arg}" MATCHES "^-I(.*)\\.framework$") + STRING(REGEX REPLACE "^-I(.*)\\.framework$" "\\1.framework" framework "${arg}") + if (IS_ABSOLUTE "${framework}") + get_filename_component(framework "${framework}" DIRECTORY) + list(APPEND additional_args "-iframework") + list(APPEND additional_args "${framework}") + endif () + endif () + endif () + endforeach () + + file(REMOVE "${PVS_STUDIO_LOG_FILE}") + execute_process(COMMAND ${PVS_STUDIO_COMMAND} ${additional_args} + RESULT_VARIABLE result + OUTPUT_VARIABLE output + ERROR_VARIABLE error) + + if (result AND NOT output MATCHES "^No compilation units were found\\.") + message(FATAL_ERROR "PVS-Studio exited with non-zero code.\nStdout:\n${output}\nStderr:\n${error}\n") + endif() + + return() +endif () + +if(__PVS_STUDIO_INCLUDED) + return() +endif() +set(__PVS_STUDIO_INCLUDED TRUE) + +set(PVS_STUDIO_SCRIPT "${CMAKE_CURRENT_LIST_FILE}") + +function (pvs_studio_log TEXT) + if (PVS_STUDIO_DEBUG) + message("PVS-Studio: ${TEXT}") + endif () +endfunction () + +function (pvs_studio_relative_path VAR ROOT FILEPATH) + if (WIN32) + STRING(REGEX REPLACE "\\\\" "/" ROOT ${ROOT}) + STRING(REGEX REPLACE "\\\\" "/" FILEPATH ${FILEPATH}) + endif() + set("${VAR}" "${FILEPATH}" PARENT_SCOPE) + if (IS_ABSOLUTE "${FILEPATH}") + file(RELATIVE_PATH RPATH "${ROOT}" "${FILEPATH}") + if (NOT IS_ABSOLUTE "${RPATH}") + set("${VAR}" "${RPATH}" PARENT_SCOPE) + endif() + endif() +endfunction () + +function (pvs_studio_join_path VAR DIR1 DIR2) + if ("${DIR2}" MATCHES "^(/|~|.:/).*$" OR "${DIR1}" STREQUAL "") + set("${VAR}" "${DIR2}" PARENT_SCOPE) + else () + set("${VAR}" "${DIR1}/${DIR2}" PARENT_SCOPE) + endif () +endfunction () + +macro (pvs_studio_append_flags_from_property CXX C DIR PREFIX) + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (PROP ${PROPERTY}) + pvs_studio_join_path(PROP "${DIR}" "${PROP}") + + if (APPLE AND "${PREFIX}" STREQUAL "-I" AND IS_ABSOLUTE "${PROP}" AND "${PROP}" MATCHES "\\.framework$") + get_filename_component(FRAMEWORK "${PROP}" DIRECTORY) + list(APPEND "${CXX}" "-iframework") + list(APPEND "${CXX}" "${FRAMEWORK}") + list(APPEND "${C}" "-iframework") + list(APPEND "${C}" "${FRAMEWORK}") + pvs_studio_log("framework: ${FRAMEWORK}") + elseif (NOT "${PROP}" STREQUAL "") + list(APPEND "${CXX}" "${PREFIX}${PROP}") + list(APPEND "${C}" "${PREFIX}${PROP}") + endif() + endforeach () + endif () +endmacro () + +macro (pvs_studio_append_standard_flag FLAGS STANDARD) + if ("${STANDARD}" MATCHES "^(99|11|14|17|20)$") + if ("${PVS_STUDIO_PREPROCESSOR}" MATCHES "gcc|clang") + list(APPEND "${FLAGS}" "-std=c++${STANDARD}") + endif () + endif () +endmacro () + +function (pvs_studio_set_directory_flags DIRECTORY CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" INCLUDE_DIRECTORIES) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "${DIRECTORY}" "-I") + + get_directory_property(PROPERTY DIRECTORY "${DIRECTORY}" COMPILE_DEFINITIONS) + pvs_studio_append_flags_from_property(CXX_FLAGS C_FLAGS "" "-D") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_set_target_flags TARGET CXX C) + set(CXX_FLAGS "${${CXX}}") + set(C_FLAGS "${${C}}") + + if (NOT MSVC) + list(APPEND CXX_FLAGS "$<$:--sysroot=${CMAKE_SYSROOT}>") + list(APPEND C_FLAGS "$<$:--sysroot=${CMAKE_SYSROOT}>") + endif () + + set(prop_incdirs "$") + list(APPEND CXX_FLAGS "$<$:-I$-I>>") + list(APPEND C_FLAGS "$<$:-I$-I>>") + + set(prop_compdefs "$") + list(APPEND CXX_FLAGS "$<$:-D$-D>>") + list(APPEND C_FLAGS "$<$:-D$-D>>") + + set(prop_compopt "$") + list(APPEND CXX_FLAGS "$<$:$>>") + list(APPEND C_FLAGS "$<$:$>>") + + set("${CXX}" "${CXX_FLAGS}" PARENT_SCOPE) + set("${C}" "${C_FLAGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_set_source_file_flags SOURCE) + set(LANGUAGE "") + + string(TOLOWER "${SOURCE}" SOURCE_LOWER) + if ("${LANGUAGE}" STREQUAL "" AND "${SOURCE_LOWER}" MATCHES "^.*\\.(c|cpp|cc|cx|cxx|cp|c\\+\\+)$") + if ("${SOURCE}" MATCHES "^.*\\.c$") + set(LANGUAGE C) + else () + set(LANGUAGE CXX) + endif () + endif () + + if ("${LANGUAGE}" STREQUAL "C") + set(CL_PARAMS ${PVS_STUDIO_C_FLAGS} ${PVS_STUDIO_TARGET_C_FLAGS} -DPVS_STUDIO) + elseif ("${LANGUAGE}" STREQUAL "CXX") + set(CL_PARAMS ${PVS_STUDIO_CXX_FLAGS} ${PVS_STUDIO_TARGET_CXX_FLAGS} -DPVS_STUDIO) + endif () + + set(PVS_STUDIO_LANGUAGE "${LANGUAGE}" PARENT_SCOPE) + set(PVS_STUDIO_CL_PARAMS "${CL_PARAMS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_analyze_file SOURCE SOURCE_DIR BINARY_DIR) + set(PLOGS ${PVS_STUDIO_PLOGS}) + pvs_studio_set_source_file_flags("${SOURCE}") + + get_filename_component(SOURCE "${SOURCE}" REALPATH) + + get_source_file_property(PROPERTY "${SOURCE}" HEADER_FILE_ONLY) + if (PROPERTY) + return() + endif () + + pvs_studio_relative_path(SOURCE_RELATIVE "${SOURCE_DIR}" "${SOURCE}") + pvs_studio_join_path(SOURCE "${SOURCE_DIR}" "${SOURCE}") + + set(LOG "${BINARY_DIR}/PVS-Studio/${SOURCE_RELATIVE}.plog") + get_filename_component(LOG "${LOG}" REALPATH) + get_filename_component(PARENT_DIR "${LOG}" DIRECTORY) + + if (EXISTS "${SOURCE}" AND NOT TARGET "${LOG}" AND NOT "${PVS_STUDIO_LANGUAGE}" STREQUAL "") + # A workaround to support implicit dependencies for ninja generators. + set(depPvsArg) + set(depCommandArg) + if (CMAKE_VERSION VERSION_GREATER 3.6 AND "${CMAKE_GENERATOR}" STREQUAL "Ninja") + pvs_studio_relative_path(relLog "${CMAKE_BINARY_DIR}" "${LOG}") + set(depPvsArg --dep-file "${LOG}.d" --dep-file-target "${relLog}") + set(depCommandArg DEPFILE "${LOG}.d") + endif () + + # https://public.kitware.com/Bug/print_bug_page.php?bug_id=14353 + # https://public.kitware.com/Bug/file/5436/expand_command.cmake + # + # It is a workaround to expand generator expressions. + set(cmdline "${PVS_STUDIO_BIN}" analyze + --output-file "${LOG}" + --source-file "${SOURCE}" + ${depPvsArg} + ${PVS_STUDIO_ARGS} + --cl-params "${PVS_STUDIO_CL_PARAMS}" "${SOURCE}") + + string(REPLACE ";" "$" cmdline "${cmdline}") + set(pvscmd "${CMAKE_COMMAND}" + -D "PVS_STUDIO_AS_SCRIPT=TRUE" + -D "PVS_STUDIO_COMMAND=${cmdline}" + -D "PVS_STUDIO_LOG_FILE=${LOG}" + -P "${PVS_STUDIO_SCRIPT}" + ) + + add_custom_command(OUTPUT "${LOG}" + COMMAND "${CMAKE_COMMAND}" -E make_directory "${PARENT_DIR}" + COMMAND "${CMAKE_COMMAND}" -E remove_directory "${LOG}" + COMMAND ${pvscmd} + WORKING_DIRECTORY "${BINARY_DIR}" + DEPENDS "${SOURCE}" "${PVS_STUDIO_SUPPRESS_BASE}" "${PVS_STUDIO_DEPENDS}" + IMPLICIT_DEPENDS "${PVS_STUDIO_LANGUAGE}" "${SOURCE}" + ${depCommandArg} + VERBATIM + COMMENT "Analyzing ${PVS_STUDIO_LANGUAGE} file ${SOURCE_RELATIVE}") + list(APPEND PLOGS "${LOG}") + endif () + set(PVS_STUDIO_PLOGS "${PLOGS}" PARENT_SCOPE) +endfunction () + +function (pvs_studio_analyze_target TARGET DIR) + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}") + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + + get_target_property(PROPERTY "${TARGET}" SOURCES) + pvs_studio_relative_path(BINARY_DIR "${CMAKE_SOURCE_DIR}" "${DIR}") + if ("${BINARY_DIR}" MATCHES "^/.*$") + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "PVS-Studio/__${BINARY_DIR}") + else () + pvs_studio_join_path(BINARY_DIR "${CMAKE_BINARY_DIR}" "${BINARY_DIR}") + endif () + + file(MAKE_DIRECTORY "${BINARY_DIR}") + + pvs_studio_set_directory_flags("${DIR}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + pvs_studio_set_target_flags("${TARGET}" PVS_STUDIO_TARGET_CXX_FLAGS PVS_STUDIO_TARGET_C_FLAGS) + + if (NOT "${PROPERTY}" STREQUAL "NOTFOUND" AND NOT "${PROPERTY}" STREQUAL "PROPERTY-NOTFOUND") + foreach (SOURCE ${PROPERTY}) + pvs_studio_join_path(SOURCE "${DIR}" "${SOURCE}") + pvs_studio_analyze_file("${SOURCE}" "${DIR}" "${BINARY_DIR}") + endforeach () + endif () + + set(PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}" PARENT_SCOPE) +endfunction () + +set(PVS_STUDIO_RECURSIVE_TARGETS) +set(PVS_STUDIO_RECURSIVE_TARGETS_NEW) + +macro(pvs_studio_get_recursive_targets TARGET) + get_target_property(libs "${TARGET}" LINK_LIBRARIES) + foreach (lib IN LISTS libs) + list(FIND PVS_STUDIO_RECURSIVE_TARGETS "${lib}" index) + if (TARGET "${lib}" AND "${index}" STREQUAL -1) + get_target_property(target_type "${lib}" TYPE) + if (NOT "${target_type}" STREQUAL "INTERFACE_LIBRARY") + list(APPEND PVS_STUDIO_RECURSIVE_TARGETS "${lib}") + list(APPEND PVS_STUDIO_RECURSIVE_TARGETS_NEW "${lib}") + pvs_studio_get_recursive_targets("${lib}") + endif () + endif () + endforeach () +endmacro() + +option(PVS_STUDIO_DISABLE OFF "Disable PVS-Studio targets") +option(PVS_STUDIO_DEBUG OFF "Add debug info") + +# pvs_studio_add_target +# Target options: +# ALL add PVS-Studio target to default build (default: off) +# TARGET target name of analysis target (default: pvs) +# ANALYZE targets... targets to analyze +# RECURSIVE analyze target's dependencies (requires CMake 3.5+) +# COMPILE_COMMANDS use compile_commands.json instead of targets (specified by the 'ANALYZE' option) to determine files for analysis +# (set CMAKE_EXPORT_COMPILE_COMMANDS, available only for Makefile and Ninja generators) +# +# Output options: +# OUTPUT prints report to stdout +# LOG path path to report (default: ${CMAKE_CURRENT_BINARY_DIR}/PVS-Studio.log) +# FORMAT format format of report +# MODE mode analyzers/levels filter (default: GA:1,2) +# HIDE_HELP do not print help message +# +# Analyzer options: +# PLATFORM name linux32/linux64 (default: linux64) +# PREPROCESSOR name preprocessor type: gcc/clang (default: auto detected) +# LICENSE path path to PVS-Studio.lic (default: ~/.config/PVS-Studio/PVS-Studio.lic) +# CONFIG path path to PVS-Studio.cfg +# CFG_TEXT text embedded PVS-Studio.cfg +# SUPPRESS_BASE path to suppress base file +# KEEP_COMBINED_PLOG do not delete combined plog file *.pvs.raw for further processing with plog-converter +# +# Misc options: +# DEPENDS targets.. additional target dependencies +# SOURCES path... list of source files to analyze +# BIN path path to pvs-studio-analyzer (Unix) or CompilerCommandsAnalyzer.exe (Windows) +# CONVERTER path path to plog-converter (Unix) or HtmlGenerator.exe (Windows) +# C_FLAGS flags... additional C_FLAGS +# CXX_FLAGS flags... additional CXX_FLAGS +# ARGS args... additional pvs-studio-analyzer/CompilerCommandsAnalyzer.exe flags +# CONVERTER_ARGS args... additional plog-converter/HtmlGenerator.exe flags +function (pvs_studio_add_target) + macro (default VAR VALUE) + if ("${${VAR}}" STREQUAL "") + set("${VAR}" "${VALUE}") + endif () + endmacro () + + set(PVS_STUDIO_SUPPORTED_PREPROCESSORS "gcc|clang|visualcpp") + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + set(DEFAULT_PREPROCESSOR "clang") + elseif (MSVC) + set(DEFAULT_PREPROCESSOR "visualcpp") + else () + set(DEFAULT_PREPROCESSOR "gcc") + endif () + + set(OPTIONAL OUTPUT ALL RECURSIVE HIDE_HELP KEEP_COMBINED_PLOG COMPILE_COMMANDS KEEP_INTERMEDIATE_FILES) + set(SINGLE LICENSE CONFIG TARGET LOG FORMAT BIN CONVERTER PLATFORM PREPROCESSOR CFG_TEXT SUPPRESS_BASE) + set(MULTI SOURCES C_FLAGS CXX_FLAGS ARGS DEPENDS ANALYZE MODE CONVERTER_ARGS) + cmake_parse_arguments(PVS_STUDIO "${OPTIONAL}" "${SINGLE}" "${MULTI}" ${ARGN}) + + + default(PVS_STUDIO_C_FLAGS "") + default(PVS_STUDIO_CXX_FLAGS "") + default(PVS_STUDIO_TARGET "pvs") + default(PVS_STUDIO_LOG "PVS-Studio.log") + + set(PATHS) + + if (WIN32) + # The registry value is only read when you do some cache operation on it. + # https://stackoverflow.com/questions/1762201/reading-registry-values-with-cmake + GET_FILENAME_COMPONENT(ROOT "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\ProgramVerificationSystems\\PVS-Studio;installDir]" ABSOLUTE CACHE) + + if(EXISTS "${ROOT}") + set(PATHS "${ROOT}") + else() + set(ROOT "PROGRAMFILES(X86)") + set(ROOT "$ENV{${ROOT}}/PVS-Studio") + string(REPLACE \\ / ROOT "${ROOT}") + + if (EXISTS "${ROOT}") + set(PATHS "${ROOT}") + else() + set(ROOT "PATH") + set(ROOT "$ENV{${ROOT}}") + set(PATHS "${ROOT}") + endif () + endif() + + + + default(PVS_STUDIO_BIN "CompilerCommandsAnalyzer.exe") + default(PVS_STUDIO_CONVERTER "HtmlGenerator.exe") + else () + default(PVS_STUDIO_BIN "pvs-studio-analyzer") + default(PVS_STUDIO_CONVERTER "plog-converter") + endif () + + find_program(PVS_STUDIO_BIN_PATH "${PVS_STUDIO_BIN}" ${PATHS}) + set(PVS_STUDIO_BIN "${PVS_STUDIO_BIN_PATH}") + + if (NOT EXISTS "${PVS_STUDIO_BIN}") + message(FATAL_ERROR "pvs-studio-analyzer is not found") + endif () + + find_program(PVS_STUDIO_CONVERTER_PATH "${PVS_STUDIO_CONVERTER}" ${PATHS}) + set(PVS_STUDIO_CONVERTER "${PVS_STUDIO_CONVERTER_PATH}") + + if (NOT EXISTS "${PVS_STUDIO_CONVERTER}") + message(FATAL_ERROR "plog-converter is not found") + endif () + + default(PVS_STUDIO_MODE "GA:1,2") + default(PVS_STUDIO_PREPROCESSOR "${DEFAULT_PREPROCESSOR}") + if (WIN32) + default(PVS_STUDIO_PLATFORM "x64") + else () + default(PVS_STUDIO_PLATFORM "linux64") + endif () + + string(REPLACE ";" "+" PVS_STUDIO_MODE "${PVS_STUDIO_MODE}") + + if ("${PVS_STUDIO_CONFIG}" STREQUAL "" AND NOT "${PVS_STUDIO_CFG_TEXT}" STREQUAL "") + set(PVS_STUDIO_CONFIG "${CMAKE_BINARY_DIR}/PVS-Studio.cfg") + + set(PVS_STUDIO_CONFIG_COMMAND "${CMAKE_COMMAND}" -E echo "${PVS_STUDIO_CFG_TEXT}" > "${PVS_STUDIO_CONFIG}") + + add_custom_command(OUTPUT "${PVS_STUDIO_CONFIG}" + COMMAND ${PVS_STUDIO_CONFIG_COMMAND} + WORKING_DIRECTORY "${BINARY_DIR}" + COMMENT "Generating PVS-Studio.cfg") + + list(APPEND PVS_STUDIO_DEPENDS "${PVS_STUDIO_CONFIG}") + endif () + if (NOT "${PVS_STUDIO_PREPROCESSOR}" MATCHES "^${PVS_STUDIO_SUPPORTED_PREPROCESSORS}$") + message(FATAL_ERROR "Preprocessor ${PVS_STUDIO_PREPROCESSOR} isn't supported. Available options: ${PVS_STUDIO_SUPPORTED_PREPROCESSORS}.") + endif () + + pvs_studio_append_standard_flag(PVS_STUDIO_CXX_FLAGS "${CMAKE_CXX_STANDARD}") + pvs_studio_set_directory_flags("${CMAKE_CURRENT_SOURCE_DIR}" PVS_STUDIO_CXX_FLAGS PVS_STUDIO_C_FLAGS) + + if (NOT "${PVS_STUDIO_LICENSE}" STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --lic-file "${PVS_STUDIO_LICENSE}") + endif () + + if (NOT ${PVS_STUDIO_CONFIG} STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --cfg "${PVS_STUDIO_CONFIG}") + endif () + + list(APPEND PVS_STUDIO_ARGS --platform "${PVS_STUDIO_PLATFORM}" + --preprocessor "${PVS_STUDIO_PREPROCESSOR}") + + if (NOT "${PVS_STUDIO_SUPPRESS_BASE}" STREQUAL "") + pvs_studio_join_path(PVS_STUDIO_SUPPRESS_BASE "${CMAKE_CURRENT_SOURCE_DIR}" "${PVS_STUDIO_SUPPRESS_BASE}") + list(APPEND PVS_STUDIO_ARGS --suppress-file "${PVS_STUDIO_SUPPRESS_BASE}") + endif () + + if (NOT "${CMAKE_CXX_COMPILER}" STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --cxx "${CMAKE_CXX_COMPILER}") + endif () + + if (NOT "${CMAKE_C_COMPILER}" STREQUAL "") + list(APPEND PVS_STUDIO_ARGS --cc "${CMAKE_C_COMPILER}") + endif () + + if (PVS_STUDIO_KEEP_INTERMEDIATE_FILES) + list(APPEND PVS_STUDIO_ARGS --dump-files) + endif() + + string(REGEX REPLACE [123,:] "" ANALYZER_MODE ${PVS_STUDIO_MODE}) + if (NOT "$ANALYZER_MODE" STREQUAL "GA") + list (APPEND PVS_STUDIO_ARGS -a "${ANALYZER_MODE}") + endif () + + set(PVS_STUDIO_PLOGS "") + + set(PVS_STUDIO_RECURSIVE_TARGETS_NEW) + if (${PVS_STUDIO_RECURSIVE}) + foreach (TARGET IN LISTS PVS_STUDIO_ANALYZE) + list(APPEND PVS_STUDIO_RECURSIVE_TARGETS_NEW "${TARGET}") + pvs_studio_get_recursive_targets("${TARGET}") + endforeach () + endif () + + set(inc_path) + + foreach (TARGET ${PVS_STUDIO_ANALYZE}) + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}") + string(FIND "${TARGET}" ":" DELIM) + if ("${DELIM}" GREATER "-1") + math(EXPR DELIMI "${DELIM}+1") + string(SUBSTRING "${TARGET}" "${DELIMI}" "-1" DIR) + string(SUBSTRING "${TARGET}" "0" "${DELIM}" TARGET) + pvs_studio_join_path(DIR "${CMAKE_CURRENT_SOURCE_DIR}" "${DIR}") + else () + get_target_property(TARGET_SOURCE_DIR "${TARGET}" SOURCE_DIR) + if (EXISTS "${TARGET_SOURCE_DIR}") + set(DIR "${TARGET_SOURCE_DIR}") + endif () + endif () + pvs_studio_analyze_target("${TARGET}" "${DIR}") + list(APPEND PVS_STUDIO_DEPENDS "${TARGET}") + + if ("${inc_path}" STREQUAL "") + set(inc_path "$") + else () + set(inc_path "${inc_path}$$") + endif () + endforeach () + + foreach (TARGET ${PVS_STUDIO_RECURSIVE_TARGETS_NEW}) + set(DIR "${CMAKE_CURRENT_SOURCE_DIR}") + get_target_property(TARGET_SOURCE_DIR "${TARGET}" SOURCE_DIR) + if (EXISTS "${TARGET_SOURCE_DIR}") + set(DIR "${TARGET_SOURCE_DIR}") + endif () + pvs_studio_analyze_target("${TARGET}" "${DIR}") + list(APPEND PVS_STUDIO_DEPENDS "${TARGET}") + endforeach () + + set(PVS_STUDIO_TARGET_CXX_FLAGS "") + set(PVS_STUDIO_TARGET_C_FLAGS "") + foreach (SOURCE ${PVS_STUDIO_SOURCES}) + pvs_studio_analyze_file("${SOURCE}" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") + endforeach () + + if (PVS_STUDIO_COMPILE_COMMANDS) + set(COMPILE_COMMANDS_LOG "${PVS_STUDIO_LOG}.pvs.analyzer.raw") + if (NOT CMAKE_EXPORT_COMPILE_COMMANDS) + message(FATAL_ERROR "You should set CMAKE_EXPORT_COMPILE_COMMANDS to TRUE") + endif () + add_custom_command( + OUTPUT "${COMPILE_COMMANDS_LOG}" + COMMAND "${PVS_STUDIO_BIN}" analyze -i + --output-file "${COMPILE_COMMANDS_LOG}.always" + ${PVS_STUDIO_ARGS} + COMMENT "Analyzing with PVS-Studio" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + DEPENDS "${PVS_STUDIO_SUPPRESS_BASE}" "${PVS_STUDIO_DEPENDS}" + ) + list(APPEND PVS_STUDIO_PLOGS_LOGS "${COMPILE_COMMANDS_LOG}.always") + list(APPEND PVS_STUDIO_PLOGS_DEPENDENCIES "${COMPILE_COMMANDS_LOG}") + endif () + + pvs_studio_relative_path(LOG_RELATIVE "${CMAKE_BINARY_DIR}" "${PVS_STUDIO_LOG}") + if (PVS_STUDIO_PLOGS OR PVS_STUDIO_COMPILE_COMMANDS) + if (WIN32) + string(REPLACE / \\ PVS_STUDIO_PLOGS "${PVS_STUDIO_PLOGS}") + endif () + if (WIN32) + set(COMMANDS COMMAND type ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_LOGS} > "${PVS_STUDIO_LOG}" 2>nul || cd .) + else () + set(COMMANDS COMMAND cat ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_LOGS} > "${PVS_STUDIO_LOG}" 2>/dev/null || true) + endif () + set(COMMENT "Generating ${LOG_RELATIVE}") + if (NOT "${PVS_STUDIO_FORMAT}" STREQUAL "" OR PVS_STUDIO_OUTPUT) + if ("${PVS_STUDIO_FORMAT}" STREQUAL "") + set(PVS_STUDIO_FORMAT "errorfile") + endif () + set(converter_no_help "") + if (PVS_STUDIO_HIDE_HELP) + set(converter_no_help "--noHelpMessages") + endif() + list(APPEND COMMANDS + COMMAND "${CMAKE_COMMAND}" -E remove -f "${PVS_STUDIO_LOG}.pvs.raw" + COMMAND "${CMAKE_COMMAND}" -E rename "${PVS_STUDIO_LOG}" "${PVS_STUDIO_LOG}.pvs.raw" + COMMAND "${PVS_STUDIO_CONVERTER}" "${PVS_STUDIO_CONVERTER_ARGS}" ${converter_no_help} -t "${PVS_STUDIO_FORMAT}" "${PVS_STUDIO_LOG}.pvs.raw" -o "${PVS_STUDIO_LOG}" -a "${PVS_STUDIO_MODE}" + ) + if(NOT PVS_STUDIO_KEEP_COMBINED_PLOG) + list(APPEND COMMANDS COMMAND "${CMAKE_COMMAND}" -E remove -f "${PVS_STUDIO_LOG}.pvs.raw") + endif() + endif () + else () + set(COMMANDS COMMAND "${CMAKE_COMMAND}" -E touch "${PVS_STUDIO_LOG}") + set(COMMENT "Generating ${LOG_RELATIVE}: no sources found") + endif () + + if (WIN32) + string(REPLACE / \\ PVS_STUDIO_LOG "${PVS_STUDIO_LOG}") + endif () + + if (CMAKE_GENERATOR STREQUAL "Unix Makefiles") + get_filename_component(LOG_NAME ${LOG_RELATIVE} NAME) + set(LOG_TARGET "${PVS_STUDIO_TARGET}-${LOG_NAME}-log") + add_custom_target("${LOG_TARGET}" + BYPRODUCTS "${PVS_STUDIO_LOG}" + ${COMMANDS} + COMMENT "${COMMENT}" + DEPENDS ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_DEPENDENCIES} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + else() + set(LOG_TARGET "${PVS_STUDIO_LOG}") + add_custom_command(OUTPUT "${LOG_TARGET}" + ${COMMANDS} + COMMENT "${COMMENT}" + DEPENDS ${PVS_STUDIO_PLOGS} ${PVS_STUDIO_PLOGS_DEPENDENCIES} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + endif() + + if (PVS_STUDIO_ALL) + set(ALL "ALL") + else () + set(ALL "") + endif () + + if (PVS_STUDIO_OUTPUT) + if (WIN32) + set(COMMANDS COMMAND type "${PVS_STUDIO_LOG}" 1>&2) + else () + set(COMMANDS COMMAND cat "${PVS_STUDIO_LOG}" 1>&2) + endif() + else () + set(COMMANDS "") + endif () + + set(props_file "${CMAKE_BINARY_DIR}/${PVS_STUDIO_TARGET}.user.props") + file(WRITE "${props_file}" [=[ + + + + + + + + true + + + +]=]) + + add_custom_target("${PVS_STUDIO_TARGET}" ${ALL} ${COMMANDS} + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" + DEPENDS ${PVS_STUDIO_DEPENDS} "${LOG_TARGET}") + set_target_properties("${PVS_STUDIO_TARGET}" PROPERTIES VS_USER_PROPS "${props_file}") + + # A workaround to add implicit dependencies of source files from include directories + set_target_properties("${PVS_STUDIO_TARGET}" PROPERTIES INCLUDE_DIRECTORIES "${inc_path}") +endfunction () diff --git a/cmake/Protobuf.cmake b/cmake/Protobuf.cmake index 6d80eefde97..891d734ba24 100644 --- a/cmake/Protobuf.cmake +++ b/cmake/Protobuf.cmake @@ -1,5 +1,11 @@ -set(protobuf_SOURCE_DIR ${CMAKE_SOURCE_DIR}/build/local/src/protobuf/protobuf-3.14.0) -set(protobuf_source_dir ${CMAKE_SOURCE_DIR}/build/local/src/protobuf/protobuf-3.14.0) +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + +set(protobuf_SOURCE_DIR ${CMAKE_SOURCE_DIR}/build/local/src/protobuf/protobuf-3.19.2) +set(protobuf_source_dir ${CMAKE_SOURCE_DIR}/build/local/src/protobuf/protobuf-3.19.2) # sort + uniq -u # https://github.com/protocolbuffers/protobuf/blob/master/cmake/libprotobuf.cmake @@ -24,11 +30,15 @@ set(protobuf_SOURCE_FILES ${protobuf_source_dir}/src/google/protobuf/extension_set_heavy.cc ${protobuf_source_dir}/src/google/protobuf/field_mask.pb.cc ${protobuf_source_dir}/src/google/protobuf/generated_enum_util.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_bases.cc ${protobuf_source_dir}/src/google/protobuf/generated_message_reflection.cc ${protobuf_source_dir}/src/google/protobuf/generated_message_table_driven.cc ${protobuf_source_dir}/src/google/protobuf/generated_message_table_driven_lite.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_full.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_lite.cc ${protobuf_source_dir}/src/google/protobuf/generated_message_util.cc ${protobuf_source_dir}/src/google/protobuf/implicit_weak_message.cc + ${protobuf_source_dir}/src/google/protobuf/inlined_string_field.cc ${protobuf_source_dir}/src/google/protobuf/io/coded_stream.cc ${protobuf_source_dir}/src/google/protobuf/io/gzip_stream.cc ${protobuf_source_dir}/src/google/protobuf/io/io_win32.cc @@ -45,6 +55,7 @@ set(protobuf_SOURCE_FILES ${protobuf_source_dir}/src/google/protobuf/parse_context.cc ${protobuf_source_dir}/src/google/protobuf/reflection_ops.cc ${protobuf_source_dir}/src/google/protobuf/repeated_field.cc + ${protobuf_source_dir}/src/google/protobuf/repeated_ptr_field.cc ${protobuf_source_dir}/src/google/protobuf/service.cc ${protobuf_source_dir}/src/google/protobuf/source_context.pb.cc ${protobuf_source_dir}/src/google/protobuf/struct.pb.cc @@ -78,7 +89,6 @@ set(protobuf_SOURCE_FILES ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectsource.cc ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectwriter.cc ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info.cc - ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info_test_helper.cc ${protobuf_source_dir}/src/google/protobuf/util/internal/utility.cc ${protobuf_source_dir}/src/google/protobuf/util/json_util.cc ${protobuf_source_dir}/src/google/protobuf/util/message_differencer.cc @@ -94,6 +104,7 @@ set(protobuf_HEADER_FILES ${protobuf_source_dir}/src/google/protobuf/any.pb.h ${protobuf_source_dir}/src/google/protobuf/api.pb.h ${protobuf_source_dir}/src/google/protobuf/arena.h + ${protobuf_source_dir}/src/google/protobuf/arena_impl.h ${protobuf_source_dir}/src/google/protobuf/arenastring.h ${protobuf_source_dir}/src/google/protobuf/compiler/importer.h ${protobuf_source_dir}/src/google/protobuf/compiler/parser.h @@ -103,39 +114,70 @@ set(protobuf_HEADER_FILES ${protobuf_source_dir}/src/google/protobuf/duration.pb.h ${protobuf_source_dir}/src/google/protobuf/dynamic_message.h ${protobuf_source_dir}/src/google/protobuf/empty.pb.h + ${protobuf_source_dir}/src/google/protobuf/explicitly_constructed.h ${protobuf_source_dir}/src/google/protobuf/extension_set.h + ${protobuf_source_dir}/src/google/protobuf/extension_set_inl.h + ${protobuf_source_dir}/src/google/protobuf/field_access_listener.h ${protobuf_source_dir}/src/google/protobuf/field_mask.pb.h + ${protobuf_source_dir}/src/google/protobuf/generated_enum_reflection.h + ${protobuf_source_dir}/src/google/protobuf/generated_enum_util.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_bases.h ${protobuf_source_dir}/src/google/protobuf/generated_message_reflection.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_table_driven.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_table_driven_lite.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_decl.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_impl.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_impl.inc ${protobuf_source_dir}/src/google/protobuf/generated_message_util.h + ${protobuf_source_dir}/src/google/protobuf/has_bits.h ${protobuf_source_dir}/src/google/protobuf/implicit_weak_message.h + ${protobuf_source_dir}/src/google/protobuf/inlined_string_field.h ${protobuf_source_dir}/src/google/protobuf/io/coded_stream.h ${protobuf_source_dir}/src/google/protobuf/io/gzip_stream.h + ${protobuf_source_dir}/src/google/protobuf/io/io_win32.h ${protobuf_source_dir}/src/google/protobuf/io/printer.h ${protobuf_source_dir}/src/google/protobuf/io/strtod.h ${protobuf_source_dir}/src/google/protobuf/io/tokenizer.h ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream.h ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream_impl.h ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream_impl_lite.h + ${protobuf_source_dir}/src/google/protobuf/map.h + ${protobuf_source_dir}/src/google/protobuf/map_entry.h + ${protobuf_source_dir}/src/google/protobuf/map_entry_lite.h ${protobuf_source_dir}/src/google/protobuf/map_field.h + ${protobuf_source_dir}/src/google/protobuf/map_field_inl.h + ${protobuf_source_dir}/src/google/protobuf/map_field_lite.h + ${protobuf_source_dir}/src/google/protobuf/map_type_handler.h ${protobuf_source_dir}/src/google/protobuf/message.h ${protobuf_source_dir}/src/google/protobuf/message_lite.h + ${protobuf_source_dir}/src/google/protobuf/metadata.h + ${protobuf_source_dir}/src/google/protobuf/metadata_lite.h ${protobuf_source_dir}/src/google/protobuf/parse_context.h + ${protobuf_source_dir}/src/google/protobuf/port.h + ${protobuf_source_dir}/src/google/protobuf/reflection.h ${protobuf_source_dir}/src/google/protobuf/reflection_ops.h ${protobuf_source_dir}/src/google/protobuf/repeated_field.h + ${protobuf_source_dir}/src/google/protobuf/repeated_ptr_field.h ${protobuf_source_dir}/src/google/protobuf/service.h ${protobuf_source_dir}/src/google/protobuf/source_context.pb.h ${protobuf_source_dir}/src/google/protobuf/struct.pb.h ${protobuf_source_dir}/src/google/protobuf/stubs/bytestream.h + ${protobuf_source_dir}/src/google/protobuf/stubs/callback.h + ${protobuf_source_dir}/src/google/protobuf/stubs/casts.h ${protobuf_source_dir}/src/google/protobuf/stubs/common.h - ${protobuf_source_dir}/src/google/protobuf/stubs/int128.h + ${protobuf_source_dir}/src/google/protobuf/stubs/hash.h + ${protobuf_source_dir}/src/google/protobuf/stubs/logging.h + ${protobuf_source_dir}/src/google/protobuf/stubs/macros.h + ${protobuf_source_dir}/src/google/protobuf/stubs/map_util.h + ${protobuf_source_dir}/src/google/protobuf/stubs/mutex.h ${protobuf_source_dir}/src/google/protobuf/stubs/once.h + ${protobuf_source_dir}/src/google/protobuf/stubs/platform_macros.h + ${protobuf_source_dir}/src/google/protobuf/stubs/port.h ${protobuf_source_dir}/src/google/protobuf/stubs/status.h - ${protobuf_source_dir}/src/google/protobuf/stubs/statusor.h + ${protobuf_source_dir}/src/google/protobuf/stubs/stl_util.h ${protobuf_source_dir}/src/google/protobuf/stubs/stringpiece.h - ${protobuf_source_dir}/src/google/protobuf/stubs/stringprintf.h ${protobuf_source_dir}/src/google/protobuf/stubs/strutil.h - ${protobuf_source_dir}/src/google/protobuf/stubs/substitute.h - ${protobuf_source_dir}/src/google/protobuf/stubs/time.h + ${protobuf_source_dir}/src/google/protobuf/stubs/template_util.h ${protobuf_source_dir}/src/google/protobuf/text_format.h ${protobuf_source_dir}/src/google/protobuf/timestamp.pb.h ${protobuf_source_dir}/src/google/protobuf/type.pb.h @@ -143,23 +185,10 @@ set(protobuf_HEADER_FILES ${protobuf_source_dir}/src/google/protobuf/util/delimited_message_util.h ${protobuf_source_dir}/src/google/protobuf/util/field_comparator.h ${protobuf_source_dir}/src/google/protobuf/util/field_mask_util.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/datapiece.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/default_value_objectwriter.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/error_listener.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/field_mask_utility.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/json_escaping.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/json_objectwriter.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/json_stream_parser.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/object_writer.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/proto_writer.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectsource.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectwriter.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info_test_helper.h - ${protobuf_source_dir}/src/google/protobuf/util/internal/utility.h ${protobuf_source_dir}/src/google/protobuf/util/json_util.h ${protobuf_source_dir}/src/google/protobuf/util/message_differencer.h ${protobuf_source_dir}/src/google/protobuf/util/time_util.h + ${protobuf_source_dir}/src/google/protobuf/util/type_resolver.h ${protobuf_source_dir}/src/google/protobuf/util/type_resolver_util.h ${protobuf_source_dir}/src/google/protobuf/wire_format.h ${protobuf_source_dir}/src/google/protobuf/wire_format_lite.h @@ -173,7 +202,7 @@ add_library(protobuf ${protobuf_SOURCE_FILES} ${protobuf_HEADER_FILES}) set_target_properties( protobuf PROPERTIES - CXX_STANDARD 17 + CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON IMPORTED_CONFIGURATIONS Release INCLUDE_DIRECTORIES ${protobuf_source_dir}/src @@ -181,12 +210,11 @@ set_target_properties( LINK_FLAGS -no-undefined ) -target_compile_options(protobuf PRIVATE -DHAVE_PTHREAD=1 -Wno-inconsistent-missing-override -Wno-shorten-64-to-32) +target_compile_options(protobuf PRIVATE -DHAVE_PTHREAD=1 -Wno-inconsistent-missing-override -Wno-shorten-64-to-32 -Wno-invalid-noreturn) install(TARGETS protobuf LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/protobuf - ) - -set(Protobuf_LIBRARIES protobuf) +) +set(Protobuf_LIBRARIES protobuf) \ No newline at end of file diff --git a/cmake/StandardSettings.cmake b/cmake/StandardSettings.cmake new file mode 100644 index 00000000000..752b2285672 --- /dev/null +++ b/cmake/StandardSettings.cmake @@ -0,0 +1,86 @@ +# +# Default settings +# +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version" FORCE) + +# +# IDE Settings +# +option(TW_IDE_CLION "Enable if your IDE is CLion" OFF) +option(TW_IDE_VSCODE "Enable if your IDE is VSCode" OFF) + +# +# Build Settings +# +option(TW_UNITY_BUILD "Enable Unity build for TrustWalletCore and unit tests." OFF) + +# +# Static analyzers +# +# Currently supporting: Clang-Tidy, PVS-Studio. +option(TW_ENABLE_CLANG_TIDY "Enable static analysis with Clang-Tidy." OFF) +option(TW_ENABLE_PVS_STUDIO "Enable static analysis with PVS-Studio." OFF) + +# +# Runtime analyzers +# +# Currently supporting: Clang ASAN. +option(TW_CLANG_ASAN "Enable ASAN dynamic address sanitizer" OFF) + +# +# Specific platforms support +# +# Currently supporting: Wasm. +option(TW_COMPILE_WASM "Target Wasm" OFF) + +# +# Coverage +# +option(TW_CODE_COVERAGE "Enable coverage reporting" OFF) + +# +# Compiler warnings options +# +option(TW_WARNINGS_AS_ERRORS "Compiler Options as Error" OFF) + +# +# Compilation Speed options +# +option(TW_ENABLE_CCACHE "Enable the usage of Ccache, in order to speed up rebuild times." ON) + +if (TW_ENABLE_CCACHE) + find_program(CCACHE_FOUND ccache) + if (CCACHE_FOUND) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache) + message(STATUS "ccache activated") + endif () +endif () + +# +# Tests/Examples options +# +option(TW_UNIT_TESTS "Enable the unit tests of the project" ON) +option(TW_BUILD_EXAMPLES "Enable the examples builds of the project" ON) + +if (ANDROID OR IOS_PLATFORM OR TW_COMPILE_WASM) + set(TW_UNIT_TESTS OFF) + set(TW_BUILD_EXAMPLES OFF) +endif() + +if (TW_UNIT_TESTS) + message(STATUS "Native unit tests activated") +else() + message(STATUS "Native unit tests skipped") +endif() + +if (TW_BUILD_EXAMPLES) + message(STATUS "Native examples activated") +else() + message(STATUS "Native examples skipped") +endif() + + diff --git a/cmake/StaticAnalyzers.cmake b/cmake/StaticAnalyzers.cmake new file mode 100644 index 00000000000..bb06683916e --- /dev/null +++ b/cmake/StaticAnalyzers.cmake @@ -0,0 +1,37 @@ +if (TW_ENABLE_CLANG_TIDY) + macro(tw_add_clang_tidy_target target) + find_program(CLANGTIDY clang-tidy) + if (CLANGTIDY) + set_property( + TARGET ${target} + PROPERTY CXX_CLANG_TIDY clang-tidy;-extra-arg=-Wno-unknown-warning-option) + message("Clang-Tidy finished setting up.") + else () + message(SEND_ERROR "Clang-Tidy requested but executable not found.") + endif () + endmacro() +endif () + +if (TW_ENABLE_PVS_STUDIO) + macro(tw_add_pvs_studio_target target) + message(STATUS "PVS-Studio analyzer enabled - ${CMAKE_SOURCE_DIR}/tools/pvs-studio/config.cfg") + include(cmake/PVS-Studio.cmake) + if (TW_IDE_VSCODE) + pvs_studio_add_target(TARGET TrustWalletCore.analyze ALL + OUTPUT FORMAT sarif-vscode + ANALYZE ${target} + MODE GA:1,2 + LOG result.sarif + CONFIG ${CMAKE_SOURCE_DIR}/tools/pvs-studio/config.cfg + ) + else () + pvs_studio_add_target(TARGET TrustWalletCore.analyze ALL + OUTPUT FORMAT json + ANALYZE ${target} + MODE GA:1,2 + LOG result.json + CONFIG ${CMAKE_SOURCE_DIR}/tools/pvs-studio/config.cfg + ) + endif () + endmacro() +endif () diff --git a/codegen/.rubocop.yml b/codegen/.rubocop.yml index c4410459c53..7e5aa453c4c 100644 --- a/codegen/.rubocop.yml +++ b/codegen/.rubocop.yml @@ -1,2 +1,13 @@ -Metrics/LineLength: +AllCops: + NewCops: enable + Exclude: + - 'vendor/**/*' + - 'spec/fixtures/**/*' + - 'tmp/**/*' + - '.git/**/*' + - 'bin/*' + TargetRubyVersion: 2.6 + SuggestExtensions: false + +Layout/LineLength: Enabled: false diff --git a/codegen/bin/codegen b/codegen/bin/codegen index d8b7a6f0873..8fc86c7e86e 100755 --- a/codegen/bin/codegen +++ b/codegen/bin/codegen @@ -20,6 +20,8 @@ options.swift = true options.java = true options.jni_h = true options.jni_c = true +options.wasm_cpp = true +options.ts_declaration = true OptionParser.new do |opts| opts.banner = 'Usage: codegen [options]' @@ -42,6 +44,12 @@ OptionParser.new do |opts| opts.on('-c', '--jnic', "Generate JNI code. Default: #{options.jni_c}") do |v| options.jni_c = v end + opts.on('-w', '--wasm-cpp', "Generate cpp code for Wasm(Emscripten). Default: #{options.wasm_cpp}") do |v| + options.wasm_cpp = v + end + opts.on('-t', '--typescript-declaration', "Generate typescript declaration file Default: #{options.ts_declaration}") do |v| + options.ts_declaration = v + end opts.on_tail('-h', '--help', 'Show this message') do puts opts exit @@ -73,3 +81,10 @@ end if options.jni_c generator.render_jni_c end +if options.wasm_cpp + generator.render_wasm_h + generator.render_wasm_cpp +end +if options.ts_declaration + generator.render_ts_declaration +end diff --git a/codegen/bin/coins b/codegen/bin/coins index 53c26f999dd..a5ea5909e05 100755 --- a/codegen/bin/coins +++ b/codegen/bin/coins @@ -14,6 +14,28 @@ def self.format_name(n) formatted end +def self.coin_name(coin) + coin['displayName'] || coin['name'] +end + +def self.derivation_path(coin) + coin['derivation'][0]['path'] +end + +def self.camel_case(id) + id[0].upcase + id[1..].downcase +end + +def self.derivation_name(deriv) + return "" if deriv['name'].nil? + deriv['name'].downcase +end + +def self.derivation_enum_name(deriv, coin) + return "TWDerivationDefault" if deriv['name'].nil? + "TWDerivation" + format_name(coin['name']) + camel_case(deriv['name']) +end + def self.coin_img(coin) "" end @@ -29,11 +51,16 @@ end json_string = File.read('registry.json') coins = JSON.parse(json_string).sort_by { |x| x['coinId'] } +# used in some cases for numbering enum values +enum_count = 0 + erbs = [ + {'template' => 'TWDerivation.h.erb', 'folder' => 'include/TrustWalletCore', 'file' => 'TWDerivation.h'}, {'template' => 'CoinInfoData.cpp.erb', 'folder' => 'src/Generated', 'file' => 'CoinInfoData.cpp'}, {'template' => 'registry.md.erb', 'folder' => 'docs', 'file' => 'registry.md'}, {'template' => 'hrp.cpp.erb', 'folder' => 'src/Generated', 'file' => 'TWHRP.cpp'}, - {'template' => 'hrp.h.erb', 'folder' => 'include/TrustWalletCore', 'file' => 'TWHRP.h'} + {'template' => 'hrp.h.erb', 'folder' => 'include/TrustWalletCore', 'file' => 'TWHRP.h'}, + {'template' => 'TWEthereumChainID.h.erb', 'folder' => 'include/TrustWalletCore', 'file' => 'TWEthereumChainID.h'} ] FileUtils.mkdir_p File.join('src', 'Generated') diff --git a/codegen/bin/cointests b/codegen/bin/cointests index b30fc1e91af..a40f56b0250 100755 --- a/codegen/bin/cointests +++ b/codegen/bin/cointests @@ -2,25 +2,25 @@ # Sript for creating/updating CoinType unit tests, based on the registry.json file # It is intended as a one-time or occasional generation, not every time! (that way the tests would have zero added value) -# Usage: codegen/bin/cointests +# Usage: codegen/bin/cointests [--coin-id coinid] [--no-skip-existing] # Files are generated to: tests//TWCoinTypeTests.cpp require 'erb' require 'fileutils' require 'json' +require 'optparse' + +options = { :no_skip_existing => false } +OptionParser.new do |opt| + opt.banner = "Usage: codegen/bin/cointests [options]" + opt.on('--coin-id ') { |o| options[:coin_id] = o.downcase } + opt.on('--no-skip-existing') { options[:no_skip_existing] = true } +end.parse! CurrentDir = File.dirname(__FILE__) $LOAD_PATH.unshift(File.join(CurrentDir, '..', 'lib')) require 'coin_test_gen' -# Transforms a coin name to a C++ name -def self.format_name(n) - formatted = n - #formatted = formatted.sub(/^([a-z]+)/, &:upcase) - formatted = formatted.sub(/\s/, '') - formatted -end - # Transforms number to hex def self.to_hex(i) hex = i.to_i().to_s(16) @@ -68,6 +68,19 @@ erbs = [ coin_test_gen = CoinTestGen.new() templateFile = 'TWCoinTypeTests.cpp.erb' +foundCoinId = false coins.each do |coin| - coin_test_gen.generate_coin_test_file(coin, templateFile) + if options[:coin_id].nil? or coin['id'] == options[:coin_id] + coin_test_gen.generate_coin_test_file(coin, templateFile, options[:no_skip_existing]) + foundCoinId = true + end end + +if not options[:coin_id].nil? and not foundCoinId + puts "Not found specified coin-id " + options[:coin_id] + supportedIds = [] + coins.each do |coin| + supportedIds << coin['id'] + end + puts "Supported coin-ids: " + supportedIds.join(", ") +end diff --git a/codegen/bin/newcoin b/codegen/bin/newcoin index 974febcd8f3..8fdacb210fe 100755 --- a/codegen/bin/newcoin +++ b/codegen/bin/newcoin @@ -14,6 +14,8 @@ require 'entity_decl' require 'code_generator' require 'coin_test_gen' +$flag_comment = " // TODO remove if the blockchain already exists, or just remove this comment if not" + # Transforms a coin name to a C++ name def self.format_name(coin) formatted = coin['name'] @@ -54,18 +56,19 @@ end def insert_blockchain_type(coin) target_file = "include/TrustWalletCore/TWBlockchain.h" - line_number = File.readlines(target_file).count - target_line = " TWBlockchain#{coin['blockchain']} = #{line_number - 17},\n" + line_number = File.readlines(target_file).count + 2 # add offset because of removed blockchain enum type + target_line = " TWBlockchain#{coin['blockchain']} = #{line_number - 17}, " + $flag_comment + "\n" insert_target_line(target_file, target_line, "};\n") end def insert_coin_entry(coin) target_file = "src/Coin.cpp" - target_line = "#include \"#{format_name(coin)}/Entry.h\"\n" + entryName = coin['blockchain'] + target_line = "#include \"#{entryName}/Entry.h\"" + $flag_comment + "\n" insert_target_line(target_file, target_line, "// end_of_coin_includes_marker_do_not_modify\n") - target_line = "#{format_name(coin)}::Entry #{format_name(coin)}DP;\n" + target_line = "#{entryName}::Entry #{entryName}DP;" + $flag_comment + "\n" insert_target_line(target_file, target_line, "// end_of_coin_dipatcher_declarations_marker_do_not_modify\n") - target_line = " case TWCoinType#{format_name(coin)}: entry = &#{format_name(coin)}DP; break;\n" + target_line = " case TWBlockchain#{entryName}: entry = &#{entryName}DP; break;" + $flag_comment + "\n" insert_target_line(target_file, target_line, " // end_of_coin_dipatcher_switch_marker_do_not_modify\n") end @@ -101,7 +104,7 @@ puts "New coin template for coin '#{coin_id}' requested" json_string = File.read('registry.json') coins = JSON.parse(json_string).sort_by { |x| x['name'] } -entity = EntityDecl.new(name: "New" + coin_id, is_struct: false) +entity = EntityDecl.new(name: "New" + coin_id, is_struct: false, comment: '') file = "new"+ coin_id generator = CodeGenerator.new(entities: [entity], files: [file], output_folder: ".") @@ -131,14 +134,14 @@ generate_file("newcoin/Proto.erb", "src/proto", "#{name}.proto", coin) generate_file("newcoin/Signer.h.erb", "src/#{name}", "Signer.h", coin) generate_file("newcoin/Signer.cpp.erb", "src/#{name}", "Signer.cpp", coin) -generate_file("newcoin/AddressTests.cpp.erb", "tests/#{name}", "AddressTests.cpp", coin) -generate_file("newcoin/SignerTests.cpp.erb", "tests/#{name}", "SignerTests.cpp", coin) -generate_file("newcoin/TWAddressTests.cpp.erb", "tests/#{name}", "TWAnyAddressTests.cpp", coin) -generate_file("newcoin/TWSignerTests.cpp.erb", "tests/#{name}", "TWAnySignerTests.cpp", coin) +generate_file("newcoin/AddressTests.cpp.erb", "tests/chains/#{name}", "AddressTests.cpp", coin) +generate_file("newcoin/SignerTests.cpp.erb", "tests/chains/#{name}", "SignerTests.cpp", coin) +generate_file("newcoin/TWAddressTests.cpp.erb", "tests/chains/#{name}", "TWAnyAddressTests.cpp", coin) +generate_file("newcoin/TWSignerTests.cpp.erb", "tests/chains/#{name}", "TWAnySignerTests.cpp", coin) generate_file("newcoin/AddressTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Address.kt", coin) generate_file("newcoin/SignerTests.kt.erb", "android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/#{format_name_lowercase(coin)}", "Test#{name}Signer.kt", coin) generate_file("newcoin/Tests.swift.erb", "swift/Tests/Blockchains", "#{name}Tests.swift", coin) -coin_test_gen.generate_coin_test_file(coin, 'TWCoinTypeTests.cpp.erb') +coin_test_gen.generate_coin_test_file(coin, 'TWCoinTypeTests.cpp.erb', true) puts "please tools/generate-files to generate Swift/Java/Protobuf files" diff --git a/codegen/lib/code_generator.rb b/codegen/lib/code_generator.rb index 56b49b892c6..7896c2f8dca 100644 --- a/codegen/lib/code_generator.rb +++ b/codegen/lib/code_generator.rb @@ -5,6 +5,8 @@ require 'java_helper' require 'jni_helper' require 'swift_helper' +require 'wasm_cpp_helper' +require 'ts_helper' # Code generation class CodeGenerator @@ -20,7 +22,7 @@ def initialize(entities:, files:, output_folder:) # Renders an enum template def render_swift_enum_template(file:, header:, template:, output_subfolder:, extension:) # split Enum to Enum.swift and Enum+Extension.swift (easier to support cocoapods subspec) - output_enum_subfolder = "#{output_subfolder + '/Enums'}" + output_enum_subfolder = "#{output_subfolder}/Enums" FileUtils.mkdir_p File.join(output_folder, output_enum_subfolder) has_extension = entity.properties.length > 0 || entity.methods.length > 0 header = render(header) @@ -39,7 +41,7 @@ def render_swift_enum_template(file:, header:, template:, output_subfolder:, ext code = +'' code << header code << render('swift/enum_extension.erb') - path = File.expand_path(File.join(output_folder, output_subfolder, "#{file + '+Extension'}.#{extension}")) + path = File.expand_path(File.join(output_folder, output_subfolder, "#{file}+Extension.#{extension}")) File.write(path, code) end end @@ -60,7 +62,7 @@ def render_template(header:, template:, output_subfolder:, extension:) unless string.nil? || string.empty? code << "\n" unless header.nil? code << string - + path = File.expand_path(File.join(output_folder, output_subfolder, "#{file}.#{extension}")) File.write(path, code) end @@ -69,7 +71,7 @@ def render_template(header:, template:, output_subfolder:, extension:) end def render_swift - render_template(header: 'swift/header.erb', template: 'swift.erb', output_subfolder: 'swift/Sources/Generated', extension: 'swift') + render_template(header: 'copyright_header.erb', template: 'swift.erb', output_subfolder: 'swift/Sources/Generated', extension: 'swift') framework_header = render('swift/TrustWalletCore.h.erb') framework_header_path = File.expand_path(File.join(output_folder, 'swift/Sources/Generated', 'WalletCore.h')) @@ -81,11 +83,24 @@ def render_java end def render_jni_h - render_template(header: 'jni/header.erb', template: 'jni_h.erb', output_subfolder: 'jni/cpp/generated', extension: 'h') + render_template(header: 'copyright_header.erb', template: 'jni_h.erb', output_subfolder: 'jni/cpp/generated', extension: 'h') end def render_jni_c - render_template(header: 'jni/header.erb', template: 'jni_c.erb', output_subfolder: 'jni/cpp/generated', extension: 'c') + render_template(header: 'copyright_header.erb', template: 'jni_c.erb', output_subfolder: 'jni/cpp/generated', extension: 'c') + end + + def render_wasm_h + render_template(header: 'copyright_header.erb', template: 'wasm_h.erb', output_subfolder: 'wasm/src/generated', extension: 'h') + end + + def render_wasm_cpp + render_template(header: 'copyright_header.erb', template: 'wasm_cpp.erb', output_subfolder: 'wasm/src/generated', extension: 'cpp') + end + + def render_ts_declaration + render_template(header: nil, template: 'wasm_d_ts.erb', output_subfolder: 'wasm/lib/generated', extension: 'd.ts') + TsHelper.combine_declaration_files() end def render(file, locals = {}) @@ -102,7 +117,7 @@ def should_return_data(method) end def should_return_string(method) - # Note: method with no parameters can also return string + # NOTE: method with no parameters can also return string method.return_type.name == :string end diff --git a/codegen/lib/coin_test_gen.rb b/codegen/lib/coin_test_gen.rb index 2583e549a1b..0b831bfb277 100755 --- a/codegen/lib/coin_test_gen.rb +++ b/codegen/lib/coin_test_gen.rb @@ -14,8 +14,10 @@ def initialize() # Transforms a coin name to a C++ name def format_name(n) formatted = n - #formatted = formatted.sub(/^([a-z]+)/, &:upcase) - formatted = formatted.sub(/\s/, '') + + # Remove whitespaces + formatted.gsub!(/\s+/, '') + formatted end @@ -56,16 +58,24 @@ def explorer_sample_account(c) end end - def generate_coin_test_file(coin, templateFile) + def generate_coin_test_file(coin, templateFile, overwriteExisting = true) path = File.expand_path(templateFile, File.join(File.dirname(__FILE__), '..', 'lib', 'templates')) template = ERB.new(File.read(path), nil, '-') result = template.result(binding) - folder = 'tests/' + format_name(coin['name']) + folder = 'tests/chains/' + if coin.key?('testFolderName') + folder += format_name(coin['testFolderName']) + else + folder += format_name(coin['name']) + end + file = 'TWCoinTypeTests.cpp' FileUtils.mkdir_p folder path = File.join(folder, file) - File.write(path, result) - puts "Generated file " + path + if not File.exist?(path) or overwriteExisting + File.write(path, result) + puts "Generated file " + path + end end end diff --git a/codegen/lib/entity_decl.rb b/codegen/lib/entity_decl.rb index f22a0ea6587..aee285ef97b 100644 --- a/codegen/lib/entity_decl.rb +++ b/codegen/lib/entity_decl.rb @@ -2,16 +2,17 @@ # Class or struct declaration class EntityDecl - attr_reader :name + attr_reader :name, :comment attr_accessor :is_struct, :methods, :properties, :static_methods, :static_properties - def initialize(name:, is_struct:) + def initialize(name:, is_struct:, comment:) @name = name @is_struct = is_struct @methods = [] @properties = [] @static_methods = [] @static_properties = [] + @comment = comment end def struct? diff --git a/codegen/lib/enum_decl.rb b/codegen/lib/enum_decl.rb index 303909dec82..0fae5774f50 100644 --- a/codegen/lib/enum_decl.rb +++ b/codegen/lib/enum_decl.rb @@ -2,11 +2,11 @@ # Enum declaration. class EnumDecl - attr_reader :name + attr_reader :name, :comment attr_accessor :cases, :raw_type attr_accessor :methods, :properties, :static_methods, :static_properties - def initialize(name:, raw_type:) + def initialize(name:, raw_type:, comment:) @name = name @cases = [] @raw_type = raw_type @@ -14,6 +14,7 @@ def initialize(name:, raw_type:) @properties = [] @static_methods = [] @static_properties = [] + @comment = comment end def struct? diff --git a/codegen/lib/function_decl.rb b/codegen/lib/function_decl.rb index cd5135f1231..de0a3f7853c 100644 --- a/codegen/lib/function_decl.rb +++ b/codegen/lib/function_decl.rb @@ -4,8 +4,9 @@ class FunctionDecl attr_reader :name, :entity attr_accessor :is_method, :return_type, :parameters, :static, :discardable_result + attr_accessor :comment, :comment_with_indent - def initialize(name:, entity:, is_method:, return_type: :void, parameters: [], static: false, discardable_result: false) + def initialize(name:, entity:, is_method:, return_type: :void, parameters: [], static: false, discardable_result: false, comment: '') @name = name @entity = entity @is_method = is_method @@ -13,6 +14,8 @@ def initialize(name:, entity:, is_method:, return_type: :void, parameters: [], s @parameters = parameters @static = static @discardable_result = discardable_result + @comment = comment + @comment_with_indent = comment.to_s.gsub('///', ' ///') end end diff --git a/codegen/lib/parser.rb b/codegen/lib/parser.rb index fab69837507..e4e287614a0 100644 --- a/codegen/lib/parser.rb +++ b/codegen/lib/parser.rb @@ -9,7 +9,7 @@ # C header parser class Parser - attr_reader :path, :entity + attr_reader :path, :entity, :entity_comment def initialize(path:, string: nil) @path = path @@ -19,32 +19,49 @@ def initialize(path:, string: nil) # Parses a C header file for class/struct declarations def parse + clear_comment until @buffer.eos? - break if @buffer.skip_until(/\n/).nil? + @buffer.skip(/\s*/) + + if !@buffer.scan(/\/\//).nil? + @entity_comment = @entity_comment + '//' + @buffer.scan_until(/(\r\n|\r|\n)/) + next + end + + if !@buffer.scan(/TW_EXTERN_C_BEGIN/).nil? + # This is to ignore very first comments from the file + clear_comment + next + end + + @entity_comment = @entity_comment.strip # Look for TW_EXPORT statements - @buffer.skip(/\s*/) - next if @buffer.scan(/TW_EXPORT_[A-Z_]+/).nil? - - # Handle statements - case @buffer[0] - when 'TW_EXPORT_CLASS' - handle_class - when 'TW_EXPORT_STRUCT' - handle_struct - when 'TW_EXPORT_ENUM' - handle_enum - when 'TW_EXPORT_FUNC' - handle_func - when 'TW_EXPORT_METHOD' - handle_method - when 'TW_EXPORT_PROPERTY' - handle_property - when 'TW_EXPORT_STATIC_METHOD' - handle_static_method - when 'TW_EXPORT_STATIC_PROPERTY' - handle_static_property + if !@buffer.scan(/TW_EXPORT_[A-Z_]+/).nil? + # Handle statements + case @buffer[0] + when 'TW_EXPORT_CLASS' + handle_class + when 'TW_EXPORT_STRUCT' + handle_struct + when 'TW_EXPORT_ENUM' + handle_enum + when 'TW_EXPORT_FUNC' + handle_func + when 'TW_EXPORT_METHOD' + handle_method + when 'TW_EXPORT_PROPERTY' + handle_property + when 'TW_EXPORT_STATIC_METHOD' + handle_static_method + when 'TW_EXPORT_STATIC_PROPERTY' + handle_static_property + end + + clear_comment end + + break if @buffer.skip_until(/\n/).nil? end @entity @@ -80,7 +97,7 @@ def parse_func @buffer.skip(/\s*/) scan_or_fail(/\w+/, 'Invalid function name') - func = FunctionDecl.new(name: @buffer[0], entity: @entity, is_method: true, return_type: return_type) + func = FunctionDecl.new(name: @buffer[0], entity: @entity, is_method: true, return_type: return_type, comment: @entity_comment) @buffer.skip(/\s*/) scan_or_fail(/\(/, 'Invalid function declaration. Expected (') @@ -117,7 +134,7 @@ def handle_class @buffer.skip(/\s*/) report_error 'Invalid type name' if @buffer.scan(/struct TW(\w+)\s*;/).nil? report_error 'Found more than one class/struct in the same file' unless @entity.nil? - @entity = EntityDecl.new(name: @buffer[1], is_struct: false) + @entity = EntityDecl.new(name: @buffer[1], is_struct: false, comment: @entity_comment) puts "Found a class #{@entity.name}" end @@ -125,7 +142,7 @@ def handle_struct @buffer.skip(/\s*/) report_error 'Invalid type name at' if @buffer.scan(/struct TW(\w+)\s*\{?/).nil? report_error 'Found more than one class/struct in the same file' unless @entity.nil? - @entity = EntityDecl.new(name: @buffer[1], is_struct: true) + @entity = EntityDecl.new(name: @buffer[1], is_struct: true, comment: @entity_comment) puts "Found a struct #{@buffer[1]}" end @@ -136,7 +153,7 @@ def handle_enum @buffer.skip(/\s*/) report_error 'Invalid enum' if @buffer.scan(/enum TW(\w+)\s*\{/).nil? - @entity = EnumDecl.new(name: @buffer[1], raw_type: TypeDecl.fromPrimitive(type)) + @entity = EnumDecl.new(name: @buffer[1], raw_type: TypeDecl.fromPrimitive(type), comment: @entity_comment) incremental_value = 0 until @buffer.eos? @@ -277,4 +294,8 @@ def report_error(message) def current_line_number @buffer.string[0..@buffer.pos].count("\n") + 1 end + + def clear_comment + @entity_comment = '' + end end diff --git a/codegen/lib/templates/CoinInfoData.cpp.erb b/codegen/lib/templates/CoinInfoData.cpp.erb index 8dc394ace16..44dd8acd044 100644 --- a/codegen/lib/templates/CoinInfoData.cpp.erb +++ b/codegen/lib/templates/CoinInfoData.cpp.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -21,16 +21,16 @@ static const CoinInfo defaultsForMissing = { TWBlockchainBitcoin, TWPurposeBIP44, TWCurveNone, - TWHDVersionNone, - TWHDVersionNone, - "", + {Derivation()}, TWPublicKeyTypeSECP256k1, 0, 0, 0, TWHRPUnknown, - Hash::sha256ripemd, - Hash::sha256d, + "", + Hash::HasherSha256ripemd, + Hash::HasherSha256d, + Hash::HasherSha256ripemd, "?", 2, "", @@ -46,20 +46,28 @@ const CoinInfo getCoinInfo(TWCoinType coin) { case TWCoinType<%= format_name(coin['name']) %>: return CoinInfo { "<%= coin['id'] %>", - <% if coin['displayName'].nil? -%>"<%= coin['name'] %>"<% else -%>"<%= coin['displayName'] %>"<% end -%>, + "<%= coin_name(coin) %>", TWBlockchain<%= format_name(coin['blockchain']) %>, - TWPurposeBIP<%= /^m\/(\d+)'?(\/\d+'?)+$/.match(coin['derivationPath'])[1] %>, + TWPurposeBIP<%= /^m\/(\d+)'?(\/\d+'?)+$/.match(derivation_path(coin))[1] %>, TWCurve<%= format_name(coin['curve']) %>, - TWHDVersion<% if coin['xpub'].nil? -%>None<% else -%><%= format_name(coin['xpub']) %><% end -%>, - TWHDVersion<% if coin['xprv'].nil? -%>None<% else -%><%= format_name(coin['xprv']) %><% end -%>, - "<%= coin['derivationPath'] %>", + { + <% coin['derivation'].each do |deriv| -%>{ + <%= derivation_enum_name(deriv, coin) %>, + "<%= deriv['path'] %>", + "<%= derivation_name(deriv) %>", + TWHDVersion<% if deriv['xpub'].nil? -%>None<% else -%><%= format_name(deriv['xpub']) %><% end -%>, + TWHDVersion<% if deriv['xprv'].nil? -%>None<% else -%><%= format_name(deriv['xprv']) %><% end -%>, + }, + <% end -%>}, TWPublicKeyType<%= format_name(coin['publicKeyType']) %>, <% if coin['staticPrefix'].nil? -%>0<% else -%><%= coin['staticPrefix'] %><% end -%>, <% if coin['p2pkhPrefix'].nil? -%>0<% else -%><%= coin['p2pkhPrefix'] %><% end -%>, <% if coin['p2shPrefix'].nil? -%>0<% else -%><%= coin['p2shPrefix'] %><% end -%>, TWHRP<% if coin['hrp'].nil? -%>Unknown<% else -%><%= format_name(coin['name']) %><% end -%>, - Hash::<% if coin['publicKeyHasher'].nil? -%>sha256ripemd<% else -%><%= coin['publicKeyHasher'] %><% end -%>, - Hash::<% if coin['base58Hasher'].nil? -%>sha256d<% else -%><%= coin['base58Hasher'] %><% end -%>, + "<%= coin['chainId'] %>", + Hash::Hasher<% if coin['publicKeyHasher'].nil? -%>Sha256ripemd<% else -%><%= camel_case(coin['publicKeyHasher']) %><% end -%>, + Hash::Hasher<% if coin['base58Hasher'].nil? -%>Sha256d<% else -%><%= camel_case(coin['base58Hasher']) %><% end -%>, + Hash::Hasher<% if coin['addressHasher'].nil? -%>Sha256ripemd<% else -%><%= camel_case(coin['addressHasher']) %><% end -%>, "<%= coin['symbol'] %>", <%= coin['decimals'] %>, "<%= explorer_tx_url(coin) %>", diff --git a/codegen/lib/templates/TWCoinTypeTests.cpp.erb b/codegen/lib/templates/TWCoinTypeTests.cpp.erb index 32a9534a95f..ab2a8fbf9c3 100644 --- a/codegen/lib/templates/TWCoinTypeTests.cpp.erb +++ b/codegen/lib/templates/TWCoinTypeTests.cpp.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,27 +8,34 @@ // Generated one-time (codegen/bin/cointests) // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include TEST(TW<%= format_name(coin['name']) %>CoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinType<%= format_name(coin['name']) %>)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("<%= explorer_sample_tx(coin) %>")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinType<%= format_name(coin['name']) %>, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("<%= explorer_sample_account(coin) %>")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinType<%= format_name(coin['name']) %>, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinType<%= format_name(coin['name']) %>)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinType<%= format_name(coin['name']) %>)); + const auto coin = TWCoinType<%= format_name(coin['name']) %>; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); +<% if !coin['chainId'].nil? -%> + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); +<% end -%> + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("<%= explorer_sample_tx(coin) %>")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("<%= explorer_sample_account(coin) %>")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinType<%= format_name(coin['name']) %>), <%= coin['decimals'] %>); - ASSERT_EQ(TWBlockchain<%= coin['blockchain'] %>, TWCoinTypeBlockchain(TWCoinType<%= format_name(coin['name']) %>)); - ASSERT_EQ(0x<%= to_hex(coin['p2shPrefix']) %>, TWCoinTypeP2shPrefix(TWCoinType<%= format_name(coin['name']) %>)); - ASSERT_EQ(0x<%= to_hex(coin['staticPrefix']) %>, TWCoinTypeStaticPrefix(TWCoinType<%= format_name(coin['name']) %>)); + assertStringsEqual(id, "<%= coin['id'] %>"); + assertStringsEqual(name, "<%= display_name(coin) %>"); assertStringsEqual(symbol, "<%= coin['symbol'] %>"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), <%= coin['decimals'] %>); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchain<%= coin['blockchain'] %>); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x<%= to_hex(coin['p2shPrefix']) %>); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x<%= to_hex(coin['staticPrefix']) %>); +<% if !coin['chainId'].nil? -%> + assertStringsEqual(chainId, "<%= format_name(coin['chainId']) %>"); +<% end -%> assertStringsEqual(txUrl, "<%= explorer_tx_url(coin) %><%= explorer_sample_tx(coin) %>"); assertStringsEqual(accUrl, "<%= explorer_account_url(coin) %><%= explorer_sample_account(coin) %>"); - assertStringsEqual(id, "<%= coin['id'] %>"); - assertStringsEqual(name, "<%= display_name(coin) %>"); } diff --git a/codegen/lib/templates/TWDerivation.h.erb b/codegen/lib/templates/TWDerivation.h.erb new file mode 100644 index 00000000000..06266d5cb82 --- /dev/null +++ b/codegen/lib/templates/TWDerivation.h.erb @@ -0,0 +1,33 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE from \registry.json, changes made here WILL BE LOST. +// + +#pragma once + +#include "TWBase.h" + +TW_EXTERN_C_BEGIN + +/// Non-default coin address derivation names (default, unnamed derivations are not included). +TW_EXPORT_ENUM() +enum TWDerivation { + TWDerivationDefault = 0, // default, for any coin + TWDerivationCustom = 1, // custom, for any coin +<% enum_count += 1 -%> +<% coins.each do |coin| -%> +<% if coin['derivation'].count > 1 -%> +<% coin['derivation'].each_with_index do |deriv, index| -%> +<% if index > 0 or !deriv['name'].nil? -%> + <%= derivation_enum_name(deriv, coin) %> = <% enum_count += 1 -%><%= enum_count %>, +<% end -%> +<% end -%> +<% end -%> +<% end -%> +}; + +TW_EXTERN_C_END diff --git a/codegen/lib/templates/TWEthereumChainID.h.erb b/codegen/lib/templates/TWEthereumChainID.h.erb new file mode 100644 index 00000000000..99862693b3f --- /dev/null +++ b/codegen/lib/templates/TWEthereumChainID.h.erb @@ -0,0 +1,26 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE from \registry.json, changes made here WILL BE LOST. +// + +#pragma once + +#include "TWBase.h" + +TW_EXTERN_C_BEGIN + +/// Chain identifiers for Ethereum-based blockchains, for convenience. Recommended to use the dynamic CoinType.ChainId() instead. +/// See also TWChainId. +TW_EXPORT_ENUM(uint32_t) +enum TWEthereumChainID { +<% chains = ['Ethereum', 'Ronin', 'Vechain'] -%> +<% coins.select{ |coin| chains.include?(coin['blockchain']) && coin['deprecated'] != true }.each do |coin| -%> + TWEthereumChainID<%= camel_case(coin['id']) %> = <%= coin['chainId'] %>, +<% end -%> +}; + +TW_EXTERN_C_END diff --git a/codegen/lib/templates/copyright_header.erb b/codegen/lib/templates/copyright_header.erb new file mode 100644 index 00000000000..be628ee1f6e --- /dev/null +++ b/codegen/lib/templates/copyright_header.erb @@ -0,0 +1,8 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here WILL BE LOST. +// diff --git a/codegen/lib/templates/cpp/class.erb b/codegen/lib/templates/cpp/class.erb new file mode 100644 index 00000000000..ced639f9ebf --- /dev/null +++ b/codegen/lib/templates/cpp/class.erb @@ -0,0 +1,37 @@ +#include "<%= entity.name %>.h" + +namespace TW::Wasm { +<%# Constructors -%> +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') || method.name.start_with?('Init') -%> +<%= render('cpp/static_method.erb', { method: method }) -%> +<% end -%> +<%# Properties -%> +<%= render('cpp/class_properties.erb') -%> + +<%# Methods -%> +<% entity.methods.each do |method| -%> +<% next if method.name == "Delete" -%> +<%= render('cpp/method.erb', { method: method }) %> +<% end -%> + +<%# EMSCRIPTEN_BINDINGS -%> +EMSCRIPTEN_BINDINGS(Wasm_TW<%= entity.name %>) { + class_<<%= WasmCppHelper.class_name(entity: entity) %>>("<%= entity.name %>") +<% entity.static_methods.each do |method| -%> +<% function_name = WasmCppHelper.function_name(entity: entity, function: method) -%> + .class_function("<%= function_name %>", &<%= WasmCppHelper.class_name(entity: entity) %>::<%= function_name %>, allow_raw_pointers()) +<% end -%> +<%- entity.properties.each do |property| -%> +<% function_name = WasmCppHelper.format_name(property.name) -%> + .function("<%= function_name %>", &<%= WasmCppHelper.class_name(entity: entity) %>::<%= function_name %>, allow_raw_pointers()) +<%- end -%> +<% entity.methods.each do |method| -%> +<% next if method.name == "Delete" -%> +<% function_name = WasmCppHelper.format_name(method.name) -%> + .function("<%= function_name %>", &<%= WasmCppHelper.class_name(entity: entity) %>::<%= function_name %>, allow_raw_pointers()) +<% end -%> + ; +} + +} // namespace TW::Wasm diff --git a/codegen/lib/templates/cpp/class_properties.erb b/codegen/lib/templates/cpp/class_properties.erb new file mode 100644 index 00000000000..0648de25352 --- /dev/null +++ b/codegen/lib/templates/cpp/class_properties.erb @@ -0,0 +1,22 @@ +<% entity.static_methods.each do |method| -%> +<% next unless method.name.start_with?('Create') -%> +auto <%= WasmCppHelper.class_name(entity: entity) %>::<%= WasmCppHelper.function_name(entity: entity, function: method) %>(<%= WasmCppHelper.parameters(method.parameters) %>) -> <%= WasmCppHelper.class_name(entity: entity) %>* { +<%= render('cpp/parameter_access.erb', { parameters: method.parameters }) -%> + TW<%= entity.name %> *instance = TW<%= entity.name %><%= method.name %>(<%= WasmCppHelper.arguments(method.parameters).join(', ') %>); +<% if method.return_type.is_nullable -%> + if (instance == nullptr) { + return nullptr; + } +<% end -%> + return new <%= WasmCppHelper.class_name(entity: entity) %>(instance); +} + +<% end -%> + +<%# Properties -%> +<%- entity.properties.each do |property| -%> +auto <%= WasmCppHelper.class_name(entity: entity) %>::<%= WasmCppHelper.format_name(property.name) %>() { +<%= render('cpp/method_forward.erb', { method: property }) -%> +} + +<%- end -%> diff --git a/codegen/lib/templates/cpp/enum.erb b/codegen/lib/templates/cpp/enum.erb new file mode 100644 index 00000000000..398420d93eb --- /dev/null +++ b/codegen/lib/templates/cpp/enum.erb @@ -0,0 +1,32 @@ +<% has_string = entity.cases.all? { |c| !c.string.nil? } -%> +<% if has_string -%> +#include + +<% end -%> +namespace TW::Wasm { + +<% if has_string -%> +auto describe<%= entity.name %>(TW<%= entity.name %> value) -> std::string { + switch (value) { +<% entity.cases.each do |c| -%> + case TW<%= entity.name %><%= c.name %>: return <%= c.string %>; +<% end -%> + default: return ""; + } +} + +<% end -%> +EMSCRIPTEN_BINDINGS(Wasm_TW<%= entity.name %>) { + enum_>("<%= entity.name %>") +<%# Cases -%> +<% entity.cases.each do |c| -%> + .value("<%= WasmCppHelper.format_name(c.name) %>", TW<%= entity.name %>::TW<%= entity.name %><%= c.name %>) +<% end -%> + ; +<%# Description -%> +<% if has_string -%> + function("describe<%= entity.name %>", describe<%= entity.name %>); +<% end -%> +} + +} // namespace TW::Wasm diff --git a/codegen/lib/templates/cpp/header.erb b/codegen/lib/templates/cpp/header.erb new file mode 100644 index 00000000000..1132e73092b --- /dev/null +++ b/codegen/lib/templates/cpp/header.erb @@ -0,0 +1,41 @@ +#include + +#include "../WasmString.h" +#include "../WasmData.h" + +namespace TW::Wasm { + +class <%= WasmCppHelper.class_name(entity: entity) %> { +<% if not entity.is_struct -%> + public: + TW<%= entity.name %> *instance; + public: + <%= WasmCppHelper.class_name(entity: entity) %>(TW<%= entity.name %> *instance) : instance(instance) {} +<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%> + ~<%= WasmCppHelper.class_name(entity: entity) %>() { + TW<%= entity.name %>Delete(instance); + } +<% end -%> +<% end -%> + public: +<%# Constructors -%> +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') || method.name.start_with?('Init') -%> + static auto <%= WasmCppHelper.function_name(entity: entity, function: method) %>(<%= WasmCppHelper.parameters(method.parameters) %>); +<% end -%> +<%# Properties -%> +<% entity.static_methods.each do |method| -%> +<% next unless method.name.start_with?('Create') -%> + static auto <%= WasmCppHelper.function_name(entity: entity, function: method) %>(<%= WasmCppHelper.parameters(method.parameters) %>) -> <%= WasmCppHelper.class_name(entity: entity) %>*; +<% end -%> +<%- entity.properties.each do |property| -%> + auto <%= WasmCppHelper.format_name(property.name) %>(); +<%- end -%> +<%# Methods -%> +<% entity.methods.each do |method| -%> +<% next if method.name == "Delete" -%> + auto <%= WasmCppHelper.format_name(method.name) %>(<%= WasmCppHelper.parameters(method.parameters.drop(1)) %>); +<% end -%> +}; // class <%= entity.name %> + +} // namespace TW::Wasm diff --git a/codegen/lib/templates/cpp/includes.erb b/codegen/lib/templates/cpp/includes.erb new file mode 100644 index 00000000000..6c3d2e996c4 --- /dev/null +++ b/codegen/lib/templates/cpp/includes.erb @@ -0,0 +1,21 @@ +<%# Include entity headers -%> +<% require 'set' -%> +<% includes = Set.new([entity.name]) -%> +<% (entity.static_methods + entity.methods).each do |method| -%> +<% includes << method.return_type.name if method.return_type.is_struct || method.return_type.is_class -%> +<% method.parameters.each do |param| -%> +<% includes << param.type.name if param.type.is_struct || param.type.is_class -%> +<% end -%> +<% end -%> +<% includes.each do |include| -%> +#include .h> +<% end -%> + +<% includes.each do |include| -%> +<% next if include == entity.name -%> +#include "./<%= include %>.h" +<% end -%> + +#include + +using namespace emscripten; diff --git a/codegen/lib/templates/cpp/method.erb b/codegen/lib/templates/cpp/method.erb new file mode 100644 index 00000000000..f4b0a5954f1 --- /dev/null +++ b/codegen/lib/templates/cpp/method.erb @@ -0,0 +1,6 @@ +<% method = locals[:method] -%> +<% arguments = locals[:arguments] || ['instance'] + WasmCppHelper.arguments(method.parameters.drop(1)) -%> +auto <%= WasmCppHelper.class_name(entity: entity) %>::<%= WasmCppHelper.format_name(method.name) %>(<%= WasmCppHelper.parameters(method.parameters.drop(1)) %>) { +<%= render('cpp/parameter_access.erb', { parameters: method.parameters }) -%> +<%= render('cpp/method_forward.erb', { method: method, arguments: arguments }) -%> +} diff --git a/codegen/lib/templates/cpp/method_call.erb b/codegen/lib/templates/cpp/method_call.erb new file mode 100644 index 00000000000..9b24e549e30 --- /dev/null +++ b/codegen/lib/templates/cpp/method_call.erb @@ -0,0 +1,5 @@ +<% + method = locals[:method] + arguments = locals[:arguments] || ['instance'] + WasmCppHelper.arguments(method.parameters.drop(1)) +-%> +TW<%= entity.name %><%= method.name %>(<%= arguments.join(', ') %>) \ No newline at end of file diff --git a/codegen/lib/templates/cpp/method_forward.erb b/codegen/lib/templates/cpp/method_forward.erb new file mode 100644 index 00000000000..46a856a5118 --- /dev/null +++ b/codegen/lib/templates/cpp/method_forward.erb @@ -0,0 +1,29 @@ +<% + method = locals[:method] + arguments = locals[:arguments] || ['instance'] + WasmCppHelper.arguments(method.parameters.drop(1)) + call = render('cpp/method_call.erb', { method: method, arguments: arguments }) + + # Method returns data + if should_return_data(method) + if method.return_type.is_nullable -%> + return TWDataToVal(<%= call %>); +<% else -%> + return TWDataToVal(<%= call %>); +<% end + # Method returns a string + elsif should_return_string(method) + if method.return_type.is_nullable -%> + return TWStringToStd(<%= call %>); +<% else -%> + return TWStringToStd(<%= call %>); +<% end + # Method returns a class or struct + elsif method.return_type.is_class || method.return_type.is_struct + if method.return_type.is_nullable -%> + return new Wasm<%= method.return_type.name %>(<%= call %>); +<% else -%> + return new Wasm<%= method.return_type.name %>(<%= call %>); +<% end + else -%> + return <%= call %>; +<%end -%> diff --git a/codegen/lib/templates/cpp/parameter_access.erb b/codegen/lib/templates/cpp/parameter_access.erb new file mode 100644 index 00000000000..da6e41d367a --- /dev/null +++ b/codegen/lib/templates/cpp/parameter_access.erb @@ -0,0 +1,8 @@ +<% parameters = locals[:parameters] -%> +<% parameters.each do |param| -%> +<% if param.type.name == :data -%> + auto <%= param.name %>Data = TW::data(<%= param.name %>); +<% elsif param.type.name == :string -%> +<% elsif param.type.is_struct || param.type.is_class -%> +<% end -%> +<% end -%> diff --git a/codegen/lib/templates/cpp/static_method.erb b/codegen/lib/templates/cpp/static_method.erb new file mode 100644 index 00000000000..86f9bc2734e --- /dev/null +++ b/codegen/lib/templates/cpp/static_method.erb @@ -0,0 +1,6 @@ +<% method = locals[:method] -%> +<% arguments = WasmCppHelper.arguments(method.parameters) -%> +auto <%= WasmCppHelper.class_name(entity: entity) %>::<%= WasmCppHelper.function_name(entity: entity, function: method) %>(<%= WasmCppHelper.parameters(method.parameters) %>) { +<%= render('cpp/parameter_access.erb', { parameters: method.parameters }) -%> +<%= render('cpp/method_forward.erb', { method: method, arguments: arguments }) -%> +} diff --git a/codegen/lib/templates/hrp.h.erb b/codegen/lib/templates/hrp.h.erb index b6706fdef39..0f4ed0432f1 100644 --- a/codegen/lib/templates/hrp.h.erb +++ b/codegen/lib/templates/hrp.h.erb @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. // -// This is a GENERATED FILE from \coins.json, changes made here WILL BE LOST. +// This is a GENERATED FILE from \registry.json, changes made here WILL BE LOST. // #pragma once diff --git a/codegen/lib/templates/java/class.erb b/codegen/lib/templates/java/class.erb index 8f9d3367739..69cd027f403 100644 --- a/codegen/lib/templates/java/class.erb +++ b/codegen/lib/templates/java/class.erb @@ -3,6 +3,7 @@ import java.util.HashSet; <% less = entity.static_methods.detect{ |i| i.name == 'Less' } -%> <% equal = entity.static_methods.detect{ |i| i.name == 'Equal' } -%> +<%= entity.comment %> <% if !less.nil? && !equal.nil? -%> public class <%= entity.name %> implements Comparable<<%= entity.name %>> { <% else -%> @@ -24,10 +25,10 @@ public class <%= entity.name %> { <% end -%> return instance; } - <%# Constructor declarations -%> <% entity.static_methods.each do |method| -%> <% next unless method.name.start_with?('Create') -%> +<%= method.comment_with_indent %> static native long native<%= method.name %>(<%= JavaHelper.parameters(method.parameters) %>); <% end -%> <%# Destructor declarations -%> @@ -35,9 +36,9 @@ public class <%= entity.name %> { <% next unless method.name.start_with?('Delete') -%> static native void native<%= method.name %>(long handle); <% end -%> - <%# Static property declarations -%> <% entity.static_properties.each do |property| -%> +<%= property.comment_with_indent %> <% if should_return_data(property) -%> public static native byte[] <%= JavaHelper.format_name(property.name) %>(<%= JavaHelper.parameters(property.parameters) %>); <% elsif should_return_string(property) -%> @@ -49,6 +50,7 @@ public class <%= entity.name %> { <%# Static method declarations -%> <% entity.static_methods.each do |method| -%> <% next if method.name.start_with?('Create') -%> +<%= method.comment_with_indent %> <% if should_return_data(method) -%> public static native byte[] <%= JavaHelper.format_name(method.name) %>(<%= JavaHelper.parameters(method.parameters) %>); <% elsif should_return_string(method) -%> @@ -59,6 +61,7 @@ public class <%= entity.name %> { <% end -%> <%# Property declarations -%> <% entity.properties.each do |property| -%> +<%= property.comment_with_indent %> <% if should_return_data(property) -%> public native byte[] <%= JavaHelper.format_name(property.name) %>(<%= JavaHelper.parameters(property.parameters.drop(1)) %>); <% elsif should_return_string(property) -%> @@ -70,6 +73,7 @@ public class <%= entity.name %> { <%# Method declarations -%> <% entity.methods.each do |method| -%> <% next if method.name.start_with?('Delete') -%> +<%= method.comment_with_indent %> <% if should_return_data(method) -%> public native byte[] <%= JavaHelper.format_name(method.name) %>(<%= JavaHelper.parameters(method.parameters.drop(1)) %>); <% elsif should_return_string(method) -%> @@ -82,10 +86,10 @@ public class <%= entity.name %> { <% compareMethod = JNIHelper.compareMethod(entity) -%> public native <%= JavaHelper.type(compareMethod.return_type) %> <%= JavaHelper.format_name(compareMethod.name) %>(<%= JavaHelper.parameters(compareMethod.parameters.drop(1)) %>); <% end -%> - <%# Constructors -%> <%- entity.static_methods.each do |method| -%> <%- next unless method.name.start_with?('Create') -%> +<%= method.comment_with_indent %> public <%= entity.name %>(<%= JavaHelper.parameters(method.parameters) %>) { nativeHandle = native<%= method.name %>(<%= JavaHelper.arguments(method.parameters) %>); if (nativeHandle == 0) { @@ -97,7 +101,6 @@ public class <%= entity.name %> { <%- end -%> } - <% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%> class <%= entity.name %>PhantomReference extends java.lang.ref.PhantomReference<<%= entity.name %>> { private static java.util.Set<<%= entity.name %>PhantomReference> references = new HashSet<<%= entity.name %>PhantomReference>(); diff --git a/codegen/lib/templates/java/enum.erb b/codegen/lib/templates/java/enum.erb index 27947182cc8..354b7c145a3 100644 --- a/codegen/lib/templates/java/enum.erb +++ b/codegen/lib/templates/java/enum.erb @@ -1,4 +1,5 @@ <% has_string = entity.cases.all? { |c| !c.string.nil? } -%> +<%= entity.comment %> <% type = entity.raw_type ? JavaHelper.type(entity.raw_type) : 'int' -%> public enum <%= entity.name %> { <%# Cases -%> diff --git a/codegen/lib/templates/java/header.erb b/codegen/lib/templates/java/header.erb index e596ae5e9fe..b1b1aeabc86 100644 --- a/codegen/lib/templates/java/header.erb +++ b/codegen/lib/templates/java/header.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen/lib/templates/java/struct.erb b/codegen/lib/templates/java/struct.erb index 5499705670d..bb57cf25017 100644 --- a/codegen/lib/templates/java/struct.erb +++ b/codegen/lib/templates/java/struct.erb @@ -2,6 +2,7 @@ import java.security.InvalidParameterException; <% less = entity.static_methods.detect{ |i| i.name == 'Less' } -%> <% equal = entity.static_methods.detect{ |i| i.name == 'Equal' } -%> +<%= entity.comment %> <% if !less.nil? && !equal.nil? -%> public class <%= entity.name %> implements Comparable<<%= entity.name %>> { <% else -%> @@ -17,15 +18,15 @@ public class <%= entity.name %> { instance.bytes = bytes; return instance; } - <%# Constructor declarations -%> <%- entity.static_methods.each do |method| -%> <%- next unless method.name.start_with?('Init') -%> +<%= method.comment_with_indent %> static native byte[] <%= JavaHelper.format_name(method.name) %>(<%= JavaHelper.parameters(method.parameters.drop(1)) %>); <%- end -%> - <%# Static property declarations -%> <%- entity.static_properties.each do |property| -%> +<%= property.comment_with_indent %> <%- if should_return_data(property) -%> public static native byte[] <%= JavaHelper.format_name(property.name) %>(<%= JavaHelper.parameters(property.parameters) %>); <%- else -%> @@ -35,6 +36,7 @@ public class <%= entity.name %> { <%# Static method declarations -%> <%- entity.static_methods.each do |method| -%> <%- next if method.name.start_with?('Init') -%> +<%= method.comment_with_indent %> <%- if should_return_data(method) -%> public static native byte[] <%= JavaHelper.format_name(method.name) %>(<%= JavaHelper.parameters(method.parameters) %>); <%- else -%> @@ -43,6 +45,7 @@ public class <%= entity.name %> { <%- end -%> <%# Property declarations -%> <%- entity.properties.each do |property| -%> +<%= property.comment_with_indent %> <%- if should_return_data(property) -%> public native byte[] <%= JavaHelper.format_name(property.name) %>(<%= JavaHelper.parameters(property.parameters.drop(1)) %>); <%- else -%> @@ -52,6 +55,7 @@ public class <%= entity.name %> { <%# Method declarations -%> <%- entity.methods.each do |method| -%> <%- next if method.name.start_with?('Delete') -%> +<%= method.comment_with_indent %> <%- if should_return_data(method) -%> public native byte[] <%= JavaHelper.format_name(method.name) %>(<%= JavaHelper.parameters(method.parameters.drop(1)) %>); <%- else -%> @@ -62,16 +66,15 @@ public class <%= entity.name %> { <% compareMethod = JNIHelper.compareMethod(entity) -%> public native <%= JavaHelper.type(compareMethod.return_type) %> <%= JavaHelper.format_name(compareMethod.name) %>(<%= JavaHelper.parameters(compareMethod.parameters.drop(1)) %>); <% end -%> - <%# Constructors -%> <%- entity.static_methods.each do |method| -%> <%- next unless method.name.start_with?('Init') -%> +<%= method.comment_with_indent %> public <%= entity.name %>(<%= JavaHelper.parameters(method.parameters.drop(1)) %>) { bytes = <%= JavaHelper.format_name(method.name) %>(<%= JavaHelper.arguments(method.parameters.drop(1)) %>); if (bytes == null) { throw new InvalidParameterException(); } } - <%- end -%> } diff --git a/codegen/lib/templates/jni/header.erb b/codegen/lib/templates/jni/header.erb deleted file mode 100644 index 0770a18cd04..00000000000 --- a/codegen/lib/templates/jni/header.erb +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here WILL BE LOST. -// diff --git a/codegen/lib/templates/jni_c.erb b/codegen/lib/templates/jni_c.erb index cf807ae79ad..d7785723aac 100644 --- a/codegen/lib/templates/jni_c.erb +++ b/codegen/lib/templates/jni_c.erb @@ -3,7 +3,7 @@ #include <% require 'set' -%> -<% includes = SortedSet.new([entity.name]) -%> +<% includes = Set.new([entity.name]) -%> <% entity.static_methods.each do |method| -%> <% includes << method.return_type.name if method.return_type.is_struct || method.return_type.is_class -%> <% method.parameters.each do |param| -%> diff --git a/codegen/lib/templates/newcoin/Address.cpp.erb b/codegen/lib/templates/newcoin/Address.cpp.erb index 0f15d92edcc..e9eb3b1412f 100644 --- a/codegen/lib/templates/newcoin/Address.cpp.erb +++ b/codegen/lib/templates/newcoin/Address.cpp.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,7 +6,7 @@ #include "Address.h" -using namespace TW::<%= format_name(coin) %>; +namespace TW::<%= format_name(coin) %> { bool Address::isValid(const std::string& string) { // TODO: Finalize implementation @@ -29,3 +29,5 @@ std::string Address::string() const { // TODO: Finalize implementation return "TODO"; } + +} // namespace TW::<%= format_name(coin) %> diff --git a/codegen/lib/templates/newcoin/Address.h.erb b/codegen/lib/templates/newcoin/Address.h.erb index a9e30cb303c..9c6f60df4f6 100644 --- a/codegen/lib/templates/newcoin/Address.h.erb +++ b/codegen/lib/templates/newcoin/Address.h.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -36,8 +36,3 @@ inline bool operator==(const Address& lhs, const Address& rhs) { } } // namespace TW::<%= format_name(coin) %> - -/// Wrapper for C interface. -struct TW<%= format_name(coin) %>Address { - TW::<%= format_name(coin) %>::Address impl; -}; diff --git a/codegen/lib/templates/newcoin/AddressTests.cpp.erb b/codegen/lib/templates/newcoin/AddressTests.cpp.erb index 9953e01fbdc..ca9d99d3139 100644 --- a/codegen/lib/templates/newcoin/AddressTests.cpp.erb +++ b/codegen/lib/templates/newcoin/AddressTests.cpp.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,8 +11,7 @@ #include #include -using namespace TW; -using namespace TW::<%= format_name(coin) %>; +namespace TW::<%= format_name(coin) %>::tests { TEST(<%= format_name(coin) %>Address, Valid) { ASSERT_TRUE(Address::isValid("__ADD_VALID_ADDRESS_HERE__")); @@ -46,3 +45,5 @@ TEST(<%= format_name(coin) %>Address, FromString) { auto address = Address("__ADD_VALID_ADDRESS_HERE__"); ASSERT_EQ(address.string(), "__ADD_SAME_VALID_ADDRESS_HERE__"); } + +} // namespace TW::<%= format_name(coin) %>::tests diff --git a/codegen/lib/templates/newcoin/AddressTests.kt.erb b/codegen/lib/templates/newcoin/AddressTests.kt.erb index f118db94a20..34781800a4c 100644 --- a/codegen/lib/templates/newcoin/AddressTests.kt.erb +++ b/codegen/lib/templates/newcoin/AddressTests.kt.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen/lib/templates/newcoin/Entry.cpp.erb b/codegen/lib/templates/newcoin/Entry.cpp.erb index 348e884d9f0..d0585ed64b9 100644 --- a/codegen/lib/templates/newcoin/Entry.cpp.erb +++ b/codegen/lib/templates/newcoin/Entry.cpp.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,19 +9,20 @@ #include "Address.h" #include "Signer.h" -using namespace TW::<%= format_name(coin) %>; -using namespace std; +namespace TW::<%= format_name(coin) %> { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress(TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::<%= format_name(coin) %> diff --git a/codegen/lib/templates/newcoin/Entry.h.erb b/codegen/lib/templates/newcoin/Entry.h.erb index ea5cd318bf7..1b58f5f78cd 100644 --- a/codegen/lib/templates/newcoin/Entry.h.erb +++ b/codegen/lib/templates/newcoin/Entry.h.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,9 +12,8 @@ namespace TW::<%= format_name(coin) %> { /// Entry point for implementation of <%= format_name(coin) %> coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinType<%= format_name(coin) %>}; } virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; diff --git a/codegen/lib/templates/newcoin/Proto.erb b/codegen/lib/templates/newcoin/Proto.erb index d7bdb047f8e..3065a609020 100644 --- a/codegen/lib/templates/newcoin/Proto.erb +++ b/codegen/lib/templates/newcoin/Proto.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen/lib/templates/newcoin/Signer.cpp.erb b/codegen/lib/templates/newcoin/Signer.cpp.erb index 381832fb176..7dec6579529 100644 --- a/codegen/lib/templates/newcoin/Signer.cpp.erb +++ b/codegen/lib/templates/newcoin/Signer.cpp.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,9 +8,7 @@ #include "Address.h" #include "../PublicKey.h" -using namespace TW; -using namespace TW::<%= name %>; - +namespace TW::<%= name %> { Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { // TODO: Check and finalize implementation @@ -24,3 +22,5 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { protoOutput.set_encoded(encoded.data(), encoded.size()); return protoOutput; } + +} // namespace TW::<%= name %> diff --git a/codegen/lib/templates/newcoin/Signer.h.erb b/codegen/lib/templates/newcoin/Signer.h.erb index 7345aad9c84..1261f9c6149 100644 --- a/codegen/lib/templates/newcoin/Signer.h.erb +++ b/codegen/lib/templates/newcoin/Signer.h.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -23,8 +23,3 @@ public: }; } // namespace TW::<%= name %> - -/// Wrapper for C interface. -struct TW<%= name %>Signer { - TW::<%= name %>::Signer impl; -}; diff --git a/codegen/lib/templates/newcoin/SignerTests.cpp.erb b/codegen/lib/templates/newcoin/SignerTests.cpp.erb index 27eb5ef11ee..9df8b26ee1e 100644 --- a/codegen/lib/templates/newcoin/SignerTests.cpp.erb +++ b/codegen/lib/templates/newcoin/SignerTests.cpp.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,8 +12,7 @@ #include -using namespace TW; -using namespace TW::<%= format_name(coin) %>; +namespace TW::<%= format_name(coin) %>::tests { // TODO: Add tests @@ -32,3 +31,5 @@ TEST(<%= format_name(coin) %>Signer, Sign) { //ASSERT_EQ(hex(serialized), "__RESULT__"); //ASSERT_EQ(...) } + +} // namespace TW::<%= format_name(coin) %>::tests diff --git a/codegen/lib/templates/newcoin/SignerTests.kt.erb b/codegen/lib/templates/newcoin/SignerTests.kt.erb index ba0bbafcfd9..e0512c55b1d 100644 --- a/codegen/lib/templates/newcoin/SignerTests.kt.erb +++ b/codegen/lib/templates/newcoin/SignerTests.kt.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb b/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb index 11331829f38..b290debae7a 100644 --- a/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb +++ b/codegen/lib/templates/newcoin/TWAddressTests.cpp.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,7 +7,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb b/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb index a59a2fdc772..59b6aa225c6 100644 --- a/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb +++ b/codegen/lib/templates/newcoin/TWSignerTests.cpp.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,7 +7,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/codegen/lib/templates/newcoin/Tests.swift.erb b/codegen/lib/templates/newcoin/Tests.swift.erb index 58851cb7024..583696882c5 100644 --- a/codegen/lib/templates/newcoin/Tests.swift.erb +++ b/codegen/lib/templates/newcoin/Tests.swift.erb @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/codegen/lib/templates/registry.md.erb b/codegen/lib/templates/registry.md.erb index a62b332c1d6..e642b3bc7cd 100644 --- a/codegen/lib/templates/registry.md.erb +++ b/codegen/lib/templates/registry.md.erb @@ -5,5 +5,5 @@ This list is generated from [./registry.json](../registry.json) | Index | Name | Symbol | Logo | URL | | ------- | ---------------- | ------ | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | <% coins.select{ |c| c['deprecated'].nil? }.each do |coin| -%> -| <%= coin['coinId'].to_s.ljust(7, " ") %> | <%= coin['name'].ljust(16, " ") %> | <%= coin['symbol'].ljust(6, " ") %> | <%= coin_img(coin['id']).ljust(123) %> | <%= "<#{coin['info']['url']}>".ljust(29, " ") %> | +| <%= coin['coinId'].to_s.ljust(7, " ") %> | <%= coin_name(coin).ljust(16, " ") %> | <%= coin['symbol'].ljust(6, " ") %> | <%= coin_img(coin['id']).ljust(123) %> | <%= "<#{coin['info']['url']}>".ljust(29, " ") %> | <% end -%> diff --git a/codegen/lib/templates/swift/class.erb b/codegen/lib/templates/swift/class.erb index 4d090e7bc70..3404e02778d 100644 --- a/codegen/lib/templates/swift/class.erb +++ b/codegen/lib/templates/swift/class.erb @@ -1,9 +1,11 @@ import Foundation <% protocols = SwiftHelper.protocol(entity) -%> +<%= entity.comment %> public final class <%= entity.name %><% unless protocols.empty? %>: <%= protocols.join(', ') %><% end %> { <%# Static properties -%> <% entity.static_properties.each do |property| -%> +<%= property.comment_with_indent %> public static var <%= SwiftHelper.format_name(property.name) %>: <%= SwiftHelper.type(property.return_type) %> { <%- if property.return_type.is_class || property.return_type.is_struct -%> return <%= SwiftHelper.type(property.return_type) %>(rawValue: TW<%= entity.name %><%= property.name %>()) diff --git a/codegen/lib/templates/swift/class_properties.erb b/codegen/lib/templates/swift/class_properties.erb index c36feb40cb7..25c9a7c4c9d 100644 --- a/codegen/lib/templates/swift/class_properties.erb +++ b/codegen/lib/templates/swift/class_properties.erb @@ -1,5 +1,6 @@ <%# Properties -%> <%- entity.properties.each do |property| -%> +<%= property.comment_with_indent %> public var <%= SwiftHelper.format_name(property.name) %>: <%= SwiftHelper.type(property.return_type) %> { <%= render('swift/method_forward.erb', { method: property }) -%> } diff --git a/codegen/lib/templates/swift/enum.erb b/codegen/lib/templates/swift/enum.erb index e54c283f148..6c1165edab5 100644 --- a/codegen/lib/templates/swift/enum.erb +++ b/codegen/lib/templates/swift/enum.erb @@ -1,4 +1,5 @@ <% has_string = entity.cases.all? { |c| !c.string.nil? } -%> +<%= entity.comment %> <% type = entity.raw_type ? SwiftHelper.type(entity.raw_type) : 'UInt32' -%> public enum <%= entity.name %>: <%= type %>, CaseIterable<% if has_string %>, CustomStringConvertible <% end %> { <%# Cases -%> diff --git a/codegen/lib/templates/swift/enum_extension.erb b/codegen/lib/templates/swift/enum_extension.erb index 133a6028953..c5f4f2d3f0c 100644 --- a/codegen/lib/templates/swift/enum_extension.erb +++ b/codegen/lib/templates/swift/enum_extension.erb @@ -1,7 +1,7 @@ extension <%= entity.name %> { <%# Properties -%> <%- entity.properties.each do |property| -%> - +<%= property.comment_with_indent %> public var <%= SwiftHelper.format_name(property.name) %>: <%= SwiftHelper.type(property.return_type) %> { <%= render('swift/method_forward.erb', { method: property, arguments: ["TW#{entity.name}(rawValue: rawValue)"] }) -%> } diff --git a/codegen/lib/templates/swift/header.erb b/codegen/lib/templates/swift/header.erb deleted file mode 100644 index 0770a18cd04..00000000000 --- a/codegen/lib/templates/swift/header.erb +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here WILL BE LOST. -// diff --git a/codegen/lib/templates/swift/method.erb b/codegen/lib/templates/swift/method.erb index d50b6a0629a..d9700123165 100644 --- a/codegen/lib/templates/swift/method.erb +++ b/codegen/lib/templates/swift/method.erb @@ -1,4 +1,5 @@ <% method = locals[:method] -%> +<%= method.comment_with_indent %> <% arguments = locals[:arguments] || ['rawValue'] + SwiftHelper.arguments(method.parameters.drop(1)) -%> <% if method.discardable_result -%> @discardableResult diff --git a/codegen/lib/templates/swift/parameter_access.erb b/codegen/lib/templates/swift/parameter_access.erb index 8521c18bb4a..ddf4335753f 100644 --- a/codegen/lib/templates/swift/parameter_access.erb +++ b/codegen/lib/templates/swift/parameter_access.erb @@ -10,12 +10,14 @@ let <%= param.name %>String: UnsafeRawPointer? if let s = <%= param.name %> { <%= param.name %>String = TWStringCreateWithNSString(s) - defer { - TWStringDelete(s) - } } else { <%= param.name %>String = nil } + defer { + if let s = <%= param.name %>String { + TWStringDelete(s) + } + } <% else -%> let <%= param.name %>String = TWStringCreateWithNSString(<%= param.name %>) defer { diff --git a/codegen/lib/templates/swift/static_method.erb b/codegen/lib/templates/swift/static_method.erb index 5a9638220cf..8ecd73229be 100644 --- a/codegen/lib/templates/swift/static_method.erb +++ b/codegen/lib/templates/swift/static_method.erb @@ -1,4 +1,5 @@ <% method = locals[:method] -%> +<%= method.comment_with_indent %> <% arguments = SwiftHelper.arguments(method.parameters) -%> <% if method.discardable_result -%> @discardableResult diff --git a/codegen/lib/templates/swift/struct.erb b/codegen/lib/templates/swift/struct.erb index e7c3b2ca7ea..518588bd0ac 100644 --- a/codegen/lib/templates/swift/struct.erb +++ b/codegen/lib/templates/swift/struct.erb @@ -1,9 +1,11 @@ import Foundation <% protocols = SwiftHelper.protocol(entity) -%> +<%= entity.comment %> public struct <%= entity.name %><% unless protocols.empty? %>: <%= protocols.join(', ') %><% end %> { <%# Static properties -%> <% entity.static_properties.each do |property| -%> +<%= property.comment_with_indent %> public static var <%= SwiftHelper.format_name(property.name) %>: <%= SwiftHelper.type(property.return_type) %> { <%- if property.return_type.is_class || property.return_type.is_struct -%> return <%= SwiftHelper.type(property.return_type) %>(rawValue: TW<%= entity.name %><%= property.name %>()) diff --git a/codegen/lib/templates/swift/struct_properties.erb b/codegen/lib/templates/swift/struct_properties.erb index ebd50de5629..10d896df1e4 100644 --- a/codegen/lib/templates/swift/struct_properties.erb +++ b/codegen/lib/templates/swift/struct_properties.erb @@ -1,6 +1,7 @@ <%# Properties -%> <%- entity.properties.each do |property| -%> +<%= property.comment_with_indent %> public var <%= SwiftHelper.format_name(property.name) %>: <%= SwiftHelper.type(property.return_type) %> { <%= render('swift/method_forward.erb', { method: property }) -%> } diff --git a/codegen/lib/templates/ts/class_d.erb b/codegen/lib/templates/ts/class_d.erb new file mode 100644 index 00000000000..87591ca7978 --- /dev/null +++ b/codegen/lib/templates/ts/class_d.erb @@ -0,0 +1,22 @@ +export class <%= entity.name %> { +<% entity.static_methods.each do |method| -%> +<% next if method.name.start_with?('Create') || method.name.start_with?('Init') -%> + static <%= WasmCppHelper.function_name(entity: entity, function: method) %>(<%= TsHelper.parameters(method.parameters) %>): <%= TsHelper.type(method.return_type) %>; +<% end -%> +<% entity.static_methods.each do |method| -%> +<% next unless method.name.start_with?('Create') -%> + static <%= WasmCppHelper.function_name(entity: entity, function: method) %>(<%= TsHelper.parameters(method.parameters) %>): <%= TsHelper.type(method.return_type) %>; +<% end -%> +<%- entity.properties.each do |property| -%> + <%= WasmCppHelper.format_name(property.name) %>(): <%= TsHelper.type(property.return_type) %>; +<%- end -%> +<% entity.methods.each do |method| -%> +<% next if method.name == "Delete" -%> + <%= WasmCppHelper.format_name(method.name) %>(<%= TsHelper.parameters(method.parameters.drop(1)) %>): <%= TsHelper.type(method.return_type) %>; +<% end -%> +<% if not entity.is_struct -%> +<% unless entity.methods.select{ |x| x.name == "Delete" }.empty? -%> + delete(): void; +<% end -%> +<% end -%> +} diff --git a/codegen/lib/templates/ts/enum_d.erb b/codegen/lib/templates/ts/enum_d.erb new file mode 100644 index 00000000000..18b5b2c973d --- /dev/null +++ b/codegen/lib/templates/ts/enum_d.erb @@ -0,0 +1,12 @@ +export class <%= entity.name %> { + value: number; +<% entity.cases.each do |c| -%> + static <%= WasmCppHelper.format_name(c.name) %>: <%= entity.name %>; +<% end -%> +} +<% has_string = entity.cases.all? { |c| !c.string.nil? } -%> +<% if has_string -%> + +declare function describe<%= entity.name %>(value: <%= entity.name %>): string; + +<% end -%> diff --git a/codegen/lib/templates/wasm_cpp.erb b/codegen/lib/templates/wasm_cpp.erb new file mode 100644 index 00000000000..2d3338e94fb --- /dev/null +++ b/codegen/lib/templates/wasm_cpp.erb @@ -0,0 +1,7 @@ +<% if entity.is_a?(EnumDecl) -%> +<%= render('cpp/includes.erb') -%> + +<%= render('cpp/enum.erb') -%> +<% else -%> +<%= render('cpp/class.erb') -%> +<% end -%> diff --git a/codegen/lib/templates/wasm_d_ts.erb b/codegen/lib/templates/wasm_d_ts.erb new file mode 100644 index 00000000000..7600b54197b --- /dev/null +++ b/codegen/lib/templates/wasm_d_ts.erb @@ -0,0 +1,5 @@ +<% if entity.is_a?(EnumDecl) -%> +<%= render('ts/enum_d.erb') -%> +<% else -%> +<%= render('ts/class_d.erb') -%> +<% end -%> diff --git a/codegen/lib/templates/wasm_h.erb b/codegen/lib/templates/wasm_h.erb new file mode 100644 index 00000000000..38215a86f2b --- /dev/null +++ b/codegen/lib/templates/wasm_h.erb @@ -0,0 +1,7 @@ +#pragma once + +<%= render('cpp/includes.erb') -%> + +<% if not entity.is_a?(EnumDecl) -%> +<%= render('cpp/header.erb') -%> +<% end -%> diff --git a/codegen/lib/ts_helper.rb b/codegen/lib/ts_helper.rb new file mode 100644 index 00000000000..a75027b8999 --- /dev/null +++ b/codegen/lib/ts_helper.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +module TsHelper + def self.parameters(params) + names = params.map do |param| + type = type(param.type) + if param.type.name == :data + type += ' | Buffer' + end + "#{param.name}: #{type}" + end + names.join(', ') + end + + def self.primitive_type(t) + case t.name + when :bool + 'boolean' + when :int, :uint8, :uint16, :uint32, :uint64, :size + 'number' + when :data + 'Uint8Array | Buffer' + when :string + 'string' + else + raise "Invalid type #{t.name}" + end + end + + def self.type(t) + case t.name + when :void + 'void' + when :bool + 'boolean' + when :int, :uint8, :int8, :uint16, :int16, :uint32, :int32, :uint64, :int64, :size + 'number' + when :data + 'Uint8Array' + when :string + 'string' + else + t.name + end + end + + def self.combine_declaration_files + wasm_src = File.expand_path(File.join(File.dirname(__FILE__), '../../wasm')) + header = File.expand_path('copyright_header.erb', File.join(File.dirname(__FILE__), 'templates')) + combined_path = "#{wasm_src}/src/wallet-core.d.ts" + + combined = File.open(combined_path, 'w') + # append header + combined.write(File.read(header)) + + # append .d.ts in src + Dir.glob("#{wasm_src}/src/*.d.ts").each do |file| + combined.write(File.read(file)) + end + + # append .d.ts in generated + Dir.glob("#{wasm_src}/lib/generated/*.d.ts").each do |file| + combined.write(File.read(file)) + end + combined.close + FileUtils.remove_dir("#{wasm_src}/lib/generated", true) + + # generate WalletCore interface + interface = "export interface WalletCore {\n" + + combined = File.open(combined_path, 'r') + all_lines = combined.read + combined.close + + export_regex = /^export (class|namespace) (.*)\b/ + declare_regex = /^declare function (.+?(?=\())/ + + all_lines.scan(export_regex).each do |match| + matched = match[1] + interface += " #{matched}: typeof #{matched};\n" + end + + all_lines.scan(declare_regex).each do |match| + matched = match[0] + interface += " #{matched}: typeof #{matched};\n" + end + interface += "}\n" + + File.open(combined_path, 'a') do |file| + file << interface + end + end +end diff --git a/codegen/lib/wasm_cpp_helper.rb b/codegen/lib/wasm_cpp_helper.rb new file mode 100644 index 00000000000..98ba4b07b46 --- /dev/null +++ b/codegen/lib/wasm_cpp_helper.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: true + +module WasmCppHelper + # Transforms an interface name to a cpp method name + def self.format_name(name) + result = name + match = /^([A-Z]+)/.match(name) + result = name.sub(match[1], match[1].downcase) unless match.nil? + result + end + + # Transforms a method/property name to a cpp function name + + def self.class_name(entity:) + "Wasm" + entity.name + end + + def self.function_name(entity:, function:) + "#{format_name(function.name)}" + end + + def self.parameters(params) + names = params.map do |param| + "#{type(param.type)} #{param.name}" + end + names.join(', ') + end + + def self.arguments(params) + params.map do |param| + if param.type.name == :data + "&#{param.name}Data" + elsif param.type.name == :string + '&' + param.name + elsif param.type.is_struct || param.type.is_class + param.name + '->instance' + else + param.name + end + end + end + + def self.primitive_type(t) + case t.name + when :bool + 'bool' + when :int + 'int' + when :uint8 + 'uint8_t' + when :size + 'size_t' + when :uint16 + 'uint16_t' + when :uint32 + 'uint32_t' + when :uint64 + 'uint64_t' + when :string + 'std::string' + else + raise "Invalid type #{t.name}" + end + end + + def self.type(t) + case t.name + when :void + 'void' + when :bool + 'bool' + when :int + 'int' + when :uint8 + 'uint8_t' + when :uint16 + 'uint16_t' + when :uint32 + 'uint32_t' + when :uint64 + 'uint64_t' + when :int8 + 'int8_t' + when :int16 + 'int16_t' + when :int32 + 'int32_t' + when :int64 + 'int64_t' + when :size + 'size_t' + when :data + 'const std::string&' + when :string + 'const std::string&' + else + if t.is_enum + "TW#{t.name}" + elsif t.is_struct || t.is_class + "Wasm#{t.name}*" + else + t.name + end + end + end + + def self.compareMethod(entity) + FunctionDecl.new( + name: 'compareTo', + entity: entity, + is_method: true, + return_type: TypeDecl.new(name: :int), + parameters: [Parameter.new(name: 'thisObject', type: entity.type), Parameter.new(name: 'other', type: entity.type)], + static: false) + end + end diff --git a/codegen/test/test_jni_helper.rb b/codegen/test/test_jni_helper.rb index 766c8dfdcf7..595ff1e53e2 100644 --- a/codegen/test/test_jni_helper.rb +++ b/codegen/test/test_jni_helper.rb @@ -8,7 +8,7 @@ def test_format_name end def test_function_name - entity = EntityDecl.new(name: 'Test', is_struct: false) + entity = EntityDecl.new(name: 'Test', is_struct: false, comment: '') method = FunctionDecl.new(name: 'Function', entity: entity, is_method: true) name = JNIHelper.function_name(entity: entity, function: method) assert_equal(name, 'Java_wallet_core_jni_Test_function') diff --git a/codegen/test/test_parser.rb b/codegen/test/test_parser.rb index 6f51a726695..7ec0af335e8 100644 --- a/codegen/test/test_parser.rb +++ b/codegen/test/test_parser.rb @@ -44,22 +44,26 @@ def test_parse_invalid_method def test_parse_method_discardable_result parser = Parser.new(path: '', string: ' + // This is a sample file TW_EXTERN_C_BEGIN struct TWEthereumAbiFunction; + // Ethereuem ABI helpers TW_EXPORT_CLASS struct TWEthereumAbiEncoder; - /// Encode function to Eth ABI binary + // Encode function to Eth ABI binary TW_EXPORT_STATIC_METHOD TW_METHOD_DISCARDABLE_RESULT TWData*_Nonnull TWEthereumAbiEncoderEncode(struct TWEthereumAbiFunction *_Nonnull func_in); TW_EXTERN_C_END ') parser.parse + assert_equal(parser.entity.name, 'EthereumAbiEncoder') method = parser.entity.static_methods[0] assert_equal(method.return_type.name, :data) assert_equal(method.name, 'Encode') assert_equal(method.discardable_result, true) + assert_equal(method.comment, '// Encode function to Eth ABI binary') end def test_init diff --git a/coverage.stats b/coverage.stats index ee1376541f1..b99dacf6dff 100644 --- a/coverage.stats +++ b/coverage.stats @@ -1 +1 @@ -94.6 +95.0 \ No newline at end of file diff --git a/docs/registry-fields.md b/docs/registry-fields.md new file mode 100644 index 00000000000..f4662d510cd --- /dev/null +++ b/docs/registry-fields.md @@ -0,0 +1,196 @@ +# Documentation for registry.json + +The file `registry.json` contains meta info about supported blockchains. +It is the input for some generated source files and documentation ([registry.md](registry.md)), and values from it are used during runtime. + +## Fields + +**`id`** +Internal ID of the chain. Lowercase letters only. Should be never changed. +Ex.: `'bitcoin'`. + +**`name`** +More readable name, can include lower/uppercase, space. +Ex.: `'Bitcoin'`. + +**`displayName`** +Optional, if present, overrides **name** for places where it is visible to the user. +Ex.: `'BNB Beacon Chain'`. + +**`coinId`** +Internal numerical ID for the chain. In most cases it is the ID used in BIP-44 address derivation. +See quasi-standard repository here: https://github.com/satoshilabs/slips/blob/master/slip-0044.md +Ex.: `0` for Bitcoin, `60` for Ethereum. + +Some typical special cases: + +- Multiple chains/coins with the same ID: adding value `10000000` to distinguish. Possible multiple times. +Ex.: `10000118` for Osmosis, `118` for Cosmos; `20000714` for BNB Smart Chain. +- Ethereum-clone chains with no own BIP-44 ID: use the `10000000 + chainID` as coinID. + +See also: `slip44` and `chainId`. + +**`slip44`** +Optionally, SLIP-44 (BIP-44) coin ID can be specified here, in case it differs from `coinId`. Most of the case the two are the same, so this can be omitted. +Ex.: `60` for Optimism (coinID is `10000070`). + +**`symbol`** +Symbol of the native coin. Typically a short, upper-case-only string. +Ex.: `BTC`, `ETH`. + +**`decimals`** +Number of decimals in coin amounts. Amounts are typically expressed as (large) integer values, with all decimal values, like `100000` for `0.00100000` BTC (decimals=8). +Ex.: `8` for Bitcoin, `18` for Ethereum. + +**`blockchain`** +Some chains are very similar and share the implementation code. +This flag is used to direct logic to the right implementation. +Ex. `'Cosmos'` for Oasis, as Oasis is handled by Cosmos implementation. + +**`derivation`** +Defines properties for address derivation, most importantly derivation paths. + +Typically only one derivation is supported per chain, in this case the definition looks like: + +``` + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], +``` + +It contains the derivation path used to derive private key (and address) from the wallet mnemonic (HD wallet). +Derivation path is usually well known for a chain implementation, and different wallet implementations use the same path (so cross-import is possible). +Note that the second number, the BIP-44 ID, usually matches the coinId. + +Some blockchains may support additional alternative derivations. These have: + +- a name +- a alternative derivation path (optional) + +Derivation may differ in the derivation path, or by address generation method (based on the derivation name). +The first derivation is considered the default. + +Examples: +Bitcoin uses Segwit address by default, but also supports earlier P2PKH addresses: + +``` + "derivation": [ + { + "name": "segwit", + "path": "m/84'/0'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" + }, + { + "name": "legacy", + "path": "m/44'/0'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], +``` + +Solana supports two derivations, which differ in derivation path: + +``` + "derivation": [ + { + "path": "m/44'/501'/0'" + }, + { + "name": "solana", + "path": "m/44'/501'/0'/0'" + } + ], +``` + +**`xpub` and `xprv`** +Defines the XPub and XPriv format used, Bitcoin-style. Defined inside the derivation section (as they may differ per derivation). + +**`curve`** +Defines the elliptic curve used in private-public key generation and signing. +Ex.: `'secp256k1'` for Bitcoin and Ethereum, `'ed25519'` for Polkadot. + +**`publicKeyType`** +The type of public key used. +Ex.: `'secp256k1'` for Bitcoin, `'secp256k1Extended'` for Ethereum. + +**`staticPrefix`** +Optional byte prefix, used in some Bitcoin-like chains. +Ex.: `7` for Decred. + +**`p2pkhPrefix` and `p2shPrefix`** +Defines the prefix byte used in P2PKH and P2SH addresses, Bitcoin style. +Ex. `0` and `5` for Bitcoin. + +**`hrp`** +Human Readable Prefix used to prefix an address, used to indicate type of address, to minimalize risk of accidental address mix-up across chains. +Ex. `'bc'` for Bitcoin, `'cosmos'` for Cosmos. + +**`chainId`** +Chain identifier, used by forks, e.g. in case of Ethereum (a decimal number), or Cosomos (a string ID). +Chain identifier, in case of Ethereum it's a constant decimal number; +for Cosmos, it's a dynamic string network id (usually changes with network upgrades). + +Please note the chain id might not be always latest in registry. In transaction building current value has to be supplied each time. + +Ex.: `'1'` for Ethereum, `'61'` for Ethereum Classic, `'osmosis-1'`for Osmosis. + +**`publicKeyHasher`** +Hash method used in XPub derivation. +Default is `sha256ripemd`. +Ex.: `'sha256ripemd'` for Bitcoin, `'blake256ripemd'` for Decred. + +**`base58Hasher`** +Hash method used in extended private and public key derivation, for checksumming within Base58 addresses. +Default is `sha256d`. +Ex.: `'sha256d'` for Bitcoin, `'blake256d'` for Decred. + +**`addressHasher`** +Hash method used in the publicKey -> address generation. +Only some chain implementation use this setting, in most implementation this is fixed (and value here is only informative). +Default is `sha256ripemd`. +Ex.: missing ('sha256ripemd') for Bitcoin, `'keccak256'` for Ethereum, `'sha256ripemd'` for Cosmos, `'keccak256'` for Native Evmos, despite being a Cosmos fork. + +**`explorer`** +Explorer web service for this chain. Sub-fields are used so that full URLs can be built for any address or transactions. +Note that the sample values should include existing IDs, so that the resulting full URL is valid. + +Example: + +``` + "explorer": { + "url": "https://blockchair.com", + "txPath": "/bitcoin/transaction/", + "accountPath": "/bitcoin/address/", + "sampleTx": "0607f62530b68cfcc91c57a1702841dd399a899d0eecda8e31ecca3f52f01df2", + "sampleAccount": "17A16QmavnUfCW11DAApiJxp7ARnxN5pGX" + }, +``` + +This results in the full URL for the sample address: +https://blockchair.com/bitcoin/address/17A16QmavnUfCW11DAApiJxp7ARnxN5pGX +which is a working URL. + +Beware of the starting-ending slashes used. + +**`info`** +Section with project info: + +**`info/url`** +Main project website. + +**`info/source`** +Link to the default implementation of the node or RPC gateway that can be used by a wallet. + +**`info/rpc`** +Optional URL to an available public RPC service. + +**`info/documentation`** +Main porject documentation site/subsite. + +**`deprecated`** +If set to `true`, the project is considered deprecated: its info is kept here, but it will not be supported. +Ex. `'true'` for Kin. diff --git a/docs/registry.md b/docs/registry.md index e87f8d2dc3d..5d89f556a91 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -16,14 +16,14 @@ This list is generated from [./registry.json](../registry.json) | 60 | Ethereum | ETH | | | | 61 | Ethereum Classic | ETC | | | | 74 | ICON | ICX | | | -| 118 | Cosmos | ATOM | | | +| 118 | Cosmos Hub | ATOM | | | | 133 | Zcash | ZEC | | | -| 136 | Zcoin | FIRO | | | +| 136 | Firo | FIRO | | | | 144 | XRP | XRP | | | | 145 | Bitcoin Cash | BCH | | | | 148 | Stellar | XLM | | | | 156 | Bitcoin Gold | BTG | | | -| 165 | Nano | NANO | | | +| 165 | Nano | XNO | | | | 175 | Ravencoin | RVN | | | | 178 | POA Network | POA | | | | 194 | EOS | EOS | | | @@ -32,10 +32,12 @@ This list is generated from [./registry.json](../registry.json) | 242 | Nimiq | NIM | | | | 283 | Algorand | ALGO | | | | 304 | IoTeX | IOTX | | | +| 309 | Nervos | CKB | | | | 313 | Zilliqa | ZIL | | | -| 330 | Terra | LUNA | | | +| 330 | Terra Classic | LUNC | | | | 354 | Polkadot | DOT | | | -| 394 | CryptoOrg | CRO | | | +| 394 | Crypto.org | CRO | | | +| 396 | Everscale | EVER | | | | 397 | NEAR | NEAR | | | | 425 | Aion | AION | | | | 434 | Kusama | KSM | | | @@ -48,13 +50,16 @@ This list is generated from [./registry.json](../registry.json) | 500 | Theta | THETA | | | | 501 | Solana | SOL | | | | 508 | Elrond | eGLD | | | -| 714 | Binance | BNB | | | +| 637 | Aptos | APT | | | +| 714 | BNB Beacon Chain | BNB | | | | 818 | VeChain | VET | | | | 820 | Callisto | CLO | | | | 888 | NEO | NEO | | | | 889 | TomoChain | TOMO | | | +| 899 | eCash | XEC | | | | 931 | THORChain | RUNE | | | | 966 | Polygon | MATIC | | | +| 996 | OKX Chain | OKT | | | | 1001 | Thunder Token | TT | | | | 1023 | Harmony | ONE | | | | 1024 | Ontology | ONT | | | @@ -64,15 +69,31 @@ This list is generated from [./registry.json](../registry.json) | 2718 | Nebulas | NAS | | | | 6060 | GoChain | GO | | | | 8964 | NULS | NULS | | | -| 19167 | Zelcash | FLUX | | | +| 18000 | Meter | MTR | | | +| 19167 | Flux | FLUX | | | | 52752 | Celo | CELO | | | | 5718350 | Wanchain | WAN | | | | 5741564 | Waves | WAVES | | | -| 10000070 | Optimism | OETH | | | -| 10000100 | xDai | xDAI | | | +| 10000025 | Cronos Chain | CRO | | | +| 10000070 | Optimistic Ethereum | ETH | | | +| 10000100 | Gnosis Chain | xDAI | | | +| 10000118 | Osmosis | OSMO | | | +| 10000145 | Smart Bitcoin Cash | BCH | | | | 10000250 | Fantom | FTM | | | -| 10000553 | ECO Chain | HT | | | +| 10000280 | zkSync v2 | ETH | | | +| 10000288 | Boba | BOBAETH | | | +| 10000321 | KuCoin Community Chain | KCS | | | +| 10000330 | Terra | LUNA | | | +| 10000553 | Huobi ECO Chain | HT | | | +| 10001088 | Metis | METIS | | | +| 10001284 | Moonbeam | GLMR | | | +| 10001285 | Moonriver | MOVR | | | | 10002020 | Ronin | RON | | | +| 10002222 | KavaEvm | KAVA | | | +| 10008217 | Klaytn | KLAY | | | | 10009000 | Avalanche C-Chain | AVAX | | | -| 10042221 | Arbitrum | ARETH | | | -| 20000714 | Smart Chain | BNB | | | +| 10009001 | Evmos | EVMOS | | | +| 10042221 | Arbitrum | ETH | | | +| 20000714 | BNB Smart Chain | BNB | | | +| 20009001 | Native Evmos | EVMOS | | | +| 1323161554 | Aurora | ETH | | | diff --git a/include/TrustWalletCore/TWAES.h b/include/TrustWalletCore/TWAES.h index 510d2f32291..b44041f36f2 100644 --- a/include/TrustWalletCore/TWAES.h +++ b/include/TrustWalletCore/TWAES.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,40 +12,47 @@ TW_EXTERN_C_BEGIN +/// AES encryption/decryption methods. TW_EXPORT_STRUCT struct TWAES { uint8_t unused; // C doesn't allow zero-sized struct }; -/// Encrypts a block of data using AES in Cipher Block Chaining (CBC) mode. +/// Encrypts a block of Data using AES in Cipher Block Chaining (CBC) mode. /// -/// \param key encryption key, must be 16, 24, or 32 bytes long. -/// \param data data to encrypt. +/// \param key encryption key Data, must be 16, 24, or 32 bytes long. +/// \param data Data to encrypt. /// \param iv initialization vector. +/// \param mode padding mode. +/// \return encrypted Data. TW_EXPORT_STATIC_METHOD TWData *_Nullable TWAESEncryptCBC(TWData *_Nonnull key, TWData *_Nonnull data, TWData *_Nonnull iv, enum TWAESPaddingMode mode); /// Decrypts a block of data using AES in Cipher Block Chaining (CBC) mode. /// -/// \param key decryption key, must be 16, 24, or 32 bytes long. -/// \param data data to decrypt. -/// \param iv initialization vector. +/// \param key decryption key Data, must be 16, 24, or 32 bytes long. +/// \param data Data to decrypt. +/// \param iv initialization vector Data. +/// \param mode padding mode. +/// \return decrypted Data. TW_EXPORT_STATIC_METHOD TWData *_Nullable TWAESDecryptCBC(TWData *_Nonnull key, TWData *_Nonnull data, TWData *_Nonnull iv, enum TWAESPaddingMode mode); /// Encrypts a block of data using AES in Counter (CTR) mode. /// -/// \param key encryption key, must be 16, 24, or 32 bytes long. -/// \param data data to encrypt. -/// \param iv initialization vector. +/// \param key encryption key Data, must be 16, 24, or 32 bytes long. +/// \param data Data to encrypt. +/// \param iv initialization vector Data. +/// \return encrypted Data. TW_EXPORT_STATIC_METHOD TWData *_Nullable TWAESEncryptCTR(TWData *_Nonnull key, TWData *_Nonnull data, TWData *_Nonnull iv); /// Decrypts a block of data using AES in Counter (CTR) mode. /// -/// \param key decryption key, must be 16, 24, or 32 bytes long. -/// \param data data to decrypt. -/// \param iv initialization vector. +/// \param key decryption key Data, must be 16, 24, or 32 bytes long. +/// \param data Data to decrypt. +/// \param iv initialization vector Data. +/// \return decrypted Data. TW_EXPORT_STATIC_METHOD TWData *_Nullable TWAESDecryptCTR(TWData *_Nonnull key, TWData *_Nonnull data, TWData *_Nonnull iv); diff --git a/include/TrustWalletCore/TWAESPaddingMode.h b/include/TrustWalletCore/TWAESPaddingMode.h index 9e4713d0ed6..5cfe2103172 100644 --- a/include/TrustWalletCore/TWAESPaddingMode.h +++ b/include/TrustWalletCore/TWAESPaddingMode.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,6 +10,7 @@ TW_EXTERN_C_BEGIN +/// Padding mode used in AES encryption. TW_EXPORT_ENUM(uint32_t) enum TWAESPaddingMode { TWAESPaddingModeZero = 0, // padding value is zero diff --git a/include/TrustWalletCore/TWAccount.h b/include/TrustWalletCore/TWAccount.h index c1cb6ef04a8..c5d6f5e33ce 100644 --- a/include/TrustWalletCore/TWAccount.h +++ b/include/TrustWalletCore/TWAccount.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,31 +7,72 @@ #pragma once #include "TWBase.h" -#include "TWString.h" #include "TWCoinType.h" +#include "TWDerivation.h" +#include "TWString.h" TW_EXTERN_C_BEGIN -/// Account for a particular coin within a wallet. +/// Represents an Account in C++ with address, coin type and public key info, an item within a keystore. TW_EXPORT_CLASS struct TWAccount; +/// Creates a new Account with an address, a coin type, derivation enum, derivationPath, publicKey, +/// and extendedPublicKey. Must be deleted with TWAccountDelete after use. +/// +/// \param address The address of the Account. +/// \param coin The coin type of the Account. +/// \param derivation The derivation of the Account. +/// \param derivationPath The derivation path of the Account. +/// \param publicKey hex encoded public key. +/// \param extendedPublicKey Base58 encoded extended public key. +/// \return A new Account. TW_EXPORT_STATIC_METHOD -struct TWAccount *_Nonnull TWAccountCreate(TWString *_Nonnull address, enum TWCoinType coin, TWString *_Nonnull derivationPath, TWString *_Nonnull extendedPublicKey); - +struct TWAccount* _Nonnull TWAccountCreate(TWString* _Nonnull address, enum TWCoinType coin, + enum TWDerivation derivation, + TWString* _Nonnull derivationPath, + TWString* _Nonnull publicKey, + TWString* _Nonnull extendedPublicKey); +/// Deletes an account. +/// +/// \param account Account to delete. TW_EXPORT_METHOD void TWAccountDelete(struct TWAccount *_Nonnull account); +/// Returns the address of an account. +/// +/// \param account Account to get the address of. TW_EXPORT_PROPERTY TWString *_Nonnull TWAccountAddress(struct TWAccount *_Nonnull account); +/// Returns the derivation enum of an account. +/// +/// \param account Account to get the derivation enum of. +TW_EXPORT_PROPERTY +enum TWDerivation TWAccountDerivation(struct TWAccount *_Nonnull account); + +/// Returns derivationPath of an account. +/// +/// \param account Account to get the derivation path of. TW_EXPORT_PROPERTY TWString *_Nonnull TWAccountDerivationPath(struct TWAccount *_Nonnull account); +/// Returns hex encoded publicKey of an account. +/// +/// \param account Account to get the public key of. +TW_EXPORT_PROPERTY +TWString* _Nonnull TWAccountPublicKey(struct TWAccount* _Nonnull account); + +/// Returns Base58 encoded extendedPublicKey of an account. +/// +/// \param account Account to get the extended public key of. TW_EXPORT_PROPERTY -TWString *_Nonnull TWAccountExtendedPublicKey(struct TWAccount *_Nonnull account); +TWString* _Nonnull TWAccountExtendedPublicKey(struct TWAccount* _Nonnull account); +/// Return CoinType enum of an account. +/// +/// \param account Account to get the coin type of. TW_EXPORT_PROPERTY -enum TWCoinType TWAccountCoin(struct TWAccount *_Nonnull account); +enum TWCoinType TWAccountCoin(struct TWAccount* _Nonnull account); TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWAnyAddress.h b/include/TrustWalletCore/TWAnyAddress.h index 3771b80e762..449d518ba2c 100644 --- a/include/TrustWalletCore/TWAnyAddress.h +++ b/include/TrustWalletCore/TWAnyAddress.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -15,38 +15,91 @@ TW_EXTERN_C_BEGIN struct TWPublicKey; -/// Represents Any blockchain address. +/// Represents an address in C++ for almost any blockchain. TW_EXPORT_CLASS struct TWAnyAddress; /// Compares two addresses for equality. +/// +/// \param lhs The first address to compare. +/// \param rhs The second address to compare. +/// \return bool indicating the addresses are equal. TW_EXPORT_STATIC_METHOD bool TWAnyAddressEqual(struct TWAnyAddress* _Nonnull lhs, struct TWAnyAddress* _Nonnull rhs); /// Determines if the string is a valid Any address. +/// +/// \param string address to validate. +/// \param coin coin type of the address. +/// \return bool indicating if the address is valid. TW_EXPORT_STATIC_METHOD bool TWAnyAddressIsValid(TWString* _Nonnull string, enum TWCoinType coin); -/// Creates an address from a string representaion. +/// Determines if the string is a valid Any address with the given hrp. +/// +/// \param string address to validate. +/// \param coin coin type of the address. +/// \param hrp explicit given hrp of the given address. +/// \return bool indicating if the address is valid. +TW_EXPORT_STATIC_METHOD +bool TWAnyAddressIsValidBech32(TWString* _Nonnull string, enum TWCoinType coin, TWString* _Nonnull hrp); + +/// Creates an address from a string representation and a coin type. Must be deleted with TWAnyAddressDelete after use. +/// +/// \param string address to create. +/// \param coin coin type of the address. +/// \return TWAnyAddress pointer or nullptr if address and coin are invalid. TW_EXPORT_STATIC_METHOD struct TWAnyAddress* _Nullable TWAnyAddressCreateWithString(TWString* _Nonnull string, enum TWCoinType coin); +/// Creates an bech32 address from a string representation, a coin type and the given hrp. Must be deleted with TWAnyAddressDelete after use. +/// +/// \param string address to create. +/// \param coin coin type of the address. +/// \param hrp hrp of the address. +/// \return TWAnyAddress pointer or nullptr if address and coin are invalid. +TW_EXPORT_STATIC_METHOD +struct TWAnyAddress* _Nullable TWAnyAddressCreateBech32(TWString* _Nonnull string, enum TWCoinType coin, TWString* _Nonnull hrp); + + /// Creates an address from a public key. +/// +/// \param publicKey derivates the address from the public key. +/// \param coin coin type of the address. +/// \return TWAnyAddress pointer or nullptr if public key is invalid. TW_EXPORT_STATIC_METHOD struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKey(struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin); +/// Creates an bech32 address from a public key and a given hrp. +/// +/// \param publicKey derivates the address from the public key. +/// \param coin coin type of the address. +/// \param hrp hrp of the address. +/// \return TWAnyAddress pointer or nullptr if public key is invalid. +TW_EXPORT_STATIC_METHOD +struct TWAnyAddress* _Nonnull TWAnyAddressCreateBech32WithPublicKey(struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, TWString* _Nonnull hrp); + +/// Deletes an address. +/// +/// \param address address to delete. TW_EXPORT_METHOD void TWAnyAddressDelete(struct TWAnyAddress* _Nonnull address); /// Returns the address string representation. +/// +/// \param address address to get the string representation of. TW_EXPORT_PROPERTY TWString* _Nonnull TWAnyAddressDescription(struct TWAnyAddress* _Nonnull address); /// Returns coin type of address. +/// +/// \param address address to get the coin type of. TW_EXPORT_PROPERTY enum TWCoinType TWAnyAddressCoin(struct TWAnyAddress* _Nonnull address); /// Returns underlaying data (public key or key hash) +/// +/// \param address address to get the data of. TW_EXPORT_PROPERTY TWData* _Nonnull TWAnyAddressData(struct TWAnyAddress* _Nonnull address); diff --git a/include/TrustWalletCore/TWAnySigner.h b/include/TrustWalletCore/TWAnySigner.h index 7e4a460112f..90276f3dea8 100644 --- a/include/TrustWalletCore/TWAnySigner.h +++ b/include/TrustWalletCore/TWAnySigner.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,21 +12,38 @@ TW_EXTERN_C_BEGIN -/// Helper class to sign any transactions. +/// Represents a signer to sign transactions for any blockchain. struct TWAnySigner; -/// Signs a transaction. +/// Signs a transaction specified by the signing input and coin type. +/// +/// \param input The serialized data of a signing input (e.g. TW.Bitcoin.Proto.SigningInput). +/// \param coin The given coin type to sign the transaction for. +/// \return The serialized data of a `SigningOutput` proto object. (e.g. TW.Bitcoin.Proto.SigningOutput). TW_EXTERN extern TWData *_Nonnull TWAnySignerSign(TWData *_Nonnull input, enum TWCoinType coin); -/// Signs a json transaction with private key. +/// Signs a transaction specified by the JSON representation of signing input, coin type and a private key, returning the JSON representation of the signing output. +/// +/// \param json JSON representation of a signing input +/// \param key The private key to sign with. +/// \param coin The given coin type to sign the transaction for. +/// \return The JSON representation of a `SigningOutput` proto object. TW_EXTERN extern TWString *_Nonnull TWAnySignerSignJSON(TWString *_Nonnull json, TWData *_Nonnull key, enum TWCoinType coin); +/// Check if AnySigner supports signing JSON representation of signing input. +/// +/// \param coin The given coin type to sign the transaction for. +/// \return true if AnySigner supports signing JSON representation of signing input for a given coin. TW_EXTERN extern bool TWAnySignerSupportsJSON(enum TWCoinType coin); -/// Plan a transaction (for UTXO chains). +/// Plans a transaction (for UTXO chains only). +/// +/// \param input The serialized data of a signing input +/// \param coin The given coin type to plan the transaction for. +/// \return The serialized data of a `TransactionPlan` proto object. TW_EXTERN extern TWData *_Nonnull TWAnySignerPlan(TWData *_Nonnull input, enum TWCoinType coin); diff --git a/include/TrustWalletCore/TWBase.h b/include/TrustWalletCore/TWBase.h index 7ce85f50e6d..db2f782dd9a 100644 --- a/include/TrustWalletCore/TWBase.h +++ b/include/TrustWalletCore/TWBase.h @@ -28,6 +28,13 @@ #define TW_EXTERN extern #endif +// Marker for default visibility +#ifdef _MSC_VER + #define TW_VISIBILITY_DEFAULT +#else + #define TW_VISIBILITY_DEFAULT __attribute__((visibility("default"))) +#endif + // Marker for exported classes #define TW_EXPORT_CLASS @@ -89,3 +96,4 @@ #include #include #include + diff --git a/include/TrustWalletCore/TWBase32.h b/include/TrustWalletCore/TWBase32.h new file mode 100644 index 00000000000..e65c67e5890 --- /dev/null +++ b/include/TrustWalletCore/TWBase32.h @@ -0,0 +1,54 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWData.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// Base32 encode / decode functions +TW_EXPORT_STRUCT +struct TWBase32; + +/// Decode a Base32 input with the given alphabet +/// +/// \param string Encoded base32 input to be decoded +/// \param alphabet Decode with the given alphabet, if nullptr ALPHABET_RFC4648 is used by default +/// \return The decoded data, can be null. +/// \note ALPHABET_RFC4648 doesn't support padding in the default alphabet +TW_EXPORT_STATIC_METHOD +TWData* _Nullable TWBase32DecodeWithAlphabet(TWString* _Nonnull string, TWString* _Nullable alphabet); + +/// Decode a Base32 input with the default alphabet (ALPHABET_RFC4648) +/// +/// \param string Encoded input to be decoded +/// \return The decoded data +/// \note Call TWBase32DecodeWithAlphabet with nullptr. +TW_EXPORT_STATIC_METHOD +TWData* _Nullable TWBase32Decode(TWString* _Nonnull string); + +/// Encode an input to Base32 with the given alphabet +/// +/// \param data Data to be encoded (raw bytes) +/// \param alphabet Encode with the given alphabet, if nullptr ALPHABET_RFC4648 is used by default +/// \return The encoded data +/// \note ALPHABET_RFC4648 doesn't support padding in the default alphabet +TW_EXPORT_STATIC_METHOD +TWString *_Nonnull TWBase32EncodeWithAlphabet(TWData *_Nonnull data, TWString* _Nullable alphabet); + +/// Encode an input to Base32 with the default alphabet (ALPHABET_RFC4648) +/// +/// \param data Data to be encoded (raw bytes) +/// \return The encoded data +/// \note Call TWBase32EncodeWithAlphabet with nullptr. +TW_EXPORT_STATIC_METHOD +TWString *_Nonnull TWBase32Encode(TWData *_Nonnull data); + +TW_EXTERN_C_END + diff --git a/include/TrustWalletCore/TWBase58.h b/include/TrustWalletCore/TWBase58.h index a37e14e06d3..6e5dbd30a48 100644 --- a/include/TrustWalletCore/TWBase58.h +++ b/include/TrustWalletCore/TWBase58.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,22 +12,35 @@ TW_EXTERN_C_BEGIN +/// Base58 encode / decode functions TW_EXPORT_STRUCT struct TWBase58; /// Encodes data as a Base58 string, including the checksum. +/// +/// \param data The data to encode. +/// \return the encoded Base58 string with checksum. TW_EXPORT_STATIC_METHOD TWString *_Nonnull TWBase58Encode(TWData *_Nonnull data); /// Encodes data as a Base58 string, not including the checksum. +/// +/// \param data The data to encode. +/// \return then encoded Base58 string without checksum. TW_EXPORT_STATIC_METHOD TWString *_Nonnull TWBase58EncodeNoCheck(TWData *_Nonnull data); -/// Decodes a Base58 string checking the checksum. +/// Decodes a Base58 string, checking the checksum. Returns null if the string is not a valid Base58 string. +/// +/// \param string The Base58 string to decode. +/// \return the decoded data, empty if the string is not a valid Base58 string with checksum. TW_EXPORT_STATIC_METHOD TWData *_Nullable TWBase58Decode(TWString *_Nonnull string); -/// Decodes a Base58 string with no checksum. +/// Decodes a Base58 string, w/o checking the checksum. Returns null if the string is not a valid Base58 string. +/// +/// \param string The Base58 string to decode. +/// \return the decoded data, empty if the string is not a valid Base58 string without checksum. TW_EXPORT_STATIC_METHOD TWData *_Nullable TWBase58DecodeNoCheck(TWString *_Nonnull string); diff --git a/include/TrustWalletCore/TWBase64.h b/include/TrustWalletCore/TWBase64.h new file mode 100644 index 00000000000..fa1c29173d1 --- /dev/null +++ b/include/TrustWalletCore/TWBase64.h @@ -0,0 +1,47 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWData.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// Base64 encode / decode functions +TW_EXPORT_STRUCT +struct TWBase64; + +/// Decode a Base64 input with the default alphabet (RFC4648 with '+', '/') +/// +/// \param string Encoded input to be decoded +/// \return The decoded data, empty if decoding failed. +TW_EXPORT_STATIC_METHOD +TWData* _Nullable TWBase64Decode(TWString* _Nonnull string); + +/// Decode a Base64 input with the alphabet safe for URL-s and filenames (RFC4648 with '-', '_') +/// +/// \param string Encoded base64 input to be decoded +/// \return The decoded data, empty if decoding failed. +TW_EXPORT_STATIC_METHOD +TWData* _Nullable TWBase64DecodeUrl(TWString* _Nonnull string); + +/// Encode an input to Base64 with the default alphabet (RFC4648 with '+', '/') +/// +/// \param data Data to be encoded (raw bytes) +/// \return The encoded data +TW_EXPORT_STATIC_METHOD +TWString *_Nonnull TWBase64Encode(TWData *_Nonnull data); + +/// Encode an input to Base64 with the alphabet safe for URL-s and filenames (RFC4648 with '-', '_') +/// +/// \param data Data to be encoded (raw bytes) +/// \return The encoded data +TW_EXPORT_STATIC_METHOD +TWString *_Nonnull TWBase64EncodeUrl(TWData *_Nonnull data); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWBitcoinAddress.h b/include/TrustWalletCore/TWBitcoinAddress.h index b42bdba717f..77ae6bf1d5d 100644 --- a/include/TrustWalletCore/TWBitcoinAddress.h +++ b/include/TrustWalletCore/TWBitcoinAddress.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -14,46 +14,75 @@ TW_EXTERN_C_BEGIN struct TWPublicKey; -/// Represents a legacy Bitcoin address. +/// Represents a legacy Bitcoin address in C++. TW_EXPORT_CLASS struct TWBitcoinAddress; /// Compares two addresses for equality. +/// +/// \param lhs The first address to compare. +/// \param rhs The second address to compare. +/// \return bool indicating the addresses are equal. TW_EXPORT_STATIC_METHOD bool TWBitcoinAddressEqual(struct TWBitcoinAddress *_Nonnull lhs, struct TWBitcoinAddress *_Nonnull rhs); /// Determines if the data is a valid Bitcoin address. +/// +/// \param data data to validate. +/// \return bool indicating if the address data is valid. TW_EXPORT_STATIC_METHOD bool TWBitcoinAddressIsValid(TWData *_Nonnull data); /// Determines if the string is a valid Bitcoin address. +/// +/// \param string string to validate. +/// \return bool indicating if the address string is valid. TW_EXPORT_STATIC_METHOD bool TWBitcoinAddressIsValidString(TWString *_Nonnull string); -/// Initializes an address from a base58 sring representaion. +/// Initializes an address from a Base58 sring. Must be deleted with TWBitcoinAddressDelete after use. +/// +/// \param string Base58 string to initialize the address from. +/// \return TWBitcoinAddress pointer or nullptr if string is invalid. TW_EXPORT_STATIC_METHOD struct TWBitcoinAddress *_Nullable TWBitcoinAddressCreateWithString(TWString *_Nonnull string); /// Initializes an address from raw data. +/// +/// \param data Raw data to initialize the address from. Must be deleted with TWBitcoinAddressDelete after use. +/// \return TWBitcoinAddress pointer or nullptr if data is invalid. TW_EXPORT_STATIC_METHOD struct TWBitcoinAddress *_Nullable TWBitcoinAddressCreateWithData(TWData *_Nonnull data); /// Initializes an address from a public key and a prefix byte. +/// +/// \param publicKey Public key to initialize the address from. +/// \param prefix Prefix byte (p2pkh, p2sh, etc). +/// \return TWBitcoinAddress pointer or nullptr if public key is invalid. TW_EXPORT_STATIC_METHOD struct TWBitcoinAddress *_Nullable TWBitcoinAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey, uint8_t prefix); +/// Deletes a legacy Bitcoin address. +/// +/// \param address Address to delete. TW_EXPORT_METHOD void TWBitcoinAddressDelete(struct TWBitcoinAddress *_Nonnull address); -/// Returns the address base58 string representation. +/// Returns the address in Base58 string representation. +/// +/// \param address Address to get the string representation of. TW_EXPORT_PROPERTY TWString *_Nonnull TWBitcoinAddressDescription(struct TWBitcoinAddress *_Nonnull address); /// Returns the address prefix. +/// +/// \param address Address to get the prefix of. TW_EXPORT_PROPERTY uint8_t TWBitcoinAddressPrefix(struct TWBitcoinAddress *_Nonnull address); -/// Returns the keyhash data. +/// Returns the key hash data. +/// +/// \param address Address to get the keyhash data of. TW_EXPORT_PROPERTY TWData *_Nonnull TWBitcoinAddressKeyhash(struct TWBitcoinAddress *_Nonnull address); diff --git a/include/TrustWalletCore/TWBitcoinMessageSigner.h b/include/TrustWalletCore/TWBitcoinMessageSigner.h new file mode 100644 index 00000000000..d0fcd2e17bd --- /dev/null +++ b/include/TrustWalletCore/TWBitcoinMessageSigner.h @@ -0,0 +1,43 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWData.h" +#include "TWString.h" +#include "TWPrivateKey.h" + +TW_EXTERN_C_BEGIN + +/// Bitcoin message signing and verification. +/// +/// Bitcoin Core and some other wallets support a message signing & verification format, to create a proof (a signature) +/// that someone has access to the private keys of a specific address. +/// This feature currently works on old legacy addresses only. +TW_EXPORT_CLASS +struct TWBitcoinMessageSigner; + +/// Sign a message. +/// +/// \param privateKey: the private key used for signing +/// \param address: the address that matches the privateKey, must be a legacy address (P2PKH) +/// \param message: A custom message which is input to the signing. +/// \note Address is derived assuming compressed public key format. +/// \returns the signature, Base64-encoded. On invalid input empty string is returned. Returned object needs to be deleteed after use. +TW_EXPORT_STATIC_METHOD +TWString* _Nonnull TWBitcoinMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull address, TWString* _Nonnull message); + +/// Verify signature for a message. +/// +/// \param address: address to use, only legacy is supported +/// \param message: the message signed (without prefix) +/// \param signature: in Base64-encoded form. +/// \returns false on any invalid input (does not throw). +TW_EXPORT_STATIC_METHOD +bool TWBitcoinMessageSignerVerifyMessage(TWString* _Nonnull address, TWString* _Nonnull message, TWString* _Nonnull signature); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWBitcoinScript.h b/include/TrustWalletCore/TWBitcoinScript.h index 701d6ad4243..c04611ab166 100644 --- a/include/TrustWalletCore/TWBitcoinScript.h +++ b/include/TrustWalletCore/TWBitcoinScript.h @@ -7,119 +7,205 @@ #pragma once #include "TWBase.h" +#include "TWBitcoinSigHashType.h" +#include "TWCoinType.h" #include "TWData.h" #include "TWPublicKey.h" -#include "TWCoinType.h" -#include "TWBitcoinSigHashType.h" TW_EXTERN_C_BEGIN +/// Bitcoin script manipulating functions TW_EXPORT_CLASS struct TWBitcoinScript; /// Creates an empty script. +/// +/// \return A pointer to the script TW_EXPORT_STATIC_METHOD -struct TWBitcoinScript *_Nonnull TWBitcoinScriptCreate(); +struct TWBitcoinScript* _Nonnull TWBitcoinScriptCreate(); /// Creates a script from a raw data representation. +/// +/// \param data The data buffer +/// \note Must be deleted with \TWBitcoinScriptDelete +/// \return A pointer to the script TW_EXPORT_STATIC_METHOD -struct TWBitcoinScript *_Nonnull TWBitcoinScriptCreateWithData(TWData *_Nonnull data); -struct TWBitcoinScript *_Nonnull TWBitcoinScriptCreateWithBytes(uint8_t *_Nonnull bytes, size_t size); +struct TWBitcoinScript* _Nonnull TWBitcoinScriptCreateWithData(TWData* _Nonnull data); -/// Creates a script by copying an existring script. +/// Creates a script from a raw bytes and size. +/// +/// \param bytes The buffer +/// \param size The size of the buffer +/// \note Must be deleted with \TWBitcoinScriptDelete +/// \return A pointer to the script +struct TWBitcoinScript* _Nonnull TWBitcoinScriptCreateWithBytes(uint8_t* _Nonnull bytes, size_t size); + +/// Creates a script by copying an existing script. +/// +/// \param script Non-null pointer to a script +/// \note Must be deleted with \TWBitcoinScriptDelete +/// \return A pointer to the script TW_EXPORT_STATIC_METHOD -struct TWBitcoinScript *_Nonnull TWBitcoinScriptCreateCopy(const struct TWBitcoinScript *_Nonnull script); +struct TWBitcoinScript* _Nonnull TWBitcoinScriptCreateCopy(const struct TWBitcoinScript* _Nonnull script); +/// Delete/Deallocate a given script. +/// +/// \param script Non-null pointer to a script TW_EXPORT_METHOD -void TWBitcoinScriptDelete(struct TWBitcoinScript *_Nonnull script); +void TWBitcoinScriptDelete(struct TWBitcoinScript* _Nonnull script); +/// Get size of a script +/// +/// \param script Non-null pointer to a script +/// \return size of the script TW_EXPORT_PROPERTY -size_t TWBitcoinScriptSize(const struct TWBitcoinScript *_Nonnull script); +size_t TWBitcoinScriptSize(const struct TWBitcoinScript* _Nonnull script); +/// Get data of a script +/// +/// \param script Non-null pointer to a script +/// \return data of the given script TW_EXPORT_PROPERTY -TWData *_Nonnull TWBitcoinScriptData(const struct TWBitcoinScript *_Nonnull script); +TWData* _Nonnull TWBitcoinScriptData(const struct TWBitcoinScript* _Nonnull script); +/// Return script hash of a script +/// +/// \param script Non-null pointer to a script +/// \return script hash of the given script TW_EXPORT_PROPERTY -TWData *_Nonnull TWBitcoinScriptScriptHash(const struct TWBitcoinScript *_Nonnull script); +TWData* _Nonnull TWBitcoinScriptScriptHash(const struct TWBitcoinScript* _Nonnull script); /// Determines whether this is a pay-to-script-hash (P2SH) script. +/// +/// \param script Non-null pointer to a script +/// \return true if this is a pay-to-script-hash (P2SH) script, false otherwise TW_EXPORT_PROPERTY -bool TWBitcoinScriptIsPayToScriptHash(const struct TWBitcoinScript *_Nonnull script); +bool TWBitcoinScriptIsPayToScriptHash(const struct TWBitcoinScript* _Nonnull script); /// Determines whether this is a pay-to-witness-script-hash (P2WSH) script. +/// +/// \param script Non-null pointer to a script +/// \return true if this is a pay-to-witness-script-hash (P2WSH) script, false otherwise TW_EXPORT_PROPERTY -bool TWBitcoinScriptIsPayToWitnessScriptHash(const struct TWBitcoinScript *_Nonnull script); +bool TWBitcoinScriptIsPayToWitnessScriptHash(const struct TWBitcoinScript* _Nonnull script); /// Determines whether this is a pay-to-witness-public-key-hash (P2WPKH) script. +/// +/// \param script Non-null pointer to a script +/// \return true if this is a pay-to-witness-public-key-hash (P2WPKH) script, false otherwise TW_EXPORT_PROPERTY -bool TWBitcoinScriptIsPayToWitnessPublicKeyHash(const struct TWBitcoinScript *_Nonnull script); +bool TWBitcoinScriptIsPayToWitnessPublicKeyHash(const struct TWBitcoinScript* _Nonnull script); -/// Determines whether this is a witness programm script. +/// Determines whether this is a witness program script. +/// +/// \param script Non-null pointer to a script +/// \return true if this is a witness program script, false otherwise TW_EXPORT_PROPERTY -bool TWBitcoinScriptIsWitnessProgram(const struct TWBitcoinScript *_Nonnull script); +bool TWBitcoinScriptIsWitnessProgram(const struct TWBitcoinScript* _Nonnull script); +/// Determines whether 2 scripts have the same content +/// +/// \param lhs Non-null pointer to the first script +/// \param rhs Non-null pointer to the second script +/// \return true if both script have the same content TW_EXPORT_STATIC_METHOD -bool TWBitcoinScriptEqual(const struct TWBitcoinScript *_Nonnull lhs, const struct TWBitcoinScript *_Nonnull rhs); +bool TWBitcoinScriptEqual(const struct TWBitcoinScript* _Nonnull lhs, const struct TWBitcoinScript* _Nonnull rhs); /// Matches the script to a pay-to-public-key (P2PK) script. /// -/// - Returns: the public key. +/// \param script Non-null pointer to a script +/// \return The public key. TW_EXPORT_METHOD -TWData *_Nullable TWBitcoinScriptMatchPayToPubkey(const struct TWBitcoinScript *_Nonnull script); +TWData* _Nullable TWBitcoinScriptMatchPayToPubkey(const struct TWBitcoinScript* _Nonnull script); /// Matches the script to a pay-to-public-key-hash (P2PKH). /// -/// - Returns: the key hash. +/// \param script Non-null pointer to a script +/// \return the key hash. TW_EXPORT_METHOD -TWData *_Nullable TWBitcoinScriptMatchPayToPubkeyHash(const struct TWBitcoinScript *_Nonnull script); +TWData* _Nullable TWBitcoinScriptMatchPayToPubkeyHash(const struct TWBitcoinScript* _Nonnull script); /// Matches the script to a pay-to-script-hash (P2SH). /// -/// - Returns: the script hash. +/// \param script Non-null pointer to a script +/// \return the script hash. TW_EXPORT_METHOD -TWData *_Nullable TWBitcoinScriptMatchPayToScriptHash(const struct TWBitcoinScript *_Nonnull script); +TWData* _Nullable TWBitcoinScriptMatchPayToScriptHash(const struct TWBitcoinScript* _Nonnull script); /// Matches the script to a pay-to-witness-public-key-hash (P2WPKH). /// -/// - Returns: the key hash. +/// \param script Non-null pointer to a script +/// \return the key hash. TW_EXPORT_METHOD -TWData *_Nullable TWBitcoinScriptMatchPayToWitnessPublicKeyHash(const struct TWBitcoinScript *_Nonnull script); +TWData* _Nullable TWBitcoinScriptMatchPayToWitnessPublicKeyHash(const struct TWBitcoinScript* _Nonnull script); /// Matches the script to a pay-to-witness-script-hash (P2WSH). /// -/// - Returns: the script hash, a SHA256 of the witness script. +/// \param script Non-null pointer to a script +/// \return the script hash, a SHA256 of the witness script.. TW_EXPORT_METHOD -TWData *_Nullable TWBitcoinScriptMatchPayToWitnessScriptHash(const struct TWBitcoinScript *_Nonnull script); +TWData* _Nullable TWBitcoinScriptMatchPayToWitnessScriptHash(const struct TWBitcoinScript* _Nonnull script); /// Encodes the script. +/// +/// \param script Non-null pointer to a script +/// \return The encoded script TW_EXPORT_METHOD -TWData *_Nonnull TWBitcoinScriptEncode(const struct TWBitcoinScript *_Nonnull script); +TWData* _Nonnull TWBitcoinScriptEncode(const struct TWBitcoinScript* _Nonnull script); /// Builds a standard 'pay to public key' script. +/// +/// \param pubkey Non-null pointer to a pubkey +/// \note Must be deleted with \TWBitcoinScriptDelete +/// \return A pointer to the built script TW_EXPORT_STATIC_METHOD -struct TWBitcoinScript *_Nonnull TWBitcoinScriptBuildPayToPublicKey(TWData *_Nonnull pubkey); +struct TWBitcoinScript* _Nonnull TWBitcoinScriptBuildPayToPublicKey(TWData* _Nonnull pubkey); /// Builds a standard 'pay to public key hash' script. +/// +/// \param hash Non-null pointer to a PublicKey hash +/// \note Must be deleted with \TWBitcoinScriptDelete +/// \return A pointer to the built script TW_EXPORT_STATIC_METHOD -struct TWBitcoinScript *_Nonnull TWBitcoinScriptBuildPayToPublicKeyHash(TWData *_Nonnull hash); +struct TWBitcoinScript* _Nonnull TWBitcoinScriptBuildPayToPublicKeyHash(TWData* _Nonnull hash); /// Builds a standard 'pay to script hash' script. +/// +/// \param scriptHash Non-null pointer to a script hash +/// \note Must be deleted with \TWBitcoinScriptDelete +/// \return A pointer to the built script TW_EXPORT_STATIC_METHOD -struct TWBitcoinScript *_Nonnull TWBitcoinScriptBuildPayToScriptHash(TWData *_Nonnull scriptHash); +struct TWBitcoinScript* _Nonnull TWBitcoinScriptBuildPayToScriptHash(TWData* _Nonnull scriptHash); -/// Builds a pay-to-witness-public-key-hash (P2WPKH) script. +/// Builds a pay-to-witness-public-key-hash (P2WPKH) script.. +/// +/// \param hash Non-null pointer to a witness public key hash +/// \note Must be deleted with \TWBitcoinScriptDelete +/// \return A pointer to the built script TW_EXPORT_STATIC_METHOD -struct TWBitcoinScript *_Nonnull TWBitcoinScriptBuildPayToWitnessPubkeyHash(TWData *_Nonnull hash); +struct TWBitcoinScript* _Nonnull TWBitcoinScriptBuildPayToWitnessPubkeyHash(TWData* _Nonnull hash); /// Builds a pay-to-witness-script-hash (P2WSH) script. +/// +/// \param scriptHash Non-null pointer to a script hash +/// \note Must be deleted with \TWBitcoinScriptDelete +/// \return A pointer to the built script TW_EXPORT_STATIC_METHOD -struct TWBitcoinScript *_Nonnull TWBitcoinScriptBuildPayToWitnessScriptHash(TWData *_Nonnull scriptHash); +struct TWBitcoinScript* _Nonnull TWBitcoinScriptBuildPayToWitnessScriptHash(TWData* _Nonnull scriptHash); -/// Builds a appropriate lock script for the given address. +/// Builds a appropriate lock script for the given address.. +/// +/// \param address Non-null pointer to an address +/// \param coin coin type +/// \note Must be deleted with \TWBitcoinScriptDelete +/// \return A pointer to the built script TW_EXPORT_STATIC_METHOD -struct TWBitcoinScript *_Nonnull TWBitcoinScriptLockScriptForAddress(TWString *_Nonnull address, enum TWCoinType coin); +struct TWBitcoinScript* _Nonnull TWBitcoinScriptLockScriptForAddress(TWString* _Nonnull address, enum TWCoinType coin); -// Return the default HashType for the given coin, such as TWBitcoinSigHashTypeAll. +/// Return the default HashType for the given coin, such as TWBitcoinSigHashTypeAll. +/// +/// \param coinType coin type +/// \return default HashType for the given coin TW_EXPORT_STATIC_METHOD uint32_t TWBitcoinScriptHashTypeForCoin(enum TWCoinType coinType); diff --git a/include/TrustWalletCore/TWBitcoinSigHashType.h b/include/TrustWalletCore/TWBitcoinSigHashType.h index 33fba1992d2..b2682d67074 100644 --- a/include/TrustWalletCore/TWBitcoinSigHashType.h +++ b/include/TrustWalletCore/TWBitcoinSigHashType.h @@ -10,6 +10,7 @@ TW_EXTERN_C_BEGIN +/// Bitcoin SIGHASH type. TW_EXPORT_ENUM(uint32_t) enum TWBitcoinSigHashType { TWBitcoinSigHashTypeAll = 0x01, @@ -20,9 +21,17 @@ enum TWBitcoinSigHashType { TWBitcoinSigHashTypeAnyoneCanPay = 0x80 }; +/// Determines if the given sig hash is single +/// +/// \param type sig hash type +/// \return true if the sigh hash type is single, false otherwise TW_EXPORT_METHOD bool TWBitcoinSigHashTypeIsSingle(enum TWBitcoinSigHashType type); +/// Determines if the given sig hash is none +/// +/// \param type sig hash type +/// \return true if the sigh hash type is none, false otherwise TW_EXPORT_METHOD bool TWBitcoinSigHashTypeIsNone(enum TWBitcoinSigHashType type); diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index c0e8db65bfd..80fe59660ce 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,6 +10,7 @@ TW_EXTERN_C_BEGIN +/// Blockchain enum type TW_EXPORT_ENUM(uint32_t) enum TWBlockchain { TWBlockchainBitcoin = 0, @@ -45,6 +46,15 @@ enum TWBlockchain { TWBlockchainFilecoin = 32, TWBlockchainElrondNetwork = 33, TWBlockchainOasisNetwork = 34, + TWBlockchainDecred = 35, // Bitcoin + TWBlockchainZcash = 36, // Bitcoin + TWBlockchainGroestlcoin = 37, // Bitcoin + TWBlockchainThorchain = 38, // Cosmos + TWBlockchainRonin = 39, // Ethereum + TWBlockchainKusama = 40, // Polkadot + TWBlockchainNervos = 41, + TWBlockchainEverscale = 42, + TWBlockchainAptos = 43, // Aptos }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCardano.h b/include/TrustWalletCore/TWCardano.h new file mode 100644 index 00000000000..0def3db33b6 --- /dev/null +++ b/include/TrustWalletCore/TWCardano.h @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWData.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// Cardano helper functions +TW_EXPORT_STRUCT +struct TWCardano; + +/// Calculates the minimum ADA amount needed for a UTXO. +/// +/// \see reference https://docs.cardano.org/native-tokens/minimum-ada-value-requirement +/// \param tokenBundle serialized data of TW.Cardano.Proto.TokenBundle. +/// \return the minimum ADA amount. +TW_EXPORT_STATIC_METHOD +uint64_t TWCardanoMinAdaAmount(TWData *_Nonnull tokenBundle) TW_VISIBILITY_DEFAULT; + +/// Return the staking address associated to (contained in) this address. Must be a Base address. +/// Empty string is returned on error. Result must be freed. +/// \param baseAddress A valid base address, as string. +/// \return the associated staking (reward) address, as string, or empty string on error. +TW_EXPORT_STATIC_METHOD +TWString *_Nonnull TWCardanoGetStakingAddress(TWString *_Nonnull baseAddress) TW_VISIBILITY_DEFAULT; + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 49e7c8e7272..52e5f2d50f5 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -14,12 +14,13 @@ #include "TWPrivateKey.h" #include "TWPurpose.h" #include "TWString.h" +#include "TWDerivation.h" TW_EXTERN_C_BEGIN /// Coin type for Level 2 of BIP44. /// -/// - SeeAlso: https://github.com/satoshilabs/slips/blob/master/slip-0044.md +/// \see https://github.com/satoshilabs/slips/blob/master/slip-0044.md TW_EXPORT_ENUM(uint32_t) enum TWCoinType { TWCoinTypeAeternity = 457, @@ -68,12 +69,13 @@ enum TWCoinType { TWCoinTypeViacoin = 14, TWCoinTypeWanchain = 5718350, TWCoinTypeZcash = 133, - TWCoinTypeZcoin = 136, + TWCoinTypeFiro = 136, TWCoinTypeZilliqa = 313, TWCoinTypeZelcash = 19167, TWCoinTypeRavencoin = 175, TWCoinTypeWaves = 5741564, - TWCoinTypeTerra = 330, + TWCoinTypeTerra = 330, // see also TerraV2 + TWCoinTypeTerraV2 = 10000330, // see also Terra TWCoinTypeHarmony = 1023, TWCoinTypeAlgorand = 283, TWCoinTypeKusama = 434, @@ -88,6 +90,7 @@ enum TWCoinType { TWCoinTypeTHORChain = 931, TWCoinTypeBluzelle = 483, TWCoinTypeOptimism = 10000070, + TWCoinTypeZksync = 10000280, TWCoinTypeArbitrum = 10042221, TWCoinTypeECOChain = 10000553, TWCoinTypeAvalancheCChain = 10009000, @@ -96,64 +99,151 @@ enum TWCoinType { TWCoinTypeCryptoOrg = 394, TWCoinTypeCelo = 52752, TWCoinTypeRonin = 10002020, + TWCoinTypeOsmosis = 10000118, + TWCoinTypeECash = 899, + TWCoinTypeCronosChain = 10000025, + TWCoinTypeSmartBitcoinCash = 10000145, + TWCoinTypeKuCoinCommunityChain = 10000321, + TWCoinTypeBoba = 10000288, + TWCoinTypeMetis = 1001088, + TWCoinTypeAurora = 1323161554, + TWCoinTypeEvmos = 10009001, + TWCoinTypeNativeEvmos = 20009001, + TWCoinTypeMoonriver = 10001285, + TWCoinTypeMoonbeam = 10001284, + TWCoinTypeKavaEvm = 10002222, + TWCoinTypeKlaytn = 10008217, + TWCoinTypeMeter = 18000, + TWCoinTypeOKXChain = 996, + TWCoinTypeNervos = 309, + TWCoinTypeEverscale = 396, + TWCoinTypeAptos = 637, }; /// Returns the blockchain for a coin type. +/// +/// \param coin A coin type +/// \return blockchain associated to the given coin type TW_EXPORT_PROPERTY enum TWBlockchain TWCoinTypeBlockchain(enum TWCoinType coin); /// Returns the purpose for a coin type. +/// +/// \param coin A coin type +/// \return purpose associated to the given coin type TW_EXPORT_PROPERTY enum TWPurpose TWCoinTypePurpose(enum TWCoinType coin); /// Returns the curve that should be used for a coin type. +/// +/// \param coin A coin type +/// \return curve that should be used for the given coin type TW_EXPORT_PROPERTY enum TWCurve TWCoinTypeCurve(enum TWCoinType coin); /// Returns the xpub HD version that should be used for a coin type. +/// +/// \param coin A coin type +/// \return xpub HD version that should be used for the given coin type TW_EXPORT_PROPERTY enum TWHDVersion TWCoinTypeXpubVersion(enum TWCoinType coin); /// Returns the xprv HD version that should be used for a coin type. +/// +/// \param coin A coin type +/// \return the xprv HD version that should be used for the given coin type. TW_EXPORT_PROPERTY enum TWHDVersion TWCoinTypeXprvVersion(enum TWCoinType coin); /// Validates an address string. +/// +/// \param coin A coin type +/// \param address A public address +/// \return true if the address is a valid public address of the given coin, false otherwise. TW_EXPORT_METHOD bool TWCoinTypeValidate(enum TWCoinType coin, TWString* _Nonnull address); /// Returns the default derivation path for a particular coin. +/// +/// \param coin A coin type +/// \return the default derivation path for the given coin type. TW_EXPORT_METHOD TWString* _Nonnull TWCoinTypeDerivationPath(enum TWCoinType coin); +/// Returns the derivation path for a particular coin with the explicit given derivation. +/// +/// \param coin A coin type +/// \param derivation A derivation type +/// \return the derivation path for the given coin with the explicit given derivation +TW_EXPORT_METHOD +TWString* _Nonnull TWCoinTypeDerivationPathWithDerivation(enum TWCoinType coin, enum TWDerivation derivation); + /// Derives the address for a particular coin from the private key. +/// +/// \param coin A coin type +/// \param privateKey A valid private key +/// \return Derived address for the given coin from the private key. TW_EXPORT_METHOD TWString* _Nonnull TWCoinTypeDeriveAddress(enum TWCoinType coin, struct TWPrivateKey* _Nonnull privateKey); /// Derives the address for a particular coin from the public key. +/// +/// \param coin A coin type +/// \param publicKey A valid public key +/// \return Derived address for the given coin from the public key. TW_EXPORT_METHOD TWString* _Nonnull TWCoinTypeDeriveAddressFromPublicKey(enum TWCoinType coin, struct TWPublicKey* _Nonnull publicKey); /// HRP for this coin type +/// +/// \param coin A coin type +/// \return HRP of the given coin type. TW_EXPORT_PROPERTY enum TWHRP TWCoinTypeHRP(enum TWCoinType coin); /// P2PKH prefix for this coin type +/// +/// \param coin A coin type +/// \return P2PKH prefix for the given coin type TW_EXPORT_PROPERTY uint8_t TWCoinTypeP2pkhPrefix(enum TWCoinType coin); /// P2SH prefix for this coin type +/// +/// \param coin A coin type +/// \return P2SH prefix for the given coin type TW_EXPORT_PROPERTY uint8_t TWCoinTypeP2shPrefix(enum TWCoinType coin); /// Static prefix for this coin type +/// +/// \param coin A coin type +/// \return Static prefix for the given coin type TW_EXPORT_PROPERTY uint8_t TWCoinTypeStaticPrefix(enum TWCoinType coin); +/// ChainID for this coin type. +/// +/// \param coin A coin type +/// \return ChainID for the given coin type. +/// \note Caller must free returned object. +TW_EXPORT_PROPERTY +TWString* _Nonnull TWCoinTypeChainId(enum TWCoinType coin); + /// SLIP-0044 id for this coin type +/// +/// \param coin A coin type +/// \return SLIP-0044 id for the given coin type TW_EXPORT_PROPERTY uint32_t TWCoinTypeSlip44Id(enum TWCoinType coin); +/// public key type for this coin type +/// +/// \param coin A coin type +/// \return public key type for the given coin type +TW_EXPORT_PROPERTY +enum TWPublicKeyType TWCoinTypePublicKeyType(enum TWCoinType coin); + TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinTypeConfiguration.h b/include/TrustWalletCore/TWCoinTypeConfiguration.h index 47458179ac5..1a04fab57d8 100644 --- a/include/TrustWalletCore/TWCoinTypeConfiguration.h +++ b/include/TrustWalletCore/TWCoinTypeConfiguration.h @@ -12,32 +12,54 @@ TW_EXTERN_C_BEGIN +/// CoinTypeConfiguration functions TW_EXPORT_STRUCT struct TWCoinTypeConfiguration { uint8_t unused; // C doesn't allow zero-sized struct }; /// Returns stock symbol of coin +/// +/// \param type A coin type +/// \return A non-null TWString stock symbol of coin +/// \note Caller must free returned object TW_EXPORT_STATIC_METHOD TWString *_Nonnull TWCoinTypeConfigurationGetSymbol(enum TWCoinType type); /// Returns max count decimal places for minimal coin unit +/// +/// \param type A coin type +/// \return Returns max count decimal places for minimal coin unit TW_EXPORT_STATIC_METHOD int TWCoinTypeConfigurationGetDecimals(enum TWCoinType type); /// Returns transaction url in blockchain explorer +/// +/// \param type A coin type +/// \param transactionID A transaction identifier +/// \return Returns a non-null TWString transaction url in blockchain explorer TW_EXPORT_STATIC_METHOD TWString *_Nonnull TWCoinTypeConfigurationGetTransactionURL(enum TWCoinType type, TWString *_Nonnull transactionID); /// Returns account url in blockchain explorer +/// +/// \param type A coin type +/// \param accountID an Account identifier +/// \return Returns a non-null TWString account url in blockchain explorer TW_EXPORT_STATIC_METHOD TWString *_Nonnull TWCoinTypeConfigurationGetAccountURL(enum TWCoinType type, TWString *_Nonnull accountID); /// Returns full name of coin in lower case +/// +/// \param type A coin type +/// \return Returns a non-null TWString, full name of coin in lower case TW_EXPORT_STATIC_METHOD TWString *_Nonnull TWCoinTypeConfigurationGetID(enum TWCoinType type); /// Returns full name of coin +/// +/// \param type A coin type +/// \return Returns a non-null TWString, full name of coin TW_EXPORT_STATIC_METHOD TWString *_Nonnull TWCoinTypeConfigurationGetName(enum TWCoinType type); diff --git a/include/TrustWalletCore/TWCurve.h b/include/TrustWalletCore/TWCurve.h index 6ce6e645539..79d2891aa40 100644 --- a/include/TrustWalletCore/TWCurve.h +++ b/include/TrustWalletCore/TWCurve.h @@ -18,7 +18,7 @@ enum TWCurve { TWCurveED25519Blake2bNano /* "ed25519-blake2b-nano" */, TWCurveCurve25519 /* "curve25519" */, TWCurveNIST256p1 /* "nist256p1" */, - TWCurveED25519Extended /* "ed25519-cardano-seed" */, + TWCurveED25519ExtendedCardano /* "ed25519-cardano-seed" */, TWCurveNone }; diff --git a/include/TrustWalletCore/TWData.h b/include/TrustWalletCore/TWData.h index 6ebe32aff78..2eebf174c39 100644 --- a/include/TrustWalletCore/TWData.h +++ b/include/TrustWalletCore/TWData.h @@ -19,54 +19,111 @@ typedef const void TWString; typedef const void TWData; /// Creates a block of data from a byte array. -TWData *_Nonnull TWDataCreateWithBytes(const uint8_t *_Nonnull bytes, size_t size); +/// +/// \param bytes Non-null raw bytes buffer +/// \param size size of the buffer +/// \return Non-null filled block of data. +TWData *_Nonnull TWDataCreateWithBytes(const uint8_t *_Nonnull bytes, size_t size) TW_VISIBILITY_DEFAULT; /// Creates an uninitialized block of data with the provided size. -TWData *_Nonnull TWDataCreateWithSize(size_t size); +/// +/// \param size size for the block of data +/// \return Non-null uninitialized block of data with the provided size +TWData *_Nonnull TWDataCreateWithSize(size_t size) TW_VISIBILITY_DEFAULT; /// Creates a block of data by copying another block of data. -TWData *_Nonnull TWDataCreateWithData(TWData *_Nonnull data); +/// +/// \param data buffer that need to be copied +/// \return Non-null filled block of data. +TWData *_Nonnull TWDataCreateWithData(TWData *_Nonnull data) TW_VISIBILITY_DEFAULT; /// Creates a block of data from a hexadecimal string. Odd length is invalid (intended grouping to bytes is not obvious). -TWData *_Nullable TWDataCreateWithHexString(const TWString *_Nonnull hex); +/// +/// \param hex input hex string +/// \return Non-null filled block of data +TWData *_Nullable TWDataCreateWithHexString(const TWString *_Nonnull hex) TW_VISIBILITY_DEFAULT; /// Returns the size in bytes. -size_t TWDataSize(TWData *_Nonnull data); +/// +/// \param data A non-null valid block of data +/// \return the size of the given block of data +size_t TWDataSize(TWData *_Nonnull data) TW_VISIBILITY_DEFAULT; /// Returns the raw pointer to the contents of data. -uint8_t *_Nonnull TWDataBytes(TWData *_Nonnull data); +/// +/// \param data A non-null valid block of data +/// \return the raw pointer to the contents of data +uint8_t *_Nonnull TWDataBytes(TWData *_Nonnull data) TW_VISIBILITY_DEFAULT; /// Returns the byte at the provided index. -uint8_t TWDataGet(TWData *_Nonnull data, size_t index); +/// +/// \param data A non-null valid block of data +/// \param index index of the byte that we want to fetch - index need to be < TWDataSize(data) +/// \return the byte at the provided index +uint8_t TWDataGet(TWData *_Nonnull data, size_t index) TW_VISIBILITY_DEFAULT; /// Sets the byte at the provided index. -void TWDataSet(TWData *_Nonnull data, size_t index, uint8_t byte); +/// +/// \param data A non-null valid block of data +/// \param index index of the byte that we want to set - index need to be < TWDataSize(data) +/// \param byte Given byte to be written in data +void TWDataSet(TWData *_Nonnull data, size_t index, uint8_t byte) TW_VISIBILITY_DEFAULT; /// Copies a range of bytes into the provided buffer. -void TWDataCopyBytes(TWData *_Nonnull data, size_t start, size_t size, uint8_t *_Nonnull output); +/// +/// \param data A non-null valid block of data +/// \param start starting index of the range - index need to be < TWDataSize(data) +/// \param size size of the range we want to copy - size need to be < TWDataSize(data) - start +/// \param output The output buffer where we want to copy the data. +void TWDataCopyBytes(TWData *_Nonnull data, size_t start, size_t size, uint8_t *_Nonnull output) TW_VISIBILITY_DEFAULT; /// Replaces a range of bytes with the contents of the provided buffer. -void TWDataReplaceBytes(TWData *_Nonnull data, size_t start, size_t size, const uint8_t *_Nonnull bytes); +/// +/// \param data A non-null valid block of data +/// \param start starting index of the range - index need to be < TWDataSize(data) +/// \param size size of the range we want to replace - size need to be < TWDataSize(data) - start +/// \param bytes The buffer that will replace the range of data +void TWDataReplaceBytes(TWData *_Nonnull data, size_t start, size_t size, const uint8_t *_Nonnull bytes) TW_VISIBILITY_DEFAULT; /// Appends data from a byte array. -void TWDataAppendBytes(TWData *_Nonnull data, const uint8_t *_Nonnull bytes, size_t size); +/// +/// \param data A non-null valid block of data +/// \param bytes Non-null byte array +/// \param size The size of the byte array +void TWDataAppendBytes(TWData *_Nonnull data, const uint8_t *_Nonnull bytes, size_t size) TW_VISIBILITY_DEFAULT; /// Appends a single byte. -void TWDataAppendByte(TWData *_Nonnull data, uint8_t byte); +/// +/// \param data A non-null valid block of data +/// \param byte A single byte +void TWDataAppendByte(TWData *_Nonnull data, uint8_t byte) TW_VISIBILITY_DEFAULT; /// Appends a block of data. -void TWDataAppendData(TWData *_Nonnull data, TWData *_Nonnull append); +/// +/// \param data A non-null valid block of data +/// \param append A non-null valid block of data +void TWDataAppendData(TWData *_Nonnull data, TWData *_Nonnull append) TW_VISIBILITY_DEFAULT; -/// Revereses the bytes. -void TWDataReverse(TWData *_Nonnull data); +/// Reverse the bytes. +/// +/// \param data A non-null valid block of data +void TWDataReverse(TWData *_Nonnull data) TW_VISIBILITY_DEFAULT; /// Sets all bytes to the given value. -void TWDataReset(TWData *_Nonnull data); +/// +/// \param data A non-null valid block of data +void TWDataReset(TWData *_Nonnull data) TW_VISIBILITY_DEFAULT; /// Deletes a block of data created with a `TWDataCreate*` method. -void TWDataDelete(TWData *_Nonnull data); +/// +/// \param data A non-null valid block of data +void TWDataDelete(TWData *_Nonnull data) TW_VISIBILITY_DEFAULT; /// Determines whether two data blocks are equal. -bool TWDataEqual(TWData *_Nonnull lhs, TWData *_Nonnull rhs); +/// +/// \param lhs left non null block of data to be compared +/// \param rhs right non null block of data to be compared +/// \return true if both block of data are equal, false otherwise +bool TWDataEqual(TWData *_Nonnull lhs, TWData *_Nonnull rhs) TW_VISIBILITY_DEFAULT; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWDataVector.h b/include/TrustWalletCore/TWDataVector.h new file mode 100644 index 00000000000..6e2e88912d0 --- /dev/null +++ b/include/TrustWalletCore/TWDataVector.h @@ -0,0 +1,62 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWData.h" + +TW_EXTERN_C_BEGIN + +/// A vector of TWData byte arrays +TW_EXPORT_CLASS +struct TWDataVector; + +/// Creates a Vector of Data. +/// +/// \note Must be deleted with \TWDataVectorDelete +/// \return a non-null Vector of Data. +TW_EXPORT_STATIC_METHOD +struct TWDataVector* _Nonnull TWDataVectorCreate(); + +/// Creates a Vector of Data with the given element +/// +/// \param data A non-null valid block of data +/// \return A Vector of data with a single given element +TW_EXPORT_STATIC_METHOD +struct TWDataVector* _Nonnull TWDataVectorCreateWithData(TWData* _Nonnull data); + +/// Delete/Deallocate a Vector of Data +/// +/// \param dataVector A non-null Vector of data +TW_EXPORT_METHOD +void TWDataVectorDelete(struct TWDataVector* _Nonnull dataVector); + +/// Add an element to a Vector of Data. Element is cloned +/// +/// \param dataVector A non-null Vector of data +/// \param data A non-null valid block of data +/// \note data input parameter must be deleted on its own +TW_EXPORT_METHOD +void TWDataVectorAdd(struct TWDataVector* _Nonnull dataVector, TWData* _Nonnull data); + +/// Retrieve the number of elements +/// +/// \param dataVector A non-null Vector of data +/// \return the size of the given vector. +TW_EXPORT_PROPERTY +size_t TWDataVectorSize(const struct TWDataVector* _Nonnull dataVector); + +/// Retrieve the n-th element. +/// +/// \param dataVector A non-null Vector of data +/// \param index index element of the vector to be retrieved, need to be < TWDataVectorSize +/// \note Returned element must be freed with \TWDataDelete +/// \return A non-null block of data +TW_EXPORT_METHOD +TWData* _Nullable TWDataVectorGet(const struct TWDataVector* _Nonnull dataVector, size_t index); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWDerivationPath.h b/include/TrustWalletCore/TWDerivationPath.h new file mode 100644 index 00000000000..910288f600f --- /dev/null +++ b/include/TrustWalletCore/TWDerivationPath.h @@ -0,0 +1,103 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWCoinType.h" +#include "TWDerivationPath.h" +#include "TWPurpose.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// Represents a BIP44 DerivationPath in C++. +TW_EXPORT_CLASS +struct TWDerivationPath; + +/// Creates a new DerivationPath with a purpose, coin, account, change and address. +/// Must be deleted with TWDerivationPathDelete after use. +/// +/// \param purpose The purpose of the Path. +/// \param coin The coin type of the Path. +/// \param account The derivation of the Path. +/// \param change The derivation path of the Path. +/// \param address hex encoded public key. +/// \return A new DerivationPath. +TW_EXPORT_STATIC_METHOD +struct TWDerivationPath* _Nonnull TWDerivationPathCreate(enum TWPurpose purpose, uint32_t coin, uint32_t account, uint32_t change, uint32_t address); + +/// Creates a new DerivationPath with a string +/// +/// \param string The string of the Path. +/// \return A new DerivationPath or null if string is invalid. +TW_EXPORT_STATIC_METHOD +struct TWDerivationPath* _Nullable TWDerivationPathCreateWithString(TWString* _Nonnull string); + +/// Deletes a DerivationPath. +/// +/// \param path DerivationPath to delete. +TW_EXPORT_METHOD +void TWDerivationPathDelete(struct TWDerivationPath* _Nonnull path); + +/// Returns the index component of a DerivationPath. +/// +/// \param path DerivationPath to get the index of. +/// \param index The index component of the DerivationPath. +/// \return DerivationPathIndex or null if index is invalid. +TW_EXPORT_METHOD +struct TWDerivationPathIndex* _Nullable TWDerivationPathIndexAt(struct TWDerivationPath* _Nonnull path, uint32_t index); + +/// Returns the indices count of a DerivationPath. +/// +/// \param path DerivationPath to get the indices count of. +/// \return The indices count of the DerivationPath. +TW_EXPORT_METHOD +uint32_t TWDerivationPathIndicesCount(struct TWDerivationPath* _Nonnull path); + +/// Returns the purpose enum of a DerivationPath. +/// +/// \param path DerivationPath to get the purpose of. +/// \return DerivationPathPurpose. +TW_EXPORT_PROPERTY +enum TWPurpose TWDerivationPathPurpose(struct TWDerivationPath* _Nonnull path); + +/// Returns the coin value of a derivation path. +/// +/// \param path DerivationPath to get the coin of. +/// \return The coin part of the DerivationPath. +TW_EXPORT_PROPERTY +uint32_t TWDerivationPathCoin(struct TWDerivationPath* _Nonnull path); + +/// Returns the account value of a derivation path. +/// +/// \param path DerivationPath to get the account of. +/// \return the account part of a derivation path. +TW_EXPORT_PROPERTY +uint32_t TWDerivationPathAccount(struct TWDerivationPath* _Nonnull path); + +/// Returns the change value of a derivation path. +/// +/// \param path DerivationPath to get the change of. +/// \return The change part of a derivation path. +TW_EXPORT_PROPERTY +uint32_t TWDerivationPathChange(struct TWDerivationPath* _Nonnull path); + +/// Returns the address value of a derivation path. +/// +/// \param path DerivationPath to get the address of. +/// \return The address part of the derivation path. +TW_EXPORT_PROPERTY +uint32_t TWDerivationPathAddress(struct TWDerivationPath* _Nonnull path); + +/// Returns the string description of a derivation path. +/// +/// \param path DerivationPath to get the address of. +/// \return The string description of the derivation path. +TW_EXPORT_PROPERTY +TWString* _Nonnull TWDerivationPathDescription(struct TWDerivationPath* _Nonnull path); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWDerivationPathIndex.h b/include/TrustWalletCore/TWDerivationPathIndex.h new file mode 100644 index 00000000000..72bd2b8344e --- /dev/null +++ b/include/TrustWalletCore/TWDerivationPathIndex.h @@ -0,0 +1,53 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// Represents a derivation path index in C++ with value and hardened flag. +TW_EXPORT_CLASS +struct TWDerivationPathIndex; + +/// Creates a new Index with a value and hardened flag. +/// Must be deleted with TWDerivationPathIndexDelete after use. +/// +/// \param value Index value +/// \param hardened Indicates if the Index is hardened. +/// \return A new Index. +TW_EXPORT_STATIC_METHOD +struct TWDerivationPathIndex* _Nonnull TWDerivationPathIndexCreate(uint32_t value, bool hardened); + +/// Deletes an Index. +/// +/// \param index Index to delete. +TW_EXPORT_METHOD +void TWDerivationPathIndexDelete(struct TWDerivationPathIndex* _Nonnull index); + +/// Returns numeric value of an Index. +/// +/// \param index Index to get the numeric value of. +TW_EXPORT_PROPERTY +uint32_t TWDerivationPathIndexValue(struct TWDerivationPathIndex* _Nonnull index); + +/// Returns hardened flag of an Index. +/// +/// \param index Index to get hardened flag. +/// \return true if hardened, false otherwise. +TW_EXPORT_PROPERTY +bool TWDerivationPathIndexHardened(struct TWDerivationPathIndex* _Nonnull index); + +/// Returns the string description of a derivation path index. +/// +/// \param path Index to get the address of. +/// \return The string description of the derivation path index. +TW_EXPORT_PROPERTY +TWString* _Nonnull TWDerivationPathIndexDescription(struct TWDerivationPathIndex* _Nonnull index); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWEthereumAbi.h b/include/TrustWalletCore/TWEthereumAbi.h index 0430ea07a3c..5baf7161942 100644 --- a/include/TrustWalletCore/TWEthereumAbi.h +++ b/include/TrustWalletCore/TWEthereumAbi.h @@ -10,24 +10,34 @@ #include "TWString.h" #include "TWData.h" -// Wrapper class for Ethereum ABI encoding & decoding. - TW_EXTERN_C_BEGIN +/// Wrapper class for Ethereum ABI encoding & decoding. struct TWEthereumAbiFunction; TW_EXPORT_STRUCT struct TWEthereumAbi; /// Encode function to Eth ABI binary +/// +/// \param fn Non-null Eth abi function +/// \return Non-null encoded block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiEncode(struct TWEthereumAbiFunction* _Nonnull fn); /// Decode function output from Eth ABI binary, fill output parameters +/// +/// \param[in] fn Non-null Eth abi function +/// \param[out] encoded Non-null block of data +/// \return true if encoded have been filled correctly, false otherwise TW_EXPORT_STATIC_METHOD bool TWEthereumAbiDecodeOutput(struct TWEthereumAbiFunction* _Nonnull fn, TWData* _Nonnull encoded); /// Decode function call data to human readable json format, according to input abi json +/// +/// \param data Non-null block of data +/// \param abi Non-null string +/// \return Non-null json string function call data TW_EXPORT_STATIC_METHOD TWString* _Nullable TWEthereumAbiDecodeCall(TWData* _Nonnull data, TWString* _Nonnull abi); @@ -66,6 +76,9 @@ TWString* _Nullable TWEthereumAbiDecodeCall(TWData* _Nonnull data, TWString* _No /// })"); /// On error, empty Data is returned. /// Returned data must be deleted (hint: use WRAPD() macro). +/// +/// \param messageJson Non-null json abi input +/// \return Non-null block of data, encoded abi input TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiEncodeTyped(TWString* _Nonnull messageJson); diff --git a/include/TrustWalletCore/TWEthereumAbiFunction.h b/include/TrustWalletCore/TWEthereumAbiFunction.h index 81daa7b3e7e..4034ade5180 100644 --- a/include/TrustWalletCore/TWEthereumAbiFunction.h +++ b/include/TrustWalletCore/TWEthereumAbiFunction.h @@ -7,182 +7,450 @@ #pragma once #include "TWBase.h" -#include "TWString.h" #include "TWData.h" +#include "TWString.h" TW_EXTERN_C_BEGIN +/// Represents Ethereum ABI function TW_EXPORT_CLASS struct TWEthereumAbiFunction; /// Creates a function object, with the given name and empty parameter list. It must be deleted at the end. +/// +/// \param name function name +/// \return Non-null Ethereum abi function TW_EXPORT_STATIC_METHOD -struct TWEthereumAbiFunction *_Nonnull TWEthereumAbiFunctionCreateWithString(TWString *_Nonnull name); +struct TWEthereumAbiFunction* _Nonnull TWEthereumAbiFunctionCreateWithString(TWString* _Nonnull name); /// Deletes a function object created with a 'TWEthereumAbiFunctionCreateWithString' method. +/// +/// \param fn Non-null Ethereum abi function TW_EXPORT_METHOD -void TWEthereumAbiFunctionDelete(struct TWEthereumAbiFunction *_Nonnull fn); +void TWEthereumAbiFunctionDelete(struct TWEthereumAbiFunction* _Nonnull fn); /// Return the function type signature, of the form "baz(int32,uint256)" +/// +/// \param fn A Non-null eth abi function +/// \return function type signature as a Non-null string. TW_EXPORT_METHOD -TWString *_Nonnull TWEthereumAbiFunctionGetType(struct TWEthereumAbiFunction *_Nonnull fn); +TWString* _Nonnull TWEthereumAbiFunctionGetType(struct TWEthereumAbiFunction* _Nonnull fn); + +/// Methods for adding parameters of the given type (input or output). +/// For output parameters (isOutput=true) a value has to be specified, although usually not need; -/// Methods for adding parameters of the given type (input or output). -/// For output parameters (isOutput=true) a value has to be specified, although usually not needd. -/// Returns the index of the parameter (0-based). +/// Add a uint8 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamUInt8(struct TWEthereumAbiFunction *_Nonnull fn, uint8_t val, bool isOutput); +int TWEthereumAbiFunctionAddParamUInt8(struct TWEthereumAbiFunction* _Nonnull fn, uint8_t val, bool isOutput); +/// Add a uint16 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamUInt16(struct TWEthereumAbiFunction *_Nonnull fn, uint16_t val, bool isOutput); +int TWEthereumAbiFunctionAddParamUInt16(struct TWEthereumAbiFunction* _Nonnull fn, uint16_t val, bool isOutput); +/// Add a uint32 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamUInt32(struct TWEthereumAbiFunction *_Nonnull fn, uint32_t val, bool isOutput); +int TWEthereumAbiFunctionAddParamUInt32(struct TWEthereumAbiFunction* _Nonnull fn, uint32_t val, bool isOutput); +/// Add a uint64 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamUInt64(struct TWEthereumAbiFunction *_Nonnull fn, uint64_t val, bool isOutput); +int TWEthereumAbiFunctionAddParamUInt64(struct TWEthereumAbiFunction* _Nonnull fn, uint64_t val, bool isOutput); +/// Add a uint256 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamUInt256(struct TWEthereumAbiFunction *_Nonnull fn, TWData *_Nonnull val, bool isOutput); +int TWEthereumAbiFunctionAddParamUInt256(struct TWEthereumAbiFunction* _Nonnull fn, TWData* _Nonnull val, bool isOutput); +/// Add a uint(bits) type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamUIntN(struct TWEthereumAbiFunction *_Nonnull fn, int bits, TWData *_Nonnull val, bool isOutput); +int TWEthereumAbiFunctionAddParamUIntN(struct TWEthereumAbiFunction* _Nonnull fn, int bits, TWData* _Nonnull val, bool isOutput); +/// Add a int8 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamInt8(struct TWEthereumAbiFunction *_Nonnull fn, int8_t val, bool isOutput); +int TWEthereumAbiFunctionAddParamInt8(struct TWEthereumAbiFunction* _Nonnull fn, int8_t val, bool isOutput); +/// Add a int16 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamInt16(struct TWEthereumAbiFunction *_Nonnull fn, int16_t val, bool isOutput); +int TWEthereumAbiFunctionAddParamInt16(struct TWEthereumAbiFunction* _Nonnull fn, int16_t val, bool isOutput); +/// Add a int32 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamInt32(struct TWEthereumAbiFunction *_Nonnull fn, int32_t val, bool isOutput); +int TWEthereumAbiFunctionAddParamInt32(struct TWEthereumAbiFunction* _Nonnull fn, int32_t val, bool isOutput); +/// Add a int64 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamInt64(struct TWEthereumAbiFunction *_Nonnull fn, int64_t val, bool isOutput); +int TWEthereumAbiFunctionAddParamInt64(struct TWEthereumAbiFunction* _Nonnull fn, int64_t val, bool isOutput); +/// Add a int256 type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified (stored in a block of data) +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamInt256(struct TWEthereumAbiFunction *_Nonnull fn, TWData *_Nonnull val, bool isOutput); +int TWEthereumAbiFunctionAddParamInt256(struct TWEthereumAbiFunction* _Nonnull fn, TWData* _Nonnull val, bool isOutput); +/// Add a int(bits) type parameter +/// +/// \param fn A Non-null eth abi function +/// \param bits Number of bits of the integer parameter +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamIntN(struct TWEthereumAbiFunction *_Nonnull fn, int bits, TWData *_Nonnull val, bool isOutput); +int TWEthereumAbiFunctionAddParamIntN(struct TWEthereumAbiFunction* _Nonnull fn, int bits, TWData* _Nonnull val, bool isOutput); + +/// Add a bool type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamBool(struct TWEthereumAbiFunction *_Nonnull fn, bool val, bool isOutput); +int TWEthereumAbiFunctionAddParamBool(struct TWEthereumAbiFunction* _Nonnull fn, bool val, bool isOutput); +/// Add a string type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamString(struct TWEthereumAbiFunction *_Nonnull fn, TWString *_Nonnull val, bool isOutput); +int TWEthereumAbiFunctionAddParamString(struct TWEthereumAbiFunction* _Nonnull fn, TWString* _Nonnull val, bool isOutput); +/// Add an address type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamAddress(struct TWEthereumAbiFunction *_Nonnull fn, TWData *_Nonnull val, bool isOutput); +int TWEthereumAbiFunctionAddParamAddress(struct TWEthereumAbiFunction* _Nonnull fn, TWData* _Nonnull val, bool isOutput); +/// Add a bytes type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamBytes(struct TWEthereumAbiFunction *_Nonnull fn, TWData *_Nonnull val, bool isOutput); +int TWEthereumAbiFunctionAddParamBytes(struct TWEthereumAbiFunction* _Nonnull fn, TWData* _Nonnull val, bool isOutput); +/// Add a bytes[N] type parameter +/// +/// \param fn A Non-null eth abi function +/// \param size fixed size of the bytes array parameter (val). +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamBytesFix(struct TWEthereumAbiFunction *_Nonnull fn, size_t size, TWData *_Nonnull val, bool isOutput); +int TWEthereumAbiFunctionAddParamBytesFix(struct TWEthereumAbiFunction* _Nonnull fn, size_t size, TWData* _Nonnull val, bool isOutput); +/// Add a type[] type parameter +/// +/// \param fn A Non-null eth abi function +/// \param val for output parameters, value has to be specified +/// \param isOutput determines if the parameter is an input or output +/// \return the index of the parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddParamArray(struct TWEthereumAbiFunction *_Nonnull fn, bool isOutput); +int TWEthereumAbiFunctionAddParamArray(struct TWEthereumAbiFunction* _Nonnull fn, bool isOutput); /// Methods for accessing the value of an output or input parameter, of different types. + +/// Get a uint8 type parameter at the given index +/// +/// \param fn A Non-null eth abi function +/// \param idx index for the parameter (0-based). +/// \param isOutput determines if the parameter is an input or output +/// \return the value of the parameter. TW_EXPORT_METHOD -uint8_t TWEthereumAbiFunctionGetParamUInt8(struct TWEthereumAbiFunction *_Nonnull fn, int idx, bool isOutput); +uint8_t TWEthereumAbiFunctionGetParamUInt8(struct TWEthereumAbiFunction* _Nonnull fn, int idx, bool isOutput); + +/// Get a uint64 type parameter at the given index +/// +/// \param fn A Non-null eth abi function +/// \param idx index for the parameter (0-based). +/// \param isOutput determines if the parameter is an input or output +/// \return the value of the parameter. TW_EXPORT_METHOD -uint64_t TWEthereumAbiFunctionGetParamUInt64(struct TWEthereumAbiFunction *_Nonnull fn, int idx, bool isOutput); +uint64_t TWEthereumAbiFunctionGetParamUInt64(struct TWEthereumAbiFunction* _Nonnull fn, int idx, bool isOutput); + +/// Get a uint256 type parameter at the given index +/// +/// \param fn A Non-null eth abi function +/// \param idx index for the parameter (0-based). +/// \param isOutput determines if the parameter is an input or output +/// \return the value of the parameter stored in a block of data. TW_EXPORT_METHOD -TWData *_Nonnull TWEthereumAbiFunctionGetParamUInt256(struct TWEthereumAbiFunction *_Nonnull fn, int idx, bool isOutput); +TWData* _Nonnull TWEthereumAbiFunctionGetParamUInt256(struct TWEthereumAbiFunction* _Nonnull fn, int idx, bool isOutput); + +/// Get a bool type parameter at the given index +/// +/// \param fn A Non-null eth abi function +/// \param idx index for the parameter (0-based). +/// \param isOutput determines if the parameter is an input or output +/// \return the value of the parameter. TW_EXPORT_METHOD -bool TWEthereumAbiFunctionGetParamBool(struct TWEthereumAbiFunction *_Nonnull fn, int idx, bool isOutput); +bool TWEthereumAbiFunctionGetParamBool(struct TWEthereumAbiFunction* _Nonnull fn, int idx, bool isOutput); + +/// Get a string type parameter at the given index +/// +/// \param fn A Non-null eth abi function +/// \param idx index for the parameter (0-based). +/// \param isOutput determines if the parameter is an input or output +/// \return the value of the parameter. TW_EXPORT_METHOD -TWString *_Nonnull TWEthereumAbiFunctionGetParamString(struct TWEthereumAbiFunction *_Nonnull fn, int idx, bool isOutput); +TWString* _Nonnull TWEthereumAbiFunctionGetParamString(struct TWEthereumAbiFunction* _Nonnull fn, int idx, bool isOutput); + +/// Get an address type parameter at the given index +/// +/// \param fn A Non-null eth abi function +/// \param idx index for the parameter (0-based). +/// \param isOutput determines if the parameter is an input or output +/// \return the value of the parameter. TW_EXPORT_METHOD -TWData *_Nonnull TWEthereumAbiFunctionGetParamAddress(struct TWEthereumAbiFunction *_Nonnull fn, int idx, bool isOutput); +TWData* _Nonnull TWEthereumAbiFunctionGetParamAddress(struct TWEthereumAbiFunction* _Nonnull fn, int idx, bool isOutput); /// Methods for adding a parameter of the given type to a top-level input parameter array. Returns the index of the parameter (0-based). /// Note that nested ParamArrays are not possible through this API, could be done by using index paths like "1/0" + +/// Adding a uint8 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamUInt8(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, uint8_t val); +int TWEthereumAbiFunctionAddInArrayParamUInt8(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, uint8_t val); +/// Adding a uint16 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamUInt16(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, uint16_t val); +int TWEthereumAbiFunctionAddInArrayParamUInt16(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, uint16_t val); +/// Adding a uint32 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamUInt32(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, uint32_t val); +int TWEthereumAbiFunctionAddInArrayParamUInt32(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, uint32_t val); +/// Adding a uint64 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamUInt64(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, uint64_t val); +int TWEthereumAbiFunctionAddInArrayParamUInt64(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, uint64_t val); +/// Adding a uint256 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter stored in a block of data +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamUInt256(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, TWData *_Nonnull val); +int TWEthereumAbiFunctionAddInArrayParamUInt256(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, TWData* _Nonnull val); +/// Adding a uint[N] type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param bits Number of bits of the integer parameter +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter stored in a block of data +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamUIntN(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, int bits, TWData *_Nonnull val); +int TWEthereumAbiFunctionAddInArrayParamUIntN(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, int bits, TWData* _Nonnull val); +/// Adding a int8 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamInt8(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, int8_t val); +int TWEthereumAbiFunctionAddInArrayParamInt8(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, int8_t val); +/// Adding a int16 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamInt16(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, int16_t val); +int TWEthereumAbiFunctionAddInArrayParamInt16(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, int16_t val); +/// Adding a int32 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamInt32(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, int32_t val); +int TWEthereumAbiFunctionAddInArrayParamInt32(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, int32_t val); +/// Adding a int64 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamInt64(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, int64_t val); +int TWEthereumAbiFunctionAddInArrayParamInt64(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, int64_t val); +/// Adding a int256 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter stored in a block of data +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamInt256(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, TWData *_Nonnull val); +int TWEthereumAbiFunctionAddInArrayParamInt256(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, TWData* _Nonnull val); +/// Adding a int[N] type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param bits Number of bits of the integer parameter +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter stored in a block of data +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamIntN(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, int bits, TWData *_Nonnull val); +int TWEthereumAbiFunctionAddInArrayParamIntN(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, int bits, TWData* _Nonnull val); +/// Adding a bool type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamBool(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, bool val); +int TWEthereumAbiFunctionAddInArrayParamBool(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, bool val); +/// Adding a string type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamString(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, TWString *_Nonnull val); +int TWEthereumAbiFunctionAddInArrayParamString(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, TWString* _Nonnull val); +/// Adding an address type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamAddress(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, TWData *_Nonnull val); +int TWEthereumAbiFunctionAddInArrayParamAddress(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, TWData* _Nonnull val); +/// Adding a bytes type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamBytes(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, TWData *_Nonnull val); +int TWEthereumAbiFunctionAddInArrayParamBytes(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, TWData* _Nonnull val); +/// Adding a int64 type parameter of to the top-level input parameter array +/// +/// \param fn A Non-null eth abi function +/// \param arrayIdx array index for the abi function (0-based). +/// \param size fixed size of the bytes array parameter (val). +/// \param val the value of the parameter +/// \return the index of the added parameter (0-based). TW_EXPORT_METHOD TW_METHOD_DISCARDABLE_RESULT -int TWEthereumAbiFunctionAddInArrayParamBytesFix(struct TWEthereumAbiFunction *_Nonnull fn, int arrayIdx, size_t size, TWData *_Nonnull val); +int TWEthereumAbiFunctionAddInArrayParamBytesFix(struct TWEthereumAbiFunction* _Nonnull fn, int arrayIdx, size_t size, TWData* _Nonnull val); TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWEthereumAbiValue.h b/include/TrustWalletCore/TWEthereumAbiValue.h index 58a2f7e74d5..9bbf6ce34eb 100644 --- a/include/TrustWalletCore/TWEthereumAbiValue.h +++ b/include/TrustWalletCore/TWEthereumAbiValue.h @@ -12,55 +12,93 @@ TW_EXTERN_C_BEGIN +/// Represents Ethereum ABI value TW_EXPORT_STRUCT struct TWEthereumAbiValue; -/// Returned data must be deleted (hint: use WRAPD() macro). -/// Encode a type according to Ethereum ABI, into 32 bytes. Values are padded by 0 on the left, unless specified otherwise. - +/// Encode a bool according to Ethereum ABI, into 32 bytes. Values are padded by 0 on the left, unless specified otherwise +/// +/// \param value a boolean value +/// \return Encoded value stored in a block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiValueEncodeBool(bool value); +/// Encode a int32 according to Ethereum ABI, into 32 bytes. Values are padded by 0 on the left, unless specified otherwise +/// +/// \param value a int32 value +/// \return Encoded value stored in a block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiValueEncodeInt32(int32_t value); +/// Encode a uint32 according to Ethereum ABI, into 32 bytes. Values are padded by 0 on the left, unless specified otherwise +/// +/// \param value a uint32 value +/// \return Encoded value stored in a block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiValueEncodeUInt32(uint32_t value); -/// Encode an int256. Input value is represented as a 32-byte value +/// Encode a int256 according to Ethereum ABI, into 32 bytes. Values are padded by 0 on the left, unless specified otherwise +/// +/// \param value a int256 value stored in a block of data +/// \return Encoded value stored in a block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiValueEncodeInt256(TWData* _Nonnull value); -/// Encode an uint256. Input value is represented as a 32-byte binary value +/// Encode an int256 according to Ethereum ABI, into 32 bytes. Values are padded by 0 on the left, unless specified otherwise +/// +/// \param value a int256 value stored in a block of data +/// \return Encoded value stored in a block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiValueEncodeUInt256(TWData* _Nonnull value); -/// Encode the 20 bytes of an address +/// Encode an address according to Ethereum ABI, 20 bytes of the address. +/// +/// \param value an address value stored in a block of data +/// \return Encoded value stored in a block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiValueEncodeAddress(TWData* _Nonnull value); -/// Encode a string by encoding its hash +/// Encode a string according to Ethereum ABI by encoding its hash. +/// +/// \param value a string value +/// \return Encoded value stored in a block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiValueEncodeString(TWString* _Nonnull value); /// Encode a number of bytes, up to 32 bytes, padded on the right. Longer arrays are truncated. +/// +/// \param value bunch of bytes +/// \return Encoded value stored in a block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiValueEncodeBytes(TWData* _Nonnull value); /// Encode a dynamic number of bytes by encoding its hash +/// +/// \param value bunch of bytes +/// \return Encoded value stored in a block of data TW_EXPORT_STATIC_METHOD TWData* _Nonnull TWEthereumAbiValueEncodeBytesDyn(TWData* _Nonnull value); - /// Decodes input data (bytes longer than 32 will be truncated) as uint256 +/// +/// \param input Data to be decoded +/// \return Non-null decoded string value TW_EXPORT_STATIC_METHOD TWString* _Nonnull TWEthereumAbiValueDecodeUInt256(TWData* _Nonnull input); /// Decode an arbitrary type, return value as string +/// +/// \param input Data to be decoded +/// \param type the underlying type that need to be decoded +/// \return Non-null decoded string value TW_EXPORT_STATIC_METHOD TWString* _Nonnull TWEthereumAbiValueDecodeValue(TWData* _Nonnull input, TWString* _Nonnull type); /// Decode an array of given simple types. Return a '\n'-separated string of elements +/// +/// \param input Data to be decoded +/// \param type the underlying type that need to be decoded +/// \return Non-null decoded string value TW_EXPORT_STATIC_METHOD TWString* _Nonnull TWEthereumAbiValueDecodeArray(TWData* _Nonnull input, TWString* _Nonnull type); diff --git a/include/TrustWalletCore/TWEthereumChainID.h b/include/TrustWalletCore/TWEthereumChainID.h deleted file mode 100644 index cbf04a1acfa..00000000000 --- a/include/TrustWalletCore/TWEthereumChainID.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "TWBase.h" - -TW_EXTERN_C_BEGIN - -/// Chain identifier for Ethereum-based blockchains. -TW_EXPORT_ENUM(uint32_t) -enum TWEthereumChainID { - TWEthereumChainIDEthereum = 1, - TWEthereumChainIDGo = 60, - TWEthereumChainIDPOA = 99, - TWEthereumChainIDCallisto = 820, - TWEthereumChainIDEthereumClassic = 61, - TWEthereumChainIDVeChain = 74, - TWEthereumChainIDThunderToken = 108, - TWEthereumChainIDTomoChain = 88, - TWEthereumChainIDBinanceSmartChain = 56, - TWEthereumChainIDPolygon = 137, - TWEthereumChainIDWanchain = 888, - TWEthereumChainIDOptimism = 10, - TWEthereumChainIDArbitrum = 42161, - TWEthereumChainIDHeco = 128, - TWEthereumChainIDAvalanche = 43114, - TWEthereumChainIDXDai = 100, - TWEthereumChainIDFantom = 250, - TWEthereumChainIDCelo = 42220, - TWEthereumChainIDRonin = 2020, -}; - -TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWEthereumFee.h b/include/TrustWalletCore/TWEthereumFee.h deleted file mode 100644 index f57f0e68c98..00000000000 --- a/include/TrustWalletCore/TWEthereumFee.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "TWBase.h" -#include "TWString.h" -#include "TWData.h" - -// Wrapper class for Ethereum EIP 1559 Fee suggestion - -TW_EXTERN_C_BEGIN - -TW_EXPORT_STRUCT -struct TWEthereumFee; - -/// Suggest baseFee and maxPriorityFee based on eth_feeHistory RPC call response -TW_EXPORT_STATIC_METHOD -TWString* _Nullable TWEthereumFeeSuggest(TWString* _Nonnull feeHistory); - -TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWFIOAccount.h b/include/TrustWalletCore/TWFIOAccount.h index 8012cbf0faf..876aabbda7d 100644 --- a/include/TrustWalletCore/TWFIOAccount.h +++ b/include/TrustWalletCore/TWFIOAccount.h @@ -15,13 +15,24 @@ TW_EXTERN_C_BEGIN TW_EXPORT_CLASS struct TWFIOAccount; +/// Create a FIO Account +/// +/// \param string Account name +/// \note Must be deleted with \TWFIOAccountDelete +/// \return Pointer to a nullable FIO Account TW_EXPORT_STATIC_METHOD struct TWFIOAccount *_Nullable TWFIOAccountCreateWithString(TWString *_Nonnull string); +/// Delete a FIO Account +/// +/// \param account Pointer to a non-null FIO Account TW_EXPORT_METHOD void TWFIOAccountDelete(struct TWFIOAccount *_Nonnull account); /// Returns the short account string representation. +/// +/// \param account Pointer to a non-null FIO Account +/// \return Account non-null string representation TW_EXPORT_PROPERTY TWString *_Nonnull TWFIOAccountDescription(struct TWFIOAccount *_Nonnull account); diff --git a/include/TrustWalletCore/TWGroestlcoinAddress.h b/include/TrustWalletCore/TWGroestlcoinAddress.h index 37edc0e8eb5..b35ce21ee64 100644 --- a/include/TrustWalletCore/TWGroestlcoinAddress.h +++ b/include/TrustWalletCore/TWGroestlcoinAddress.h @@ -19,25 +19,47 @@ TW_EXPORT_CLASS struct TWGroestlcoinAddress; /// Compares two addresses for equality. +/// +/// \param lhs left Non-null GroestlCoin address to be compared +/// \param rhs right Non-null GroestlCoin address to be compared +/// \return true if both address are equal, false otherwise TW_EXPORT_STATIC_METHOD bool TWGroestlcoinAddressEqual(struct TWGroestlcoinAddress *_Nonnull lhs, struct TWGroestlcoinAddress *_Nonnull rhs); /// Determines if the string is a valid Groestlcoin address. +/// +/// \param string Non-null string. +/// \return true if it's a valid address, false otherwise TW_EXPORT_STATIC_METHOD bool TWGroestlcoinAddressIsValidString(TWString *_Nonnull string); -/// Create an address from a base58 sring representaion. +/// Create an address from a base58 string representation. +/// +/// \param string Non-null string +/// \note Must be deleted with \TWGroestlcoinAddressDelete +/// \return Non-null GroestlcoinAddress TW_EXPORT_STATIC_METHOD struct TWGroestlcoinAddress *_Nullable TWGroestlcoinAddressCreateWithString(TWString *_Nonnull string); /// Create an address from a public key and a prefix byte. +/// +/// \param publicKey Non-null public key +/// \param prefix public key prefix +/// \note Must be deleted with \TWGroestlcoinAddressDelete +/// \return Non-null GroestlcoinAddress TW_EXPORT_STATIC_METHOD struct TWGroestlcoinAddress *_Nonnull TWGroestlcoinAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey, uint8_t prefix); +/// Delete a Groestlcoin address +/// +/// \param address Non-null GroestlcoinAddress TW_EXPORT_METHOD void TWGroestlcoinAddressDelete(struct TWGroestlcoinAddress *_Nonnull address); /// Returns the address base58 string representation. +/// +/// \param address Non-null GroestlcoinAddress +/// \return Address description as a non-null string TW_EXPORT_PROPERTY TWString *_Nonnull TWGroestlcoinAddressDescription(struct TWGroestlcoinAddress *_Nonnull address); diff --git a/include/TrustWalletCore/TWHDVersion.h b/include/TrustWalletCore/TWHDVersion.h index 9e93aed3aa7..dec82a60ebc 100644 --- a/include/TrustWalletCore/TWHDVersion.h +++ b/include/TrustWalletCore/TWHDVersion.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,9 +10,9 @@ TW_EXTERN_C_BEGIN -/// Registered HD version bytes +/// Registered HD version bytes /// -/// - SeeAlso: https://github.com/satoshilabs/slips/blob/master/slip-0132.md +/// \see https://github.com/satoshilabs/slips/blob/master/slip-0132.md TW_EXPORT_ENUM(uint32_t) enum TWHDVersion { TWHDVersionNone = 0, @@ -40,9 +40,17 @@ enum TWHDVersion { TWHDVersionDGPV = 0x02fac398, }; +/// Determine if the HD Version is public +/// +/// \param version HD version +/// \return true if the version is public, false otherwise TW_EXPORT_PROPERTY bool TWHDVersionIsPublic(enum TWHDVersion version); +/// Determine if the HD Version is private +/// +/// \param version HD version +/// \return true if the version is private, false otherwise TW_EXPORT_PROPERTY bool TWHDVersionIsPrivate(enum TWHDVersion version); diff --git a/include/TrustWalletCore/TWHDWallet.h b/include/TrustWalletCore/TWHDWallet.h index 5f8a2d849af..55ab3e13b4a 100644 --- a/include/TrustWalletCore/TWHDWallet.h +++ b/include/TrustWalletCore/TWHDWallet.h @@ -10,6 +10,7 @@ #include "TWCoinType.h" #include "TWCurve.h" #include "TWData.h" +#include "TWDerivation.h" #include "TWHDVersion.h" #include "TWPrivateKey.h" #include "TWPublicKey.h" @@ -18,79 +19,218 @@ TW_EXTERN_C_BEGIN +/// Hierarchical Deterministic (HD) Wallet TW_EXPORT_CLASS struct TWHDWallet; -/// TWHDWalletIsValid has been deprecated; use TWMnemonicIsValid(). - /// Creates a new HDWallet with a new random mnemonic with the provided strength in bits. -/// Null is returned on invalid strength. Returned object needs to be deleted. +/// +/// \param strength strength in bits +/// \param passphrase non-null passphrase +/// \note Null is returned on invalid strength +/// \note Returned object needs to be deleted with \TWHDWalletDelete +/// \return Nullable TWHDWallet TW_EXPORT_STATIC_METHOD -struct TWHDWallet *_Nullable TWHDWalletCreate(int strength, TWString *_Nonnull passphrase); +struct TWHDWallet* _Nullable TWHDWalletCreate(int strength, TWString* _Nonnull passphrase); /// Creates an HDWallet from a valid BIP39 English mnemonic and a passphrase. -/// Null is returned on invalid mnemonic. Returned object needs to be deleted. +/// +/// \param mnemonic non-null Valid BIP39 mnemonic +/// \param passphrase non-null passphrase +/// \note Null is returned on invalid mnemonic +/// \note Returned object needs to be deleted with \TWHDWalletDelete +/// \return Nullable TWHDWallet TW_EXPORT_STATIC_METHOD -struct TWHDWallet *_Nullable TWHDWalletCreateWithMnemonic(TWString *_Nonnull mnemonic, TWString *_Nonnull passphrase); +struct TWHDWallet* _Nullable TWHDWalletCreateWithMnemonic(TWString* _Nonnull mnemonic, TWString* _Nonnull passphrase); /// Creates an HDWallet from a BIP39 mnemonic, a passphrase and validation flag. -/// Null is returned on invalid mnemonic. Returned object needs to be deleted. +/// +/// \param mnemonic non-null Valid BIP39 mnemonic +/// \param passphrase non-null passphrase +/// \param check validation flag +/// \note Null is returned on invalid mnemonic +/// \note Returned object needs to be deleted with \TWHDWalletDelete +/// \return Nullable TWHDWallet TW_EXPORT_STATIC_METHOD -struct TWHDWallet *_Nullable TWHDWalletCreateWithMnemonicCheck(TWString *_Nonnull mnemonic, TWString *_Nonnull passphrase, bool check); +struct TWHDWallet* _Nullable TWHDWalletCreateWithMnemonicCheck(TWString* _Nonnull mnemonic, TWString* _Nonnull passphrase, bool check); /// Creates an HDWallet from entropy (corresponding to a mnemonic). -/// Null is returned on invalid input. Returned object needs to be deleted. +/// +/// \param entropy Non-null entropy data (corresponding to a mnemonic) +/// \param passphrase non-null passphrase +/// \note Null is returned on invalid input +/// \note Returned object needs to be deleted with \TWHDWalletDelete +/// \return Nullable TWHDWallet TW_EXPORT_STATIC_METHOD -struct TWHDWallet *_Nullable TWHDWalletCreateWithEntropy(TWData *_Nonnull entropy, TWString *_Nonnull passphrase); +struct TWHDWallet* _Nullable TWHDWalletCreateWithEntropy(TWData* _Nonnull entropy, TWString* _Nonnull passphrase); /// Deletes a wallet. +/// +/// \param wallet non-null TWHDWallet TW_EXPORT_METHOD -void TWHDWalletDelete(struct TWHDWallet *_Nonnull wallet); +void TWHDWalletDelete(struct TWHDWallet* _Nonnull wallet); /// Wallet seed. +/// +/// \param wallet non-null TWHDWallet +/// \return The wallet seed as a Non-null block of data. TW_EXPORT_PROPERTY -TWData *_Nonnull TWHDWalletSeed(struct TWHDWallet *_Nonnull wallet); +TWData* _Nonnull TWHDWalletSeed(struct TWHDWallet* _Nonnull wallet); -// Wallet Mnemonic +/// Wallet Mnemonic +/// +/// \param wallet non-null TWHDWallet +/// \return The wallet mnemonic as a non-null TWString TW_EXPORT_PROPERTY -TWString *_Nonnull TWHDWalletMnemonic(struct TWHDWallet *_Nonnull wallet); +TWString* _Nonnull TWHDWalletMnemonic(struct TWHDWallet* _Nonnull wallet); -// Wallet entropy +/// Wallet entropy +/// +/// \param wallet non-null TWHDWallet +/// \return The wallet entropy as a non-null block of data. TW_EXPORT_PROPERTY -TWData *_Nonnull TWHDWalletEntropy(struct TWHDWallet *_Nonnull wallet); +TWData* _Nonnull TWHDWalletEntropy(struct TWHDWallet* _Nonnull wallet); -/// Returns master key. Returned object needs to be deleted. +/// Returns master key. +/// +/// \param wallet non-null TWHDWallet +/// \param curve a curve +/// \note Returned object needs to be deleted with \TWPrivateKeyDelete +/// \return Non-null corresponding private key TW_EXPORT_METHOD -struct TWPrivateKey *_Nonnull TWHDWalletGetMasterKey(struct TWHDWallet *_Nonnull wallet, enum TWCurve curve); +struct TWPrivateKey* _Nonnull TWHDWalletGetMasterKey(struct TWHDWallet* _Nonnull wallet, enum TWCurve curve); -/// Generates the default private key for the specified coin. Returned object needs to be deleted. +/// Generates the default private key for the specified coin. +/// +/// \param wallet non-null TWHDWallet +/// \param coin a coin type +/// \note Returned object needs to be deleted with \TWPrivateKeyDelete +/// \return return the default private key for the specified coin TW_EXPORT_METHOD -struct TWPrivateKey *_Nonnull TWHDWalletGetKeyForCoin(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin); +struct TWPrivateKey* _Nonnull TWHDWalletGetKeyForCoin(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin); /// Generates the default address for the specified coin (without exposing intermediary private key). +/// +/// \param wallet non-null TWHDWallet +/// \param coin a coin type +/// \return return the default address for the specified coin as a non-null TWString +TW_EXPORT_METHOD +TWString* _Nonnull TWHDWalletGetAddressForCoin(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin); + +/// Generates the private key for the specified derivation path. +/// +/// \param wallet non-null TWHDWallet +/// \param coin a coin type +/// \param derivationPath a non-null derivation path +/// \note Returned object needs to be deleted with \TWPrivateKeyDelete +/// \return The private key for the specified derivation path/coin +TW_EXPORT_METHOD +struct TWPrivateKey* _Nonnull TWHDWalletGetKey(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, TWString* _Nonnull derivationPath); + +/// Generates the private key for the specified derivation path and curve. +/// +/// \param wallet non-null TWHDWallet +/// \param curve a curve +/// \param derivationPath a non-null derivation path +/// \note Returned object needs to be deleted with \TWPrivateKeyDelete +/// \return The private key for the specified derivation path/curve +TW_EXPORT_METHOD +struct TWPrivateKey* _Nonnull TWHDWalletGetKeyByCurve(struct TWHDWallet* _Nonnull wallet, enum TWCurve curve, TWString* _Nonnull derivationPath); + +/// Shortcut method to generate private key with the specified account/change/address (bip44 standard). +/// +/// \see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki +/// +/// \param wallet non-null TWHDWallet +/// \param coin a coin type +/// \param account valid bip44 account +/// \param change valid bip44 change +/// \param address valid bip44 address +/// \note Returned object needs to be deleted with \TWPrivateKeyDelete +/// \return The private key for the specified bip44 parameters +TW_EXPORT_METHOD +struct TWPrivateKey* _Nonnull TWHDWalletGetDerivedKey(struct TWHDWallet* _Nonnull wallet, enum TWCoinType coin, uint32_t account, uint32_t change, uint32_t address); + +/// Returns the extended private key (for default 0 account). +/// +/// \param wallet non-null TWHDWallet +/// \param purpose a purpose +/// \param coin a coin type +/// \param version hd version +/// \note Returned object needs to be deleted with \TWStringDelete +/// \return Extended private key as a non-null TWString +TW_EXPORT_METHOD +TWString* _Nonnull TWHDWalletGetExtendedPrivateKey(struct TWHDWallet* _Nonnull wallet, enum TWPurpose purpose, enum TWCoinType coin, enum TWHDVersion version); + +/// Returns the extended public key (for default 0 account). +/// +/// \param wallet non-null TWHDWallet +/// \param purpose a purpose +/// \param coin a coin type +/// \param version hd version +/// \note Returned object needs to be deleted with \TWStringDelete +/// \return Extended public key as a non-null TWString TW_EXPORT_METHOD -TWString *_Nonnull TWHDWalletGetAddressForCoin(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin); +TWString* _Nonnull TWHDWalletGetExtendedPublicKey(struct TWHDWallet* _Nonnull wallet, enum TWPurpose purpose, enum TWCoinType coin, enum TWHDVersion version); -/// Generates the private key for the specified derivation path. Returned object needs to be deleted. +/// Returns the extended private key, for custom account. +/// +/// \param wallet non-null TWHDWallet +/// \param purpose a purpose +/// \param coin a coin type +/// \param derivation a derivation +/// \param version an hd version +/// \param account valid bip44 account +/// \note Returned object needs to be deleted with \TWStringDelete +/// \return Extended private key as a non-null TWString TW_EXPORT_METHOD -struct TWPrivateKey *_Nonnull TWHDWalletGetKey(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin, TWString *_Nonnull derivationPath); +TWString* _Nonnull TWHDWalletGetExtendedPrivateKeyAccount(struct TWHDWallet* _Nonnull wallet, enum TWPurpose purpose, enum TWCoinType coin, enum TWDerivation derivation, enum TWHDVersion version, uint32_t account); -/// Shortcut method to generate private key with the specified account/change/address (bip44 standard). Returned object needs to be deleted. +/// Returns the extended public key, for custom account. /// -/// @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki +/// \param wallet non-null TWHDWallet +/// \param purpose a purpose +/// \param coin a coin type +/// \param derivation a derivation +/// \param version an hd version +/// \param account valid bip44 account +/// \note Returned object needs to be deleted with \TWStringDelete +/// \return Extended public key as a non-null TWString TW_EXPORT_METHOD -struct TWPrivateKey *_Nonnull TWHDWalletGetDerivedKey(struct TWHDWallet *_Nonnull wallet, enum TWCoinType coin, uint32_t account, uint32_t change, uint32_t address); +TWString* _Nonnull TWHDWalletGetExtendedPublicKeyAccount(struct TWHDWallet* _Nonnull wallet, enum TWPurpose purpose, enum TWCoinType coin, enum TWDerivation derivation, enum TWHDVersion version, uint32_t account); -/// Returns the extended private key. +/// Returns the extended private key (for default 0 account with derivation). +/// +/// \param wallet non-null TWHDWallet +/// \param purpose a purpose +/// \param coin a coin type +/// \param derivation a derivation +/// \param version an hd version +/// \note Returned object needs to be deleted with \TWStringDelete +/// \return Extended private key as a non-null TWString TW_EXPORT_METHOD -TWString *_Nonnull TWHDWalletGetExtendedPrivateKey(struct TWHDWallet *_Nonnull wallet, enum TWPurpose purpose, enum TWCoinType coin, enum TWHDVersion version); +TWString* _Nonnull TWHDWalletGetExtendedPrivateKeyDerivation(struct TWHDWallet* _Nonnull wallet, enum TWPurpose purpose, enum TWCoinType coin, enum TWDerivation derivation, enum TWHDVersion version); -/// Returns the exteded public key. Returned object needs to be deleted. +/// Returns the extended public key (for default 0 account with derivation). +/// +/// \param wallet non-null TWHDWallet +/// \param purpose a purpose +/// \param coin a coin type +/// \param derivation a derivation +/// \param version an hd version +/// \note Returned object needs to be deleted with \TWStringDelete +/// \return Extended public key as a non-null TWString TW_EXPORT_METHOD -TWString *_Nonnull TWHDWalletGetExtendedPublicKey(struct TWHDWallet *_Nonnull wallet, enum TWPurpose purpose, enum TWCoinType coin, enum TWHDVersion version); +TWString* _Nonnull TWHDWalletGetExtendedPublicKeyDerivation(struct TWHDWallet* _Nonnull wallet, enum TWPurpose purpose, enum TWCoinType coin, enum TWDerivation derivation, enum TWHDVersion version); -/// Computes the public key from an exteded public key representation. Returned object needs to be deleted. +/// Computes the public key from an extended public key representation. +/// +/// \param extended extended public key +/// \param coin a coin type +/// \param derivationPath a derivation path +/// \note Returned object needs to be deleted with \TWPublicKeyDelete +/// \return Nullable TWPublic key TW_EXPORT_STATIC_METHOD -struct TWPublicKey *_Nullable TWHDWalletGetPublicKeyFromExtended(TWString *_Nonnull extended, enum TWCoinType coin, TWString *_Nonnull derivationPath); +struct TWPublicKey* _Nullable TWHDWalletGetPublicKeyFromExtended(TWString* _Nonnull extended, enum TWCoinType coin, TWString* _Nonnull derivationPath); TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWHash.h b/include/TrustWalletCore/TWHash.h index 280dc5f9b7f..ccdb7d0fb9e 100644 --- a/include/TrustWalletCore/TWHash.h +++ b/include/TrustWalletCore/TWHash.h @@ -11,6 +11,7 @@ TW_EXTERN_C_BEGIN +/// Hash functions TW_EXPORT_STRUCT struct TWHash { uint8_t unused; // C doesn't allow zero-sized struct @@ -22,63 +23,128 @@ static const size_t TWHashSHA512Length = 64; static const size_t TWHashRipemdLength = 20; /// Computes the SHA1 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed SHA1 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashSHA1(TWData *_Nonnull data); +/// Computes the SHA256 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed SHA256 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashSHA256(TWData *_Nonnull data); +/// Computes the SHA512 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed SHA512 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashSHA512(TWData *_Nonnull data); +/// Computes the SHA512_256 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed SHA512_256 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashSHA512_256(TWData *_Nonnull data); +/// Computes the Keccak256 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed Keccak256 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashKeccak256(TWData *_Nonnull data); +/// Computes the Keccak512 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed Keccak512 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashKeccak512(TWData *_Nonnull data); +/// Computes the SHA3_256 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed SHA3_256 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashSHA3_256(TWData *_Nonnull data); +/// Computes the SHA3_512 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed SHA3_512 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashSHA3_512(TWData *_Nonnull data); +/// Computes the RIPEMD of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed RIPEMD block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashRIPEMD(TWData *_Nonnull data); +/// Computes the Blake256 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed Blake256 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashBlake256(TWData *_Nonnull data); +/// Computes the Blake2b of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed Blake2b block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashBlake2b(TWData *_Nonnull data, size_t size); +/// Computes the Groestl512 of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed Groestl512 block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashGroestl512(TWData *_Nonnull data); -TW_EXPORT_STATIC_METHOD -TWData *_Nonnull TWHashXXHash64(TWData *_Nonnull data, uint64_t seed); - -TW_EXPORT_STATIC_METHOD -TWData *_Nonnull TWHashTwoXXHash64Concat(TWData *_Nonnull data); - +/// Computes the SHA256D of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed SHA256D block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashSHA256SHA256(TWData *_Nonnull data); +/// Computes the SHA256RIPEMD of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed SHA256RIPEMD block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashSHA256RIPEMD(TWData *_Nonnull data); +/// Computes the SHA3_256RIPEMD of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed SHA3_256RIPEMD block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashSHA3_256RIPEMD(TWData *_Nonnull data); +/// Computes the Blake256D of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed Blake256D block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashBlake256Blake256(TWData *_Nonnull data); +/// Computes the Blake256RIPEMD of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed Blake256RIPEMD block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashBlake256RIPEMD(TWData *_Nonnull data); +/// Computes the Groestl512D of a block of data. +/// +/// \param data Non-null block of data +/// \return Non-null computed Groestl512D block of data TW_EXPORT_STATIC_METHOD TWData *_Nonnull TWHashGroestl512Groestl512(TWData *_Nonnull data); diff --git a/include/TrustWalletCore/TWMnemonic.h b/include/TrustWalletCore/TWMnemonic.h index b8b60d46158..f74b6ad164f 100644 --- a/include/TrustWalletCore/TWMnemonic.h +++ b/include/TrustWalletCore/TWMnemonic.h @@ -11,18 +11,28 @@ TW_EXTERN_C_BEGIN +/// Mnemonic validate / lookup functions TW_EXPORT_STRUCT struct TWMnemonic; /// Determines whether a BIP39 English mnemonic phrase is valid. +/// +/// \param mnemonic Non-null BIP39 english mnemonic +/// \return true if the mnemonic is valid, false otherwise TW_EXPORT_STATIC_METHOD bool TWMnemonicIsValid(TWString *_Nonnull mnemonic); -/// Determines whether word is a valid BIP39 English menemonic word. +/// Determines whether word is a valid BIP39 English mnemonic word. +/// +/// \param word Non-null BIP39 English mnemonic word +/// \return true if the word is a valid BIP39 English mnemonic word, false otherwise TW_EXPORT_STATIC_METHOD bool TWMnemonicIsValidWord(TWString *_Nonnull word); /// Return BIP39 English words that match the given prefix. A single string is returned, with space-separated list of words. +/// +/// \param prefix Non-null string prefix +/// \return Single non-null string, space-separated list of words containing BIP39 words that match the given prefix. TW_EXPORT_STATIC_METHOD TWString* _Nonnull TWMnemonicSuggest(TWString *_Nonnull prefix); diff --git a/include/TrustWalletCore/TWNEARAccount.h b/include/TrustWalletCore/TWNEARAccount.h index 66b4c8ee3ec..73e7662fe34 100644 --- a/include/TrustWalletCore/TWNEARAccount.h +++ b/include/TrustWalletCore/TWNEARAccount.h @@ -15,13 +15,24 @@ TW_EXTERN_C_BEGIN TW_EXPORT_CLASS struct TWNEARAccount; +/// Create a NEAR Account +/// +/// \param string Account name +/// \note Account should be deleted by calling \TWNEARAccountDelete +/// \return Pointer to a nullable NEAR Account. TW_EXPORT_STATIC_METHOD struct TWNEARAccount *_Nullable TWNEARAccountCreateWithString(TWString *_Nonnull string); +/// Delete the given Near Account +/// +/// \param account Pointer to a non-null NEAR Account TW_EXPORT_METHOD void TWNEARAccountDelete(struct TWNEARAccount *_Nonnull account); /// Returns the user friendly string representation. +/// +/// \param account Pointer to a non-null NEAR Account +/// \return Non-null string account description TW_EXPORT_PROPERTY TWString *_Nonnull TWNEARAccountDescription(struct TWNEARAccount *_Nonnull account); diff --git a/include/TrustWalletCore/TWNervosAddress.h b/include/TrustWalletCore/TWNervosAddress.h new file mode 100644 index 00000000000..b12e663aec1 --- /dev/null +++ b/include/TrustWalletCore/TWNervosAddress.h @@ -0,0 +1,71 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWData.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// Represents a Nervos address. +TW_EXPORT_CLASS +struct TWNervosAddress; + +/// Compares two addresses for equality. +/// +/// \param lhs The first address to compare. +/// \param rhs The second address to compare. +/// \return bool indicating the addresses are equal. +TW_EXPORT_STATIC_METHOD +bool TWNervosAddressEqual(struct TWNervosAddress *_Nonnull lhs, struct TWNervosAddress *_Nonnull rhs); + +/// Determines if the string is a valid Nervos address. +/// +/// \param string string to validate. +/// \return bool indicating if the address is valid. +TW_EXPORT_STATIC_METHOD +bool TWNervosAddressIsValidString(TWString *_Nonnull string); + +/// Initializes an address from a sring representaion. +/// +/// \param string Bech32 string to initialize the address from. +/// \return TWNervosAddress pointer or nullptr if string is invalid. +TW_EXPORT_STATIC_METHOD +struct TWNervosAddress *_Nullable TWNervosAddressCreateWithString(TWString *_Nonnull string); + +/// Deletes a Nervos address. +/// +/// \param address Address to delete. +TW_EXPORT_METHOD +void TWNervosAddressDelete(struct TWNervosAddress *_Nonnull address); + +/// Returns the address string representation. +/// +/// \param address Address to get the string representation of. +TW_EXPORT_PROPERTY +TWString *_Nonnull TWNervosAddressDescription(struct TWNervosAddress *_Nonnull address); + +/// Returns the Code hash +/// +/// \param address Address to get the keyhash data of. +TW_EXPORT_PROPERTY +TWData *_Nonnull TWNervosAddressCodeHash(struct TWNervosAddress *_Nonnull address); + +/// Returns the address hash type +/// +/// \param address Address to get the hash type of. +TW_EXPORT_PROPERTY +TWString *_Nonnull TWNervosAddressHashType(struct TWNervosAddress *_Nonnull address); + +/// Returns the address args data. +/// +/// \param address Address to get the args data of. +TW_EXPORT_PROPERTY +TWData *_Nonnull TWNervosAddressArgs(struct TWNervosAddress *_Nonnull address); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWPBKDF2.h b/include/TrustWalletCore/TWPBKDF2.h new file mode 100644 index 00000000000..a7edcc65380 --- /dev/null +++ b/include/TrustWalletCore/TWPBKDF2.h @@ -0,0 +1,38 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWData.h" + +TW_EXTERN_C_BEGIN + +/// Password-Based Key Derivation Function 2 +TW_EXPORT_STRUCT +struct TWPBKDF2; + +/// Derives a key from a password and a salt using PBKDF2 + Sha256. +/// +/// \param password is the master password from which a derived key is generated +/// \param salt is a sequence of bits, known as a cryptographic salt +/// \param iterations is the number of iterations desired +/// \param dkLen is the desired bit-length of the derived key +/// \return the derived key data. +TW_EXPORT_STATIC_METHOD +TWData *_Nullable TWPBKDF2HmacSha256(TWData *_Nonnull password, TWData *_Nonnull salt, uint32_t iterations, uint32_t dkLen); + +/// Derives a key from a password and a salt using PBKDF2 + Sha512. +/// +/// \param password is the master password from which a derived key is generated +/// \param salt is a sequence of bits, known as a cryptographic salt +/// \param iterations is the number of iterations desired +/// \param dkLen is the desired bit-length of the derived key +/// \return the derived key data. +TW_EXPORT_STATIC_METHOD +TWData *_Nullable TWPBKDF2HmacSha512(TWData *_Nonnull password, TWData *_Nonnull salt, uint32_t iterations, uint32_t dkLen); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWPrivateKey.h b/include/TrustWalletCore/TWPrivateKey.h index 8400050e654..ee4cf3fc7e2 100644 --- a/include/TrustWalletCore/TWPrivateKey.h +++ b/include/TrustWalletCore/TWPrivateKey.h @@ -13,68 +13,132 @@ TW_EXTERN_C_BEGIN +/// Represents a private key. TW_EXPORT_CLASS struct TWPrivateKey; static const size_t TWPrivateKeySize = 32; +/// Create a random private key +/// +/// \note Should be deleted with \TWPrivateKeyDelete +/// \return Non-null Private key TW_EXPORT_STATIC_METHOD -struct TWPrivateKey *_Nonnull TWPrivateKeyCreate(void); +struct TWPrivateKey* _Nonnull TWPrivateKeyCreate(void); +/// Create a private key with the given block of data +/// +/// \param data a block of data +/// \note Should be deleted with \TWPrivateKeyDelete +/// \return Nullable pointer to Private Key TW_EXPORT_STATIC_METHOD -struct TWPrivateKey *_Nullable TWPrivateKeyCreateWithData(TWData *_Nonnull data); +struct TWPrivateKey* _Nullable TWPrivateKeyCreateWithData(TWData* _Nonnull data); +/// Deep copy a given private key +/// +/// \param key Non-null private key to be copied +/// \note Should be deleted with \TWPrivateKeyDelete +/// \return Deep copy, Nullable pointer to Private key TW_EXPORT_STATIC_METHOD -struct TWPrivateKey *_Nullable TWPrivateKeyCreateCopy(struct TWPrivateKey *_Nonnull key); +struct TWPrivateKey* _Nullable TWPrivateKeyCreateCopy(struct TWPrivateKey* _Nonnull key); +/// Delete the given private key +/// +/// \param pk Non-null pointer to private key TW_EXPORT_METHOD -void TWPrivateKeyDelete(struct TWPrivateKey *_Nonnull pk); +void TWPrivateKeyDelete(struct TWPrivateKey* _Nonnull pk); +/// Determines if the given private key is valid or not. +/// +/// \param data block of data (private key bytes) +/// \param curve Eliptic curve of the private key +/// \return true if the private key is valid, false otherwise TW_EXPORT_STATIC_METHOD -bool TWPrivateKeyIsValid(TWData *_Nonnull data, enum TWCurve curve); +bool TWPrivateKeyIsValid(TWData* _Nonnull data, enum TWCurve curve); +/// Convert the given private key to raw-bytes block of data +/// +/// \param pk Non-null pointer to the private key +/// \return Non-null block of data (raw bytes) of the given private key TW_EXPORT_PROPERTY -TWData *_Nonnull TWPrivateKeyData(struct TWPrivateKey *_Nonnull pk); +TWData* _Nonnull TWPrivateKeyData(struct TWPrivateKey* _Nonnull pk); -/// Returns the public key associated with this private key. +/// Returns the Secp256k1 public key associated with the given private key +/// +/// \param pk Non-null pointer to the private key +/// \param compressed if the given private key is compressed or not +/// \return Non-null pointer to the corresponding public key TW_EXPORT_METHOD -struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeySecp256k1(struct TWPrivateKey *_Nonnull pk, bool compressed); +struct TWPublicKey* _Nonnull TWPrivateKeyGetPublicKeySecp256k1(struct TWPrivateKey* _Nonnull pk, bool compressed); -/// Returns the public key associated with this private key. +/// Returns the Nist256p1 public key associated with the given private key +/// +/// \param pk Non-null pointer to the private key +/// \return Non-null pointer to the corresponding public key TW_EXPORT_METHOD -struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyNist256p1(struct TWPrivateKey *_Nonnull pk); +struct TWPublicKey* _Nonnull TWPrivateKeyGetPublicKeyNist256p1(struct TWPrivateKey* _Nonnull pk); -/// Returns the public key associated with this private key. +/// Returns the Ed25519 public key associated with the given private key +/// +/// \param pk Non-null pointer to the private key +/// \return Non-null pointer to the corresponding public key TW_EXPORT_METHOD -struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyEd25519(struct TWPrivateKey *_Nonnull pk); +struct TWPublicKey* _Nonnull TWPrivateKeyGetPublicKeyEd25519(struct TWPrivateKey* _Nonnull pk); -/// Returns the public key associated with this private key. +/// Returns the Ed25519Blake2b public key associated with the given private key +/// +/// \param pk Non-null pointer to the private key +/// \return Non-null pointer to the corresponding public key TW_EXPORT_METHOD -struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyEd25519Blake2b(struct TWPrivateKey *_Nonnull pk); +struct TWPublicKey* _Nonnull TWPrivateKeyGetPublicKeyEd25519Blake2b(struct TWPrivateKey* _Nonnull pk); -/// Returns the public key associated with this private key. +/// Returns the Ed25519Cardano public key associated with the given private key +/// +/// \param pk Non-null pointer to the private key +/// \return Non-null pointer to the corresponding public key TW_EXPORT_METHOD -struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyEd25519Extended(struct TWPrivateKey *_Nonnull pk); +struct TWPublicKey* _Nonnull TWPrivateKeyGetPublicKeyEd25519Cardano(struct TWPrivateKey* _Nonnull pk); -/// Returns the public key associated with this private key. +/// Returns the Curve25519 public key associated with the given private key +/// +/// \param pk Non-null pointer to the private key +/// \return Non-null pointer to the corresponding public key TW_EXPORT_METHOD -struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyCurve25519(struct TWPrivateKey *_Nonnull pk); +struct TWPublicKey* _Nonnull TWPrivateKeyGetPublicKeyCurve25519(struct TWPrivateKey* _Nonnull pk); /// Computes an EC Diffie-Hellman secret in constant time /// Supported curves: secp256k1 +/// +/// \param pk Non-null pointer to a Private key +/// \param publicKey Non-null pointer to the corresponding public key +/// \param curve Eliptic curve +/// \return The corresponding shared key as a non-null block of data TW_EXPORT_METHOD -TWData *_Nullable TWPrivateKeyGetSharedKey(const struct TWPrivateKey *_Nonnull pk, const struct TWPublicKey *_Nonnull publicKey, enum TWCurve curve); +TWData* _Nullable TWPrivateKeyGetSharedKey(const struct TWPrivateKey* _Nonnull pk, const struct TWPublicKey* _Nonnull publicKey, enum TWCurve curve); /// Signs a digest using ECDSA and given curve. +/// +/// \param pk Non-null pointer to a Private key +/// \param digest Non-null digest block of data +/// \param curve Eliptic curve +/// \return Signature as a Non-null block of data TW_EXPORT_METHOD -TWData *_Nullable TWPrivateKeySign(struct TWPrivateKey *_Nonnull pk, TWData *_Nonnull digest, enum TWCurve curve); +TWData* _Nullable TWPrivateKeySign(struct TWPrivateKey* _Nonnull pk, TWData* _Nonnull digest, enum TWCurve curve); -/// Signs a digest using ECDSA and given curve. The result is encoded with DER. +/// Signs a digest using ECDSA. The result is encoded with DER. +/// +/// \param pk Non-null pointer to a Private key +/// \param digest Non-null digest block of data +/// \return Signature as a Non-null block of data TW_EXPORT_METHOD -TWData *_Nullable TWPrivateKeySignAsDER(struct TWPrivateKey *_Nonnull pk, TWData *_Nonnull digest, enum TWCurve curve); +TWData* _Nullable TWPrivateKeySignAsDER(struct TWPrivateKey* _Nonnull pk, TWData* _Nonnull digest); -/// Signs a digest using ECDSA and given curve, returns schnoor signature. +/// Signs a digest using ECDSA and Zilliqa schnorr signature scheme. +/// +/// \param pk Non-null pointer to a Private key +/// \param message Non-null message +/// \return Signature as a Non-null block of data TW_EXPORT_METHOD -TWData *_Nullable TWPrivateKeySignSchnorr(struct TWPrivateKey *_Nonnull pk, TWData *_Nonnull message, enum TWCurve curve); +TWData* _Nullable TWPrivateKeySignZilliqaSchnorr(struct TWPrivateKey* _Nonnull pk, TWData* _Nonnull message); TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWPrivateKeyType.h b/include/TrustWalletCore/TWPrivateKeyType.h new file mode 100644 index 00000000000..868b5d2dfea --- /dev/null +++ b/include/TrustWalletCore/TWPrivateKeyType.h @@ -0,0 +1,20 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" + +TW_EXTERN_C_BEGIN + +/// Private key types, the vast majority of chains use the default, 32-byte key. +TW_EXPORT_ENUM(uint32_t) +enum TWPrivateKeyType { + TWPrivateKeyTypeDefault = 0, // 32 bytes long + TWPrivateKeyTypeCardano = 1, // 2 extended keys plus chainCode, 96 bytes long, used by Cardano +}; + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWPublicKey.h b/include/TrustWalletCore/TWPublicKey.h index 220672d2a1e..d9164855382 100644 --- a/include/TrustWalletCore/TWPublicKey.h +++ b/include/TrustWalletCore/TWPublicKey.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -16,42 +16,108 @@ TW_EXTERN_C_BEGIN static const size_t TWPublicKeyCompressedSize = 33; static const size_t TWPublicKeyUncompressedSize = 65; +/// Represents a public key. TW_EXPORT_CLASS struct TWPublicKey; +/// Create a public key from a block of data +/// +/// \param data Non-null block of data representing the public key +/// \param type type of the public key +/// \note Should be deleted with \TWPublicKeyDelete +/// \return Nullable pointer to the public key TW_EXPORT_STATIC_METHOD struct TWPublicKey *_Nullable TWPublicKeyCreateWithData(TWData *_Nonnull data, enum TWPublicKeyType type); +/// Delete the given public key +/// +/// \param pk Non-null pointer to a public key TW_EXPORT_METHOD void TWPublicKeyDelete(struct TWPublicKey *_Nonnull pk); +/// Determines if the given public key is valid or not +/// +/// \param data Non-null block of data representing the public key +/// \param type type of the public key +/// \return true if the block of data is a valid public key, false otherwise TW_EXPORT_STATIC_METHOD bool TWPublicKeyIsValid(TWData *_Nonnull data, enum TWPublicKeyType type); +/// Determines if the given public key is compressed or not +/// +/// \param pk Non-null pointer to a public key +/// \return true if the public key is compressed, false otherwise TW_EXPORT_PROPERTY bool TWPublicKeyIsCompressed(struct TWPublicKey *_Nonnull pk); +/// Give the compressed public key of the given non-compressed public key +/// +/// \param from Non-null pointer to a non-compressed public key +/// \return Non-null pointer to the corresponding compressed public-key TW_EXPORT_PROPERTY struct TWPublicKey *_Nonnull TWPublicKeyCompressed(struct TWPublicKey *_Nonnull from); +/// Give the non-compressed public key of a corresponding compressed public key +/// +/// \param from Non-null pointer to the corresponding compressed public key +/// \return Non-null pointer to the corresponding non-compressed public key TW_EXPORT_PROPERTY struct TWPublicKey *_Nonnull TWPublicKeyUncompressed(struct TWPublicKey *_Nonnull from); +/// Gives the raw data of a given public-key +/// +/// \param pk Non-null pointer to a public key +/// \return Non-null pointer to the raw block of data of the given public key TW_EXPORT_PROPERTY TWData *_Nonnull TWPublicKeyData(struct TWPublicKey *_Nonnull pk); +/// Verify the validity of a signature and a message using the given public key +/// +/// \param pk Non-null pointer to a public key +/// \param signature Non-null pointer to a block of data corresponding to the signature +/// \param message Non-null pointer to a block of data corresponding to the message +/// \return true if the signature and the message belongs to the given public key, false otherwise TW_EXPORT_METHOD bool TWPublicKeyVerify(struct TWPublicKey *_Nonnull pk, TWData *_Nonnull signature, TWData *_Nonnull message); +/// Verify the validity as DER of a signature and a message using the given public key +/// +/// \param pk Non-null pointer to a public key +/// \param signature Non-null pointer to a block of data corresponding to the signature +/// \param message Non-null pointer to a block of data corresponding to the message +/// \return true if the signature and the message belongs to the given public key, false otherwise TW_EXPORT_METHOD -bool TWPublicKeyVerifySchnorr(struct TWPublicKey *_Nonnull pk, TWData *_Nonnull signature, TWData *_Nonnull message); +bool TWPublicKeyVerifyAsDER(struct TWPublicKey *_Nonnull pk, TWData *_Nonnull signature, TWData *_Nonnull message); + +/// Verify a Zilliqa schnorr signature with a signature and message. +/// +/// \param pk Non-null pointer to a public key +/// \param signature Non-null pointer to a block of data corresponding to the signature +/// \param message Non-null pointer to a block of data corresponding to the message +/// \return true if the signature and the message belongs to the given public key, false otherwise +TW_EXPORT_METHOD +bool TWPublicKeyVerifyZilliqaSchnorr(struct TWPublicKey *_Nonnull pk, TWData *_Nonnull signature, TWData *_Nonnull message); +/// Give the public key type (eliptic) of a given public key +/// +/// \param publicKey Non-null pointer to a public key +/// \return The public key type of the given public key (eliptic) TW_EXPORT_PROPERTY enum TWPublicKeyType TWPublicKeyKeyType(struct TWPublicKey *_Nonnull publicKey); +/// Get the public key description from a given public key +/// +/// \param publicKey Non-null pointer to a public key +/// \return Non-null pointer to a string representing the description of the public key TW_EXPORT_PROPERTY TWString *_Nonnull TWPublicKeyDescription(struct TWPublicKey *_Nonnull publicKey); +/// Try to get a public key from a given signature and a message +/// +/// \param signature Non-null pointer to a block of data corresponding to the signature +/// \param message Non-null pointer to a block of data corresponding to the message +/// \return Null pointer if the public key can't be recover from the given signature and message, +/// pointer to the public key otherwise TW_EXPORT_STATIC_METHOD struct TWPublicKey *_Nullable TWPublicKeyRecover(TWData *_Nonnull signature, TWData *_Nonnull message); diff --git a/include/TrustWalletCore/TWPublicKeyType.h b/include/TrustWalletCore/TWPublicKeyType.h index e16727754df..e9d0e53b347 100644 --- a/include/TrustWalletCore/TWPublicKeyType.h +++ b/include/TrustWalletCore/TWPublicKeyType.h @@ -20,7 +20,7 @@ enum TWPublicKeyType { TWPublicKeyTypeED25519 = 4, TWPublicKeyTypeED25519Blake2b = 5, TWPublicKeyTypeCURVE25519 = 6, - TWPublicKeyTypeED25519Extended = 7, + TWPublicKeyTypeED25519Cardano = 7, }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWPurpose.h b/include/TrustWalletCore/TWPurpose.h index ad4d9206cc7..509b813bbec 100644 --- a/include/TrustWalletCore/TWPurpose.h +++ b/include/TrustWalletCore/TWPurpose.h @@ -12,9 +12,9 @@ TW_EXTERN_C_BEGIN /// HD wallet purpose /// -/// See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki -/// See https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki -/// See https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki +/// \see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki +/// \see https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki +/// \see https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki TW_EXPORT_ENUM(uint32_t) enum TWPurpose { TWPurposeBIP44 = 44, diff --git a/include/TrustWalletCore/TWRippleXAddress.h b/include/TrustWalletCore/TWRippleXAddress.h index 8b41daf1a82..401b885e554 100644 --- a/include/TrustWalletCore/TWRippleXAddress.h +++ b/include/TrustWalletCore/TWRippleXAddress.h @@ -20,29 +20,54 @@ TW_EXPORT_CLASS struct TWRippleXAddress; /// Compares two addresses for equality. +/// +/// \param lhs left non-null pointer to a Ripple Address +/// \param rhs right non-null pointer to a Ripple Address +/// \return true if both address are equal, false otherwise TW_EXPORT_STATIC_METHOD bool TWRippleXAddressEqual(struct TWRippleXAddress *_Nonnull lhs, struct TWRippleXAddress *_Nonnull rhs); /// Determines if the string is a valid Ripple address. +/// +/// \param string Non-null pointer to a string that represent the Ripple Address to be checked +/// \return true if the given address is a valid Ripple address, false otherwise TW_EXPORT_STATIC_METHOD bool TWRippleXAddressIsValidString(TWString *_Nonnull string); -/// Creates an address from a string representaion. +/// Creates an address from a string representation. +/// +/// \param string Non-null pointer to a string that should be a valid ripple address +/// \note Should be deleted with \TWRippleXAddressDelete +/// \return Null pointer if the given string is an invalid ripple address, pointer to a Ripple address otherwise TW_EXPORT_STATIC_METHOD struct TWRippleXAddress *_Nullable TWRippleXAddressCreateWithString(TWString *_Nonnull string); /// Creates an address from a public key and destination tag. +/// +/// \param publicKey Non-null pointer to a public key +/// \param tag valid ripple destination tag (1-10) +/// \note Should be deleted with \TWRippleXAddressDelete +/// \return Non-null pointer to a Ripple Address TW_EXPORT_STATIC_METHOD struct TWRippleXAddress *_Nonnull TWRippleXAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey, uint32_t tag); +/// Delete the given ripple address +/// +/// \param address Non-null pointer to a Ripple Address TW_EXPORT_METHOD void TWRippleXAddressDelete(struct TWRippleXAddress *_Nonnull address); /// Returns the address string representation. +/// +/// \param address Non-null pointer to a Ripple Address +/// \return Non-null pointer to the ripple address string representation TW_EXPORT_PROPERTY TWString *_Nonnull TWRippleXAddressDescription(struct TWRippleXAddress *_Nonnull address); /// Returns the destination tag. +/// +/// \param address Non-null pointer to a Ripple Address +/// \return The destination tag of the given Ripple Address (1-10) TW_EXPORT_PROPERTY uint32_t TWRippleXAddressTag(struct TWRippleXAddress *_Nonnull address); diff --git a/include/TrustWalletCore/TWSS58AddressType.h b/include/TrustWalletCore/TWSS58AddressType.h index 13ca9769e39..b6bf7e50927 100644 --- a/include/TrustWalletCore/TWSS58AddressType.h +++ b/include/TrustWalletCore/TWSS58AddressType.h @@ -1,5 +1,5 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,9 +11,9 @@ TW_EXTERN_C_BEGIN -/// Substrate based chains Address Type +/// Substrate based chains Address Type /// -/// - See Also: https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58)#address-type +/// \see https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58)#address-type TW_EXPORT_ENUM(uint8_t) enum TWSS58AddressType { TWSS58AddressTypePolkadot = 0, diff --git a/include/TrustWalletCore/TWSegwitAddress.h b/include/TrustWalletCore/TWSegwitAddress.h index 57fbd3fed5a..3e106375527 100644 --- a/include/TrustWalletCore/TWSegwitAddress.h +++ b/include/TrustWalletCore/TWSegwitAddress.h @@ -20,33 +20,69 @@ TW_EXPORT_CLASS struct TWSegwitAddress; /// Compares two addresses for equality. +/// +/// \param lhs left non-null pointer to a Bech32 Address +/// \param rhs right non-null pointer to a Bech32 Address +/// \return true if both address are equal, false otherwise TW_EXPORT_STATIC_METHOD bool TWSegwitAddressEqual(struct TWSegwitAddress *_Nonnull lhs, struct TWSegwitAddress *_Nonnull rhs); /// Determines if the string is a valid Bech32 address. +/// +/// \param string Non-null pointer to a Bech32 address as a string +/// \return true if the string is a valid Bech32 address, false otherwise. TW_EXPORT_STATIC_METHOD bool TWSegwitAddressIsValidString(TWString *_Nonnull string); -/// Creates an address from a string representaion. +/// Creates an address from a string representation. +/// +/// \param string Non-null pointer to a Bech32 address as a string +/// \note should be deleted with \TWSegwitAddressDelete +/// \return Pointer to a Bech32 address if the string is a valid Bech32 address, null pointer otherwise TW_EXPORT_STATIC_METHOD struct TWSegwitAddress *_Nullable TWSegwitAddressCreateWithString(TWString *_Nonnull string); -/// Creates an address from a public key. +/// Creates a segwit-version-0 address from a public key and HRP prefix. +/// Taproot (v>=1) is not supported by this method. +/// +/// \param hrp HRP of the utxo coin targeted +/// \param publicKey Non-null pointer to the public key of the targeted coin +/// \note should be deleted with \TWSegwitAddressDelete +/// \return Non-null pointer to the corresponding Segwit address TW_EXPORT_STATIC_METHOD struct TWSegwitAddress *_Nonnull TWSegwitAddressCreateWithPublicKey(enum TWHRP hrp, struct TWPublicKey *_Nonnull publicKey); +/// Delete the given Segwit address +/// +/// \param address Non-null pointer to a Segwit address TW_EXPORT_METHOD void TWSegwitAddressDelete(struct TWSegwitAddress *_Nonnull address); /// Returns the address string representation. +/// +/// \param address Non-null pointer to a Segwit Address +/// \return Non-null pointer to the segwit address string representation TW_EXPORT_PROPERTY TWString *_Nonnull TWSegwitAddressDescription(struct TWSegwitAddress *_Nonnull address); /// Returns the human-readable part. +/// +/// \param address Non-null pointer to a Segwit Address +/// \return the HRP part of the given address TW_EXPORT_PROPERTY enum TWHRP TWSegwitAddressHRP(struct TWSegwitAddress *_Nonnull address); +/// Returns the human-readable part. +/// +/// \param address Non-null pointer to a Segwit Address +/// \return returns the witness version of the given segwit address +TW_EXPORT_PROPERTY +int TWSegwitAddressWitnessVersion(struct TWSegwitAddress *_Nonnull address); + /// Returns the witness program +/// +/// \param address Non-null pointer to a Segwit Address +/// \return returns the witness data of the given segwit address as a non-null pointer block of data TW_EXPORT_PROPERTY TWData *_Nonnull TWSegwitAddressWitnessProgram(struct TWSegwitAddress *_Nonnull address); diff --git a/include/TrustWalletCore/TWSolanaAddress.h b/include/TrustWalletCore/TWSolanaAddress.h index 76f7fe863e7..b8a372155e6 100644 --- a/include/TrustWalletCore/TWSolanaAddress.h +++ b/include/TrustWalletCore/TWSolanaAddress.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,21 +11,36 @@ TW_EXTERN_C_BEGIN +/// Solana address helper functions TW_EXPORT_CLASS struct TWSolanaAddress; -/// Creates an address from a string representaion. +/// Creates an address from a string representation. +/// +/// \param string Non-null pointer to a solana address string +/// \note Should be deleted with \TWSolanaAddressDelete +/// \return Non-null pointer to a Solana address data structure TW_EXPORT_STATIC_METHOD struct TWSolanaAddress* _Nullable TWSolanaAddressCreateWithString(TWString* _Nonnull string); +/// Delete the given Solana address +/// +/// \param address Non-null pointer to a Solana Address TW_EXPORT_METHOD void TWSolanaAddressDelete(struct TWSolanaAddress* _Nonnull address); /// Derive default token address for token +/// +/// \param address Non-null pointer to a Solana Address +/// \param tokenMintAddress Non-null pointer to a token mint address as a string +/// \return Null pointer if the Default token address for a token is not found, valid pointer otherwise TW_EXPORT_METHOD TWString* _Nullable TWSolanaAddressDefaultTokenAddress(struct TWSolanaAddress* _Nonnull address, TWString* _Nonnull tokenMintAddress); /// Returns the address string representation. +/// +/// \param address Non-null pointer to a Solana Address +/// \return Non-null pointer to the Solana address string representation TW_EXPORT_PROPERTY TWString *_Nonnull TWSolanaAddressDescription(struct TWSolanaAddress *_Nonnull address); diff --git a/include/TrustWalletCore/TWStellarMemoType.h b/include/TrustWalletCore/TWStellarMemoType.h index 45f9c629206..ef3f42702e4 100644 --- a/include/TrustWalletCore/TWStellarMemoType.h +++ b/include/TrustWalletCore/TWStellarMemoType.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,6 +10,7 @@ TW_EXTERN_C_BEGIN +/// Stellar memo type. TW_EXPORT_ENUM(uint32_t) enum TWStellarMemoType { TWStellarMemoTypeNone = 0, diff --git a/include/TrustWalletCore/TWStellarPassphrase.h b/include/TrustWalletCore/TWStellarPassphrase.h index 630adf3ef83..4854a4d3977 100644 --- a/include/TrustWalletCore/TWStellarPassphrase.h +++ b/include/TrustWalletCore/TWStellarPassphrase.h @@ -10,6 +10,7 @@ TW_EXTERN_C_BEGIN +/// Stellar network passphrase string. TW_EXPORT_ENUM() enum TWStellarPassphrase { TWStellarPassphraseStellar /* "Public Global Stellar Network ; September 2015" */, diff --git a/include/TrustWalletCore/TWStellarVersionByte.h b/include/TrustWalletCore/TWStellarVersionByte.h index b694c80b6bb..4439d1a555a 100644 --- a/include/TrustWalletCore/TWStellarVersionByte.h +++ b/include/TrustWalletCore/TWStellarVersionByte.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,11 +10,12 @@ TW_EXTERN_C_BEGIN +/// Stellar address version byte. TW_EXPORT_ENUM(uint16_t) enum TWStellarVersionByte { - TWStellarVersionByteAccountID = 0x30, // G - TWStellarVersionByteSeed = 0xc0, // S - TWStellarVersionBytePreAuthTX = 0xc8, // T + TWStellarVersionByteAccountID = 0x30, // G + TWStellarVersionByteSeed = 0xc0, // S + TWStellarVersionBytePreAuthTX = 0xc8, // T TWStellarVersionByteSHA256Hash = 0x118, // X }; diff --git a/include/TrustWalletCore/TWStoredKey.h b/include/TrustWalletCore/TWStoredKey.h index c5b6484d3cb..02bd0ae4dad 100644 --- a/include/TrustWalletCore/TWStoredKey.h +++ b/include/TrustWalletCore/TWStoredKey.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,8 +9,10 @@ #include "TWBase.h" #include "TWCoinType.h" #include "TWData.h" +#include "TWDerivation.h" #include "TWHDWallet.h" #include "TWPrivateKey.h" +#include "TWStoredKeyEncryptionLevel.h" #include "TWString.h" TW_EXTERN_C_BEGIN @@ -19,90 +21,240 @@ TW_EXTERN_C_BEGIN TW_EXPORT_CLASS struct TWStoredKey; -/// Loads a key from a file. Returned object needs to be deleted. +/// Loads a key from a file. +/// +/// \param path filepath to the key as a non-null string +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return Nullptr if the key can't be load, the stored key otherwise TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nullable TWStoredKeyLoad(TWString* _Nonnull path); -/// Imports a private key. Returned object needs to be deleted. +/// Imports a private key. +/// +/// \param privateKey Non-null Block of data private key +/// \param name The name of the stored key to import as a non-null string +/// \param password Non-null block of data, password of the stored key +/// \param coin the coin type +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return Nullptr if the key can't be imported, the stored key otherwise TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin); -/// Imports an HD wallet. Returned object needs to be deleted. +/// Imports an HD wallet. +/// +/// \param mnemonic Non-null bip39 mnemonic +/// \param name The name of the stored key to import as a non-null string +/// \param password Non-null block of data, password of the stored key +/// \param coin the coin type +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return Nullptr if the key can't be imported, the stored key otherwise TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nullable TWStoredKeyImportHDWallet(TWString* _Nonnull mnemonic, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin); -/// Imports a key from JSON. Returned object needs to be deleted. +/// Imports a key from JSON. +/// +/// \param json Json stored key import format as a non-null block of data +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return Nullptr if the key can't be imported, the stored key otherwise TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nullable TWStoredKeyImportJSON(TWData* _Nonnull json); -/// Creates a new key. Returned object needs to be deleted. +/// Creates a new key, with given encryption strength level. Returned object needs to be deleted. +/// +/// \param name The name of the key to be stored +/// \param password Non-null block of data, password of the stored key +/// \param encryptionLevel The level of encryption, see \TWStoredKeyEncryptionLevel +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return The stored key as a non-null pointer TW_EXPORT_STATIC_METHOD -struct TWStoredKey* _Nonnull TWStoredKeyCreate(TWString* _Nonnull name, TWData* _Nonnull password); +struct TWStoredKey* _Nonnull TWStoredKeyCreateLevel(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryptionLevel encryptionLevel); +/// Creates a new key. +/// +/// \deprecated use TWStoredKeyCreateLevel. +/// \param name The name of the key to be stored +/// \param password Non-null block of data, password of the stored key +/// \note Returned object needs to be deleted with \TWStoredKeyDelete +/// \return The stored key as a non-null pointer +TW_EXPORT_STATIC_METHOD struct TWStoredKey* _Nonnull TWStoredKeyCreate(TWString* _Nonnull name, TWData* _Nonnull password); + +/// Delete a stored key +/// +/// \param key The key to be deleted TW_EXPORT_METHOD void TWStoredKeyDelete(struct TWStoredKey* _Nonnull key); -/// Stored key uniqie identifier. Returned object needs to be deleted. +/// Stored key unique identifier. +/// +/// \param key Non-null pointer to a stored key +/// \note Returned object needs to be deleted with \TWStringDelete +/// \return The stored key unique identifier if it's found, null pointer otherwise. TW_EXPORT_PROPERTY TWString* _Nullable TWStoredKeyIdentifier(struct TWStoredKey* _Nonnull key); -/// Stored key namer. Returned object needs to be deleted. +/// Stored key namer. +/// +/// \param key Non-null pointer to a stored key +/// \note Returned object needs to be deleted with \TWStringDelete +/// \return The stored key name as a non-null string pointer. TW_EXPORT_PROPERTY TWString* _Nonnull TWStoredKeyName(struct TWStoredKey* _Nonnull key); /// Whether this key is a mnemonic phrase for a HD wallet. +/// +/// \param key Non-null pointer to a stored key +/// \return true if the given stored key is a mnemonic, false otherwise TW_EXPORT_PROPERTY bool TWStoredKeyIsMnemonic(struct TWStoredKey* _Nonnull key); /// The number of accounts. +/// +/// \param key Non-null pointer to a stored key +/// \return the number of accounts associated to the given stored key TW_EXPORT_PROPERTY size_t TWStoredKeyAccountCount(struct TWStoredKey* _Nonnull key); -/// Returns the account at a given index. Returned object needs to be deleted. +/// Returns the account at a given index. +/// +/// \param key Non-null pointer to a stored key +/// \param index the account index to be retrieved +/// \note Returned object needs to be deleted with \TWAccountDelete +/// \return Null pointer if the associated account is not found, pointer to the account otherwise. TW_EXPORT_METHOD struct TWAccount* _Nullable TWStoredKeyAccount(struct TWStoredKey* _Nonnull key, size_t index); -/// Returns the account for a specific coin, creating it if necessary. Returned object needs to be deleted. +/// Returns the account for a specific coin, creating it if necessary. +/// +/// \param key Non-null pointer to a stored key +/// \param coin The coin type +/// \param wallet The associated HD wallet, can be null. +/// \note Returned object needs to be deleted with \TWAccountDelete +/// \return Null pointer if the associated account is not found/not created, pointer to the account otherwise. TW_EXPORT_METHOD struct TWAccount* _Nullable TWStoredKeyAccountForCoin(struct TWStoredKey* _Nonnull key, enum TWCoinType coin, struct TWHDWallet* _Nullable wallet); +/// Returns the account for a specific coin + derivation, creating it if necessary. +/// +/// \param key Non-null pointer to a stored key +/// \param coin The coin type +/// \param derivation The derivation for the given coin +/// \param wallet the associated HD wallet, can be null. +/// \note Returned object needs to be deleted with \TWAccountDelete +/// \return Null pointer if the associated account is not found/not created, pointer to the account otherwise. +TW_EXPORT_METHOD +struct TWAccount* _Nullable TWStoredKeyAccountForCoinDerivation(struct TWStoredKey* _Nonnull key, enum TWCoinType coin, enum TWDerivation derivation, struct TWHDWallet* _Nullable wallet); + +/// Adds a new account, using given derivation (usually TWDerivationDefault) +/// and derivation path (usually matches path from derivation, but custom possible). +/// +/// \param key Non-null pointer to a stored key +/// \param address Non-null pointer to the address of the coin for this account +/// \param coin coin type +/// \param derivation derivation of the given coin type +/// \param derivationPath HD bip44 derivation path of the given coin +/// \param publicKey Non-null public key of the given coin/address +/// \param extendedPublicKey Non-null extended public key of the given coin/address +TW_EXPORT_METHOD +void TWStoredKeyAddAccountDerivation(struct TWStoredKey* _Nonnull key, TWString* _Nonnull address, enum TWCoinType coin, enum TWDerivation derivation, TWString* _Nonnull derivationPath, TWString* _Nonnull publicKey, TWString* _Nonnull extendedPublicKey); + +/// Adds a new account, using given derivation path. +/// +/// \deprecated Use TWStoredKeyAddAccountDerivation (with TWDerivationDefault) instead. +/// \param key Non-null pointer to a stored key +/// \param address Non-null pointer to the address of the coin for this account +/// \param coin coin type +/// \param derivationPath HD bip44 derivation path of the given coin +/// \param publicKey Non-null public key of the given coin/address +/// \param extendedPublicKey Non-null extended public key of the given coin/address +TW_EXPORT_METHOD +void TWStoredKeyAddAccount(struct TWStoredKey* _Nonnull key, TWString* _Nonnull address, enum TWCoinType coin, TWString* _Nonnull derivationPath, TWString* _Nonnull publicKey, TWString* _Nonnull extendedPublicKey); + /// Remove the account for a specific coin +/// +/// \param key Non-null pointer to a stored key +/// \param coin Account coin type to be removed TW_EXPORT_METHOD void TWStoredKeyRemoveAccountForCoin(struct TWStoredKey* _Nonnull key, enum TWCoinType coin); -/// Adds a new account. +/// Remove the account for a specific coin with the given derivation. +/// +/// \param key Non-null pointer to a stored key +/// \param coin Account coin type to be removed +/// \param derivation The derivation of the given coin type +TW_EXPORT_METHOD +void TWStoredKeyRemoveAccountForCoinDerivation(struct TWStoredKey* _Nonnull key, enum TWCoinType coin, enum TWDerivation derivation); + +/// Remove the account for a specific coin with the given derivation path. +/// +/// \param key Non-null pointer to a stored key +/// \param coin Account coin type to be removed +/// \param derivationPath The derivation path (bip44) of the given coin type TW_EXPORT_METHOD -void TWStoredKeyAddAccount(struct TWStoredKey* _Nonnull key, TWString* _Nonnull address, enum TWCoinType coin, TWString* _Nonnull derivationPath, TWString* _Nonnull extetndedPublicKey); +void TWStoredKeyRemoveAccountForCoinDerivationPath(struct TWStoredKey* _Nonnull key, enum TWCoinType coin, TWString* _Nonnull derivationPath); /// Saves the key to a file. +/// +/// \param key Non-null pointer to a stored key +/// \param path Non-null string filepath where the key will be saved +/// \return true if the key was successfully stored in the given filepath file, false otherwise TW_EXPORT_METHOD bool TWStoredKeyStore(struct TWStoredKey* _Nonnull key, TWString* _Nonnull path); /// Decrypts the private key. +/// +/// \param key Non-null pointer to a stored key +/// \param password Non-null block of data, password of the stored key +/// \return Decrypted private key as a block of data if success, null pointer otherwise TW_EXPORT_METHOD TWData* _Nullable TWStoredKeyDecryptPrivateKey(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password); /// Decrypts the mnemonic phrase. +/// +/// \param key Non-null pointer to a stored key +/// \param password Non-null block of data, password of the stored key +/// \return Bip39 decrypted mnemonic if success, null pointer otherwise TW_EXPORT_METHOD TWString* _Nullable TWStoredKeyDecryptMnemonic(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password); /// Returns the private key for a specific coin. Returned object needs to be deleted. +/// +/// \param key Non-null pointer to a stored key +/// \param coin Account coin type to be queried +/// \note Returned object needs to be deleted with \TWPrivateKeyDelete +/// \return Null pointer on failure, pointer to the private key otherwise TW_EXPORT_METHOD struct TWPrivateKey* _Nullable TWStoredKeyPrivateKey(struct TWStoredKey* _Nonnull key, enum TWCoinType coin, TWData* _Nonnull password); -/// Dercrypts and returns the HD Wallet for mnemonic phrase keys. Returned object needs to be deleted. +/// Decrypts and returns the HD Wallet for mnemonic phrase keys. Returned object needs to be deleted. +/// +/// \param key Non-null pointer to a stored key +/// \param password Non-null block of data, password of the stored key +/// \note Returned object needs to be deleted with \TWHDWalletDelete +/// \return Null pointer on failure, pointer to the HDWallet otherwise TW_EXPORT_METHOD struct TWHDWallet* _Nullable TWStoredKeyWallet(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password); /// Exports the key as JSON +/// +/// \param key Non-null pointer to a stored key +/// \return Null pointer on failure, pointer to a block of data containing the json otherwise TW_EXPORT_METHOD TWData* _Nullable TWStoredKeyExportJSON(struct TWStoredKey* _Nonnull key); /// Fills in empty and invalid addresses. -/// /// This method needs the encryption password to re-derive addresses from private keys. -/// @returns `false` if the password is incorrect. +/// +/// \param key Non-null pointer to a stored key +/// \param password Non-null block of data, password of the stored key +/// \return `false` if the password is incorrect, true otherwise. TW_EXPORT_METHOD bool TWStoredKeyFixAddresses(struct TWStoredKey* _Nonnull key, TWData* _Nonnull password); +/// Retrieve stored key encoding parameters, as JSON string. +/// +/// \param key Non-null pointer to a stored key +/// \return Null pointer on failure, encoding parameter as a json string otherwise. +TW_EXPORT_PROPERTY +TWString* _Nullable TWStoredKeyEncryptionParameters(struct TWStoredKey* _Nonnull key); + TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWStoredKeyEncryptionLevel.h b/include/TrustWalletCore/TWStoredKeyEncryptionLevel.h new file mode 100644 index 00000000000..6b0b79476ea --- /dev/null +++ b/include/TrustWalletCore/TWStoredKeyEncryptionLevel.h @@ -0,0 +1,26 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" + +TW_EXTERN_C_BEGIN + +/// Preset encryption parameter with different security strength, for key store +TW_EXPORT_ENUM(uint32_t) +enum TWStoredKeyEncryptionLevel { + /// Default, which is one of the below values, determined by the implementation. + TWStoredKeyEncryptionLevelDefault = 0, + /// Minimal sufficient level of encryption strength (scrypt 4096) + TWStoredKeyEncryptionLevelMinimal = 1, + /// Weak encryption strength (scrypt 16k) + TWStoredKeyEncryptionLevelWeak = 2, + /// Standard level of encryption strength (scrypt 262k) + TWStoredKeyEncryptionLevelStandard = 3, +}; + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWString.h b/include/TrustWalletCore/TWString.h index 64c2eb5eafc..4ad92566126 100644 --- a/include/TrustWalletCore/TWString.h +++ b/include/TrustWalletCore/TWString.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -14,41 +14,60 @@ typedef const void TWData; /// Defines a resizable string. /// -/// The implementantion of these methods should be language-specific to minimize translation overhead. For instance it -/// should be a `jstring` for Java and an `NSString` for Swift. -/// Create allocates memory, the delete call should be called at the end to release memory. +/// The implementantion of these methods should be language-specific to minimize translation +/// overhead. For instance it should be a `jstring` for Java and an `NSString` for Swift. Create +/// allocates memory, the delete call should be called at the end to release memory. typedef const void TWString; -/// Creates a string from a null-terminated UTF8 byte array. It must be deleted at the end. +/// Creates a TWString from a null-terminated UTF8 byte array. It must be deleted at the end. +/// +/// \param bytes a null-terminated UTF8 byte array. TW_EXTERN -TWString *_Nonnull TWStringCreateWithUTF8Bytes(const char *_Nonnull bytes); +TWString* _Nonnull TWStringCreateWithUTF8Bytes(const char* _Nonnull bytes) TW_VISIBILITY_DEFAULT; -/// Creates a string from a raw byte array and size. +/// Creates a string from a raw byte array and size. It must be deleted at the end. +/// +/// \param bytes a raw byte array. +/// \param size the size of the byte array. TW_EXTERN -TWString *_Nonnull TWStringCreateWithRawBytes(const uint8_t *_Nonnull bytes, size_t size); +TWString* _Nonnull TWStringCreateWithRawBytes(const uint8_t* _Nonnull bytes, size_t size) TW_VISIBILITY_DEFAULT; /// Creates a hexadecimal string from a block of data. It must be deleted at the end. +/// +/// \param data a block of data. TW_EXTERN -TWString *_Nonnull TWStringCreateWithHexData(TWData *_Nonnull data); +TWString* _Nonnull TWStringCreateWithHexData(TWData* _Nonnull data) TW_VISIBILITY_DEFAULT; /// Returns the string size in bytes. +/// +/// \param string a TWString pointer. TW_EXTERN -size_t TWStringSize(TWString *_Nonnull string); +size_t TWStringSize(TWString* _Nonnull string) TW_VISIBILITY_DEFAULT; /// Returns the byte at the provided index. +/// +/// \param string a TWString pointer. +/// \param index the index of the byte. TW_EXTERN -char TWStringGet(TWString *_Nonnull string, size_t index); +char TWStringGet(TWString* _Nonnull string, size_t index) TW_VISIBILITY_DEFAULT; /// Returns the raw pointer to the string's UTF8 bytes (null-terminated). +/// +/// \param string a TWString pointer. TW_EXTERN -const char *_Nonnull TWStringUTF8Bytes(TWString *_Nonnull string); +const char* _Nonnull TWStringUTF8Bytes(TWString* _Nonnull string) TW_VISIBILITY_DEFAULT; -/// Deletes a string created with a `TWStringCreate*` method. After delete it must not be used (can segfault)! +/// Deletes a string created with a `TWStringCreate*` method and frees the memory. +/// +/// \param string a TWString pointer. TW_EXTERN -void TWStringDelete(TWString *_Nonnull string); +void TWStringDelete(TWString* _Nonnull string) TW_VISIBILITY_DEFAULT; /// Determines whether two string blocks are equal. +/// +/// \param lhs a TWString pointer. +/// \param rhs another TWString pointer. TW_EXTERN -bool TWStringEqual(TWString *_Nonnull lhs, TWString *_Nonnull rhs); +bool TWStringEqual(TWString* _Nonnull lhs, TWString* _Nonnull rhs) TW_VISIBILITY_DEFAULT; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWTHORChainSwap.h b/include/TrustWalletCore/TWTHORChainSwap.h new file mode 100644 index 00000000000..27708cec96f --- /dev/null +++ b/include/TrustWalletCore/TWTHORChainSwap.h @@ -0,0 +1,25 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +#pragma once + +#include "TWBase.h" +#include "TWData.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// THORChain swap functions +TW_EXPORT_STRUCT +struct TWTHORChainSwap; + +/// Builds a THORChainSwap transaction input. +/// +/// \param input The serialized data of SwapInput. +/// \return The serialized data of SwapOutput. +TW_EXPORT_STATIC_METHOD +TWData *_Nonnull TWTHORChainSwapBuildSwap(TWData *_Nonnull input); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWTransactionCompiler.h b/include/TrustWalletCore/TWTransactionCompiler.h new file mode 100644 index 00000000000..93e68c65cce --- /dev/null +++ b/include/TrustWalletCore/TWTransactionCompiler.h @@ -0,0 +1,63 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" +#include "TWCoinType.h" +#include "TWData.h" +#include "TWDataVector.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// Non-core transaction utility methods, like building a transaction using an external signature. +TW_EXPORT_STRUCT +struct TWTransactionCompiler; + +/// Builds a coin-specific SigningInput (proto object) from a simple transaction. +/// +/// \param coin coin type. +/// \param from sender of the transaction. +/// \param to receiver of the transaction. +/// \param amount transaction amount in string +/// \param asset optional asset name, like "BNB" +/// \param memo optional memo +/// \param chainId optional chainId to override default +/// \return serialized data of the SigningInput proto object. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWTransactionCompilerBuildInput(enum TWCoinType coinType, TWString* _Nonnull from, + TWString* _Nonnull to, TWString* _Nonnull amount, + TWString* _Nonnull asset, TWString* _Nonnull memo, + TWString* _Nonnull chainId); + +/// Obtains pre-signing hashes of a transaction. +/// +/// We provide a default `PreSigningOutput` in TransactionCompiler.proto. +/// For some special coins, such as bitcoin, we will create a custom `PreSigningOutput` object in its proto file. +/// \param coin coin type. +/// \param txInputData The serialized data of a signing input +/// \return serialized data of a proto object `PreSigningOutput` includes hash. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWTransactionCompilerPreImageHashes(enum TWCoinType coinType, + TWData* _Nonnull txInputData); + +/// Compiles a complete transation with one or more external signatures. +/// +/// Puts together from transaction input and provided public keys and signatures. The signatures must match the hashes +/// returned by TWTransactionCompilerPreImageHashes, in the same order. The publicKeyHash attached +/// to the hashes enable identifying the private key needed for signing the hash. +/// \param coin coin type. +/// \param txInputData The serialized data of a signing input. +/// \param signatures signatures to compile, using TWDataVector. +/// \param publicKeys public keys for signers to match private keys, using TWDataVector. +/// \return serialized data of a proto object `SigningOutput`. +TW_EXPORT_STATIC_METHOD +TWData* _Nonnull TWTransactionCompilerCompileWithSignatures( + enum TWCoinType coinType, TWData* _Nonnull txInputData, + const struct TWDataVector* _Nonnull signatures, const struct TWDataVector* _Nonnull publicKeys); + +TW_EXTERN_C_END diff --git a/jni/java/wallet/core/java/AnySigner.java b/jni/java/wallet/core/java/AnySigner.java index 47f9b4fce9a..49c7770d3d1 100644 --- a/jni/java/wallet/core/java/AnySigner.java +++ b/jni/java/wallet/core/java/AnySigner.java @@ -6,13 +6,13 @@ package wallet.core.java; -import com.google.protobuf.Message; +import com.google.protobuf.MessageLite; import com.google.protobuf.Parser; import wallet.core.jni.CoinType; public class AnySigner { - public static T sign(Message input, CoinType coin, Parser parser) throws Exception { + public static T sign(MessageLite input, CoinType coin, Parser parser) throws Exception { byte[] data = input.toByteArray(); byte[] outputData = nativeSign(data, coin.value()); T output = parser.parseFrom(outputData); @@ -25,7 +25,7 @@ public static T sign(Message input, CoinType coin, Parser public static native boolean supportsJSON(int coin); - public static T plan(Message input, CoinType coin, Parser parser) throws Exception { + public static T plan(MessageLite input, CoinType coin, Parser parser) throws Exception { byte[] data = input.toByteArray(); byte[] outputData = nativePlan(data, coin.value()); T output = parser.parseFrom(outputData); diff --git a/protobuf-plugin/CMakeLists.txt b/protobuf-plugin/CMakeLists.txt index e636f4a2fb4..2d8c3a722fb 100644 --- a/protobuf-plugin/CMakeLists.txt +++ b/protobuf-plugin/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.2 FATAL_ERROR) project(TrustWalletCoreProtobufPlugin) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version" FORCE) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) if ("$ENV{PREFIX}" STREQUAL "") diff --git a/protobuf-plugin/swift_typealias.cc b/protobuf-plugin/swift_typealias.cc index a5877616af2..57a169dea03 100644 --- a/protobuf-plugin/swift_typealias.cc +++ b/protobuf-plugin/swift_typealias.cc @@ -4,6 +4,8 @@ #include #include #include +#include +#include using namespace google::protobuf; @@ -26,31 +28,48 @@ class Generator : public compiler::CodeGenerator { "// file LICENSE at the root of the source code distribution tree.\n" "\n" ); + + std::vector names; + std::vector> aliases; + for (int i = 0; i < file->message_type_count(); i += 1) { - auto message = file->message_type(i); - auto parts = Generator::getParts(message->full_name()); + const auto* message = file->message_type(i); + names.emplace_back(message->full_name()); + } + + for (int i = 0; i < file->enum_type_count(); i += 1) { + const auto* enum_type = file->enum_type(i); + names.emplace_back(enum_type->full_name()); + } + + for (auto& name : names) { + auto parts = Generator::getParts(name); if (parts.size() < 3 || parts[0] != "TW") { - std::cerr << "Invalid proto name '" << message->full_name() << "'" << std::endl; + std::cerr << "Invalid proto name '" << name << "'" << std::endl; continue; } - std::string def = "public typealias "; + + std::string alias = ""; for (auto i = 0; i < parts.size(); i += 1) { if (i == 0 || i == 2) { continue; } - def += parts[i]; + alias += parts[i]; } - def += " = "; - + std::string type = ""; for (auto& part : parts) { - def += part + "_"; + type += part + "_"; } - def = def.substr(0, def.size() - 1); - def += ";\n"; - printer.Print(def.c_str()); + type = type.substr(0, type.size() - 1); + + aliases.emplace_back(std::make_tuple(alias, type)); } + for (auto& alias : aliases) { + std::string line = "public typealias " + std::get<0>(alias) + " = " + std::get<1>(alias) + "\n"; + printer.Print(line.c_str()); + } return true; } diff --git a/registry.json b/registry.json index 40f406e3051..d77105beebe 100644 --- a/registry.json +++ b/registry.json @@ -6,7 +6,26 @@ "symbol": "BTC", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/84'/0'/0'/0/0", + "derivation": [ + { + "name": "segwit", + "path": "m/84'/0'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" + }, + { + "name": "legacy", + "path": "m/44'/0'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + }, + { + "name": "testnet", + "path": "m/84'/1'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 0, @@ -14,8 +33,6 @@ "hrp": "bc", "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "zpub", - "xprv": "zprv", "explorer": { "url": "https://blockchair.com", "txPath": "/bitcoin/transaction/", @@ -37,7 +54,19 @@ "symbol": "LTC", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/84'/2'/0'/0/0", + "derivation": [ + { + "path": "m/84'/2'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" + }, + { + "name": "legacy", + "path": "m/44'/2'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 48, @@ -45,8 +74,6 @@ "hrp": "ltc", "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "zpub", - "xprv": "zprv", "explorer": { "url": "https://blockchair.com", "txPath": "/litecoin/transaction/", @@ -66,15 +93,19 @@ "symbol": "DOGE", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/44'/3'/0'/0/0", + "derivation": [ + { + "path": "m/44'/3'/0'/0/0", + "xpub": "dgub", + "xprv": "dgpv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 30, "p2shPrefix": 22, "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "dgub", - "xprv": "dgpv", "explorer": { "url": "https://blockchair.com", "txPath": "/dogecoin/transaction/", @@ -94,15 +125,19 @@ "symbol": "DASH", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/44'/5'/0'/0/0", + "derivation": [ + { + "path": "m/44'/5'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 76, "p2shPrefix": 16, "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "xpub", - "xprv": "xprv", "explorer": { "url": "https://blockchair.com", "txPath": "/dash/transaction/", @@ -122,7 +157,13 @@ "symbol": "VIA", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/84'/14'/0'/0/0", + "derivation": [ + { + "path": "m/84'/14'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 71, @@ -130,8 +171,6 @@ "hrp": "via", "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "zpub", - "xprv": "zprv", "explorer": { "url": "https://explorer.viacoin.org", "txPath": "/tx/", @@ -150,8 +189,14 @@ "coinId": 17, "symbol": "GRS", "decimals": 8, - "blockchain": "Bitcoin", - "derivationPath": "m/84'/17'/0'/0/0", + "blockchain": "Groestlcoin", + "derivation": [ + { + "path": "m/84'/17'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 36, @@ -159,8 +204,6 @@ "hrp": "grs", "publicKeyHasher": "sha256ripemd", "base58Hasher": "groestl512d", - "xpub": "zpub", - "xprv": "zprv", "explorer": { "url": "https://blockchair.com", "txPath": "/groestlcoin/transaction/", @@ -180,7 +223,13 @@ "symbol": "DGB", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/84'/20'/0'/0/0", + "derivation": [ + { + "path": "m/84'/20'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 30, @@ -188,8 +237,6 @@ "hrp": "dgb", "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "zpub", - "xprv": "zprv", "explorer": { "url": "https://digiexplorer.info", "txPath": "/tx/", @@ -209,7 +256,13 @@ "symbol": "MONA", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/44'/22'/0'/0/0", + "derivation": [ + { + "path": "m/44'/22'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 50, @@ -217,8 +270,6 @@ "hrp": "mona", "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "xpub", - "xprv": "xprv", "explorer": { "url": "https://blockbook.electrum-mona.org", "txPath": "/tx/", @@ -237,8 +288,14 @@ "coinId": 42, "symbol": "DCR", "decimals": 8, - "blockchain": "Bitcoin", - "derivationPath": "m/44'/42'/0'/0/0", + "blockchain": "Decred", + "derivation": [ + { + "path": "m/44'/42'/0'/0/0", + "xpub": "dpub", + "xprv": "dprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "staticPrefix": 7, @@ -246,8 +303,6 @@ "p2shPrefix": 26, "publicKeyHasher": "blake256ripemd", "base58Hasher": "blake256d", - "xpub": "dpub", - "xprv": "dprv", "explorer": { "url": "https://dcrdata.decred.org", "txPath": "/tx/", @@ -267,9 +322,15 @@ "symbol": "ETH", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "1", + "addressHasher": "keccak256", "explorer": { "url": "https://etherscan.io", "txPath": "/tx/", @@ -291,9 +352,15 @@ "symbol": "ETC", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/61'/0'/0/0", + "derivation": [ + { + "path": "m/44'/61'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "61", + "addressHasher": "keccak256", "explorer": { "url": "https://blockscout.com/etc/mainnet", "txPath": "/tx/", @@ -313,7 +380,11 @@ "symbol": "ICX", "decimals": 18, "blockchain": "Icon", - "derivationPath": "m/44'/74'/0'/0/0", + "derivation": [ + { + "path": "m/44'/74'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", "explorer": { @@ -328,19 +399,56 @@ "documentation": "https://www.icondev.io/docs/icon-json-rpc-v3" } }, + { + "id": "aptos", + "name": "Aptos", + "displayName": "Aptos", + "coinId": 637, + "symbol": "APT", + "decimals": 8, + "chainId": "1", + "blockchain": "Aptos", + "derivation": [ + { + "path": "m/44'/637'/0'/0'/0'" + } + ], + "curve": "ed25519", + "publicKeyType": "ed25519", + "explorer": { + "url": "https://explorer.aptoslabs.com", + "txPath": "/txn/", + "accountPath": "/account/", + "sampleTx": "91424546", + "sampleAccount": "0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108" + }, + "info": { + "url": "https://aptoslabs.com/", + "source": "https://github.com/aptos-labs/aptos-core", + "rpc": "https://fullnode.devnet.aptoslabs.com", + "documentation": "https://fullnode.devnet.aptoslabs.com/v1/spec#/" + } + }, { "id": "cosmos", "name": "Cosmos", + "displayName": "Cosmos Hub", "coinId": 118, "symbol": "ATOM", "decimals": 6, + "chainId": "cosmoshub-4", "blockchain": "Cosmos", - "derivationPath": "m/44'/118'/0'/0/0", + "derivation": [ + { + "path": "m/44'/118'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "cosmos", + "addressHasher": "sha256ripemd", "explorer": { - "url": "https://www.mintscan.io", + "url": "https://mintscan.io/cosmos", "txPath": "/txs/", "accountPath": "/account/" }, @@ -357,8 +465,14 @@ "coinId": 133, "symbol": "ZEC", "decimals": 8, - "blockchain": "Bitcoin", - "derivationPath": "m/44'/133'/0'/0/0", + "blockchain": "Zcash", + "derivation": [ + { + "path": "m/44'/133'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "staticPrefix": 28, @@ -366,8 +480,6 @@ "p2shPrefix": 189, "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "xpub", - "xprv": "xprv", "explorer": { "url": "https://blockchair.com/zcash", "txPath": "/transaction/", @@ -381,22 +493,25 @@ } }, { - "id": "zcoin", - "name": "Zcoin", - "displayName": "Firo", + "id": "firo", + "name": "Firo", "coinId": 136, "symbol": "FIRO", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/44'/136'/0'/0/0", + "derivation": [ + { + "path": "m/44'/136'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 82, "p2shPrefix": 7, "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "xpub", - "xprv": "xprv", "explorer": { "url": "https://explorer.firo.org", "txPath": "/tx/", @@ -416,7 +531,11 @@ "symbol": "XRP", "decimals": 6, "blockchain": "Ripple", - "derivationPath": "m/44'/144'/0'/0/0", + "derivation": [ + { + "path": "m/44'/144'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "explorer": { @@ -440,7 +559,13 @@ "symbol": "BCH", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/44'/145'/0'/0/0", + "derivation": [ + { + "path": "m/44'/145'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 0, @@ -448,8 +573,6 @@ "hrp": "bitcoincash", "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "xpub", - "xprv": "xprv", "explorer": { "url": "https://blockchair.com", "txPath": "/bitcoin-cash/transaction/", @@ -469,7 +592,11 @@ "symbol": "XLM", "decimals": 7, "blockchain": "Stellar", - "derivationPath": "m/44'/148'/0'", + "derivation": [ + { + "path": "m/44'/148'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { @@ -491,7 +618,13 @@ "symbol": "BTG", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/84'/156'/0'/0/0", + "derivation": [ + { + "path": "m/84'/156'/0'/0/0", + "xpub": "zpub", + "xprv": "zprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 38, @@ -499,8 +632,6 @@ "hrp": "btg", "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "zpub", - "xprv": "zprv", "explorer": { "url": "https://explorer.bitcoingold.org/insight", "txPath": "/tx/", @@ -517,10 +648,14 @@ "id": "nano", "name": "Nano", "coinId": 165, - "symbol": "NANO", + "symbol": "XNO", "decimals": 30, "blockchain": "Nano", - "derivationPath": "m/44'/165'/0'", + "derivation": [ + { + "path": "m/44'/165'/0'" + } + ], "curve": "ed25519Blake2bNano", "publicKeyType": "ed25519Blake2b", "url": "https://nano.org", @@ -545,15 +680,19 @@ "symbol": "RVN", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/44'/175'/0'/0/0", + "derivation": [ + { + "path": "m/44'/175'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 60, "p2shPrefix": 122, "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "xpub", - "xprv": "xprv", "explorer": { "url": "https://ravencoin.network", "txPath": "/tx/", @@ -573,9 +712,15 @@ "symbol": "POA", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/178'/0'/0/0", + "derivation": [ + { + "path": "m/44'/178'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "99", + "addressHasher": "keccak256", "explorer": { "url": "https://blockscout.com", "txPath": "/poa/core/tx/", @@ -595,7 +740,11 @@ "symbol": "EOS", "decimals": 4, "blockchain": "EOS", - "derivationPath": "m/44'/194'/0'/0/0", + "derivation": [ + { + "path": "m/44'/194'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "explorer": { @@ -617,7 +766,11 @@ "symbol": "TRX", "decimals": 6, "blockchain": "Tron", - "derivationPath": "m/44'/195'/0'/0/0", + "derivation": [ + { + "path": "m/44'/195'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", "explorer": { @@ -639,7 +792,11 @@ "symbol": "FIO", "decimals": 9, "blockchain": "FIO", - "derivationPath": "m/44'/235'/0'/0/0", + "derivation": [ + { + "path": "m/44'/235'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "url": "https://fioprotocol.io/", @@ -662,7 +819,11 @@ "symbol": "NIM", "decimals": 5, "blockchain": "Nimiq", - "derivationPath": "m/44'/242'/0'/0'", + "derivation": [ + { + "path": "m/44'/242'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { @@ -684,7 +845,11 @@ "symbol": "ALGO", "decimals": 6, "blockchain": "Algorand", - "derivationPath": "m/44'/283'/0'/0'/0'", + "derivation": [ + { + "path": "m/44'/283'/0'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { @@ -708,7 +873,11 @@ "symbol": "IOTX", "decimals": 18, "blockchain": "IoTeX", - "derivationPath": "m/44'/304'/0'/0/0", + "derivation": [ + { + "path": "m/44'/304'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", "hrp": "io", @@ -724,6 +893,33 @@ "documentation": "https://docs.iotex.io/#api" } }, + { + "id": "nervos", + "name": "Nervos", + "coinId": 309, + "symbol": "CKB", + "decimals": 8, + "blockchain": "Nervos", + "derivation": [ + { + "path": "m/44'/309'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "hrp": "ckb", + "explorer": { + "url": "https://explorer.nervos.org", + "txPath": "/transaction/", + "accountPath": "/address/" + }, + "info": { + "url": "https://nervos.org", + "source": "https://github.com/nervosnetwork/ckb", + "rpc": "https://mainnet.ckb.dev/rpc", + "documentation": "https://github.com/nervosnetwork/rfcs" + } + }, { "id": "zilliqa", "name": "Zilliqa", @@ -731,7 +927,11 @@ "symbol": "ZIL", "decimals": 12, "blockchain": "Zilliqa", - "derivationPath": "m/44'/313'/0'/0/0", + "derivation": [ + { + "path": "m/44'/313'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "zil", @@ -750,23 +950,60 @@ { "id": "terra", "name": "Terra", + "displayName": "Terra Classic", "coinId": 330, + "symbol": "LUNC", + "decimals": 6, + "blockchain": "Cosmos", + "chainId": "columbus-5", + "derivation": [ + { + "path": "m/44'/330'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "hrp": "terra", + "addressHasher": "sha256ripemd", + "explorer": { + "url": "https://finder.terra.money/classic", + "txPath": "/tx/", + "accountPath": "/address/" + }, + "info": { + "url": "https://terra.money", + "source": "https://github.com/terra-project/core", + "rpc": "https://columbus-fcd.terra.dev", + "documentation": "https://docs.terra.money" + } + }, + { + "id": "terrav2", + "name": "TerraV2", + "displayName": "Terra", + "coinId": 10000330, "symbol": "LUNA", "decimals": 6, "blockchain": "Cosmos", - "derivationPath": "m/44'/330'/0'/0/0", + "derivation": [ + { + "path": "m/44'/330'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "terra", + "chainId": "phoenix-1", + "addressHasher": "sha256ripemd", "explorer": { - "url": "https://finder.terra.money/tx", + "url": "https://finder.terra.money/mainnet", "txPath": "/tx/", "accountPath": "/address/" }, "info": { "url": "https://terra.money", "source": "https://github.com/terra-project/core", - "rpc": "https://rpc.terra.dev", + "rpc": "https://phoenix-lcd.terra.dev", "documentation": "https://docs.terra.money" } }, @@ -777,9 +1014,14 @@ "symbol": "DOT", "decimals": 10, "blockchain": "Polkadot", - "derivationPath": "m/44'/354'/0'/0'/0'", + "derivation": [ + { + "path": "m/44'/354'/0'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", + "addressHasher": "keccak256", "explorer": { "url": "https://polkadot.subscan.io", "txPath": "/extrinsic/", @@ -792,6 +1034,34 @@ "documentation": "https://polkadot.js.org/api/substrate/rpc.html" } }, + { + "id": "everscale", + "name": "Everscale", + "coinId": 396, + "symbol": "EVER", + "decimals": 9, + "blockchain": "Everscale", + "derivation": [ + { + "path": "m/44'/396'/0'/0/0" + } + ], + "curve": "ed25519", + "publicKeyType": "ed25519", + "explorer": { + "url": "https://everscan.io", + "txPath": "/transactions/", + "accountPath": "/accounts/", + "sampleTx": "781238b2b0d15cd4cd2e2a0a142753750cd5e1b2c8b506fcede75a90e02f1268", + "sampleAccount": "0:d2bf59964a05dee84a0dd1ddc0ad83ba44d49719cf843d689dc8b726d0fb59d8" + }, + "info": { + "url": "https://everscale.network/", + "source": "https://github.com/tonlabs/evernode-ds", + "rpc": "https://evercloud.dev", + "documentation": "https://docs.everos.dev/evernode-platform/products/evercloud/get-started" + } + }, { "id": "near", "name": "NEAR", @@ -799,7 +1069,11 @@ "symbol": "NEAR", "decimals": 24, "blockchain": "NEAR", - "derivationPath": "m/44'/397'/0'", + "derivation": [ + { + "path": "m/44'/397'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { @@ -821,7 +1095,11 @@ "symbol": "AION", "decimals": 18, "blockchain": "Aion", - "derivationPath": "m/44'/425'/0'/0'/0'", + "derivation": [ + { + "path": "m/44'/425'/0'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { @@ -842,10 +1120,15 @@ "coinId": 434, "symbol": "KSM", "decimals": 12, - "blockchain": "Polkadot", - "derivationPath": "m/44'/434'/0'/0'/0'", + "blockchain": "Kusama", + "derivation": [ + { + "path": "m/44'/434'/0'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", + "addressHasher": "keccak256", "explorer": { "url": "https://kusama.subscan.io", "txPath": "/extrinsic/", @@ -867,7 +1150,11 @@ "symbol": "AE", "decimals": 18, "blockchain": "Aeternity", - "derivationPath": "m/44'/457'/0'/0'/0'", + "derivation": [ + { + "path": "m/44'/457'/0'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { @@ -889,12 +1176,18 @@ "symbol": "KAVA", "decimals": 6, "blockchain": "Cosmos", - "derivationPath": "m/44'/459'/0'/0/0", + "chainId": "kava_2222-10", + "derivation": [ + { + "path": "m/44'/459'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "kava", + "addressHasher": "sha256ripemd", "explorer": { - "url": "https://kava.mintscan.io", + "url": "https://mintscan.io/kava", "txPath": "/txs/", "accountPath": "/account/", "sampleTx": "2988DF83FCBFAA38179D583A96734CBD071541D6768221BB23111BC8136D5E6A", @@ -914,7 +1207,11 @@ "symbol": "FIL", "decimals": 18, "blockchain": "Filecoin", - "derivationPath": "m/44'/461'/0'/0/0", + "derivation": [ + { + "path": "m/44'/461'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", "explorer": { @@ -938,10 +1235,15 @@ "symbol": "BLZ", "decimals": 6, "blockchain": "Cosmos", - "derivationPath": "m/44'/483'/0'/0/0", + "derivation": [ + { + "path": "m/44'/483'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "bluzelle", + "addressHasher": "sha256ripemd", "explorer": { "url": "https://bigdipper.net.bluzelle.com", "txPath": "/transactions/", @@ -963,10 +1265,16 @@ "coinId": 494, "decimals": 6, "blockchain": "Cosmos", - "derivationPath": "m/44'/494'/0'/0/0", + "chainId": "laozi-mainnet", + "derivation": [ + { + "path": "m/44'/494'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "band", + "addressHasher": "sha256ripemd", "explorer": { "url": "https://scan-wenchang-testnet2.bandchain.org/", "txPath": "/tx/", @@ -988,7 +1296,11 @@ "symbol": "THETA", "decimals": 18, "blockchain": "Theta", - "derivationPath": "m/44'/500'/0'/0/0", + "derivation": [ + { + "path": "m/44'/500'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", "explorer": { @@ -1010,7 +1322,15 @@ "symbol": "SOL", "decimals": 9, "blockchain": "Solana", - "derivationPath": "m/44'/501'/0'", + "derivation": [ + { + "path": "m/44'/501'/0'" + }, + { + "name": "solana", + "path": "m/44'/501'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { @@ -1034,7 +1354,11 @@ "symbol": "eGLD", "decimals": 18, "blockchain": "ElrondNetwork", - "derivationPath": "m/44'/508'/0'/0'/0'", + "derivation": [ + { + "path": "m/44'/508'/0'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "hrp": "erd", @@ -1053,15 +1377,20 @@ { "id": "binance", "name": "Binance", - "displayName": "BNB", + "displayName": "BNB Beacon Chain", "coinId": 714, "symbol": "BNB", "decimals": 8, "blockchain": "Binance", - "derivationPath": "m/44'/714'/0'/0/0", + "derivation": [ + { + "path": "m/44'/714'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "bnb", + "chainId": "Binance-Chain-Tigris", "explorer": { "url": "https://explorer.binance.org", "txPath": "/tx/", @@ -1083,9 +1412,14 @@ "symbol": "VET", "decimals": 18, "blockchain": "Vechain", - "derivationPath": "m/44'/818'/0'/0/0", + "derivation": [ + { + "path": "m/44'/818'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "74", "explorer": { "url": "https://explore.vechain.org", "txPath": "/transactions/", @@ -1105,11 +1439,17 @@ "symbol": "CLO", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/820'/0'/0/0", + "derivation": [ + { + "path": "m/44'/820'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "820", + "addressHasher": "keccak256", "explorer": { - "url": "https://explorer2.callisto.network", + "url": "https://explorer.callisto.network", "txPath": "/tx/", "accountPath": "/addr/" }, @@ -1127,7 +1467,11 @@ "symbol": "NEO", "decimals": 8, "blockchain": "NEO", - "derivationPath": "m/44'/888'/0'/0/0", + "derivation": [ + { + "path": "m/44'/888'/0'/0/0" + } + ], "curve": "nist256p1", "publicKeyType": "nist256p1", "explorer": { @@ -1151,9 +1495,15 @@ "symbol": "TOMO", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/889'/0'/0/0", + "derivation": [ + { + "path": "m/44'/889'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "88", + "addressHasher": "keccak256", "explorer": { "url": "https://tomoscan.io", "txPath": "/tx/", @@ -1173,9 +1523,15 @@ "symbol": "TT", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/1001'/0'/0/0", + "derivation": [ + { + "path": "m/44'/1001'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "108", + "addressHasher": "keccak256", "explorer": { "url": "https://scan.thundercore.com", "txPath": "/transactions/", @@ -1195,7 +1551,11 @@ "symbol": "ONE", "decimals": 18, "blockchain": "Harmony", - "derivationPath": "m/44'/1023'/0'/0/0", + "derivation": [ + { + "path": "m/44'/1023'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", "hrp": "one", @@ -1218,7 +1578,11 @@ "symbol": "ROSE", "decimals": 9, "blockchain": "OasisNetwork", - "derivationPath": "m/44'/474'/0'", + "derivation": [ + { + "path": "m/44'/474'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "hrp": "oasis", @@ -1243,7 +1607,11 @@ "symbol": "ONT", "decimals": 0, "blockchain": "Ontology", - "derivationPath": "m/44'/1024'/0'/0/0", + "derivation": [ + { + "path": "m/44'/1024'/0'/0/0" + } + ], "curve": "nist256p1", "publicKeyType": "nist256p1", "explorer": { @@ -1265,7 +1633,11 @@ "symbol": "XTZ", "decimals": 6, "blockchain": "Tezos", - "derivationPath": "m/44'/1729'/0'/0'", + "derivation": [ + { + "path": "m/44'/1729'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { @@ -1287,13 +1659,17 @@ "symbol": "ADA", "decimals": 6, "blockchain": "Cardano", - "derivationPath": "m/1852'/1815'/0'/0/0", - "curve": "ed25519Extended", - "publicKeyType": "ed25519Extended", + "derivation": [ + { + "path": "m/1852'/1815'/0'/0/0" + } + ], + "curve": "ed25519ExtendedCardano", + "publicKeyType": "ed25519Cardano", "hrp": "addr", "explorer": { - "url": "https://shelleyexplorer.cardano.org", - "txPath": "/tx/", + "url": "https://cardanoscan.io", + "txPath": "/transaction/", "accountPath": "/address/", "sampleTx": "b7a6c5cadab0f64bdc89c77ee4a351463aba5c33f2cef6bbd6542a74a90a3af3", "sampleAccount": "addr1s3xuxwfetyfe7q9u3rfn6je9stlvcgmj8rezd87qjjegdtxm3y3f2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59jz29g8j" @@ -1312,7 +1688,11 @@ "symbol": "KIN", "decimals": 5, "blockchain": "Stellar", - "derivationPath": "m/44'/2017'/0'", + "derivation": [ + { + "path": "m/44'/2017'/0'" + } + ], "curve": "ed25519", "publicKeyType": "ed25519", "explorer": { @@ -1335,7 +1715,13 @@ "symbol": "QTUM", "decimals": 8, "blockchain": "Bitcoin", - "derivationPath": "m/44'/2301'/0'/0/0", + "derivation": [ + { + "path": "m/44'/2301'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "p2pkhPrefix": 58, @@ -1343,8 +1729,6 @@ "hrp": "qc", "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "xpub", - "xprv": "xprv", "explorer": { "url": "https://qtum.info", "txPath": "/tx/", @@ -1364,7 +1748,11 @@ "symbol": "NAS", "decimals": 18, "blockchain": "Nebulas", - "derivationPath": "m/44'/2718'/0'/0/0", + "derivation": [ + { + "path": "m/44'/2718'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", "explorer": { @@ -1386,9 +1774,15 @@ "symbol": "GO", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/6060'/0'/0/0", + "derivation": [ + { + "path": "m/44'/6060'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "60", + "addressHasher": "keccak256", "explorer": { "url": "https://explorer.gochain.io", "txPath": "/tx/", @@ -1408,7 +1802,11 @@ "symbol": "NULS", "decimals": 8, "blockchain": "NULS", - "derivationPath": "m/44'/8964'/0'/0/0", + "derivation": [ + { + "path": "m/44'/8964'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "explorer": { @@ -1430,8 +1828,14 @@ "coinId": 19167, "symbol": "FLUX", "decimals": 8, - "blockchain": "Bitcoin", - "derivationPath": "m/44'/19167'/0'/0/0", + "blockchain": "Zcash", + "derivation": [ + { + "path": "m/44'/19167'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "staticPrefix": 28, @@ -1439,8 +1843,6 @@ "p2shPrefix": 189, "publicKeyHasher": "sha256ripemd", "base58Hasher": "sha256d", - "xpub": "xpub", - "xprv": "xprv", "explorer": { "url": "https://explorer.runonflux.io", "txPath": "/tx/", @@ -1460,9 +1862,15 @@ "symbol": "WAN", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/5718350'/0'/0/0", + "derivation": [ + { + "path": "m/44'/5718350'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "888", + "addressHasher": "keccak256", "explorer": { "url": "https://www.wanscan.org", "txPath": "/tx/", @@ -1484,7 +1892,11 @@ "symbol": "WAVES", "decimals": 8, "blockchain": "Waves", - "derivationPath": "m/44'/5741564'/0'/0'/0'", + "derivation": [ + { + "path": "m/44'/5741564'/0'/0'/0'" + } + ], "curve": "ed25519", "publicKeyType": "curve25519", "explorer": { @@ -1507,9 +1919,15 @@ "symbol": "BNB", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/714'/0'/0/0", + "derivation": [ + { + "path": "m/44'/714'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "56", + "addressHasher": "keccak256", "explorer": { "url": "https://bscscan.com", "txPath": "/tx/", @@ -1523,19 +1941,27 @@ "rpc": "https://data-seed-prebsc-1-s1.binance.org:8545", "documentation": "https://eth.wiki/json-rpc/API" }, - "deprecated": true + "deprecated": true, + "testFolderName" : "Binance" }, { "id": "smartchain", "name": "Smart Chain", + "displayName": "BNB Smart Chain", "coinId": 20000714, "slip44": 714, "symbol": "BNB", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "56", + "addressHasher": "keccak256", "explorer": { "url": "https://bscscan.com", "txPath": "/tx/", @@ -1548,7 +1974,8 @@ "source": "https://github.com/binance-chain/bsc", "rpc": "https://bsc-dataseed1.binance.org", "documentation": "https://eth.wiki/json-rpc/API" - } + }, + "testFolderName" : "Binance" }, { "id": "polygon", @@ -1557,9 +1984,15 @@ "symbol": "MATIC", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "137", + "addressHasher": "keccak256", "explorer": { "url": "https://polygonscan.com", "txPath": "/tx/", @@ -1570,7 +2003,7 @@ "info": { "url": "https://polygon.technology", "source": "https://github.com/maticnetwork/contracts", - "rpc": "https://rpc-mainnet.matic.network", + "rpc": "https://polygon-rpc.com", "documentation": "https://eth.wiki/json-rpc/API" } }, @@ -1580,11 +2013,16 @@ "coinId": 931, "symbol": "RUNE", "decimals": 8, - "blockchain": "Cosmos", - "derivationPath": "m/44'/931'/0'/0/0", + "blockchain": "Thorchain", + "derivation": [ + { + "path": "m/44'/931'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "thor", + "chainId": "thorchain-mainnet-v1", "explorer": { "url": "https://viewblock.io/thorchain", "txPath": "/tx/", @@ -1605,12 +2043,18 @@ "displayName": "Optimistic Ethereum", "coinId": 10000070, "slip44": 60, - "symbol": "OETH", + "symbol": "ETH", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "10", + "addressHasher": "keccak256", "explorer": { "url": "https://optimistic.etherscan.io", "txPath": "/tx/", @@ -1623,17 +2067,53 @@ "documentation": "https://eth.wiki/json-rpc/API" } }, + { + "id": "zksync", + "name": "Zksync", + "displayName": "zkSync v2", + "coinId": 10000280, + "slip44": 60, + "symbol": "ETH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "280", + "addressHasher": "keccak256", + "explorer": { + "url": "https://zksync2-testnet.zkscan.io", + "txPath": "/tx/", + "accountPath": "/address/" + }, + "info": { + "url": "https://portal.zksync.io/", + "source": "https://github.com/matter-labs/zksync", + "rpc": "https://zksync2-testnet.zksync.dev", + "documentation": "https://v2-docs.zksync.io" + } + }, { "id": "arbitrum", "name": "Arbitrum", "coinId": 10042221, "slip44": 60, - "symbol": "ARETH", + "symbol": "ETH", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "42161", + "addressHasher": "keccak256", "explorer": { "url": "https://arbiscan.io", "txPath": "/tx/", @@ -1655,9 +2135,15 @@ "symbol": "HT", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "128", + "addressHasher": "keccak256", "explorer": { "url": "https://hecoinfo.com", "txPath": "/tx/", @@ -1668,7 +2154,8 @@ "source": "https://github.com/HuobiGroup/huobi-eco-chain", "rpc": "https://http-mainnet-node.huobichain.com", "documentation": "https://eth.wiki/json-rpc/API" - } + }, + "testFolderName" : "ECO" }, { "id": "avalanchec", @@ -1677,11 +2164,17 @@ "symbol": "AVAX", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "43114", + "addressHasher": "keccak256", "explorer": { - "url": "https://cchain.explorer.avax.network", + "url": "https://snowtrace.io", "txPath": "/tx/", "accountPath": "/address/", "sampleTx": "0x9243890b844219accefd8798271052f5a056453ec18984a56e81c92921330d54", @@ -1692,18 +2185,26 @@ "client": "https://github.com/ava-labs/avalanchego", "clientPublic": "https://api.avax.network/ext/bc/C/rpc", "clientDocs": "https://docs.avax.network/" - } + }, + "testFolderName" : "Avalanche" }, { "id": "xdai", "name": "xDai", + "displayName": "Gnosis Chain", "coinId": 10000100, "symbol": "xDAI", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "100", + "addressHasher": "keccak256", "explorer": { "url": "https://blockscout.com/xdai/mainnet", "txPath": "/tx/", @@ -1714,7 +2215,7 @@ "info": { "url": "https://www.xdaichain.com", "client": "https://github.com/openethereum/openethereum", - "clientPublic": "https://rpc.xdaichain.com", + "clientPublic": "https://rpc.gnosischain.com", "clientDocs": "https://eth.wiki/json-rpc/API" } }, @@ -1725,9 +2226,15 @@ "symbol": "FTM", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "250", + "addressHasher": "keccak256", "explorer": { "url": "https://ftmscan.com", "txPath": "/tx/", @@ -1750,10 +2257,16 @@ "symbol": "CRO", "decimals": 8, "blockchain": "Cosmos", - "derivationPath": "m/44'/394'/0'/0/0", + "derivation": [ + { + "path": "m/44'/394'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1", "hrp": "cro", + "chainId": "crypto-org-chain-mainnet-1", + "addressHasher": "sha256ripemd", "explorer": { "url": "https://crypto.org/explorer", "txPath": "/tx/", @@ -1775,9 +2288,15 @@ "symbol": "CELO", "decimals": 18, "blockchain": "Ethereum", - "derivationPath": "m/44'/52752'/0'/0/0", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "42220", + "addressHasher": "keccak256", "explorer": { "url": "https://explorer.celo.org", "txPath": "/tx/", @@ -1799,10 +2318,16 @@ "slip44": 60, "symbol": "RON", "decimals": 18, - "blockchain": "Ethereum", - "derivationPath": "m/44'/60'/0'/0/0", + "blockchain": "Ronin", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], "curve": "secp256k1", "publicKeyType": "secp256k1Extended", + "chainId": "2020", + "addressHasher": "keccak256", "explorer": { "url": "https://explorer.roninchain.com", "txPath": "/tx/", @@ -1816,5 +2341,483 @@ "clientPublic": "https://api.roninchain.com/rpc", "clientDocs": "https://eth.wiki/json-rpc/API" } + }, + { + "id": "osmosis", + "name": "Osmosis", + "displayName": "Osmosis", + "coinId": 10000118, + "symbol": "OSMO", + "decimals": 6, + "blockchain": "Cosmos", + "derivation": [ + { + "path": "m/44'/118'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "hrp": "osmo", + "chainId": "osmosis-1", + "addressHasher": "sha256ripemd", + "explorer": { + "url": "https://mintscan.io/osmosis", + "txPath": "/txs/", + "accountPath": "/account/", + "sampleTx": "5A6E50A6F2927E4B8C87BB094D5FBF15F1287429A09111806FC44B3CD86CACA8", + "sampleAccount": "osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5" + }, + "info": { + "url": "https://osmosis.zone/", + "client": "https://github.com/osmosis-labs/osmosis", + "clientPublic": "https://rpc-osmosis.keplr.app/", + "clientDocs": "" + } + }, + { + "id": "ecash", + "name": "eCash", + "coinId": 899, + "symbol": "XEC", + "decimals": 2, + "blockchain": "Bitcoin", + "derivation": [ + { + "path": "m/44'/899'/0'/0/0", + "xpub": "xpub", + "xprv": "xprv" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "p2pkhPrefix": 0, + "p2shPrefix": 5, + "hrp": "ecash", + "publicKeyHasher": "sha256ripemd", + "base58Hasher": "sha256d", + "explorer": { + "url": "https://explorer.bitcoinabc.org", + "txPath": "/tx/", + "accountPath": "/address/" + }, + "info": { + "url": "https://e.cash", + "source": "https://github.com/trezor/blockbook", + "rpc": "https://blockbook.fabien.cash:9197", + "documentation": "https://github.com/trezor/blockbook/blob/master/docs/api.md" + } + }, + { + "id": "cronos", + "name": "Cronos Chain", + "coinId": 10000025, + "symbol": "CRO", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "25", + "addressHasher": "keccak256", + "explorer": { + "url": "https://cronoscan.com", + "txPath": "/tx/", + "accountPath": "/address/" + }, + "info": { + "url": "https://cronos.org", + "client": "https://github.com/crypto-org-chain/cronos", + "clientPublic": "https://evm-cronos.crypto.org", + "clientDocs": "https://eth.wiki/json-rpc/API" + }, + "testFolderName" : "Cronos" + }, + { + "id": "kavaevm", + "name": "KavaEvm", + "coinId": 10002222, + "symbol": "KAVA", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "2222", + "addressHasher": "keccak256", + "explorer": { + "url": "https://explorer.kava.io", + "txPath": "/tx/", + "accountPath": "/address/" + }, + "info": { + "url": "https://www.kava.io/", + "client": "https://github.com/Kava-Labs/kava", + "documentation": "https://docs.kava.io/docs/ethereum/overview/", + "rpc": "https://evm.kava.io" + } + }, + { + "id": "smartbch", + "name": "Smart Bitcoin Cash", + "coinId": 10000145, + "symbol": "BCH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "10000", + "addressHasher": "keccak256", + "explorer": { + "url": "https://www.smartscan.cash", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x6413466b455b17d03c7a8ce2d7f99fec34bcd338628bdd2d0580a21e3197a4d9", + "sampleAccount": "0xFeEc227410E1DF9f3b4e6e2E284DC83051ae468F" + }, + "info": { + "url": "https://smartbch.org/", + "source": "https://github.com/smartbch/smartbch", + "rpc": "https://smartbch.fountainhead.cash/mainnet", + "documentation": "https://github.com/smartbch/docs/blob/main/developers-guide/jsonrpc.md" + }, + "testFolderName" : "Bitcoin" + }, + { + "id": "kcc", + "name": "KuCoin Community Chain", + "coinId": 10000321, + "symbol": "KCS", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "321", + "addressHasher": "keccak256", + "explorer": { + "url": "https://explorer.kcc.io/en", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x2f0d79cd289a02f3181b68b9583a64c3809fe7387810b274275985c29d02c80d", + "sampleAccount": "0x4446fc4eb47f2f6586f9faab68b3498f86c07521" + }, + "info": { + "url": "https://www.kcc.io/", + "source": "https://github.com/kcc-community/kcc", + "rpc": "https://rpc-mainnet.kcc.network", + "documentation": "https://docs.kcc.io/#/en-us/" + }, + "testFolderName" : "KuCoinCommunityChain" + }, + { + "id": "boba", + "name": "Boba", + "coinId": 10000288, + "symbol": "BOBAETH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "288", + "addressHasher": "keccak256", + "explorer": { + "url": "https://blockexplorer.boba.network", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x31533707c3feb3b10f7deeea387ff8893f229253e65ca6b14d2400bf95b5d103", + "sampleAccount": "0x4F96F50eDB37a19216d87693E5dB241e31bD3735" + }, + "info": { + "url": "https://boba.network/", + "source": "https://github.com/bobanetwork/boba", + "rpc": "https://mainnet.boba.network", + "documentation": "https://docs.boba.network/" + } + }, + { + "id": "metis", + "name": "Metis", + "coinId": 10001088, + "symbol": "METIS", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "1088", + "addressHasher": "keccak256", + "explorer": { + "url": "https://andromeda-explorer.metis.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x422f2ebbede32d4434ad0cf0ae55d44a84e14d3d5725a760133255b42676d8ce", + "sampleAccount": "0xBe9E8Ec25866B21bA34e97b9393BCabBcB4A5C86" + }, + "info": { + "url": "https://www.metis.io/", + "source": "https://github.com/MetisProtocol/mvm", + "rpc": "https://andromeda.metis.io/?owner=1088", + "documentation": "https://docs.metis.io/" + } + }, + { + "id": "aurora", + "name": "Aurora", + "coinId": 1323161554, + "symbol": "ETH", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "1313161554", + "addressHasher": "keccak256", + "explorer": { + "url": "https://aurorascan.dev", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x99deebdb70f8027037abb3d3d0f3c7523daee857d85e9056d2671593ff2f2f28", + "sampleAccount": "0x8707cdE20dd43E3dB1F74c28fcd509ef38B0bA51" + }, + "info": { + "url": "https://aurora.dev/", + "source": "https://github.com/aurora-is-near/aurora-engine", + "rpc": "https://mainnet.aurora.dev/", + "documentation": "https://doc.aurora.dev/" + } + }, + { + "id": "evmos", + "name": "Evmos", + "coinId": 10009001, + "symbol": "EVMOS", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "9001", + "addressHasher": "keccak256", + "explorer": { + "url": "https://evm.evmos.org", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x24af42cf4977a96d62e3a82c3cd9b519c3e7c53dd83398b88f0cb435d867b422", + "sampleAccount": "0x30627903124Aa1e71384bc52e1cb96E4AB3252b6" + }, + "info": { + "url": "https://evmos.org/", + "source": "https://github.com/tharsis/evmos", + "rpc": "https://eth.bd.evmos.org:8545", + "documentation": "https://docs.evmos.org/" + } + }, + { + "id": "nativeevmos", + "name": "NativeEvmos", + "displayName": "Native Evmos", + "coinId": 20009001, + "symbol": "EVMOS", + "decimals": 18, + "blockchain": "Cosmos", + "chainId": "evmos_9001-2", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "hrp": "evmos", + "addressHasher": "keccak256", + "explorer": { + "url": "https://mintscan.io/evmos", + "txPath": "/txs/", + "accountPath": "/account/", + "sampleTx": "A16C211C83AD1E684DE46F694FAAC17D8465C864BD7385A81EC062CDE0638811", + "sampleAccount": "evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34" + }, + "info": { + "url": "https://evmos.org/", + "client": "https://github.com/tharsis/evmos", + "clientPublic": "https://rest.bd.evmos.org:1317", + "clientDocs": "" + } + }, + { + "id": "moonriver", + "name": "Moonriver", + "coinId": 10001285, + "symbol": "MOVR", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "1285", + "explorer": { + "url": "https://moonriver.moonscan.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x2e2daa3943ba65d9bbb910a4f6765aa6a466a0ef8935090547ca9d30e201e032", + "sampleAccount": "0x899831D937937d011305E73EE782cce0455DF15a" + }, + "info": { + "url": "https://moonbeam.network/networks/moonriver", + "rpc": "https://moonriver.public.blastapi.io" + } + }, + { + "id": "moonbeam", + "name": "Moonbeam", + "coinId": 10001284, + "symbol": "GLMR", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "1284", + "explorer": { + "url": "https://moonscan.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0xb22a146c933e6e51affbfa5f712a266b5f5e92ae453cd2f252bcc3c36ff035a6", + "sampleAccount": "0x201bb4f276C765dF7225e5A4153E17edD23a67eC" + }, + "info": { + "url": "https://moonbeam.network", + "rpc": "https://rpc.api.moonbeam.network", + "documentation": "https://docs.moonbeam.network" + } + }, + { + "id": "klaytn", + "name": "Klaytn", + "coinId": 10008217, + "symbol": "KLAY", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "8217", + "explorer": { + "url": "https://scope.klaytn.com", + "txPath": "/tx/", + "accountPath": "/account/", + "sampleTx": "0x93ea92687845fe7bb6cacd69c76a16a2a3c2bbb85a8a93ff0e032d0098d583d7", + "sampleAccount": "0x2ad9656bf5b82caf10847b431012e28e301e83ba" + }, + "info": { + "url": "https://klaytn.foundation", + "rpc": "https://public-node-api.klaytnapi.com/v1/cypress", + "documentation": "https://docs.klaytn.foundation" + } + }, + { + "id": "meter", + "name": "Meter", + "coinId": 18000, + "chainId": "82", + "symbol": "MTR", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "explorer": { + "url": "https://scan.meter.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x8ea268d5dbb40217c763b800a75fc063cf28b56f40f2bc69dc043f5c4bbdc144", + "sampleAccount": "0xe5a273954d24eddf9ae9ea4cef2347d584cfa3dd" + }, + "info": { + "url": "https://meter.io/", + "source": "https://github.com/meterio/meter-pov", + "rpc": "https://rpc.meter.io", + "documentation": "https://docs.meter.io/" + } + }, + { + "id": "okc", + "name": "OKX Chain", + "coinId": 996, + "chainId": "66", + "symbol": "OKT", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "addressHasher": "keccak256", + "explorer": { + "url": "https://www.oklink.com/en/okc", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x46C3A947E8248570FBD28E4FE456CC8F80DFD90716533878FB67857B95FA3D37", + "sampleAccount": "0x074faafd0b20fad2efa115b8ed7e75993e580b85" + }, + "info": { + "url": "https://www.okx.com/okc", + "source": "https://github.com/okex/exchain", + "rpc": "https://exchainrpc.okex.org", + "documentation": "https://okc-docs.readthedocs.io/en/latest" + } } ] diff --git a/samples/android/app/build.gradle b/samples/android/app/build.gradle index abe39291f50..71479d4bac2 100644 --- a/samples/android/app/build.gradle +++ b/samples/android/app/build.gradle @@ -5,11 +5,11 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 29 + compileSdkVersion 32 defaultConfig { applicationId "com.trust.walletcore.example" minSdkVersion 23 - targetSdkVersion 29 + targetSdkVersion 32 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -23,7 +23,7 @@ android { } project.ext { - walletcore_version = "2.6.16" + walletcore_version = "2.9.8" } dependencies { diff --git a/samples/android/app/src/main/AndroidManifest.xml b/samples/android/app/src/main/AndroidManifest.xml index 8cb737a9b5d..a198932f7df 100644 --- a/samples/android/app/src/main/AndroidManifest.xml +++ b/samples/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,7 @@ android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> - + diff --git a/samples/android/build.gradle b/samples/android/build.gradle index ce4fc31295f..5aa93a5e31a 100644 --- a/samples/android/build.gradle +++ b/samples/android/build.gradle @@ -1,5 +1,15 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. +Properties properties = new Properties() +File localProps = new File(rootDir.absolutePath, "local.properties") +if (localProps.exists()) { + properties.load(localProps.newDataInputStream()) + println "Authenticating user: " + properties.getProperty("gpr.user") +} else { + println "local.properties not found, please create it next to build.gradle and set gpr.user and gpr.key (Create a GitHub package read only + non expiration token at https://github.com/settings/tokens)\n" + + "Or set GITHUB_USER and GITHUB_TOKEN environment variables" +} + buildscript { ext.kotlin_version = '1.3.50' repositories { @@ -7,7 +17,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.1' + classpath 'com.android.tools.build:gradle:7.2.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -19,8 +29,8 @@ allprojects { maven { url = uri("https://maven.pkg.github.com/trustwallet/wallet-core") credentials { - username = project.findProperty("gpr.user") as String?: System.getenv("GITHUB_USER") - password = project.findProperty("gpr.key") as String?: System.getenv("GITHUB_TOKEN") + username = properties.getProperty("gpr.user") as String?: System.getenv("GITHUB_USER") + password = properties.getProperty("gpr.key") as String?: System.getenv("GITHUB_TOKEN") } } } diff --git a/samples/android/gradle/wrapper/gradle-wrapper.properties b/samples/android/gradle/wrapper/gradle-wrapper.properties index 98e52a713b4..87163a3434d 100644 --- a/samples/android/gradle/wrapper/gradle-wrapper.properties +++ b/samples/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip diff --git a/samples/cpp/CMakeLists.txt b/samples/cpp/CMakeLists.txt index 58715bcf9ad..5ecf5e6ba0d 100644 --- a/samples/cpp/CMakeLists.txt +++ b/samples/cpp/CMakeLists.txt @@ -1,32 +1,42 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + # Expected input configuration: WALLET_CORE: directory for TrustWalletCore build dir # e.g. cmake . -DWALLET_CORE=../wallet-core -cmake_minimum_required (VERSION 3.4) +cmake_minimum_required (VERSION 3.8 FATAL_ERROR) project (wallet-core-demo-cpp) +if (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + message(FATAL_ERROR "You should use clang compiler") +endif() + set (SETUP_MESSAGE "Please provide TrustWalletCore build directory with -DWALLET_CORE. Example: cmake . -DWALLET_CORE=../wallet-core") if (NOT WALLET_CORE) message (FATAL_ERROR "${SETUP_MESSAGE}") endif () +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set (CMAKE_C_STANDARD 11) +set (CMAKE_C_STANDARD_REQUIRED ON) + # Include dirs: # ${WALLET_CORE}/include -- public TrustWalletCore includes # ${WALLET_CORE}/src -- internal TrustWalletCore files, for signer protobuf messages # ${WALLET_CORE}/build/local/include) -- for protobuf includes include_directories (${CMAKE_SOURCE_DIR} ${WALLET_CORE}/include ${WALLET_CORE}/src ${WALLET_CORE}/build/local/include) -link_directories (${WALLET_CORE}/build/install/lib ${WALLET_CORE}/build ${WALLET_CORE}/build/trezor-crypto ${WALLET_CORE}/build/local/lib) +link_directories (${WALLET_CORE}/build ${WALLET_CORE}/build/trezor-crypto ${WALLET_CORE}/build/local/lib) -find_library(WALLET_CORE_LIB_RELEASE TrustWalletCore PATH ${WALLET_CORE}/build ${WALLET_CORE}/build/install/lib) -find_library(WALLET_CORE_LIB_DEBUG TrustWalletCored PATH ${WALLET_CORE}/build ${WALLET_CORE}/build/install/lib) -if (NOT WALLET_CORE_LIB_RELEASE) +find_library(WALLET_CORE_LIB_FILE TrustWalletCore PATH ${WALLET_CORE}/build) +if (NOT WALLET_CORE_LIB_FILE) message (FATAL_ERROR "TrustWalletCore library not found. ${SETUP_MESSAGE}") else () - if (WALLET_CORE_LIB_DEBUG) - set (WALLET_CORE_LIBRARIES optimized ${WALLET_CORE_LIB_RELEASE} debug ${WALLET_CORE_LIB_DEBUG}) - else () - set (WALLET_CORE_LIBRARIES ${WALLET_CORE_LIB_RELEASE}) - endif () - message ("TrustWalletCore library found here: ${WALLET_CORE_LIBRARIES}") + message ("TrustWalletCore library found here: ${WALLET_CORE_LIB_FILE}") endif () # Create all libraries and executables in the root binary dir @@ -45,16 +55,6 @@ else () add_compile_options (-Werror=switch) endif () -if (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") -endif () - -set (CMAKE_C_STANDARD 11) -set (CMAKE_C_STANDARD_REQUIRED ON) - -set (CMAKE_CXX_STANDARD 14) -set (CMAKE_CXX_STANDARD_REQUIRED ON) - if (WIN32) add_definitions(/bigobj) endif () @@ -71,7 +71,4 @@ SET (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${PLATFORM_LINK_FLAGS}") add_executable (sample sample.cpp) # link with our library, and default platform libraries -target_link_libraries (sample ${WALLET_CORE_LIBRARIES} ${Protobuf_LIBRARIES} ${PLATFORM_LIBS}) -if (NOT WIN32) - target_link_libraries (TrezorCrypto) -endif () +target_link_libraries (sample TrustWalletCore TrezorCrypto protobuf pthread ${PLATFORM_LIBS}) diff --git a/samples/cpp/sample.cpp b/samples/cpp/sample.cpp index a50e807a031..bb69ec05b1c 100644 --- a/samples/cpp/sample.cpp +++ b/samples/cpp/sample.cpp @@ -17,106 +17,111 @@ using namespace std; int main() { - { - cout << endl; - cout << " Wallet Core Demo, C++" << endl; - cout << endl; - cout << " *** DISCLAIMER ***" << endl; - cout << " THIS IS A SAMPLE APPLICATION WITH DEMONSTRATION PURPOSES ONLY." << endl; - cout << " DO NOT USE WITH REAL SECRETS, REAL ADDRESSESS, OR REAL TRANSACTIONS. USE IT AT YOUR OWN RISK." << endl; - cout << " *** DISCLAIMER ***" << endl; - cout << endl; - } + try { + { + cout << endl; + cout << " Wallet Core Demo, C++" << endl; + cout << endl; + cout << " *** DISCLAIMER ***" << endl; + cout << " THIS IS A SAMPLE APPLICATION WITH DEMONSTRATION PURPOSES ONLY." << endl; + cout << " DO NOT USE WITH REAL SECRETS, REAL ADDRESSESS, OR REAL TRANSACTIONS. USE IT AT YOUR OWN RISK." << endl; + cout << " *** DISCLAIMER ***" << endl; + cout << endl; + } - TWHDWallet* walletImp = nullptr; - { - // Create a new multi-coin HD wallet, with new recovery phrase (mnemonic) - cout << "Creating a new HD wallet ... "; - TWHDWallet* walletNew = TWHDWalletCreate(128, TWStringCreateWithUTF8Bytes("")); - cout << "done." << endl; - cout << "Secret mnemonic for new wallet: '"; - cout << TWStringUTF8Bytes(TWHDWalletMnemonic(walletNew)) << "'." << endl; - TWHDWalletDelete(walletNew); + TWHDWallet* walletImp = nullptr; + { + // Create a new multi-coin HD wallet, with new recovery phrase (mnemonic) + cout << "Creating a new HD wallet ... "; + TWHDWallet* walletNew = TWHDWalletCreate(128, TWStringCreateWithUTF8Bytes("")); + cout << "done." << endl; + cout << "Secret mnemonic for new wallet: '"; + cout << TWStringUTF8Bytes(TWHDWalletMnemonic(walletNew)) << "'." << endl; + TWHDWalletDelete(walletNew); - // Alternative: Import wallet with existing recovery phrase (mnemonic) - cout << "Importing an HD wallet from earlier ... "; - auto secretMnemonic = TWStringCreateWithUTF8Bytes("ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"); - walletImp = TWHDWalletCreateWithMnemonic(secretMnemonic, TWStringCreateWithUTF8Bytes("")); - TWStringDelete(secretMnemonic); - cout << "done." << endl; - cout << "Secret mnemonic for imported wallet: '"; - cout << TWStringUTF8Bytes(TWHDWalletMnemonic(walletImp)) << "'." << endl; - cout << endl; - } + // Alternative: Import wallet with existing recovery phrase (mnemonic) + cout << "Importing an HD wallet from earlier ... "; + auto secretMnemonic = TWStringCreateWithUTF8Bytes("ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"); + walletImp = TWHDWalletCreateWithMnemonic(secretMnemonic, TWStringCreateWithUTF8Bytes("")); + TWStringDelete(secretMnemonic); + cout << "done." << endl; + cout << "Secret mnemonic for imported wallet: '"; + cout << TWStringUTF8Bytes(TWHDWalletMnemonic(walletImp)) << "'." << endl; + cout << endl; + } - { - // coin type: we use Ethereum - const TWCoinType coinType = TWCoinType::TWCoinTypeEthereum; // TWCoinTypeBitcoin, TWCoinTypeEthereum - cout << "Working with coin: " << - TWStringUTF8Bytes(TWCoinTypeConfigurationGetName(coinType)) << " " << - TWStringUTF8Bytes(TWCoinTypeConfigurationGetSymbol(coinType)) << endl; + { + // coin type: we use Ethereum + const TWCoinType coinType = TWCoinType::TWCoinTypeEthereum; // TWCoinTypeBitcoin, TWCoinTypeEthereum + cout << "Working with coin: " << + TWStringUTF8Bytes(TWCoinTypeConfigurationGetName(coinType)) << " " << + TWStringUTF8Bytes(TWCoinTypeConfigurationGetSymbol(coinType)) << endl; - // Derive default address. - cout << "Obtaining default address ... "; - string address = TWStringUTF8Bytes(TWHDWalletGetAddressForCoin(walletImp, coinType)); - cout << " done." << endl; - cout << "Default address: '" << address << "'" << endl; + // Derive default address. + cout << "Obtaining default address ... "; + string address = TWStringUTF8Bytes(TWHDWalletGetAddressForCoin(walletImp, coinType)); + cout << " done." << endl; + cout << "Default address: '" << address << "'" << endl; - // Alternative: Derive address using default derivation path. - // Done in 2 steps: derive private key, then address from private key. - // Note that private key is passed around between the two calls by the wallet -- be always cautious when handling secrets, avoid the risk of leaking secrets. - cout << "Default derivation path: " << TWStringUTF8Bytes(TWCoinTypeDerivationPath(coinType)) << endl; - TWPrivateKey* secretPrivateKeyDefault = TWHDWalletGetKeyForCoin(walletImp, coinType); - string addressDefault = TWStringUTF8Bytes(TWCoinTypeDeriveAddress(coinType, secretPrivateKeyDefault)); - cout << "Address from default key: '" << addressDefault << "'" << endl; + // Alternative: Derive address using default derivation path. + // Done in 2 steps: derive private key, then address from private key. + // Note that private key is passed around between the two calls by the wallet -- be always cautious when handling secrets, avoid the risk of leaking secrets. + cout << "Default derivation path: " << TWStringUTF8Bytes(TWCoinTypeDerivationPath(coinType)) << endl; + TWPrivateKey* secretPrivateKeyDefault = TWHDWalletGetKeyForCoin(walletImp, coinType); + string addressDefault = TWStringUTF8Bytes(TWCoinTypeDeriveAddress(coinType, secretPrivateKeyDefault)); + cout << "Address from default key: '" << addressDefault << "'" << endl; - // Alternative: Derive address using custom derivation path. Done in 2 steps: derive private key, then address. - auto customDerivationPath = TWStringCreateWithUTF8Bytes("m/44'/60'/1'/0/0"); - TWPrivateKey* secretPrivateKeyCustom = TWHDWalletGetKey(walletImp, coinType, customDerivationPath); - TWStringDelete(customDerivationPath); - string addressCustom = TWStringUTF8Bytes(TWCoinTypeDeriveAddress(coinType, secretPrivateKeyCustom)); - cout << "Custom-derived address: '" << addressCustom << "'" << endl; - cout << endl; + // Alternative: Derive address using custom derivation path. Done in 2 steps: derive private key, then address. + auto customDerivationPath = TWStringCreateWithUTF8Bytes("m/44'/60'/1'/0/0"); + TWPrivateKey* secretPrivateKeyCustom = TWHDWalletGetKey(walletImp, coinType, customDerivationPath); + TWStringDelete(customDerivationPath); + string addressCustom = TWStringUTF8Bytes(TWCoinTypeDeriveAddress(coinType, secretPrivateKeyCustom)); + cout << "Custom-derived address: '" << addressCustom << "'" << endl; + cout << endl; - cout << "RECEIVE funds: Perform send from somewehere else to this address: " << address << " ." << endl; - cout << endl; + cout << "RECEIVE funds: Perform send from somewehere else to this address: " << address << " ." << endl; + cout << endl; - // Steps for sending: - // 1. put together a send message (contains sender and receiver address, amount, gas price, etc.) - // 2. sign this message - // 3. broadcast this message to the P2P network -- not done in this sample - // Note that Signer input and output are represented as protobuf binary messages, for which support is missing in C++. - // Therefore some direct serialization/parsing is done in helper methods. - cout << "SEND funds:" << endl; - const string dummyReceiverAddress = "0xC37054b3b48C3317082E7ba872d7753D13da4986"; - auto secretPrivKey = TWPrivateKeyData(secretPrivateKeyDefault); + // Steps for sending: + // 1. put together a send message (contains sender and receiver address, amount, gas price, etc.) + // 2. sign this message + // 3. broadcast this message to the P2P network -- not done in this sample + // Note that Signer input and output are represented as protobuf binary messages, for which support is missing in C++. + // Therefore some direct serialization/parsing is done in helper methods. + cout << "SEND funds:" << endl; + const string dummyReceiverAddress = "0xC37054b3b48C3317082E7ba872d7753D13da4986"; + auto secretPrivKey = TWPrivateKeyData(secretPrivateKeyDefault); - cout << "preparing transaction (using AnySigner) ... "; - string chainIdB64 = "AQ=="; // base64(parse_hex("01")) - string gasPriceB64 = "1pOkAA=="; // base64(parse_hex("d693a4")) decimal 3600000000 - string gasLimitB64 = "Ugg="; // base64(parse_hex("5208")) decimal 21000 - string amountB64 = "A0i8paFgAA=="; // base64(parse_hex("0348bca5a160")) 924400000000000 - string transaction = "{" - "\"chainId\":\"" + chainIdB64 + - "\",\"gasPrice\":\"" + gasPriceB64 + - "\",\"gasLimit\":\"" + gasLimitB64 + - "\",\"toAddress\":\"" + dummyReceiverAddress + - "\",\"transaction\":{\"transfer\":{\"amount\":\"" + amountB64 + - "\"}}}"; - cout << "transaction: " << transaction << endl; + cout << "preparing transaction (using AnySigner) ... "; + string chainIdB64 = "AQ=="; // base64(parse_hex("01")) + string gasPriceB64 = "1pOkAA=="; // base64(parse_hex("d693a4")) decimal 3600000000 + string gasLimitB64 = "Ugg="; // base64(parse_hex("5208")) decimal 21000 + string amountB64 = "A0i8paFgAA=="; // base64(parse_hex("0348bca5a160")) 924400000000000 + string transaction = "{" + "\"chainId\":\"" + chainIdB64 + + "\",\"gasPrice\":\"" + gasPriceB64 + + "\",\"gasLimit\":\"" + gasLimitB64 + + "\",\"toAddress\":\"" + dummyReceiverAddress + + "\",\"transaction\":{\"transfer\":{\"amount\":\"" + amountB64 + + "\"}}}"; + cout << "transaction: " << transaction << endl; - cout << "signing transaction ... "; + cout << "signing transaction ... "; - auto json = TWStringCreateWithUTF8Bytes(transaction.c_str()); - auto result = TWAnySignerSignJSON(json, secretPrivKey, TWCoinTypeEthereum); - auto signedTransaction = string(TWStringUTF8Bytes(result)); - cout << "done" << endl; - cout << "Signed transaction data (to be broadcast to network): (len " << signedTransaction.length() << ") '" << signedTransaction << "'" << endl; - // see e.g. https://github.com/flightwallet/decode-eth-tx for checking binary output content - cout << endl; - TWStringDelete(json); - TWStringDelete(result); + auto json = TWStringCreateWithUTF8Bytes(transaction.c_str()); + auto result = TWAnySignerSignJSON(json, secretPrivKey, TWCoinTypeEthereum); + auto signedTransaction = string(TWStringUTF8Bytes(result)); + cout << "done" << endl; + cout << "Signed transaction data (to be broadcast to network): (len " << signedTransaction.length() << ") '" << signedTransaction << "'" << endl; + // see e.g. https://github.com/flightwallet/decode-eth-tx for checking binary output content + cout << endl; + TWStringDelete(json); + TWStringDelete(result); + } + cout << "Bye!" << endl; + TWHDWalletDelete(walletImp); + } catch (const exception& ex) { + cout << "EXCEPTION: " << ex.what() << endl; + throw ex; } - cout << "Bye!" << endl; - TWHDWalletDelete(walletImp); } diff --git a/samples/go/README.md b/samples/go/README.md index d28a5dd0ad6..96716cabb90 100644 --- a/samples/go/README.md +++ b/samples/go/README.md @@ -1,18 +1,18 @@ # Sample Go Integration for [Wallet-Core](https://github.com/trustwallet/wallet-core) -## Overview +## 🔖 Overview This folder contains a small **Go** sample integration with [Wallet Core](https://github.com/trustwallet/wallet-core) library (part of [Trust Wallet](https://trustwallet.com)), using [cgo](https://golang.org/cmd/cgo/). -## DISCLAIMER +## ⚠️ DISCLAIMER > This is a sample application with demonstration purpose only, > do not use it with real addresses, real transactions, or real funds. > Use it at your own risk. -## Documentation +## 📜 Documentation See the official [Trust Wallet developer documentation here](https://developer.trustwallet.com). @@ -20,31 +20,57 @@ See especially Wallet Core [Integration Guide](https://developer.trustwallet.com/wallet-core/integration-guide), and [Build Instructions](https://developer.trustwallet.com/wallet-core/building). -## Prerequisites +## 🛠 Prerequisites -* Docker +`macOS` or `Docker` -## Building and Running +## ⚙️ Building and Running +###  macOS +#### Prerequisites on macOS +* CMake `brew install cmake` +* Boost `brew install boost` +* Xcode +* Xcode command line tools: `xcode-select --install` +* Other tools: `brew install git ninja autoconf automake libtool xcodegen clang-format` +* GoLang: [download](https://go.dev/dl/) +* Protobuf: `brew install protobuf protoc-gen-go` +#### Full Build + +1. Clone the wallet-core repo and go inside: +```shell +git clone https://github.com/trustwallet/wallet-core.git + +cd wallet-core +``` +2. The full build can be triggered with one top-level script: +```shell +./bootstrap.sh +``` + +### 🐳 Docker 1. Run `docker run -it trustwallet/wallet-core` The librabry is already built in this image (Build instructions [here](building.md)) Note: may not be the most recent version. 2. Install go: `apt-get update && apt-get install golang` -(or download from here [go1.14.2](https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz), configure `GOROOT` and append `GOROOT/bin` to `PATH`). -3. Go to the **samples/go** folder within wallet core repo: +(or download from here [go1.16.12](https://go.dev/dl/go1.16.12.linux-amd64.tar.gz), configure `GOROOT` and append `GOROOT/bin` to `PATH`). + +### 🏃🏽‍♂️ **Run** (macOS & Docker) +1. Go to the **samples/go** folder within wallet core repo: ```shell -git clone https://github.com/trustwallet/wallet-core.git cd wallet-core/samples/go ``` -4. Compile it by `go build -o main`. Relavant source file is `main.go`. +2. Compile it by `go build -o main`. Relavant source file is `main.go`. -5. Run `./main` and you will see the output below: +3. Run `./main` and you will see the output below: ```shell ==> calling wallet core from go ==> mnemonic is valid: true -==> bitcoin... +==> Bitcoin... ``` -6. You might want to copy and run `main` outside of the docker container, make sure you have `libc++1` and `libc++abi1` installed in your host Ubuntu. +4. *(optional)* You might want to copy and run `main` outside of the docker container, make sure you have `libc++1` and `libc++abi1` installed in your host Ubuntu. + +5. *(optional)* If you want to make transaction on other networks you need to compile `src/proto` proto files and to do that, just run the `./compile.sh` . you can also modify it based on your project. \ No newline at end of file diff --git a/samples/go/compile.sh b/samples/go/compile.sh new file mode 100644 index 00000000000..a0484037828 --- /dev/null +++ b/samples/go/compile.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Run this file ./compile.sh to generate all go protobuf file from src/proto + +# Clean +rm -rf protos +mkdir protos + +PROTO_PATH=../../src/proto +for FILE in "$PROTO_PATH"/*.proto; do + # Reading proto files + FILE_NAME="${FILE#"$PROTO_PATH"/}" + PKG=$(echo "${FILE_NAME%.proto}" | tr '[:upper:]' '[:lower:]') + # Generate Go protobuf files + # + # manual --go_opt=M... declarations is because of + # dependencies between some proto files + mkdir protos/"$PKG" + protoc -I=$PROTO_PATH --go_out=protos/"$PKG" \ + --go_opt=paths=source_relative \ + --go_opt=M"$FILE_NAME"=tw/protos/"$PKG" \ + --go_opt=MCommon.proto=tw/protos/common \ + --go_opt=MBitcoin.proto=tw/protos/bitcoin \ + --go_opt=MEthereum.proto=tw/protos/ethereum \ + --go_opt=MBinance.proto=tw/protos/binance \ + "$PROTO_PATH"/"$FILE_NAME" +done diff --git a/samples/go/core/bitcoin.go b/samples/go/core/bitcoin.go new file mode 100644 index 00000000000..5613b452ffa --- /dev/null +++ b/samples/go/core/bitcoin.go @@ -0,0 +1,54 @@ +package core + +// #cgo CFLAGS: -I../../../include +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #include +// #include +// #include +import "C" + +import "tw/types" + +const ( + BitcoinSigHashTypeAll = C.TWBitcoinSigHashTypeAll + BitcoinSigHashTypeNone = C.TWBitcoinSigHashTypeNone + BitcoinSigHashTypeSingle = C.TWBitcoinSigHashTypeSingle + BitcoinSigHashTypeFork = C.TWBitcoinSigHashTypeFork + BitcoinSigHashTypeForkBTG = C.TWBitcoinSigHashTypeForkBTG + BitcoinSigHashTypeAnyoneCanPay = C.TWBitcoinSigHashTypeAnyoneCanPay +) + +func BitcoinScriptLockScriptForAddress(addr string, ct CoinType) []byte { + address := types.TWStringCreateWithGoString(addr) + defer C.TWStringDelete(address) + + script := C.TWBitcoinScriptLockScriptForAddress(address, C.enum_TWCoinType(ct)) + scriptData := C.TWBitcoinScriptData(script) + defer C.TWBitcoinScriptDelete(script) + defer C.TWDataDelete(scriptData) + + return types.TWDataGoBytes(scriptData) +} + +func BitcoinScriptBuildPayToPublicKeyHash(hash []byte) []byte { + hashData := types.TWDataCreateWithGoBytes(hash) + defer C.TWDataDelete(hashData) + + script := C.TWBitcoinScriptBuildPayToPublicKeyHash(hashData) + scriptData := C.TWBitcoinScriptData(script) + defer C.TWBitcoinScriptDelete(script) + defer C.TWDataDelete(scriptData) + + return types.TWDataGoBytes(scriptData) +} + +func BitcoinScriptMatchPayToWitnessPublicKeyHash(script []byte) []byte { + scriptData := types.TWDataCreateWithGoBytes(script) + defer C.TWDataDelete(scriptData) + scriptObj := C.TWBitcoinScriptCreateWithData(scriptData) + defer C.TWBitcoinScriptDelete(scriptObj) + + hash := C.TWBitcoinScriptMatchPayToWitnessPublicKeyHash(scriptObj) + defer C.TWDataDelete(hash) + return types.TWDataGoBytes(hash) +} diff --git a/samples/go/core/coin.go b/samples/go/core/coin.go new file mode 100644 index 00000000000..122e9a286fb --- /dev/null +++ b/samples/go/core/coin.go @@ -0,0 +1,28 @@ +package core + +// #cgo CFLAGS: -I../../../include +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #include +// #include +import "C" + +import "tw/types" + +type CoinType uint32 + +const ( + CoinTypeBitcoin CoinType = C.TWCoinTypeBitcoin + CoinTypeBinance CoinType = C.TWCoinTypeBinance + CoinTypeEthereum CoinType = C.TWCoinTypeEthereum + CoinTypeTron CoinType = C.TWCoinTypeTron +) + +func (c CoinType) GetName() string { + name := C.TWCoinTypeConfigurationGetName(C.enum_TWCoinType(c)) + defer C.TWStringDelete(name) + return types.TWStringGoString(name) +} + +func (c CoinType) Decimals() int { + return int(C.TWCoinTypeConfigurationGetDecimals(C.enum_TWCoinType(c))) +} diff --git a/samples/go/core/datavector.go b/samples/go/core/datavector.go new file mode 100644 index 00000000000..014030e8817 --- /dev/null +++ b/samples/go/core/datavector.go @@ -0,0 +1,31 @@ +package core + +// #cgo CFLAGS: -I../../../include +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #include +import "C" +import "tw/types" + +type TWDataVector *C.struct_TWDataVector + +// Go [][]byte -> C.TWDataVector +func TWDataVectorCreateWithGoBytes(d [][]byte) TWDataVector { + vec := C.TWDataVectorCreate() + for i := 0; i < len(d); i++ { + elem := types.TWDataCreateWithGoBytes(d[i]) + C.TWDataVectorAdd(vec, elem) + } + return vec +} + +// C.TWDataVector -> Go [][]byte +func TWDataVectorGoBytes(d *C.struct_TWDataVector) [][]byte { + var vec [][]byte + cSize := int(C.TWDataVectorSize(d)) + for i := 0; i < cSize; i++ { + elemC := C.TWDataVectorGet(d, C.ulong(i)) + elemG := types.TWDataGoBytes(elemC) + vec = append(vec, elemG) + } + return vec +} diff --git a/samples/go/core/mnemonic.go b/samples/go/core/mnemonic.go new file mode 100644 index 00000000000..12394cc86b9 --- /dev/null +++ b/samples/go/core/mnemonic.go @@ -0,0 +1,14 @@ +package core + +// #cgo CFLAGS: -I../../../include +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #include +import "C" + +import "tw/types" + +func IsMnemonicValid(mn string) bool { + str := types.TWStringCreateWithGoString(mn) + defer C.TWStringDelete(str) + return bool(C.TWMnemonicIsValid(str)) +} diff --git a/samples/go/core/publicKey.go b/samples/go/core/publicKey.go new file mode 100644 index 00000000000..d116f8e7270 --- /dev/null +++ b/samples/go/core/publicKey.go @@ -0,0 +1,41 @@ +package core + +// #cgo CFLAGS: -I../../../include +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #include +import "C" + +import "tw/types" + +type PublicKeyType uint32 + +const ( + PublicKeyTypeSECP256k1 PublicKeyType = C.TWPublicKeyTypeSECP256k1 + PublicKeyTypeSECP256k1Extended PublicKeyType = C.TWPublicKeyTypeSECP256k1Extended +) + +func PublicKeyVerify(key []byte, keyType PublicKeyType, signature []byte, message []byte) bool { + keyData := types.TWDataCreateWithGoBytes(key) + defer C.TWDataDelete(keyData) + publicKey := C.TWPublicKeyCreateWithData(keyData, C.enum_TWPublicKeyType(keyType)) + defer C.TWPublicKeyDelete(publicKey) + sig := types.TWDataCreateWithGoBytes(signature) + defer C.TWDataDelete(sig) + msg := types.TWDataCreateWithGoBytes(message) + defer C.TWDataDelete(msg) + + return bool(C.TWPublicKeyVerify(publicKey, sig, msg)) +} + +func PublicKeyVerifyAsDER(key []byte, keyType PublicKeyType, signature []byte, message []byte) bool { + keyData := types.TWDataCreateWithGoBytes(key) + defer C.TWDataDelete(keyData) + publicKey := C.TWPublicKeyCreateWithData(keyData, C.enum_TWPublicKeyType(keyType)) + defer C.TWPublicKeyDelete(publicKey) + sig := types.TWDataCreateWithGoBytes(signature) + defer C.TWDataDelete(sig) + msg := types.TWDataCreateWithGoBytes(message) + defer C.TWDataDelete(msg) + + return bool(C.TWPublicKeyVerifyAsDER(publicKey, sig, msg)) +} diff --git a/samples/go/core/transaction.go b/samples/go/core/transaction.go new file mode 100644 index 00000000000..3b3333d5a11 --- /dev/null +++ b/samples/go/core/transaction.go @@ -0,0 +1,28 @@ +package core + +// #cgo CFLAGS: -I../../../include +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #include +// #include +import "C" + +import ( + "tw/types" + + "google.golang.org/protobuf/proto" +) + +func CreateSignedTx(inputData proto.Message, ct CoinType, outputData proto.Message) error { + ibytes, _ := proto.Marshal(inputData) + idata := types.TWDataCreateWithGoBytes(ibytes) + defer C.TWDataDelete(idata) + + odata := C.TWAnySignerSign(idata, C.enum_TWCoinType(ct)) + defer C.TWDataDelete(odata) + + err := proto.Unmarshal(types.TWDataGoBytes(odata), outputData) + if err != nil { + return err + } + return nil +} diff --git a/samples/go/core/transactionHelper.go b/samples/go/core/transactionHelper.go new file mode 100644 index 00000000000..c8414724a32 --- /dev/null +++ b/samples/go/core/transactionHelper.go @@ -0,0 +1,49 @@ +package core + +// #cgo CFLAGS: -I../../../include +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #include +import "C" +import "tw/types" + +func BuildInput(c CoinType, from, to string, amount string, asset string, memo string, chainId string) []byte { + fromStr := types.TWStringCreateWithGoString(from) + defer C.TWStringDelete(fromStr) + toStr := types.TWStringCreateWithGoString(to) + defer C.TWStringDelete(toStr) + amountStr := types.TWStringCreateWithGoString(amount) + defer C.TWStringDelete(amountStr) + assetStr := types.TWStringCreateWithGoString(asset) + defer C.TWStringDelete(assetStr) + memoStr := types.TWStringCreateWithGoString(memo) + defer C.TWStringDelete(memoStr) + chainIdStr := types.TWStringCreateWithGoString(chainId) + defer C.TWStringDelete(chainIdStr) + + result := C.TWTransactionCompilerBuildInput(C.enum_TWCoinType(c), fromStr, toStr, amountStr, assetStr, memoStr, chainIdStr) + defer C.TWDataDelete(result) + return types.TWDataGoBytes(result) +} + +func PreImageHashes(c CoinType, txInputData []byte) []byte { + input := types.TWDataCreateWithGoBytes(txInputData) + defer C.TWDataDelete(input) + + result := C.TWTransactionCompilerPreImageHashes(C.enum_TWCoinType(c), input) + defer C.TWDataDelete(result) + return types.TWDataGoBytes(result) +} + +func CompileWithSignatures(c CoinType, txInputData []byte, signatures [][]byte, publicKeyHashes [][]byte) []byte { + input := types.TWDataCreateWithGoBytes(txInputData) + defer C.TWDataDelete(input) + + sigs := TWDataVectorCreateWithGoBytes(signatures) + defer C.TWDataVectorDelete(sigs) + pubkeyhashes := TWDataVectorCreateWithGoBytes(publicKeyHashes) + defer C.TWDataVectorDelete(pubkeyhashes) + + result := C.TWTransactionCompilerCompileWithSignatures(C.enum_TWCoinType(c), input, sigs, pubkeyhashes) + defer C.TWDataDelete(result) + return types.TWDataGoBytes(result) +} diff --git a/samples/go/core/wallet.go b/samples/go/core/wallet.go new file mode 100644 index 00000000000..09af05cdd42 --- /dev/null +++ b/samples/go/core/wallet.go @@ -0,0 +1,54 @@ +package core + +// #cgo CFLAGS: -I../../../include +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm +// #include +// #include +// #include +import "C" + +import ( + "errors" + "tw/types" +) + +type Wallet struct { + Address string + PriKey string + PubKey string + CoinType +} + +func CreateWalletWithMnemonic(mn string, ct CoinType) (*Wallet, error) { + if !IsMnemonicValid(mn) { + return nil, errors.New("mnemonic is not valid") + } + + str := types.TWStringCreateWithGoString(mn) + empty := types.TWStringCreateWithGoString("") + defer C.TWStringDelete(str) + defer C.TWStringDelete(empty) + + tw := C.TWHDWalletCreateWithMnemonic(str, empty) + defer C.TWHDWalletDelete(tw) + + priKey := C.TWHDWalletGetKeyForCoin(tw, C.enum_TWCoinType(ct)) + defer C.TWPrivateKeyDelete(priKey) + priKeyData := C.TWPrivateKeyData(priKey) + defer C.TWDataDelete(priKeyData) + + pubKey := C.TWPrivateKeyGetPublicKeySecp256k1(priKey, true) + defer C.TWPublicKeyDelete(pubKey) + pubKeyData := C.TWPublicKeyData(pubKey) + defer C.TWDataDelete(pubKeyData) + + address := C.TWHDWalletGetAddressForCoin(tw, C.enum_TWCoinType(ct)) + defer C.TWStringDelete(address) + + return &Wallet{ + Address: types.TWStringGoString(address), + PriKey: types.TWDataHexString(priKeyData), + PubKey: types.TWDataHexString(pubKeyData), + CoinType: ct, + }, nil +} diff --git a/samples/go/dev-console/.gitignore b/samples/go/dev-console/.gitignore new file mode 100644 index 00000000000..6320cd248dd --- /dev/null +++ b/samples/go/dev-console/.gitignore @@ -0,0 +1 @@ +data \ No newline at end of file diff --git a/samples/go/dev-console/README.md b/samples/go/dev-console/README.md new file mode 100644 index 00000000000..9c1b0ed6495 --- /dev/null +++ b/samples/go/dev-console/README.md @@ -0,0 +1,12 @@ +## Quick start + +Compile wallet core with `${PROJECT_ROOT}/bootstrap.sh` +Use `./prepare.sh` +Compile the cli with `cd cmd && go build -o ../tw_dev_console && cd -` +Start `./tw_dev_console` + +If there is any compilation error on MacOS you can try the following command: + +`go get -u golang.org/x/sys` + +source: https://stackoverflow.com/questions/71507321/go-1-18-build-error-on-mac-unix-syscall-darwin-1-13-go253-golinkname-mus \ No newline at end of file diff --git a/samples/go/dev-console/cli/address.go b/samples/go/dev-console/cli/address.go new file mode 100644 index 00000000000..d2d11a8d6f7 --- /dev/null +++ b/samples/go/dev-console/cli/address.go @@ -0,0 +1,23 @@ +package cli + +import ( + "dev-console/native" + "dev-console/wallet" + "github.com/kyokomi/emoji/v2" +) + +func DumpAllAddress() { + if wallet.GlobalWallet == nil || !wallet.GlobalWallet.Ks.IsLoaded() { + emoji.Printf(":warning:No wallet loaded, use load_wallet or create_wallet first :warning:\n") + return + } + nbWallets := wallet.GlobalWallet.Ks.AccountCount() + var accounts []*native.Account + for idx := int32(0); idx < nbWallets; idx++ { + accounts = append(accounts, wallet.GlobalWallet.Ks.Account(idx)) + } + native.ToTableAccounts(accounts) + for _, account := range accounts { + account.Delete() + } +} diff --git a/samples/go/dev-console/cli/completer.go b/samples/go/dev-console/cli/completer.go new file mode 100644 index 00000000000..15021fec4d5 --- /dev/null +++ b/samples/go/dev-console/cli/completer.go @@ -0,0 +1,37 @@ +package cli + +import ( + "github.com/c-bata/go-prompt" + "strings" +) + +var gCommands = []prompt.Suggest{ + {Text: "exit", Description: "Quit the CLI"}, + {Text: "create_wallet", Description: "Create a new wallet"}, + {Text: "load_wallet", Description: "Load an existing wallet"}, + {Text: "delete_wallet", Description: "Delete an existing wallet"}, + {Text: "address_all", Description: "Show the addresses of all accounts from the current wallet"}, + {Text: "help", Description: "Show the global help"}, +} + +type Completer struct { +} + +func NewCompleter() (*Completer, error) { + return &Completer{}, nil +} + +func (c *Completer) argumentsCompleter(args []string) []prompt.Suggest { + if len(args) <= 1 { + return prompt.FilterContains(gCommands, args[0], true) + } + return []prompt.Suggest{} +} + +func (c *Completer) Complete(d prompt.Document) []prompt.Suggest { + if d.TextBeforeCursor() == "" { + return []prompt.Suggest{} + } + args := strings.Split(d.TextBeforeCursor(), " ") + return c.argumentsCompleter(args) +} diff --git a/samples/go/dev-console/cli/create_wallet.go b/samples/go/dev-console/cli/create_wallet.go new file mode 100644 index 00000000000..11eb80914f4 --- /dev/null +++ b/samples/go/dev-console/cli/create_wallet.go @@ -0,0 +1,118 @@ +package cli + +import ( + "dev-console/native" + "dev-console/wallet" + "fmt" + "github.com/kyokomi/emoji/v2" + "github.com/manifoldco/promptui" + "log" + "path/filepath" +) + +func ConfigureMnemonic() *native.Wallet { + prompt := promptui.Select{ + Label: "Passphrase Configuration", + Items: []string{"Generate a Seed", "Restore a Seed"}, + } + _, result, _ := prompt.Run() + + if result == "Generate a Seed" { + return generateSeed() + } else if result == "Restore a Seed" { + return restoreSeed() + } + return nil +} + +func restoreSeed() *native.Wallet { + promptRestoreSeed := promptui.Prompt{ + Label: "Please enter your seed", + } + resultSeed, err := promptRestoreSeed.Run() + if err != nil { + fmt.Printf("Prompt failed %v\n", err) + return restoreSeed() + } + + if len(resultSeed) == 0 { + fmt.Println("You're custom seed cannot be empty, please try again") + return restoreSeed() + } + + if !native.IsMnemonicValid(resultSeed) { + fmt.Println("You're seed is not a valid bip39 seed, please retry") + return restoreSeed() + } + + wallet, err := native.NewWalletWithMnemonic(resultSeed) + if err != nil { + log.Fatalf("Couldn't create the wallet: %v", err) + } + return wallet +} + +func generateSeed() *native.Wallet { + wallet := native.NewWalletWithRandomMnemonic() + return wallet +} + +func ConfigureWalletName() string { + promptWalletName := promptui.Prompt{ + Label: "Please enter your wallet name", + } + walletName, err := promptWalletName.Run() + if err != nil { + fmt.Printf("Prompt failed %v\n", err) + walletName = ConfigureWalletName() + } + + if len(walletName) == 0 { + fmt.Println("You're custom seed cannot be empty, please try again") + walletName = ConfigureWalletName() + } + + return walletName +} + +func ConfigurePassword() string { + promptPassword := promptui.Prompt{ + Label: "Please, choose a wallet password", + } + walletPassword, err := promptPassword.Run() + if err != nil { + fmt.Printf("Prompt failed %v\n", err) + walletPassword = ConfigureWalletName() + } + + if len(walletPassword) == 0 { + fmt.Println("You're password cannot be empty, please try again") + walletPassword = ConfigureWalletName() + } + + // Do We really need to check if the password is strong? It's a dev console. + return walletPassword +} + +func CreateWallet() { + walletName := ConfigureWalletName() + freshWallet := ConfigureMnemonic() + password := ConfigurePassword() + defer freshWallet.Delete() + storedKey := native.NewStoredKeyFromHDWallet(freshWallet.Mnemonic(), walletName, password, native.CoinTypeBitcoin) + storedKey.AccountForCoin(password, native.CoinTypeBinance) + storedKey.AccountForCoin(password, native.CoinTypeEthereum) + if storedKey != nil { + res := storedKey.Store(filepath.Join(wallet.GetWalletDataDirectory(), walletName+".json")) + if res { + _, _ = emoji.Println("Wallet successfully created :white_check_mark:") + // the global wallet can be loaded on creation or with load_wallet `wallet_name` - afterwards we can query accounts and address + // open to change the package name + // I guess keep the password the time the app is open is OK, no need to query it again + wallet.GlobalWallet = &wallet.Wallet{Ks: storedKey, Password: password, WalletName: walletName} + wallet.GlobalWallet.Dump() + } + } else { + fmt.Println("Couldn't create the wallet") + } +} diff --git a/samples/go/dev-console/cli/delete_wallet.go b/samples/go/dev-console/cli/delete_wallet.go new file mode 100644 index 00000000000..cd61a2b26b6 --- /dev/null +++ b/samples/go/dev-console/cli/delete_wallet.go @@ -0,0 +1,41 @@ +package cli + +import ( + "dev-console/native" + "dev-console/wallet" + "fmt" + "github.com/kyokomi/emoji/v2" + "log" + "os" + "path/filepath" +) + +func DeleteWallet() { + wallets := listWallets() + if len(wallets) == 0 { + fmt.Println("No wallets found, use create_wallet instead.") + return + } + walletName := chooseWallet(wallets) + walletPath := filepath.Join(wallet.GetWalletDataDirectory(), walletName+".json") + + walletPassword := queryWalletPassword() + storedKey := native.Load(walletPath) + if storedKey.IsLoaded() { + + if hdWallet := storedKey.Wallet(walletPassword); hdWallet.IsValid() { + e := os.Remove(walletPath) + if e != nil { + log.Fatal(e) + } + wallet.GlobalWallet = nil + emoji.Printf("Wallet %s successfully deleted :white_check_mark:\n", walletName) + defer hdWallet.Delete() + } else { + fmt.Println("Password from the wallet is incorrect") + DeleteWallet() + } + } else { + fmt.Println("Can't load the wallet.") + } +} diff --git a/samples/go/dev-console/cli/executor.go b/samples/go/dev-console/cli/executor.go new file mode 100644 index 00000000000..7c157234783 --- /dev/null +++ b/samples/go/dev-console/cli/executor.go @@ -0,0 +1,28 @@ +package cli + +import ( + "fmt" + "os" + "strings" +) + +func Executor(fullCommand string) { + fullCommand = strings.TrimSpace(fullCommand) + command := strings.Split(fullCommand, " ") + switch command[0] { + case "create_wallet": + CreateWallet() + case "load_wallet": + LoadWallet() + case "delete_wallet": + DeleteWallet() + case "address_all": + DumpAllAddress() + case "help": + ShowGlobalHelp() + case "exit": + fmt.Println("Quitting the CLI") + os.Exit(0) + } + return +} diff --git a/samples/go/dev-console/cli/help.go b/samples/go/dev-console/cli/help.go new file mode 100644 index 00000000000..c47277c9029 --- /dev/null +++ b/samples/go/dev-console/cli/help.go @@ -0,0 +1,36 @@ +package cli + +import ( + "github.com/olekukonko/tablewriter" + "os" +) + +const ( + createWalletHelp = `The create_wallet command create a new wallet with a given name and password. +You can choose to restore or generate a seed.` + createWalletUsage = `create_wallet` + loadWalletHelp = `The load_wallet command load the current wallet, the password will be prompted.` + loadWalletUsage = `load_wallet` + deleteWalletHelp = `The delete_wallet command delete the chosen wallet, password will be prompted.` + deleteWalletUsage = `delete_wallet` + addressAllHelp = `The address_all command dump a table of all address accounts of the current loaded wallet. +Wallet need to be loaded with load_wallet before usage.` + addressAllUsage = `address_all` +) + +func ShowGlobalHelp() { + data := [][]string{ + {"create_wallet", "", createWalletHelp, createWalletUsage}, + {"load_wallet", "", loadWalletHelp, loadWalletUsage}, + {"delete_wallet", "", deleteWalletHelp, deleteWalletUsage}, + {"address_all", "", addressAllHelp, addressAllUsage}, + } + + table := tablewriter.NewWriter(os.Stdout) + table.SetAutoWrapText(false) + table.SetHeader([]string{"Command", "Args", "Description", "Usage"}) + table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) + table.SetCenterSeparator("|") + table.AppendBulk(data) // Add Bulk Data + table.Render() +} diff --git a/samples/go/dev-console/cli/load_wallet.go b/samples/go/dev-console/cli/load_wallet.go new file mode 100644 index 00000000000..c9f2968ce9c --- /dev/null +++ b/samples/go/dev-console/cli/load_wallet.go @@ -0,0 +1,77 @@ +package cli + +import ( + "dev-console/native" + "dev-console/wallet" + "fmt" + "github.com/manifoldco/promptui" + "log" + "os" + "path/filepath" + "strings" +) + +func listWallets() []string { + files, err := os.ReadDir(wallet.GetWalletDataDirectory()) + if err != nil { + log.Fatal(err) + } + + var wallets []string + for _, file := range files { + wallets = append(wallets, strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))) + } + return wallets +} + +func chooseWallet(wallets []string) string { + prompt := promptui.Select{ + Label: "Choose the wallet to load", + Items: wallets, + } + _, result, _ := prompt.Run() + return result +} + +func queryWalletPassword() string { + promptPassword := promptui.Prompt{ + Label: "Please, enter your wallet password", + } + walletPassword, err := promptPassword.Run() + if err != nil { + fmt.Printf("Prompt failed %v\n", err) + walletPassword = queryWalletPassword() + } + + if len(walletPassword) == 0 { + fmt.Println("You're password cannot be empty, please try again") + walletPassword = queryWalletPassword() + } + + return walletPassword +} + +func LoadWallet() { + wallets := listWallets() + if len(wallets) == 0 { + fmt.Println("No wallets found, use create_wallet instead.") + return + } + walletName := chooseWallet(wallets) + walletPath := filepath.Join(wallet.GetWalletDataDirectory(), walletName+".json") + walletPassword := queryWalletPassword() + storedKey := native.Load(walletPath) + if storedKey.IsLoaded() { + wallet.GlobalWallet = &wallet.Wallet{Ks: storedKey, Password: walletPassword, WalletName: walletName} + if hdWallet := storedKey.Wallet(walletPassword); hdWallet.IsValid() { + fmt.Printf("Wallet %s successfully loaded\n", walletName) + defer hdWallet.Delete() + } else { + fmt.Println("Password from the wallet is incorrect") + wallet.GlobalWallet = nil + LoadWallet() + } + } else { + fmt.Println("Can't load the wallet.") + } +} diff --git a/samples/go/dev-console/cmd/cli.go b/samples/go/dev-console/cmd/cli.go new file mode 100644 index 00000000000..07ee64ec2a0 --- /dev/null +++ b/samples/go/dev-console/cmd/cli.go @@ -0,0 +1,15 @@ +package main + +import ( + "dev-console/cli" + "github.com/c-bata/go-prompt" +) + +func main() { + completer, _ := cli.NewCompleter() + p := prompt.New( + cli.Executor, + completer.Complete, + ) + p.Run() +} diff --git a/samples/go/dev-console/go.mod b/samples/go/dev-console/go.mod new file mode 100644 index 00000000000..f134213cc0d --- /dev/null +++ b/samples/go/dev-console/go.mod @@ -0,0 +1,20 @@ +module dev-console + +go 1.19 + +require ( + github.com/c-bata/go-prompt v0.2.6 + github.com/kyokomi/emoji/v2 v2.2.10 + github.com/manifoldco/promptui v0.9.0 + github.com/olekukonko/tablewriter v0.0.5 +) + +require ( + github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect + github.com/mattn/go-colorable v0.1.7 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/mattn/go-tty v0.0.3 // indirect + github.com/pkg/term v1.2.0-beta.2 // indirect + golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 // indirect +) diff --git a/samples/go/dev-console/go.sum b/samples/go/dev-console/go.sum new file mode 100644 index 00000000000..18a71a2e408 --- /dev/null +++ b/samples/go/dev-console/go.sum @@ -0,0 +1,39 @@ +github.com/c-bata/go-prompt v0.2.6 h1:POP+nrHE+DfLYx370bedwNhsqmpCUynWPxuHi0C5vZI= +github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/kyokomi/emoji/v2 v2.2.10 h1:1z5eMVcxFifsmEoNpdeq4UahbcicgQ4FEHuzrCVwmiI= +github.com/kyokomi/emoji/v2 v2.2.10/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= +github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pkg/term v1.2.0-beta.2 h1:L3y/h2jkuBVFdWiJvNfYfKmzcCnILw7mJWm2JQuMppw= +github.com/pkg/term v1.2.0-beta.2/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261 h1:v6hYoSR9T5oet+pMXwUWkbiVqx/63mlHjefrHmxwfeY= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/samples/go/dev-console/native/cgo.go b/samples/go/dev-console/native/cgo.go new file mode 100644 index 00000000000..3e0569e9162 --- /dev/null +++ b/samples/go/dev-console/native/cgo.go @@ -0,0 +1,14 @@ +package native + +// #cgo CFLAGS: -I packaged/include +// #cgo LDFLAGS: -lTrustWalletCore -lstdc++ -lm -lprotobuf -lTrezorCrypto +// +// +// #cgo LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib -L${SRCDIR}/packaged/lib +// +import "C" + +import ( + _ "dev-console/native/packaged/include" + _ "dev-console/native/packaged/lib" +) diff --git a/samples/go/dev-console/native/packaged/.gitignore b/samples/go/dev-console/native/packaged/.gitignore new file mode 100644 index 00000000000..a48ad1e8d53 --- /dev/null +++ b/samples/go/dev-console/native/packaged/.gitignore @@ -0,0 +1,2 @@ +*.h +*.a \ No newline at end of file diff --git a/samples/go/dev-console/native/packaged/.gitkeep b/samples/go/dev-console/native/packaged/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/samples/go/dev-console/native/packaged/include/.gitkeep b/samples/go/dev-console/native/packaged/include/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/samples/go/dev-console/native/packaged/include/dummy.go b/samples/go/dev-console/native/packaged/include/dummy.go new file mode 100644 index 00000000000..3f807a2211b --- /dev/null +++ b/samples/go/dev-console/native/packaged/include/dummy.go @@ -0,0 +1,2 @@ +// See https://github.com/golang/go/issues/26366. +package include diff --git a/samples/go/dev-console/native/packaged/lib/.gitkeep b/samples/go/dev-console/native/packaged/lib/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/samples/go/dev-console/native/packaged/lib/dummy.go b/samples/go/dev-console/native/packaged/lib/dummy.go new file mode 100644 index 00000000000..662a7ee75a7 --- /dev/null +++ b/samples/go/dev-console/native/packaged/lib/dummy.go @@ -0,0 +1,2 @@ +// See https://github.com/golang/go/issues/26366. +package lib diff --git a/samples/go/dev-console/native/twaccount.go b/samples/go/dev-console/native/twaccount.go new file mode 100644 index 00000000000..33e5b22f2fe --- /dev/null +++ b/samples/go/dev-console/native/twaccount.go @@ -0,0 +1,44 @@ +package native + +// #include +import "C" +import ( + "github.com/olekukonko/tablewriter" + "os" +) + +type Account struct { + account *C.struct_TWAccount +} + +func (self *Account) Delete() { + C.TWAccountDelete(self.account) +} + +func (self *Account) Address() string { + address := TWString{s: C.TWAccountAddress(self.account)} + defer address.Delete() + return address.String() +} + +func (self *Account) CoinType() CoinType { + return CoinType(C.TWAccountCoin(self.account)) +} + +func ToTableAccounts(accounts []*Account) { + var data [][]string + + for _, account := range accounts { + cur := []string{account.CoinType().GetName(), account.Address()} + data = append(data, cur) + } + + table := tablewriter.NewWriter(os.Stdout) + table.SetAutoWrapText(false) + table.SetHeader([]string{"Coin", "Address"}) + table.SetFooter([]string{"", ""}) + table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) + table.SetCenterSeparator("|") + table.AppendBulk(data) // Add Bulk Data + table.Render() +} diff --git a/samples/go/dev-console/native/twcoin.go b/samples/go/dev-console/native/twcoin.go new file mode 100644 index 00000000000..42eb2010640 --- /dev/null +++ b/samples/go/dev-console/native/twcoin.go @@ -0,0 +1,24 @@ +package native + +// #include +// #include +import "C" + +type CoinType uint32 + +const ( + CoinTypeBitcoin CoinType = C.TWCoinTypeBitcoin + CoinTypeBinance CoinType = C.TWCoinTypeBinance + CoinTypeEthereum CoinType = C.TWCoinTypeEthereum + CoinTypeTron CoinType = C.TWCoinTypeTron +) + +func (c CoinType) GetName() string { + name := C.TWCoinTypeConfigurationGetName(C.enum_TWCoinType(c)) + defer C.TWStringDelete(name) + return TWString{s: name}.String() +} + +func (c CoinType) Decimals() int { + return int(C.TWCoinTypeConfigurationGetDecimals(C.enum_TWCoinType(c))) +} diff --git a/samples/go/dev-console/native/twdata.go b/samples/go/dev-console/native/twdata.go new file mode 100644 index 00000000000..c558bddfa05 --- /dev/null +++ b/samples/go/dev-console/native/twdata.go @@ -0,0 +1,29 @@ +package native + +// #include +import "C" + +import ( + "encoding/hex" + "unsafe" +) + +// C.TWData -> Go byte[] +func TWDataGoBytes(d unsafe.Pointer) []byte { + cBytes := C.TWDataBytes(d) + cSize := C.TWDataSize(d) + return C.GoBytes(unsafe.Pointer(cBytes), C.int(cSize)) +} + +// Go byte[] -> C.TWData +func TWDataCreateWithGoBytes(d []byte) unsafe.Pointer { + cBytes := C.CBytes(d) + defer C.free(unsafe.Pointer(cBytes)) + data := C.TWDataCreateWithBytes((*C.uchar)(cBytes), C.ulong(len(d))) + return data +} + +// C.TWData -> Go hex string +func TWDataHexString(d unsafe.Pointer) string { + return hex.EncodeToString(TWDataGoBytes(d)) +} diff --git a/samples/go/dev-console/native/twmnemonic.go b/samples/go/dev-console/native/twmnemonic.go new file mode 100644 index 00000000000..f159a1a486a --- /dev/null +++ b/samples/go/dev-console/native/twmnemonic.go @@ -0,0 +1,10 @@ +package native + +// #include +import "C" + +func IsMnemonicValid(mn string) bool { + str := NewTWString(mn) + defer str.Delete() + return bool(C.TWMnemonicIsValid(str.s)) +} diff --git a/samples/go/dev-console/native/twstoredkey.go b/samples/go/dev-console/native/twstoredkey.go new file mode 100644 index 00000000000..c84976ed47e --- /dev/null +++ b/samples/go/dev-console/native/twstoredkey.go @@ -0,0 +1,54 @@ +package native + +// #include +import "C" + +type StoredKey struct { + storedKey *C.struct_TWStoredKey +} + +func NewStoredKeyFromHDWallet(mnemonic string, walletName string, password string, coinType CoinType) *StoredKey { + mnemonicRaw := NewTWString(mnemonic) + defer mnemonicRaw.Delete() + walletNameRaw := NewTWString(walletName) + defer walletNameRaw.Delete() + passwordRaw := TWDataCreateWithGoBytes([]byte(password)) + sk := C.TWStoredKeyImportHDWallet(mnemonicRaw.s, walletNameRaw.s, passwordRaw, uint32(coinType)) + if sk != nil { + return &StoredKey{storedKey: sk} + } + return nil +} + +func (self *StoredKey) IsLoaded() bool { + return self.storedKey != nil +} + +func Load(path string) *StoredKey { + pathRaw := NewTWString(path) + return &StoredKey{storedKey: C.TWStoredKeyLoad(pathRaw.s)} +} + +func (self *StoredKey) AccountForCoin(password string, coinType CoinType) *Account { + wallet := self.Wallet(password) + defer wallet.Delete() + return &Account{account: C.TWStoredKeyAccountForCoin(self.storedKey, uint32(coinType), wallet.wallet)} +} + +func (self *StoredKey) Store(path string) bool { + pathRaw := NewTWString(path) + defer pathRaw.Delete() + return bool(C.TWStoredKeyStore(self.storedKey, pathRaw.s)) +} + +func (self *StoredKey) Wallet(password string) *Wallet { + return &Wallet{wallet: C.TWStoredKeyWallet(self.storedKey, TWDataCreateWithGoBytes([]byte(password)))} +} + +func (self *StoredKey) AccountCount() int32 { + return int32(C.TWStoredKeyAccountCount(self.storedKey)) +} + +func (self *StoredKey) Account(index int32) *Account { + return &Account{account: C.TWStoredKeyAccount(self.storedKey, C.size_t(index))} +} diff --git a/samples/go/dev-console/native/twstring.go b/samples/go/dev-console/native/twstring.go new file mode 100644 index 00000000000..b8df5b2349b --- /dev/null +++ b/samples/go/dev-console/native/twstring.go @@ -0,0 +1,31 @@ +package native + +// #include +import "C" + +import ( + "unsafe" +) + +type TWString struct { + s unsafe.Pointer +} + +func NewTWString(s string) TWString { + cStr := C.CString(s) + defer C.free(unsafe.Pointer(cStr)) + str := C.TWStringCreateWithUTF8Bytes(cStr) + return TWString{s: str} +} + +func (self TWString) Delete() { + C.TWStringDelete(self.s) +} + +func (self TWString) String() string { + return C.GoString(C.TWStringUTF8Bytes(self.s)) +} + +func (self TWString) Size() int64 { + return int64(C.size_t(C.TWStringSize(self.s))) +} diff --git a/samples/go/dev-console/native/twwallet.go b/samples/go/dev-console/native/twwallet.go new file mode 100644 index 00000000000..c2453580b96 --- /dev/null +++ b/samples/go/dev-console/native/twwallet.go @@ -0,0 +1,44 @@ +package native + +// #include +import "C" +import "errors" + +type Wallet struct { + wallet *C.struct_TWHDWallet +} + +func NewWalletWithRandomMnemonic() *Wallet { + return &Wallet{wallet: C.TWHDWalletCreate(256, NewTWString("").s)} +} + +func NewWalletWithMnemonic(mn string) (*Wallet, error) { + if !IsMnemonicValid(mn) { + return nil, errors.New("mnemonic is not valid") + } + str := NewTWString(mn) + empty := NewTWString("") + defer str.Delete() + defer empty.Delete() + + tw := C.TWHDWalletCreateWithMnemonic(str.s, empty.s) + return &Wallet{wallet: tw}, nil +} + +func (self *Wallet) IsValid() bool { + return self.wallet != nil +} + +func (self *Wallet) Delete() { + C.TWHDWalletDelete(self.wallet) +} + +func (self *Wallet) Seed() string { + return TWDataHexString(C.TWHDWalletSeed(self.wallet)) +} + +func (self *Wallet) Mnemonic() string { + str := TWString{s: C.TWHDWalletMnemonic(self.wallet)} + defer str.Delete() + return str.String() +} diff --git a/samples/go/dev-console/prepare.sh b/samples/go/dev-console/prepare.sh new file mode 100644 index 00000000000..76ec7e9bb85 --- /dev/null +++ b/samples/go/dev-console/prepare.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +mkdir -p build && cd build +cmake -DCMAKE_BUILD_TYPE=Release -DTW_UNIT_TESTS=OFF -DTW_BUILD_EXAMPLES=OFF -DTW_UNITY_BUILD=ON -GNinja "$PWD"/../../../../ +ninja +cp libTrustWalletCore.a ../native/packaged/lib/ +cp libprotobuf.a ../native/packaged/lib +cp trezor-crypto/libTrezorCrypto.a ../native/packaged/lib +cd - +cp -R ../../../include native/packaged/ \ No newline at end of file diff --git a/samples/go/dev-console/wallet/wallet.go b/samples/go/dev-console/wallet/wallet.go new file mode 100644 index 00000000000..4f29e713666 --- /dev/null +++ b/samples/go/dev-console/wallet/wallet.go @@ -0,0 +1,41 @@ +package wallet + +import ( + "dev-console/native" + "errors" + "github.com/kyokomi/emoji/v2" + "log" + "os" + "path/filepath" +) + +type Wallet struct { + Ks *native.StoredKey + Password string + WalletName string +} + +var GlobalWallet *Wallet = nil + +func (self *Wallet) Dump() { + emoji.Printf("Wallet Name: %s :white_check_mark:\n", self.WalletName) + // Should we really dump password? + emoji.Printf("Wallet Password: %s :white_check_mark:\n", self.Password) + wallet := self.Ks.Wallet(self.Password) + defer wallet.Delete() + emoji.Printf("Wallet Mnemonic: %s :white_check_mark:\n", wallet.Mnemonic()) + emoji.Printf("Wallet Accounts count: %d :white_check_mark:\n", self.Ks.AccountCount()) + // Should we dump other infos? +} + +func GetWalletDataDirectory() string { + pwd, _ := os.Getwd() + dir := filepath.Join(pwd, "data") + if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) { + err := os.Mkdir(dir, os.ModePerm) + if err != nil { + log.Println(err) + } + } + return dir +} diff --git a/samples/go/go.mod b/samples/go/go.mod index 9a564387542..2835844a409 100644 --- a/samples/go/go.mod +++ b/samples/go/go.mod @@ -1,5 +1,5 @@ module tw -go 1.14 +go 1.16 -require github.com/golang/protobuf v1.4.2 +require google.golang.org/protobuf v1.27.1 diff --git a/samples/go/go.sum b/samples/go/go.sum index 0eeb4918550..03b1917b5a4 100644 --- a/samples/go/go.sum +++ b/samples/go/go.sum @@ -1,20 +1,8 @@ -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= diff --git a/samples/go/main.go b/samples/go/main.go index 27933dff91e..8aae9f98dac 100644 --- a/samples/go/main.go +++ b/samples/go/main.go @@ -1,55 +1,94 @@ package main -// #cgo CFLAGS: -I../../include -// #cgo LDFLAGS: -L../../build -L../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lc++ -lm -// #include -// #include -// #include -// #include -// #include -import "C" - import ( "encoding/hex" "fmt" + "math" + "math/big" + "tw/core" "tw/protos/bitcoin" - "tw/types" - - "github.com/golang/protobuf/proto" + "tw/protos/common" + "tw/protos/ethereum" + "tw/sample" ) func main() { fmt.Println("==> calling wallet core from go") - str := types.TWStringCreateWithGoString("confirm bleak useless tail chalk destroy horn step bulb genuine attract split") - emtpy := types.TWStringCreateWithGoString("") - defer C.TWStringDelete(str) - defer C.TWStringDelete(emtpy) - fmt.Println("==> mnemonic is valid: ", C.TWMnemonicIsValid(str)) + mn := "confirm bleak useless tail chalk destroy horn step bulb genuine attract split" - wallet := C.TWHDWalletCreateWithMnemonic(str, emtpy) - defer C.TWHDWalletDelete(wallet) + fmt.Println("==> mnemonic is valid: ", core.IsMnemonicValid(mn)) - key := C.TWHDWalletGetKeyForCoin(wallet, C.TWCoinTypeBitcoin) - keyData := C.TWPrivateKeyData(key) - defer C.TWDataDelete(keyData) + // bitcoin wallet + bw, err := core.CreateWalletWithMnemonic(mn, core.CoinTypeBitcoin) + if err != nil { + panic(err) + } + printWallet(bw) - fmt.Println("<== bitcoin private key: ", types.TWDataHexString(keyData)) + // ethereum wallet + ew, err := core.CreateWalletWithMnemonic(mn, core.CoinTypeEthereum) + if err != nil { + panic(err) + } + printWallet(ew) - pubKey, _ := hex.DecodeString("0288be7586c41a0498c1f931a0aaf08c15811ee2651a5fe0fa213167dcaba59ae8") - pubKeyData := types.TWDataCreateWithGoBytes(pubKey) - defer C.TWDataDelete(pubKeyData) - fmt.Println("==> bitcoin public key is valid: ", C.TWPublicKeyIsValid(pubKeyData, C.TWPublicKeyTypeSECP256k1)) + // tron wallet + tw, err := core.CreateWalletWithMnemonic(mn, core.CoinTypeTron) + if err != nil { + panic(err) + } + printWallet(tw) - address := C.TWHDWalletGetAddressForCoin(wallet, C.TWCoinTypeBitcoin) - defer C.TWStringDelete(address) - fmt.Println("<== bitcoin address: ", types.TWStringGoString(address)) + // Ethereum transaction + ethTxn := createEthTransaction(ew) + fmt.Println("Ethereum signed tx:") + fmt.Println("\t", ethTxn) - script := C.TWBitcoinScriptLockScriptForAddress(address, C.TWCoinTypeBitcoin) - scriptData := C.TWBitcoinScriptData(script) - defer C.TWBitcoinScriptDelete(script) - defer C.TWDataDelete(scriptData) - fmt.Println("<== bitcoin address lock script: ", types.TWDataHexString(scriptData)) + // Bitcion transaction + btcTxn := createBtcTransaction(bw) + fmt.Println("\nBitcoin signed tx:") + fmt.Println("\t", btcTxn) + + sample.ExternalSigningDemo() +} + +func createEthTransaction(ew *core.Wallet) string { + priKeyByte, _ := hex.DecodeString(ew.PriKey) + + input := ethereum.SigningInput{ + ChainId: big.NewInt(4).Bytes(), // mainnet: 1, rinkeby: 4 https://chainlist.org/ + Nonce: big.NewInt(0).Bytes(), // get nonce from network + TxMode: ethereum.TransactionMode_Legacy, + GasPrice: big.NewInt(100000000000).Bytes(), // 100 gwei + GasLimit: big.NewInt(21000).Bytes(), + ToAddress: "0xE9B511C0753649E5F3E78Ed8AdBEE92d0d2Db384", + PrivateKey: priKeyByte, + Transaction: ðereum.Transaction{ + TransactionOneof: ðereum.Transaction_Transfer_{ + Transfer: ðereum.Transaction_Transfer{ + // amount should be in wei unit, eth * (10^decimals) = wei + Amount: big.NewInt(int64( + 0.01 * math.Pow10(ew.CoinType.Decimals()), + )).Bytes(), + Data: []byte{}, + }, + }, + }, + } + + var output ethereum.SigningOutput + err := core.CreateSignedTx(&input, ew.CoinType, &output) + if err != nil { + panic(err) + } + return hex.EncodeToString(output.GetEncoded()) +} + +func createBtcTransaction(bw *core.Wallet) string { + lockScript := core.BitcoinScriptLockScriptForAddress(bw.Address, bw.CoinType) + fmt.Println("\nBitcoin address lock script:") + fmt.Println("\t", hex.EncodeToString(lockScript)) utxoHash, _ := hex.DecodeString("fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f") @@ -60,28 +99,37 @@ func main() { Sequence: 4294967295, }, Amount: 625000000, - Script: types.TWDataGoBytes(scriptData), + Script: lockScript, } + priKeyByte, _ := hex.DecodeString(bw.PriKey) + input := bitcoin.SigningInput{ - HashType: 1, // TWBitcoinSigHashTypeAll + HashType: uint32(core.BitcoinSigHashTypeAll), Amount: 1000000, ByteFee: 1, ToAddress: "1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx", ChangeAddress: "1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU", - PrivateKey: [][]byte{types.TWDataGoBytes(keyData)}, + PrivateKey: [][]byte{priKeyByte}, Utxo: []*bitcoin.UnspentTransaction{&utxo}, - CoinType: 0, // TWCoinTypeBitcoin + CoinType: uint32(core.CoinTypeBitcoin), } - inputBytes, _ := proto.Marshal(&input) - inputData := types.TWDataCreateWithGoBytes(inputBytes) - defer C.TWDataDelete(inputData) - - outputData := C.TWAnySignerSign(inputData, C.TWCoinTypeBitcoin) - defer C.TWDataDelete(outputData) - var output bitcoin.SigningOutput - _ = proto.Unmarshal(types.TWDataGoBytes(outputData), &output) - fmt.Println("<== bitcoin signed tx: ", hex.EncodeToString(output.Encoded)) + err := core.CreateSignedTx(&input, bw.CoinType, &output) + if err != nil { + panic(err) + } + if output.GetError() != common.SigningError_OK { + panic(output.GetError().String()) + } + return hex.EncodeToString(output.GetEncoded()) +} + +func printWallet(w *core.Wallet) { + fmt.Printf("%s wallet: \n", w.CoinType.GetName()) + fmt.Printf("\t address: %s \n", w.Address) + fmt.Printf("\t pri key: %s \n", w.PriKey) + fmt.Printf("\t pub key: %s \n", w.PubKey) + fmt.Println("") } diff --git a/samples/go/protos/binance/Binance.pb.go b/samples/go/protos/binance/Binance.pb.go new file mode 100644 index 00000000000..9205f012247 --- /dev/null +++ b/samples/go/protos/binance/Binance.pb.go @@ -0,0 +1,2965 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.19.2 +// source: Binance.proto + +package binance + +import ( + common "tw/protos/common" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Transaction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // int64 SIZE-OF-ENCODED // varint encoded length of the structure after encoding + // 0xF0625DEE // prefix + Msgs [][]byte `protobuf:"bytes,1,rep,name=msgs,proto3" json:"msgs,omitempty"` // array of size 1, containing the transaction message, which are one of the transaction type below + Signatures [][]byte `protobuf:"bytes,2,rep,name=signatures,proto3" json:"signatures,omitempty"` // array of size 1, containing the standard signature structure of the transaction sender + Memo string `protobuf:"bytes,3,opt,name=memo,proto3" json:"memo,omitempty"` // a short sentence of remark for the transaction, only for `Transfer` transactions. + Source int64 `protobuf:"varint,4,opt,name=source,proto3" json:"source,omitempty"` // an identifier for tools triggerring this transaction, set to zero if unwilling to disclose. + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` // reserved for future use +} + +func (x *Transaction) Reset() { + *x = Transaction{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction) ProtoMessage() {} + +func (x *Transaction) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction.ProtoReflect.Descriptor instead. +func (*Transaction) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{0} +} + +func (x *Transaction) GetMsgs() [][]byte { + if x != nil { + return x.Msgs + } + return nil +} + +func (x *Transaction) GetSignatures() [][]byte { + if x != nil { + return x.Signatures + } + return nil +} + +func (x *Transaction) GetMemo() string { + if x != nil { + return x.Memo + } + return "" +} + +func (x *Transaction) GetSource() int64 { + if x != nil { + return x.Source + } + return 0 +} + +func (x *Transaction) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type Signature struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + PubKey []byte `protobuf:"bytes,1,opt,name=pub_key,json=pubKey,proto3" json:"pub_key,omitempty"` // public key bytes of the signer address + Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` // signature bytes, please check chain access section for signature generation + AccountNumber int64 `protobuf:"varint,3,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` // another identifier of signer, which can be read from chain by account REST API or RPC + Sequence int64 `protobuf:"varint,4,opt,name=sequence,proto3" json:"sequence,omitempty"` // sequence number for the next transaction +} + +func (x *Signature) Reset() { + *x = Signature{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Signature) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Signature) ProtoMessage() {} + +func (x *Signature) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Signature.ProtoReflect.Descriptor instead. +func (*Signature) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{1} +} + +func (x *Signature) GetPubKey() []byte { + if x != nil { + return x.PubKey + } + return nil +} + +func (x *Signature) GetSignature() []byte { + if x != nil { + return x.Signature + } + return nil +} + +func (x *Signature) GetAccountNumber() int64 { + if x != nil { + return x.AccountNumber + } + return 0 +} + +func (x *Signature) GetSequence() int64 { + if x != nil { + return x.Sequence + } + return 0 +} + +type TradeOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0xCE6DC043 // prefix + Sender []byte `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` // originating address + Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` // order id, optional + Symbol string `protobuf:"bytes,3,opt,name=symbol,proto3" json:"symbol,omitempty"` // symbol for trading pair in full name of the tokens + Ordertype int64 `protobuf:"varint,4,opt,name=ordertype,proto3" json:"ordertype,omitempty"` // only accept 2 for now, meaning limit order + Side int64 `protobuf:"varint,5,opt,name=side,proto3" json:"side,omitempty"` // 1 for buy and 2 fory sell + Price int64 `protobuf:"varint,6,opt,name=price,proto3" json:"price,omitempty"` // price of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer + Quantity int64 `protobuf:"varint,7,opt,name=quantity,proto3" json:"quantity,omitempty"` // quantity of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer + Timeinforce int64 `protobuf:"varint,8,opt,name=timeinforce,proto3" json:"timeinforce,omitempty"` // 1 for Good Till Expire(GTE) order and 3 for Immediate Or Cancel (IOC) +} + +func (x *TradeOrder) Reset() { + *x = TradeOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TradeOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TradeOrder) ProtoMessage() {} + +func (x *TradeOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TradeOrder.ProtoReflect.Descriptor instead. +func (*TradeOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{2} +} + +func (x *TradeOrder) GetSender() []byte { + if x != nil { + return x.Sender + } + return nil +} + +func (x *TradeOrder) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *TradeOrder) GetSymbol() string { + if x != nil { + return x.Symbol + } + return "" +} + +func (x *TradeOrder) GetOrdertype() int64 { + if x != nil { + return x.Ordertype + } + return 0 +} + +func (x *TradeOrder) GetSide() int64 { + if x != nil { + return x.Side + } + return 0 +} + +func (x *TradeOrder) GetPrice() int64 { + if x != nil { + return x.Price + } + return 0 +} + +func (x *TradeOrder) GetQuantity() int64 { + if x != nil { + return x.Quantity + } + return 0 +} + +func (x *TradeOrder) GetTimeinforce() int64 { + if x != nil { + return x.Timeinforce + } + return 0 +} + +type CancelTradeOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0x166E681B // prefix + Sender []byte `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"` // originating address + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // symbol for trading pair in full name of the tokens + Refid string `protobuf:"bytes,3,opt,name=refid,proto3" json:"refid,omitempty"` // order id to cancel +} + +func (x *CancelTradeOrder) Reset() { + *x = CancelTradeOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CancelTradeOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CancelTradeOrder) ProtoMessage() {} + +func (x *CancelTradeOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CancelTradeOrder.ProtoReflect.Descriptor instead. +func (*CancelTradeOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{3} +} + +func (x *CancelTradeOrder) GetSender() []byte { + if x != nil { + return x.Sender + } + return nil +} + +func (x *CancelTradeOrder) GetSymbol() string { + if x != nil { + return x.Symbol + } + return "" +} + +func (x *CancelTradeOrder) GetRefid() string { + if x != nil { + return x.Refid + } + return "" +} + +type SendOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Inputs []*SendOrder_Input `protobuf:"bytes,1,rep,name=inputs,proto3" json:"inputs,omitempty"` + Outputs []*SendOrder_Output `protobuf:"bytes,2,rep,name=outputs,proto3" json:"outputs,omitempty"` +} + +func (x *SendOrder) Reset() { + *x = SendOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendOrder) ProtoMessage() {} + +func (x *SendOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendOrder.ProtoReflect.Descriptor instead. +func (*SendOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{4} +} + +func (x *SendOrder) GetInputs() []*SendOrder_Input { + if x != nil { + return x.Inputs + } + return nil +} + +func (x *SendOrder) GetOutputs() []*SendOrder_Output { + if x != nil { + return x.Outputs + } + return nil +} + +type TokenIssueOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0x17EFAB80 // prefix + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // token name + Symbol string `protobuf:"bytes,3,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix + TotalSupply int64 `protobuf:"varint,4,opt,name=total_supply,json=totalSupply,proto3" json:"total_supply,omitempty"` // total supply + Mintable bool `protobuf:"varint,5,opt,name=mintable,proto3" json:"mintable,omitempty"` // mintable +} + +func (x *TokenIssueOrder) Reset() { + *x = TokenIssueOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TokenIssueOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TokenIssueOrder) ProtoMessage() {} + +func (x *TokenIssueOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TokenIssueOrder.ProtoReflect.Descriptor instead. +func (*TokenIssueOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{5} +} + +func (x *TokenIssueOrder) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *TokenIssueOrder) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *TokenIssueOrder) GetSymbol() string { + if x != nil { + return x.Symbol + } + return "" +} + +func (x *TokenIssueOrder) GetTotalSupply() int64 { + if x != nil { + return x.TotalSupply + } + return 0 +} + +func (x *TokenIssueOrder) GetMintable() bool { + if x != nil { + return x.Mintable + } + return false +} + +type TokenMintOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0x467E0829 // prefix + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` // amount to mint +} + +func (x *TokenMintOrder) Reset() { + *x = TokenMintOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TokenMintOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TokenMintOrder) ProtoMessage() {} + +func (x *TokenMintOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TokenMintOrder.ProtoReflect.Descriptor instead. +func (*TokenMintOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{6} +} + +func (x *TokenMintOrder) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *TokenMintOrder) GetSymbol() string { + if x != nil { + return x.Symbol + } + return "" +} + +func (x *TokenMintOrder) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +type TokenBurnOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0x7ED2D2A0 // prefix + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` // amount to burn +} + +func (x *TokenBurnOrder) Reset() { + *x = TokenBurnOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TokenBurnOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TokenBurnOrder) ProtoMessage() {} + +func (x *TokenBurnOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TokenBurnOrder.ProtoReflect.Descriptor instead. +func (*TokenBurnOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{7} +} + +func (x *TokenBurnOrder) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *TokenBurnOrder) GetSymbol() string { + if x != nil { + return x.Symbol + } + return "" +} + +func (x *TokenBurnOrder) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +type TokenFreezeOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0xE774B32D // prefix + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` // amount of token to freeze +} + +func (x *TokenFreezeOrder) Reset() { + *x = TokenFreezeOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TokenFreezeOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TokenFreezeOrder) ProtoMessage() {} + +func (x *TokenFreezeOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TokenFreezeOrder.ProtoReflect.Descriptor instead. +func (*TokenFreezeOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{8} +} + +func (x *TokenFreezeOrder) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *TokenFreezeOrder) GetSymbol() string { + if x != nil { + return x.Symbol + } + return "" +} + +func (x *TokenFreezeOrder) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +type TokenUnfreezeOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0x6515FF0D // prefix + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // owner address + Symbol string `protobuf:"bytes,2,opt,name=symbol,proto3" json:"symbol,omitempty"` // token symbol, in full name with "-" suffix + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` // amount of token to unfreeze +} + +func (x *TokenUnfreezeOrder) Reset() { + *x = TokenUnfreezeOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TokenUnfreezeOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TokenUnfreezeOrder) ProtoMessage() {} + +func (x *TokenUnfreezeOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TokenUnfreezeOrder.ProtoReflect.Descriptor instead. +func (*TokenUnfreezeOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{9} +} + +func (x *TokenUnfreezeOrder) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *TokenUnfreezeOrder) GetSymbol() string { + if x != nil { + return x.Symbol + } + return "" +} + +func (x *TokenUnfreezeOrder) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +type HTLTOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0xB33F9A24 // prefix + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // signer address + To []byte `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` // recipient address + RecipientOtherChain string `protobuf:"bytes,3,opt,name=recipient_other_chain,json=recipientOtherChain,proto3" json:"recipient_other_chain,omitempty"` + SenderOtherChain string `protobuf:"bytes,4,opt,name=sender_other_chain,json=senderOtherChain,proto3" json:"sender_other_chain,omitempty"` + RandomNumberHash []byte `protobuf:"bytes,5,opt,name=random_number_hash,json=randomNumberHash,proto3" json:"random_number_hash,omitempty"` //hash of a random number and timestamp, based on SHA256 + Timestamp int64 `protobuf:"varint,6,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Amount []*SendOrder_Token `protobuf:"bytes,7,rep,name=amount,proto3" json:"amount,omitempty"` + ExpectedIncome string `protobuf:"bytes,8,opt,name=expected_income,json=expectedIncome,proto3" json:"expected_income,omitempty"` // expected gained token on the other chain + HeightSpan int64 `protobuf:"varint,9,opt,name=height_span,json=heightSpan,proto3" json:"height_span,omitempty"` + CrossChain bool `protobuf:"varint,10,opt,name=cross_chain,json=crossChain,proto3" json:"cross_chain,omitempty"` +} + +func (x *HTLTOrder) Reset() { + *x = HTLTOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HTLTOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HTLTOrder) ProtoMessage() {} + +func (x *HTLTOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HTLTOrder.ProtoReflect.Descriptor instead. +func (*HTLTOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{10} +} + +func (x *HTLTOrder) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *HTLTOrder) GetTo() []byte { + if x != nil { + return x.To + } + return nil +} + +func (x *HTLTOrder) GetRecipientOtherChain() string { + if x != nil { + return x.RecipientOtherChain + } + return "" +} + +func (x *HTLTOrder) GetSenderOtherChain() string { + if x != nil { + return x.SenderOtherChain + } + return "" +} + +func (x *HTLTOrder) GetRandomNumberHash() []byte { + if x != nil { + return x.RandomNumberHash + } + return nil +} + +func (x *HTLTOrder) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *HTLTOrder) GetAmount() []*SendOrder_Token { + if x != nil { + return x.Amount + } + return nil +} + +func (x *HTLTOrder) GetExpectedIncome() string { + if x != nil { + return x.ExpectedIncome + } + return "" +} + +func (x *HTLTOrder) GetHeightSpan() int64 { + if x != nil { + return x.HeightSpan + } + return 0 +} + +func (x *HTLTOrder) GetCrossChain() bool { + if x != nil { + return x.CrossChain + } + return false +} + +type DepositHTLTOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0xB33F9A24 // prefix + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // signer address + Amount []*SendOrder_Token `protobuf:"bytes,2,rep,name=amount,proto3" json:"amount,omitempty"` + SwapId []byte `protobuf:"bytes,3,opt,name=swap_id,json=swapId,proto3" json:"swap_id,omitempty"` +} + +func (x *DepositHTLTOrder) Reset() { + *x = DepositHTLTOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DepositHTLTOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DepositHTLTOrder) ProtoMessage() {} + +func (x *DepositHTLTOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DepositHTLTOrder.ProtoReflect.Descriptor instead. +func (*DepositHTLTOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{11} +} + +func (x *DepositHTLTOrder) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *DepositHTLTOrder) GetAmount() []*SendOrder_Token { + if x != nil { + return x.Amount + } + return nil +} + +func (x *DepositHTLTOrder) GetSwapId() []byte { + if x != nil { + return x.SwapId + } + return nil +} + +type ClaimHTLOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0xC1665300 // prefix + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // signer address + SwapId []byte `protobuf:"bytes,2,opt,name=swap_id,json=swapId,proto3" json:"swap_id,omitempty"` + RandomNumber []byte `protobuf:"bytes,3,opt,name=random_number,json=randomNumber,proto3" json:"random_number,omitempty"` +} + +func (x *ClaimHTLOrder) Reset() { + *x = ClaimHTLOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ClaimHTLOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ClaimHTLOrder) ProtoMessage() {} + +func (x *ClaimHTLOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ClaimHTLOrder.ProtoReflect.Descriptor instead. +func (*ClaimHTLOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{12} +} + +func (x *ClaimHTLOrder) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *ClaimHTLOrder) GetSwapId() []byte { + if x != nil { + return x.SwapId + } + return nil +} + +func (x *ClaimHTLOrder) GetRandomNumber() []byte { + if x != nil { + return x.RandomNumber + } + return nil +} + +type RefundHTLTOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // 0x3454A27C // prefix + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` // signer address + SwapId []byte `protobuf:"bytes,2,opt,name=swap_id,json=swapId,proto3" json:"swap_id,omitempty"` +} + +func (x *RefundHTLTOrder) Reset() { + *x = RefundHTLTOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RefundHTLTOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RefundHTLTOrder) ProtoMessage() {} + +func (x *RefundHTLTOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RefundHTLTOrder.ProtoReflect.Descriptor instead. +func (*RefundHTLTOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{13} +} + +func (x *RefundHTLTOrder) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *RefundHTLTOrder) GetSwapId() []byte { + if x != nil { + return x.SwapId + } + return nil +} + +type TransferOut struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + From []byte `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + To []byte `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` + Amount *SendOrder_Token `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` + ExpireTime int64 `protobuf:"varint,4,opt,name=expire_time,json=expireTime,proto3" json:"expire_time,omitempty"` +} + +func (x *TransferOut) Reset() { + *x = TransferOut{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TransferOut) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TransferOut) ProtoMessage() {} + +func (x *TransferOut) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TransferOut.ProtoReflect.Descriptor instead. +func (*TransferOut) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{14} +} + +func (x *TransferOut) GetFrom() []byte { + if x != nil { + return x.From + } + return nil +} + +func (x *TransferOut) GetTo() []byte { + if x != nil { + return x.To + } + return nil +} + +func (x *TransferOut) GetAmount() *SendOrder_Token { + if x != nil { + return x.Amount + } + return nil +} + +func (x *TransferOut) GetExpireTime() int64 { + if x != nil { + return x.ExpireTime + } + return 0 +} + +type SideChainDelegate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DelegatorAddr []byte `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + ValidatorAddr []byte `protobuf:"bytes,2,opt,name=validator_addr,json=validatorAddr,proto3" json:"validator_addr,omitempty"` + Delegation *SendOrder_Token `protobuf:"bytes,3,opt,name=delegation,proto3" json:"delegation,omitempty"` + ChainId string `protobuf:"bytes,4,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (x *SideChainDelegate) Reset() { + *x = SideChainDelegate{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SideChainDelegate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SideChainDelegate) ProtoMessage() {} + +func (x *SideChainDelegate) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SideChainDelegate.ProtoReflect.Descriptor instead. +func (*SideChainDelegate) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{15} +} + +func (x *SideChainDelegate) GetDelegatorAddr() []byte { + if x != nil { + return x.DelegatorAddr + } + return nil +} + +func (x *SideChainDelegate) GetValidatorAddr() []byte { + if x != nil { + return x.ValidatorAddr + } + return nil +} + +func (x *SideChainDelegate) GetDelegation() *SendOrder_Token { + if x != nil { + return x.Delegation + } + return nil +} + +func (x *SideChainDelegate) GetChainId() string { + if x != nil { + return x.ChainId + } + return "" +} + +type SideChainRedelegate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DelegatorAddr []byte `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + ValidatorSrcAddr []byte `protobuf:"bytes,2,opt,name=validator_src_addr,json=validatorSrcAddr,proto3" json:"validator_src_addr,omitempty"` + ValidatorDstAddr []byte `protobuf:"bytes,3,opt,name=validator_dst_addr,json=validatorDstAddr,proto3" json:"validator_dst_addr,omitempty"` + Amount *SendOrder_Token `protobuf:"bytes,4,opt,name=amount,proto3" json:"amount,omitempty"` + ChainId string `protobuf:"bytes,5,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (x *SideChainRedelegate) Reset() { + *x = SideChainRedelegate{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SideChainRedelegate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SideChainRedelegate) ProtoMessage() {} + +func (x *SideChainRedelegate) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SideChainRedelegate.ProtoReflect.Descriptor instead. +func (*SideChainRedelegate) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{16} +} + +func (x *SideChainRedelegate) GetDelegatorAddr() []byte { + if x != nil { + return x.DelegatorAddr + } + return nil +} + +func (x *SideChainRedelegate) GetValidatorSrcAddr() []byte { + if x != nil { + return x.ValidatorSrcAddr + } + return nil +} + +func (x *SideChainRedelegate) GetValidatorDstAddr() []byte { + if x != nil { + return x.ValidatorDstAddr + } + return nil +} + +func (x *SideChainRedelegate) GetAmount() *SendOrder_Token { + if x != nil { + return x.Amount + } + return nil +} + +func (x *SideChainRedelegate) GetChainId() string { + if x != nil { + return x.ChainId + } + return "" +} + +type SideChainUndelegate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DelegatorAddr []byte `protobuf:"bytes,1,opt,name=delegator_addr,json=delegatorAddr,proto3" json:"delegator_addr,omitempty"` + ValidatorAddr []byte `protobuf:"bytes,2,opt,name=validator_addr,json=validatorAddr,proto3" json:"validator_addr,omitempty"` + Amount *SendOrder_Token `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"` + ChainId string `protobuf:"bytes,4,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` +} + +func (x *SideChainUndelegate) Reset() { + *x = SideChainUndelegate{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SideChainUndelegate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SideChainUndelegate) ProtoMessage() {} + +func (x *SideChainUndelegate) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SideChainUndelegate.ProtoReflect.Descriptor instead. +func (*SideChainUndelegate) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{17} +} + +func (x *SideChainUndelegate) GetDelegatorAddr() []byte { + if x != nil { + return x.DelegatorAddr + } + return nil +} + +func (x *SideChainUndelegate) GetValidatorAddr() []byte { + if x != nil { + return x.ValidatorAddr + } + return nil +} + +func (x *SideChainUndelegate) GetAmount() *SendOrder_Token { + if x != nil { + return x.Amount + } + return nil +} + +func (x *SideChainUndelegate) GetChainId() string { + if x != nil { + return x.ChainId + } + return "" +} + +type TimeLockOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FromAddress []byte `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` // owner address + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + // Array of symbol/amount pairs. see SDK https://github.com/binance-chain/javascript-sdk/blob/master/docs/api-docs/classes/tokenmanagement.md#timelock + Amount []*SendOrder_Token `protobuf:"bytes,3,rep,name=amount,proto3" json:"amount,omitempty"` + LockTime int64 `protobuf:"varint,4,opt,name=lock_time,json=lockTime,proto3" json:"lock_time,omitempty"` +} + +func (x *TimeLockOrder) Reset() { + *x = TimeLockOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TimeLockOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TimeLockOrder) ProtoMessage() {} + +func (x *TimeLockOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TimeLockOrder.ProtoReflect.Descriptor instead. +func (*TimeLockOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{18} +} + +func (x *TimeLockOrder) GetFromAddress() []byte { + if x != nil { + return x.FromAddress + } + return nil +} + +func (x *TimeLockOrder) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *TimeLockOrder) GetAmount() []*SendOrder_Token { + if x != nil { + return x.Amount + } + return nil +} + +func (x *TimeLockOrder) GetLockTime() int64 { + if x != nil { + return x.LockTime + } + return 0 +} + +type TimeRelockOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FromAddress []byte `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` // owner address + Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` // order ID + Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` + // Array of symbol/amount pairs. + Amount []*SendOrder_Token `protobuf:"bytes,4,rep,name=amount,proto3" json:"amount,omitempty"` + LockTime int64 `protobuf:"varint,5,opt,name=lock_time,json=lockTime,proto3" json:"lock_time,omitempty"` +} + +func (x *TimeRelockOrder) Reset() { + *x = TimeRelockOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TimeRelockOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TimeRelockOrder) ProtoMessage() {} + +func (x *TimeRelockOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TimeRelockOrder.ProtoReflect.Descriptor instead. +func (*TimeRelockOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{19} +} + +func (x *TimeRelockOrder) GetFromAddress() []byte { + if x != nil { + return x.FromAddress + } + return nil +} + +func (x *TimeRelockOrder) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *TimeRelockOrder) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *TimeRelockOrder) GetAmount() []*SendOrder_Token { + if x != nil { + return x.Amount + } + return nil +} + +func (x *TimeRelockOrder) GetLockTime() int64 { + if x != nil { + return x.LockTime + } + return 0 +} + +type TimeUnlockOrder struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FromAddress []byte `protobuf:"bytes,1,opt,name=from_address,json=fromAddress,proto3" json:"from_address,omitempty"` // owner address + Id int64 `protobuf:"varint,2,opt,name=id,proto3" json:"id,omitempty"` // order ID +} + +func (x *TimeUnlockOrder) Reset() { + *x = TimeUnlockOrder{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TimeUnlockOrder) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TimeUnlockOrder) ProtoMessage() {} + +func (x *TimeUnlockOrder) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TimeUnlockOrder.ProtoReflect.Descriptor instead. +func (*TimeUnlockOrder) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{20} +} + +func (x *TimeUnlockOrder) GetFromAddress() []byte { + if x != nil { + return x.FromAddress + } + return nil +} + +func (x *TimeUnlockOrder) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + +// Input data necessary to create a signed order. +type SigningInput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ChainId string `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + AccountNumber int64 `protobuf:"varint,2,opt,name=account_number,json=accountNumber,proto3" json:"account_number,omitempty"` + Sequence int64 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` + Source int64 `protobuf:"varint,4,opt,name=source,proto3" json:"source,omitempty"` + Memo string `protobuf:"bytes,5,opt,name=memo,proto3" json:"memo,omitempty"` + PrivateKey []byte `protobuf:"bytes,6,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` + // Types that are assignable to OrderOneof: + // *SigningInput_TradeOrder + // *SigningInput_CancelTradeOrder + // *SigningInput_SendOrder + // *SigningInput_FreezeOrder + // *SigningInput_UnfreezeOrder + // *SigningInput_HtltOrder + // *SigningInput_DepositHTLTOrder + // *SigningInput_ClaimHTLTOrder + // *SigningInput_RefundHTLTOrder + // *SigningInput_IssueOrder + // *SigningInput_MintOrder + // *SigningInput_BurnOrder + // *SigningInput_TransferOutOrder + // *SigningInput_SideDelegateOrder + // *SigningInput_SideRedelegateOrder + // *SigningInput_SideUndelegateOrder + // *SigningInput_TimeLockOrder + // *SigningInput_TimeRelockOrder + // *SigningInput_TimeUnlockOrder + OrderOneof isSigningInput_OrderOneof `protobuf_oneof:"order_oneof"` +} + +func (x *SigningInput) Reset() { + *x = SigningInput{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SigningInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SigningInput) ProtoMessage() {} + +func (x *SigningInput) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SigningInput.ProtoReflect.Descriptor instead. +func (*SigningInput) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{21} +} + +func (x *SigningInput) GetChainId() string { + if x != nil { + return x.ChainId + } + return "" +} + +func (x *SigningInput) GetAccountNumber() int64 { + if x != nil { + return x.AccountNumber + } + return 0 +} + +func (x *SigningInput) GetSequence() int64 { + if x != nil { + return x.Sequence + } + return 0 +} + +func (x *SigningInput) GetSource() int64 { + if x != nil { + return x.Source + } + return 0 +} + +func (x *SigningInput) GetMemo() string { + if x != nil { + return x.Memo + } + return "" +} + +func (x *SigningInput) GetPrivateKey() []byte { + if x != nil { + return x.PrivateKey + } + return nil +} + +func (m *SigningInput) GetOrderOneof() isSigningInput_OrderOneof { + if m != nil { + return m.OrderOneof + } + return nil +} + +func (x *SigningInput) GetTradeOrder() *TradeOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_TradeOrder); ok { + return x.TradeOrder + } + return nil +} + +func (x *SigningInput) GetCancelTradeOrder() *CancelTradeOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_CancelTradeOrder); ok { + return x.CancelTradeOrder + } + return nil +} + +func (x *SigningInput) GetSendOrder() *SendOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_SendOrder); ok { + return x.SendOrder + } + return nil +} + +func (x *SigningInput) GetFreezeOrder() *TokenFreezeOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_FreezeOrder); ok { + return x.FreezeOrder + } + return nil +} + +func (x *SigningInput) GetUnfreezeOrder() *TokenUnfreezeOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_UnfreezeOrder); ok { + return x.UnfreezeOrder + } + return nil +} + +func (x *SigningInput) GetHtltOrder() *HTLTOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_HtltOrder); ok { + return x.HtltOrder + } + return nil +} + +func (x *SigningInput) GetDepositHTLTOrder() *DepositHTLTOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_DepositHTLTOrder); ok { + return x.DepositHTLTOrder + } + return nil +} + +func (x *SigningInput) GetClaimHTLTOrder() *ClaimHTLOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_ClaimHTLTOrder); ok { + return x.ClaimHTLTOrder + } + return nil +} + +func (x *SigningInput) GetRefundHTLTOrder() *RefundHTLTOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_RefundHTLTOrder); ok { + return x.RefundHTLTOrder + } + return nil +} + +func (x *SigningInput) GetIssueOrder() *TokenIssueOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_IssueOrder); ok { + return x.IssueOrder + } + return nil +} + +func (x *SigningInput) GetMintOrder() *TokenMintOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_MintOrder); ok { + return x.MintOrder + } + return nil +} + +func (x *SigningInput) GetBurnOrder() *TokenBurnOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_BurnOrder); ok { + return x.BurnOrder + } + return nil +} + +func (x *SigningInput) GetTransferOutOrder() *TransferOut { + if x, ok := x.GetOrderOneof().(*SigningInput_TransferOutOrder); ok { + return x.TransferOutOrder + } + return nil +} + +func (x *SigningInput) GetSideDelegateOrder() *SideChainDelegate { + if x, ok := x.GetOrderOneof().(*SigningInput_SideDelegateOrder); ok { + return x.SideDelegateOrder + } + return nil +} + +func (x *SigningInput) GetSideRedelegateOrder() *SideChainRedelegate { + if x, ok := x.GetOrderOneof().(*SigningInput_SideRedelegateOrder); ok { + return x.SideRedelegateOrder + } + return nil +} + +func (x *SigningInput) GetSideUndelegateOrder() *SideChainUndelegate { + if x, ok := x.GetOrderOneof().(*SigningInput_SideUndelegateOrder); ok { + return x.SideUndelegateOrder + } + return nil +} + +func (x *SigningInput) GetTimeLockOrder() *TimeLockOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_TimeLockOrder); ok { + return x.TimeLockOrder + } + return nil +} + +func (x *SigningInput) GetTimeRelockOrder() *TimeRelockOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_TimeRelockOrder); ok { + return x.TimeRelockOrder + } + return nil +} + +func (x *SigningInput) GetTimeUnlockOrder() *TimeUnlockOrder { + if x, ok := x.GetOrderOneof().(*SigningInput_TimeUnlockOrder); ok { + return x.TimeUnlockOrder + } + return nil +} + +type isSigningInput_OrderOneof interface { + isSigningInput_OrderOneof() +} + +type SigningInput_TradeOrder struct { + TradeOrder *TradeOrder `protobuf:"bytes,8,opt,name=trade_order,json=tradeOrder,proto3,oneof"` +} + +type SigningInput_CancelTradeOrder struct { + CancelTradeOrder *CancelTradeOrder `protobuf:"bytes,9,opt,name=cancel_trade_order,json=cancelTradeOrder,proto3,oneof"` +} + +type SigningInput_SendOrder struct { + SendOrder *SendOrder `protobuf:"bytes,10,opt,name=send_order,json=sendOrder,proto3,oneof"` +} + +type SigningInput_FreezeOrder struct { + FreezeOrder *TokenFreezeOrder `protobuf:"bytes,11,opt,name=freeze_order,json=freezeOrder,proto3,oneof"` +} + +type SigningInput_UnfreezeOrder struct { + UnfreezeOrder *TokenUnfreezeOrder `protobuf:"bytes,12,opt,name=unfreeze_order,json=unfreezeOrder,proto3,oneof"` +} + +type SigningInput_HtltOrder struct { + HtltOrder *HTLTOrder `protobuf:"bytes,13,opt,name=htlt_order,json=htltOrder,proto3,oneof"` +} + +type SigningInput_DepositHTLTOrder struct { + DepositHTLTOrder *DepositHTLTOrder `protobuf:"bytes,14,opt,name=depositHTLT_order,json=depositHTLTOrder,proto3,oneof"` +} + +type SigningInput_ClaimHTLTOrder struct { + ClaimHTLTOrder *ClaimHTLOrder `protobuf:"bytes,15,opt,name=claimHTLT_order,json=claimHTLTOrder,proto3,oneof"` +} + +type SigningInput_RefundHTLTOrder struct { + RefundHTLTOrder *RefundHTLTOrder `protobuf:"bytes,16,opt,name=refundHTLT_order,json=refundHTLTOrder,proto3,oneof"` +} + +type SigningInput_IssueOrder struct { + IssueOrder *TokenIssueOrder `protobuf:"bytes,17,opt,name=issue_order,json=issueOrder,proto3,oneof"` +} + +type SigningInput_MintOrder struct { + MintOrder *TokenMintOrder `protobuf:"bytes,18,opt,name=mint_order,json=mintOrder,proto3,oneof"` +} + +type SigningInput_BurnOrder struct { + BurnOrder *TokenBurnOrder `protobuf:"bytes,19,opt,name=burn_order,json=burnOrder,proto3,oneof"` +} + +type SigningInput_TransferOutOrder struct { + TransferOutOrder *TransferOut `protobuf:"bytes,20,opt,name=transfer_out_order,json=transferOutOrder,proto3,oneof"` +} + +type SigningInput_SideDelegateOrder struct { + SideDelegateOrder *SideChainDelegate `protobuf:"bytes,21,opt,name=side_delegate_order,json=sideDelegateOrder,proto3,oneof"` +} + +type SigningInput_SideRedelegateOrder struct { + SideRedelegateOrder *SideChainRedelegate `protobuf:"bytes,22,opt,name=side_redelegate_order,json=sideRedelegateOrder,proto3,oneof"` +} + +type SigningInput_SideUndelegateOrder struct { + SideUndelegateOrder *SideChainUndelegate `protobuf:"bytes,23,opt,name=side_undelegate_order,json=sideUndelegateOrder,proto3,oneof"` +} + +type SigningInput_TimeLockOrder struct { + TimeLockOrder *TimeLockOrder `protobuf:"bytes,24,opt,name=time_lock_order,json=timeLockOrder,proto3,oneof"` +} + +type SigningInput_TimeRelockOrder struct { + TimeRelockOrder *TimeRelockOrder `protobuf:"bytes,25,opt,name=time_relock_order,json=timeRelockOrder,proto3,oneof"` +} + +type SigningInput_TimeUnlockOrder struct { + TimeUnlockOrder *TimeUnlockOrder `protobuf:"bytes,26,opt,name=time_unlock_order,json=timeUnlockOrder,proto3,oneof"` +} + +func (*SigningInput_TradeOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_CancelTradeOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_SendOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_FreezeOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_UnfreezeOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_HtltOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_DepositHTLTOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_ClaimHTLTOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_RefundHTLTOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_IssueOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_MintOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_BurnOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_TransferOutOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_SideDelegateOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_SideRedelegateOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_SideUndelegateOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_TimeLockOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_TimeRelockOrder) isSigningInput_OrderOneof() {} + +func (*SigningInput_TimeUnlockOrder) isSigningInput_OrderOneof() {} + +// Transaction signing output. +type SigningOutput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Signed and encoded transaction bytes. + Encoded []byte `protobuf:"bytes,1,opt,name=encoded,proto3" json:"encoded,omitempty"` + /// error code, 0 is ok, other codes will be treated as errors + Error common.SigningError `protobuf:"varint,2,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` + /// error description + ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (x *SigningOutput) Reset() { + *x = SigningOutput{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SigningOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SigningOutput) ProtoMessage() {} + +func (x *SigningOutput) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SigningOutput.ProtoReflect.Descriptor instead. +func (*SigningOutput) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{22} +} + +func (x *SigningOutput) GetEncoded() []byte { + if x != nil { + return x.Encoded + } + return nil +} + +func (x *SigningOutput) GetError() common.SigningError { + if x != nil { + return x.Error + } + return common.SigningError(0) +} + +func (x *SigningOutput) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +type Signature_PubKey struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *Signature_PubKey) Reset() { + *x = Signature_PubKey{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Signature_PubKey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Signature_PubKey) ProtoMessage() {} + +func (x *Signature_PubKey) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Signature_PubKey.ProtoReflect.Descriptor instead. +func (*Signature_PubKey) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{1, 0} +} + +// 0x2A2C87FA +// A symbol-amount pair. Could be moved out of SendOrder; kept here for backward compatibility. +type SendOrder_Token struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty"` + Amount int64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` +} + +func (x *SendOrder_Token) Reset() { + *x = SendOrder_Token{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendOrder_Token) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendOrder_Token) ProtoMessage() {} + +func (x *SendOrder_Token) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendOrder_Token.ProtoReflect.Descriptor instead. +func (*SendOrder_Token) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{4, 0} +} + +func (x *SendOrder_Token) GetDenom() string { + if x != nil { + return x.Denom + } + return "" +} + +func (x *SendOrder_Token) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 +} + +type SendOrder_Input struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Coins []*SendOrder_Token `protobuf:"bytes,2,rep,name=coins,proto3" json:"coins,omitempty"` +} + +func (x *SendOrder_Input) Reset() { + *x = SendOrder_Input{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendOrder_Input) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendOrder_Input) ProtoMessage() {} + +func (x *SendOrder_Input) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendOrder_Input.ProtoReflect.Descriptor instead. +func (*SendOrder_Input) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{4, 1} +} + +func (x *SendOrder_Input) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *SendOrder_Input) GetCoins() []*SendOrder_Token { + if x != nil { + return x.Coins + } + return nil +} + +type SendOrder_Output struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Coins []*SendOrder_Token `protobuf:"bytes,2,rep,name=coins,proto3" json:"coins,omitempty"` +} + +func (x *SendOrder_Output) Reset() { + *x = SendOrder_Output{} + if protoimpl.UnsafeEnabled { + mi := &file_Binance_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendOrder_Output) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendOrder_Output) ProtoMessage() {} + +func (x *SendOrder_Output) ProtoReflect() protoreflect.Message { + mi := &file_Binance_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendOrder_Output.ProtoReflect.Descriptor instead. +func (*SendOrder_Output) Descriptor() ([]byte, []int) { + return file_Binance_proto_rawDescGZIP(), []int{4, 2} +} + +func (x *SendOrder_Output) GetAddress() []byte { + if x != nil { + return x.Address + } + return nil +} + +func (x *SendOrder_Output) GetCoins() []*SendOrder_Token { + if x != nil { + return x.Coins + } + return nil +} + +var File_Binance_proto protoreflect.FileDescriptor + +var file_Binance_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x10, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x81, 0x01, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x12, 0x0a, 0x04, 0x6d, 0x73, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x04, 0x6d, + 0x73, 0x67, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x22, 0x8f, 0x01, 0x0a, 0x09, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x12, 0x17, 0x0a, 0x07, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, + 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x1a, 0x08, 0x0a, 0x06, 0x50, + 0x75, 0x62, 0x4b, 0x65, 0x79, 0x22, 0xd2, 0x01, 0x0a, 0x0a, 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, + 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x70, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, + 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, + 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, + 0x69, 0x6d, 0x65, 0x69, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x58, 0x0a, 0x10, 0x43, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, + 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x14, + 0x0a, 0x05, 0x72, 0x65, 0x66, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, + 0x65, 0x66, 0x69, 0x64, 0x22, 0xf4, 0x02, 0x0a, 0x09, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x12, 0x39, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x3c, 0x0a, + 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x1a, 0x35, 0x0a, 0x05, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x64, 0x65, 0x6e, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x1a, 0x5a, 0x0a, 0x05, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, + 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x1a, 0x5b, + 0x0a, 0x06, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x37, 0x0a, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x63, 0x6f, 0x69, 0x6e, 0x73, 0x22, 0x90, 0x01, 0x0a, 0x0f, + 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, + 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, + 0x72, 0x6f, 0x6d, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, + 0x21, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6c, 0x79, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x53, 0x75, 0x70, 0x70, + 0x6c, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x69, 0x6e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x54, + 0x0a, 0x0e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4d, 0x69, 0x6e, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x54, 0x0a, 0x0e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x75, 0x72, + 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, + 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, + 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x56, 0x0a, 0x10, 0x54, 0x6f, + 0x6b, 0x65, 0x6e, 0x46, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, + 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, + 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x22, 0x58, 0x0a, 0x12, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x6e, 0x66, 0x72, 0x65, + 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, + 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x83, 0x03, 0x0a, + 0x09, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, + 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, + 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x32, + 0x0a, 0x15, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x72, + 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x5f, 0x6f, 0x74, 0x68, + 0x65, 0x72, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, + 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x12, 0x2c, 0x0a, 0x12, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x72, 0x61, + 0x6e, 0x64, 0x6f, 0x6d, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x48, 0x61, 0x73, 0x68, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x39, 0x0a, 0x06, + 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, + 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, + 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x5f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x65, 0x78, 0x70, 0x65, 0x63, 0x74, 0x65, 0x64, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x73, 0x70, 0x61, 0x6e, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x53, 0x70, 0x61, + 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x22, 0x7a, 0x0a, 0x10, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, + 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, + 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x77, 0x61, 0x70, 0x49, 0x64, 0x22, 0x61, + 0x0a, 0x0d, 0x43, 0x6c, 0x61, 0x69, 0x6d, 0x48, 0x54, 0x4c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, + 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x66, + 0x72, 0x6f, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x77, 0x61, 0x70, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, + 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x22, 0x3e, 0x0a, 0x0f, 0x52, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x48, 0x54, 0x4c, 0x54, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x77, 0x61, 0x70, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x77, 0x61, 0x70, 0x49, + 0x64, 0x22, 0x8d, 0x01, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4f, 0x75, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, + 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, + 0x12, 0x1f, 0x0a, 0x0b, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x54, 0x69, 0x6d, + 0x65, 0x22, 0xbf, 0x01, 0x0a, 0x11, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x44, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x6c, 0x65, 0x67, + 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x25, + 0x0a, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x41, 0x0a, 0x0a, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, + 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, + 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0a, 0x64, 0x65, + 0x6c, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x49, 0x64, 0x22, 0xee, 0x01, 0x0a, 0x13, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x52, 0x65, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x64, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, + 0x64, 0x72, 0x12, 0x2c, 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, + 0x73, 0x72, 0x63, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x72, 0x63, 0x41, 0x64, 0x64, 0x72, + 0x12, 0x2c, 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x64, 0x73, + 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x44, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x12, 0x39, + 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x49, 0x64, 0x22, 0xb9, 0x01, 0x0a, 0x13, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x25, 0x0a, 0x0e, + 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x41, + 0x64, 0x64, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x64, 0x64, 0x72, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, + 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, + 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, + 0x22, 0xac, 0x01, 0x0a, 0x0d, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, + 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4f, + 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x22, + 0xbe, 0x01, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x6e, 0x64, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, + 0x22, 0x44, 0x0a, 0x0f, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x22, 0xf9, 0x0c, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x69, + 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x73, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x6d, 0x65, 0x6d, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x65, 0x6d, + 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, + 0x65, 0x79, 0x12, 0x3f, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x64, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, + 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x64, 0x65, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x12, 0x52, 0x0a, 0x12, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x5f, 0x74, 0x72, + 0x61, 0x64, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x72, 0x61, 0x64, 0x65, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x54, 0x72, 0x61, + 0x64, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0a, 0x73, 0x65, 0x6e, 0x64, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x54, 0x57, + 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, + 0x65, 0x6e, 0x64, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x64, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x47, 0x0a, 0x0c, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x54, 0x57, + 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x46, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, + 0x00, 0x52, 0x0b, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4d, + 0x0a, 0x0e, 0x75, 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, + 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x55, + 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0d, + 0x75, 0x6e, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x3c, 0x0a, + 0x0a, 0x68, 0x74, 0x6c, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x09, 0x68, 0x74, 0x6c, 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x51, 0x0a, 0x11, 0x64, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, 0x54, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, + 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x10, 0x64, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4a, + 0x0a, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x48, 0x54, 0x4c, 0x54, 0x5f, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, + 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x6c, 0x61, 0x69, 0x6d, + 0x48, 0x54, 0x4c, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x63, 0x6c, 0x61, 0x69, + 0x6d, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4e, 0x0a, 0x10, 0x72, 0x65, + 0x66, 0x75, 0x6e, 0x64, 0x48, 0x54, 0x4c, 0x54, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x10, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, + 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x66, 0x75, 0x6e, 0x64, 0x48, 0x54, + 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0f, 0x72, 0x65, 0x66, 0x75, 0x6e, + 0x64, 0x48, 0x54, 0x4c, 0x54, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0b, 0x69, 0x73, + 0x73, 0x75, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x73, 0x73, 0x75, 0x65, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x73, 0x73, 0x75, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x12, 0x41, 0x0a, 0x0a, 0x6d, 0x69, 0x6e, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x12, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, + 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x4d, 0x69, 0x6e, + 0x74, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x6d, 0x69, 0x6e, 0x74, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x0a, 0x62, 0x75, 0x72, 0x6e, 0x5f, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, + 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x42, 0x75, 0x72, 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, 0x00, 0x52, 0x09, 0x62, 0x75, 0x72, + 0x6e, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x4d, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, + 0x65, 0x72, 0x5f, 0x6f, 0x75, 0x74, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4f, 0x75, + 0x74, 0x48, 0x00, 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4f, 0x75, 0x74, + 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x55, 0x0a, 0x13, 0x73, 0x69, 0x64, 0x65, 0x5f, 0x64, 0x65, + 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x15, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x44, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x11, 0x73, 0x69, 0x64, 0x65, 0x44, + 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x15, + 0x73, 0x69, 0x64, 0x65, 0x5f, 0x72, 0x65, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x54, 0x57, + 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, + 0x69, 0x64, 0x65, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x48, 0x00, 0x52, 0x13, 0x73, 0x69, 0x64, 0x65, 0x52, 0x65, 0x64, 0x65, 0x6c, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x15, 0x73, 0x69, 0x64, + 0x65, 0x5f, 0x75, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x64, 0x65, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, 0x65, 0x48, + 0x00, 0x52, 0x13, 0x73, 0x69, 0x64, 0x65, 0x55, 0x6e, 0x64, 0x65, 0x6c, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x12, 0x49, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x48, 0x00, 0x52, 0x0d, 0x74, 0x69, 0x6d, 0x65, 0x4c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, + 0x72, 0x12, 0x4f, 0x0a, 0x11, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, + 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, 0x48, + 0x00, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, + 0x65, 0x72, 0x12, 0x4f, 0x0a, 0x11, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x75, 0x6e, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x1a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x54, 0x57, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, 0x64, 0x65, 0x72, + 0x48, 0x00, 0x52, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x55, 0x6e, 0x6c, 0x6f, 0x63, 0x6b, 0x4f, 0x72, + 0x64, 0x65, 0x72, 0x42, 0x0d, 0x0a, 0x0b, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x6f, 0x6e, 0x65, + 0x6f, 0x66, 0x22, 0x83, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x12, 0x33, + 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, + 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, + 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, + 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x56, 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, + 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x62, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_Binance_proto_rawDescOnce sync.Once + file_Binance_proto_rawDescData = file_Binance_proto_rawDesc +) + +func file_Binance_proto_rawDescGZIP() []byte { + file_Binance_proto_rawDescOnce.Do(func() { + file_Binance_proto_rawDescData = protoimpl.X.CompressGZIP(file_Binance_proto_rawDescData) + }) + return file_Binance_proto_rawDescData +} + +var file_Binance_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_Binance_proto_goTypes = []interface{}{ + (*Transaction)(nil), // 0: TW.Binance.Proto.Transaction + (*Signature)(nil), // 1: TW.Binance.Proto.Signature + (*TradeOrder)(nil), // 2: TW.Binance.Proto.TradeOrder + (*CancelTradeOrder)(nil), // 3: TW.Binance.Proto.CancelTradeOrder + (*SendOrder)(nil), // 4: TW.Binance.Proto.SendOrder + (*TokenIssueOrder)(nil), // 5: TW.Binance.Proto.TokenIssueOrder + (*TokenMintOrder)(nil), // 6: TW.Binance.Proto.TokenMintOrder + (*TokenBurnOrder)(nil), // 7: TW.Binance.Proto.TokenBurnOrder + (*TokenFreezeOrder)(nil), // 8: TW.Binance.Proto.TokenFreezeOrder + (*TokenUnfreezeOrder)(nil), // 9: TW.Binance.Proto.TokenUnfreezeOrder + (*HTLTOrder)(nil), // 10: TW.Binance.Proto.HTLTOrder + (*DepositHTLTOrder)(nil), // 11: TW.Binance.Proto.DepositHTLTOrder + (*ClaimHTLOrder)(nil), // 12: TW.Binance.Proto.ClaimHTLOrder + (*RefundHTLTOrder)(nil), // 13: TW.Binance.Proto.RefundHTLTOrder + (*TransferOut)(nil), // 14: TW.Binance.Proto.TransferOut + (*SideChainDelegate)(nil), // 15: TW.Binance.Proto.SideChainDelegate + (*SideChainRedelegate)(nil), // 16: TW.Binance.Proto.SideChainRedelegate + (*SideChainUndelegate)(nil), // 17: TW.Binance.Proto.SideChainUndelegate + (*TimeLockOrder)(nil), // 18: TW.Binance.Proto.TimeLockOrder + (*TimeRelockOrder)(nil), // 19: TW.Binance.Proto.TimeRelockOrder + (*TimeUnlockOrder)(nil), // 20: TW.Binance.Proto.TimeUnlockOrder + (*SigningInput)(nil), // 21: TW.Binance.Proto.SigningInput + (*SigningOutput)(nil), // 22: TW.Binance.Proto.SigningOutput + (*Signature_PubKey)(nil), // 23: TW.Binance.Proto.Signature.PubKey + (*SendOrder_Token)(nil), // 24: TW.Binance.Proto.SendOrder.Token + (*SendOrder_Input)(nil), // 25: TW.Binance.Proto.SendOrder.Input + (*SendOrder_Output)(nil), // 26: TW.Binance.Proto.SendOrder.Output + (common.SigningError)(0), // 27: TW.Common.Proto.SigningError +} +var file_Binance_proto_depIdxs = []int32{ + 25, // 0: TW.Binance.Proto.SendOrder.inputs:type_name -> TW.Binance.Proto.SendOrder.Input + 26, // 1: TW.Binance.Proto.SendOrder.outputs:type_name -> TW.Binance.Proto.SendOrder.Output + 24, // 2: TW.Binance.Proto.HTLTOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 24, // 3: TW.Binance.Proto.DepositHTLTOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 24, // 4: TW.Binance.Proto.TransferOut.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 24, // 5: TW.Binance.Proto.SideChainDelegate.delegation:type_name -> TW.Binance.Proto.SendOrder.Token + 24, // 6: TW.Binance.Proto.SideChainRedelegate.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 24, // 7: TW.Binance.Proto.SideChainUndelegate.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 24, // 8: TW.Binance.Proto.TimeLockOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 24, // 9: TW.Binance.Proto.TimeRelockOrder.amount:type_name -> TW.Binance.Proto.SendOrder.Token + 2, // 10: TW.Binance.Proto.SigningInput.trade_order:type_name -> TW.Binance.Proto.TradeOrder + 3, // 11: TW.Binance.Proto.SigningInput.cancel_trade_order:type_name -> TW.Binance.Proto.CancelTradeOrder + 4, // 12: TW.Binance.Proto.SigningInput.send_order:type_name -> TW.Binance.Proto.SendOrder + 8, // 13: TW.Binance.Proto.SigningInput.freeze_order:type_name -> TW.Binance.Proto.TokenFreezeOrder + 9, // 14: TW.Binance.Proto.SigningInput.unfreeze_order:type_name -> TW.Binance.Proto.TokenUnfreezeOrder + 10, // 15: TW.Binance.Proto.SigningInput.htlt_order:type_name -> TW.Binance.Proto.HTLTOrder + 11, // 16: TW.Binance.Proto.SigningInput.depositHTLT_order:type_name -> TW.Binance.Proto.DepositHTLTOrder + 12, // 17: TW.Binance.Proto.SigningInput.claimHTLT_order:type_name -> TW.Binance.Proto.ClaimHTLOrder + 13, // 18: TW.Binance.Proto.SigningInput.refundHTLT_order:type_name -> TW.Binance.Proto.RefundHTLTOrder + 5, // 19: TW.Binance.Proto.SigningInput.issue_order:type_name -> TW.Binance.Proto.TokenIssueOrder + 6, // 20: TW.Binance.Proto.SigningInput.mint_order:type_name -> TW.Binance.Proto.TokenMintOrder + 7, // 21: TW.Binance.Proto.SigningInput.burn_order:type_name -> TW.Binance.Proto.TokenBurnOrder + 14, // 22: TW.Binance.Proto.SigningInput.transfer_out_order:type_name -> TW.Binance.Proto.TransferOut + 15, // 23: TW.Binance.Proto.SigningInput.side_delegate_order:type_name -> TW.Binance.Proto.SideChainDelegate + 16, // 24: TW.Binance.Proto.SigningInput.side_redelegate_order:type_name -> TW.Binance.Proto.SideChainRedelegate + 17, // 25: TW.Binance.Proto.SigningInput.side_undelegate_order:type_name -> TW.Binance.Proto.SideChainUndelegate + 18, // 26: TW.Binance.Proto.SigningInput.time_lock_order:type_name -> TW.Binance.Proto.TimeLockOrder + 19, // 27: TW.Binance.Proto.SigningInput.time_relock_order:type_name -> TW.Binance.Proto.TimeRelockOrder + 20, // 28: TW.Binance.Proto.SigningInput.time_unlock_order:type_name -> TW.Binance.Proto.TimeUnlockOrder + 27, // 29: TW.Binance.Proto.SigningOutput.error:type_name -> TW.Common.Proto.SigningError + 24, // 30: TW.Binance.Proto.SendOrder.Input.coins:type_name -> TW.Binance.Proto.SendOrder.Token + 24, // 31: TW.Binance.Proto.SendOrder.Output.coins:type_name -> TW.Binance.Proto.SendOrder.Token + 32, // [32:32] is the sub-list for method output_type + 32, // [32:32] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name +} + +func init() { file_Binance_proto_init() } +func file_Binance_proto_init() { + if File_Binance_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_Binance_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Signature); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TradeOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CancelTradeOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TokenIssueOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TokenMintOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TokenBurnOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TokenFreezeOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TokenUnfreezeOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HTLTOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DepositHTLTOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ClaimHTLOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RefundHTLTOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransferOut); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SideChainDelegate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SideChainRedelegate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SideChainUndelegate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TimeLockOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TimeRelockOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TimeUnlockOrder); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SigningInput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SigningOutput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Signature_PubKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendOrder_Token); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendOrder_Input); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Binance_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendOrder_Output); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_Binance_proto_msgTypes[21].OneofWrappers = []interface{}{ + (*SigningInput_TradeOrder)(nil), + (*SigningInput_CancelTradeOrder)(nil), + (*SigningInput_SendOrder)(nil), + (*SigningInput_FreezeOrder)(nil), + (*SigningInput_UnfreezeOrder)(nil), + (*SigningInput_HtltOrder)(nil), + (*SigningInput_DepositHTLTOrder)(nil), + (*SigningInput_ClaimHTLTOrder)(nil), + (*SigningInput_RefundHTLTOrder)(nil), + (*SigningInput_IssueOrder)(nil), + (*SigningInput_MintOrder)(nil), + (*SigningInput_BurnOrder)(nil), + (*SigningInput_TransferOutOrder)(nil), + (*SigningInput_SideDelegateOrder)(nil), + (*SigningInput_SideRedelegateOrder)(nil), + (*SigningInput_SideUndelegateOrder)(nil), + (*SigningInput_TimeLockOrder)(nil), + (*SigningInput_TimeRelockOrder)(nil), + (*SigningInput_TimeUnlockOrder)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_Binance_proto_rawDesc, + NumEnums: 0, + NumMessages: 27, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_Binance_proto_goTypes, + DependencyIndexes: file_Binance_proto_depIdxs, + MessageInfos: file_Binance_proto_msgTypes, + }.Build() + File_Binance_proto = out.File + file_Binance_proto_rawDesc = nil + file_Binance_proto_goTypes = nil + file_Binance_proto_depIdxs = nil +} diff --git a/samples/go/protos/bitcoin/Bitcoin.pb.go b/samples/go/protos/bitcoin/Bitcoin.pb.go index ed93c62556a..88b884e6912 100644 --- a/samples/go/protos/bitcoin/Bitcoin.pb.go +++ b/samples/go/protos/bitcoin/Bitcoin.pb.go @@ -1,27 +1,31 @@ // Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.19.2 // source: Bitcoin.proto package bitcoin import ( - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" + common "tw/protos/common" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" ) -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) type Transaction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Transaction data format version. Version int32 `protobuf:"zigzag32,1,opt,name=version,proto3" json:"version,omitempty"` // The block number or timestamp at which this transaction is unlocked. @@ -29,293 +33,397 @@ type Transaction struct { // A list of 1 or more transaction inputs or sources for coins. Inputs []*TransactionInput `protobuf:"bytes,3,rep,name=inputs,proto3" json:"inputs,omitempty"` // A list of 1 or more transaction outputs or destinations for coins - Outputs []*TransactionOutput `protobuf:"bytes,4,rep,name=outputs,proto3" json:"outputs,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Outputs []*TransactionOutput `protobuf:"bytes,4,rep,name=outputs,proto3" json:"outputs,omitempty"` } -func (m *Transaction) Reset() { *m = Transaction{} } -func (m *Transaction) String() string { return proto.CompactTextString(m) } -func (*Transaction) ProtoMessage() {} -func (*Transaction) Descriptor() ([]byte, []int) { - return fileDescriptor_9fbb050c4cb9ab40, []int{0} +func (x *Transaction) Reset() { + *x = Transaction{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *Transaction) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Transaction.Unmarshal(m, b) +func (x *Transaction) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *Transaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Transaction.Marshal(b, m, deterministic) -} -func (m *Transaction) XXX_Merge(src proto.Message) { - xxx_messageInfo_Transaction.Merge(m, src) -} -func (m *Transaction) XXX_Size() int { - return xxx_messageInfo_Transaction.Size(m) -} -func (m *Transaction) XXX_DiscardUnknown() { - xxx_messageInfo_Transaction.DiscardUnknown(m) + +func (*Transaction) ProtoMessage() {} + +func (x *Transaction) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_Transaction proto.InternalMessageInfo +// Deprecated: Use Transaction.ProtoReflect.Descriptor instead. +func (*Transaction) Descriptor() ([]byte, []int) { + return file_Bitcoin_proto_rawDescGZIP(), []int{0} +} -func (m *Transaction) GetVersion() int32 { - if m != nil { - return m.Version +func (x *Transaction) GetVersion() int32 { + if x != nil { + return x.Version } return 0 } -func (m *Transaction) GetLockTime() uint32 { - if m != nil { - return m.LockTime +func (x *Transaction) GetLockTime() uint32 { + if x != nil { + return x.LockTime } return 0 } -func (m *Transaction) GetInputs() []*TransactionInput { - if m != nil { - return m.Inputs +func (x *Transaction) GetInputs() []*TransactionInput { + if x != nil { + return x.Inputs } return nil } -func (m *Transaction) GetOutputs() []*TransactionOutput { - if m != nil { - return m.Outputs +func (x *Transaction) GetOutputs() []*TransactionOutput { + if x != nil { + return x.Outputs } return nil } // Bitcoin transaction input. type TransactionInput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Reference to the previous transaction's output. PreviousOutput *OutPoint `protobuf:"bytes,1,opt,name=previousOutput,proto3" json:"previousOutput,omitempty"` // Transaction version as defined by the sender. Sequence uint32 `protobuf:"varint,2,opt,name=sequence,proto3" json:"sequence,omitempty"` // Computational script for confirming transaction authorization. - Script []byte `protobuf:"bytes,3,opt,name=script,proto3" json:"script,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Script []byte `protobuf:"bytes,3,opt,name=script,proto3" json:"script,omitempty"` } -func (m *TransactionInput) Reset() { *m = TransactionInput{} } -func (m *TransactionInput) String() string { return proto.CompactTextString(m) } -func (*TransactionInput) ProtoMessage() {} -func (*TransactionInput) Descriptor() ([]byte, []int) { - return fileDescriptor_9fbb050c4cb9ab40, []int{1} +func (x *TransactionInput) Reset() { + *x = TransactionInput{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *TransactionInput) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TransactionInput.Unmarshal(m, b) -} -func (m *TransactionInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TransactionInput.Marshal(b, m, deterministic) +func (x *TransactionInput) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TransactionInput) XXX_Merge(src proto.Message) { - xxx_messageInfo_TransactionInput.Merge(m, src) -} -func (m *TransactionInput) XXX_Size() int { - return xxx_messageInfo_TransactionInput.Size(m) -} -func (m *TransactionInput) XXX_DiscardUnknown() { - xxx_messageInfo_TransactionInput.DiscardUnknown(m) + +func (*TransactionInput) ProtoMessage() {} + +func (x *TransactionInput) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_TransactionInput proto.InternalMessageInfo +// Deprecated: Use TransactionInput.ProtoReflect.Descriptor instead. +func (*TransactionInput) Descriptor() ([]byte, []int) { + return file_Bitcoin_proto_rawDescGZIP(), []int{1} +} -func (m *TransactionInput) GetPreviousOutput() *OutPoint { - if m != nil { - return m.PreviousOutput +func (x *TransactionInput) GetPreviousOutput() *OutPoint { + if x != nil { + return x.PreviousOutput } return nil } -func (m *TransactionInput) GetSequence() uint32 { - if m != nil { - return m.Sequence +func (x *TransactionInput) GetSequence() uint32 { + if x != nil { + return x.Sequence } return 0 } -func (m *TransactionInput) GetScript() []byte { - if m != nil { - return m.Script +func (x *TransactionInput) GetScript() []byte { + if x != nil { + return x.Script } return nil } // Bitcoin transaction out-point reference. type OutPoint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // The hash of the referenced transaction. Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` // The index of the specific output in the transaction. Index uint32 `protobuf:"varint,2,opt,name=index,proto3" json:"index,omitempty"` // Transaction version as defined by the sender. - Sequence uint32 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Sequence uint32 `protobuf:"varint,3,opt,name=sequence,proto3" json:"sequence,omitempty"` } -func (m *OutPoint) Reset() { *m = OutPoint{} } -func (m *OutPoint) String() string { return proto.CompactTextString(m) } -func (*OutPoint) ProtoMessage() {} -func (*OutPoint) Descriptor() ([]byte, []int) { - return fileDescriptor_9fbb050c4cb9ab40, []int{2} +func (x *OutPoint) Reset() { + *x = OutPoint{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *OutPoint) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_OutPoint.Unmarshal(m, b) -} -func (m *OutPoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_OutPoint.Marshal(b, m, deterministic) +func (x *OutPoint) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *OutPoint) XXX_Merge(src proto.Message) { - xxx_messageInfo_OutPoint.Merge(m, src) -} -func (m *OutPoint) XXX_Size() int { - return xxx_messageInfo_OutPoint.Size(m) -} -func (m *OutPoint) XXX_DiscardUnknown() { - xxx_messageInfo_OutPoint.DiscardUnknown(m) + +func (*OutPoint) ProtoMessage() {} + +func (x *OutPoint) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_OutPoint proto.InternalMessageInfo +// Deprecated: Use OutPoint.ProtoReflect.Descriptor instead. +func (*OutPoint) Descriptor() ([]byte, []int) { + return file_Bitcoin_proto_rawDescGZIP(), []int{2} +} -func (m *OutPoint) GetHash() []byte { - if m != nil { - return m.Hash +func (x *OutPoint) GetHash() []byte { + if x != nil { + return x.Hash } return nil } -func (m *OutPoint) GetIndex() uint32 { - if m != nil { - return m.Index +func (x *OutPoint) GetIndex() uint32 { + if x != nil { + return x.Index } return 0 } -func (m *OutPoint) GetSequence() uint32 { - if m != nil { - return m.Sequence +func (x *OutPoint) GetSequence() uint32 { + if x != nil { + return x.Sequence } return 0 } // Bitcoin transaction output. type TransactionOutput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Transaction amount. Value int64 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"` // Usually contains the public key as a Bitcoin script setting up conditions to claim this output. - Script []byte `protobuf:"bytes,2,opt,name=script,proto3" json:"script,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Script []byte `protobuf:"bytes,2,opt,name=script,proto3" json:"script,omitempty"` } -func (m *TransactionOutput) Reset() { *m = TransactionOutput{} } -func (m *TransactionOutput) String() string { return proto.CompactTextString(m) } -func (*TransactionOutput) ProtoMessage() {} -func (*TransactionOutput) Descriptor() ([]byte, []int) { - return fileDescriptor_9fbb050c4cb9ab40, []int{3} +func (x *TransactionOutput) Reset() { + *x = TransactionOutput{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *TransactionOutput) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TransactionOutput.Unmarshal(m, b) -} -func (m *TransactionOutput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TransactionOutput.Marshal(b, m, deterministic) +func (x *TransactionOutput) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TransactionOutput) XXX_Merge(src proto.Message) { - xxx_messageInfo_TransactionOutput.Merge(m, src) -} -func (m *TransactionOutput) XXX_Size() int { - return xxx_messageInfo_TransactionOutput.Size(m) -} -func (m *TransactionOutput) XXX_DiscardUnknown() { - xxx_messageInfo_TransactionOutput.DiscardUnknown(m) + +func (*TransactionOutput) ProtoMessage() {} + +func (x *TransactionOutput) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_TransactionOutput proto.InternalMessageInfo +// Deprecated: Use TransactionOutput.ProtoReflect.Descriptor instead. +func (*TransactionOutput) Descriptor() ([]byte, []int) { + return file_Bitcoin_proto_rawDescGZIP(), []int{3} +} -func (m *TransactionOutput) GetValue() int64 { - if m != nil { - return m.Value +func (x *TransactionOutput) GetValue() int64 { + if x != nil { + return x.Value } return 0 } -func (m *TransactionOutput) GetScript() []byte { - if m != nil { - return m.Script +func (x *TransactionOutput) GetScript() []byte { + if x != nil { + return x.Script } return nil } +// An unspent transaction output, that can serve as input to a transaction type UnspentTransaction struct { - OutPoint *OutPoint `protobuf:"bytes,1,opt,name=out_point,json=outPoint,proto3" json:"out_point,omitempty"` - Script []byte `protobuf:"bytes,2,opt,name=script,proto3" json:"script,omitempty"` - Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The unspent output + OutPoint *OutPoint `protobuf:"bytes,1,opt,name=out_point,json=outPoint,proto3" json:"out_point,omitempty"` + // Script for claiming this UTXO + Script []byte `protobuf:"bytes,2,opt,name=script,proto3" json:"script,omitempty"` + // Amount of the UTXO + Amount int64 `protobuf:"varint,3,opt,name=amount,proto3" json:"amount,omitempty"` +} + +func (x *UnspentTransaction) Reset() { + *x = UnspentTransaction{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UnspentTransaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UnspentTransaction) ProtoMessage() {} + +func (x *UnspentTransaction) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -func (m *UnspentTransaction) Reset() { *m = UnspentTransaction{} } -func (m *UnspentTransaction) String() string { return proto.CompactTextString(m) } -func (*UnspentTransaction) ProtoMessage() {} +// Deprecated: Use UnspentTransaction.ProtoReflect.Descriptor instead. func (*UnspentTransaction) Descriptor() ([]byte, []int) { - return fileDescriptor_9fbb050c4cb9ab40, []int{4} + return file_Bitcoin_proto_rawDescGZIP(), []int{4} } -func (m *UnspentTransaction) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UnspentTransaction.Unmarshal(m, b) +func (x *UnspentTransaction) GetOutPoint() *OutPoint { + if x != nil { + return x.OutPoint + } + return nil } -func (m *UnspentTransaction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UnspentTransaction.Marshal(b, m, deterministic) + +func (x *UnspentTransaction) GetScript() []byte { + if x != nil { + return x.Script + } + return nil } -func (m *UnspentTransaction) XXX_Merge(src proto.Message) { - xxx_messageInfo_UnspentTransaction.Merge(m, src) + +func (x *UnspentTransaction) GetAmount() int64 { + if x != nil { + return x.Amount + } + return 0 } -func (m *UnspentTransaction) XXX_Size() int { - return xxx_messageInfo_UnspentTransaction.Size(m) + +// Pair of destination address and amount, used for extra outputs +type OutputAddress struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Destination address + ToAddress string `protobuf:"bytes,1,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"` + // Amount to be paid to this output + Amount int64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` } -func (m *UnspentTransaction) XXX_DiscardUnknown() { - xxx_messageInfo_UnspentTransaction.DiscardUnknown(m) + +func (x *OutputAddress) Reset() { + *x = OutputAddress{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -var xxx_messageInfo_UnspentTransaction proto.InternalMessageInfo +func (x *OutputAddress) String() string { + return protoimpl.X.MessageStringOf(x) +} -func (m *UnspentTransaction) GetOutPoint() *OutPoint { - if m != nil { - return m.OutPoint +func (*OutputAddress) ProtoMessage() {} + +func (x *OutputAddress) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms } - return nil + return mi.MessageOf(x) +} + +// Deprecated: Use OutputAddress.ProtoReflect.Descriptor instead. +func (*OutputAddress) Descriptor() ([]byte, []int) { + return file_Bitcoin_proto_rawDescGZIP(), []int{5} } -func (m *UnspentTransaction) GetScript() []byte { - if m != nil { - return m.Script +func (x *OutputAddress) GetToAddress() string { + if x != nil { + return x.ToAddress } - return nil + return "" } -func (m *UnspentTransaction) GetAmount() int64 { - if m != nil { - return m.Amount +func (x *OutputAddress) GetAmount() int64 { + if x != nil { + return x.Amount } return 0 } // Input data necessary to create a signed transaction. type SigningInput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Hash type to use when signing. HashType uint32 `protobuf:"varint,1,opt,name=hash_type,json=hashType,proto3" json:"hash_type,omitempty"` - // Amount to send. + // Amount to send. Transaction created will have this amount in its output, + // except when use_max_amount is set, in that case this amount is not relevant, maximum possible amount will be used (max avail less fee). + // If amount is equal or more than the available amount, also max amount will be used. Amount int64 `protobuf:"varint,2,opt,name=amount,proto3" json:"amount,omitempty"` // Transaction fee per byte. ByteFee int64 `protobuf:"varint,3,opt,name=byte_fee,json=byteFee,proto3" json:"byte_fee,omitempty"` @@ -324,126 +432,182 @@ type SigningInput struct { // Change address. ChangeAddress string `protobuf:"bytes,5,opt,name=change_address,json=changeAddress,proto3" json:"change_address,omitempty"` // Available private keys. - PrivateKey [][]byte `protobuf:"bytes,10,rep,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` + PrivateKey [][]byte `protobuf:"bytes,6,rep,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` // Available redeem scripts indexed by script hash. - Scripts map[string][]byte `protobuf:"bytes,11,rep,name=scripts,proto3" json:"scripts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Scripts map[string][]byte `protobuf:"bytes,7,rep,name=scripts,proto3" json:"scripts,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // Available unspent transaction outputs. - Utxo []*UnspentTransaction `protobuf:"bytes,12,rep,name=utxo,proto3" json:"utxo,omitempty"` + Utxo []*UnspentTransaction `protobuf:"bytes,8,rep,name=utxo,proto3" json:"utxo,omitempty"` // If sending max amount. - UseMaxAmount bool `protobuf:"varint,13,opt,name=use_max_amount,json=useMaxAmount,proto3" json:"use_max_amount,omitempty"` - // Coin type (forks). - CoinType uint32 `protobuf:"varint,14,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` + UseMaxAmount bool `protobuf:"varint,9,opt,name=use_max_amount,json=useMaxAmount,proto3" json:"use_max_amount,omitempty"` + CoinType uint32 `protobuf:"varint,10,opt,name=coin_type,json=coinType,proto3" json:"coin_type,omitempty"` // Optional transaction plan - Plan *TransactionPlan `protobuf:"bytes,15,opt,name=plan,proto3" json:"plan,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Plan *TransactionPlan `protobuf:"bytes,11,opt,name=plan,proto3" json:"plan,omitempty"` + // Optional lockTime, default value 0 means no time locking. + // If all inputs have final (`0xffffffff`) sequence numbers then `lockTime` is irrelevant. + // Otherwise, the transaction may not be added to a block until after `lockTime`. + // value < 500000000 : Block number at which this transaction is unlocked + // value >= 500000000 : UNIX timestamp at which this transaction is unlocked + LockTime uint32 `protobuf:"varint,12,opt,name=lock_time,json=lockTime,proto3" json:"lock_time,omitempty"` + // Optional zero-amount, OP_RETURN output + OutputOpReturn []byte `protobuf:"bytes,13,opt,name=output_op_return,json=outputOpReturn,proto3" json:"output_op_return,omitempty"` + // Optional additional destination addresses, additional to first to_address output + ExtraOutputs []*OutputAddress `protobuf:"bytes,14,rep,name=extra_outputs,json=extraOutputs,proto3" json:"extra_outputs,omitempty"` + // If use max utxo. + UseMaxUtxo bool `protobuf:"varint,15,opt,name=use_max_utxo,json=useMaxUtxo,proto3" json:"use_max_utxo,omitempty"` + // If disable dust filter. + DisableDustFilter bool `protobuf:"varint,16,opt,name=disable_dust_filter,json=disableDustFilter,proto3" json:"disable_dust_filter,omitempty"` +} + +func (x *SigningInput) Reset() { + *x = SigningInput{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *SigningInput) Reset() { *m = SigningInput{} } -func (m *SigningInput) String() string { return proto.CompactTextString(m) } -func (*SigningInput) ProtoMessage() {} -func (*SigningInput) Descriptor() ([]byte, []int) { - return fileDescriptor_9fbb050c4cb9ab40, []int{5} +func (x *SigningInput) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *SigningInput) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SigningInput.Unmarshal(m, b) -} -func (m *SigningInput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SigningInput.Marshal(b, m, deterministic) -} -func (m *SigningInput) XXX_Merge(src proto.Message) { - xxx_messageInfo_SigningInput.Merge(m, src) -} -func (m *SigningInput) XXX_Size() int { - return xxx_messageInfo_SigningInput.Size(m) -} -func (m *SigningInput) XXX_DiscardUnknown() { - xxx_messageInfo_SigningInput.DiscardUnknown(m) +func (*SigningInput) ProtoMessage() {} + +func (x *SigningInput) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_SigningInput proto.InternalMessageInfo +// Deprecated: Use SigningInput.ProtoReflect.Descriptor instead. +func (*SigningInput) Descriptor() ([]byte, []int) { + return file_Bitcoin_proto_rawDescGZIP(), []int{6} +} -func (m *SigningInput) GetHashType() uint32 { - if m != nil { - return m.HashType +func (x *SigningInput) GetHashType() uint32 { + if x != nil { + return x.HashType } return 0 } -func (m *SigningInput) GetAmount() int64 { - if m != nil { - return m.Amount +func (x *SigningInput) GetAmount() int64 { + if x != nil { + return x.Amount } return 0 } -func (m *SigningInput) GetByteFee() int64 { - if m != nil { - return m.ByteFee +func (x *SigningInput) GetByteFee() int64 { + if x != nil { + return x.ByteFee } return 0 } -func (m *SigningInput) GetToAddress() string { - if m != nil { - return m.ToAddress +func (x *SigningInput) GetToAddress() string { + if x != nil { + return x.ToAddress } return "" } -func (m *SigningInput) GetChangeAddress() string { - if m != nil { - return m.ChangeAddress +func (x *SigningInput) GetChangeAddress() string { + if x != nil { + return x.ChangeAddress } return "" } -func (m *SigningInput) GetPrivateKey() [][]byte { - if m != nil { - return m.PrivateKey +func (x *SigningInput) GetPrivateKey() [][]byte { + if x != nil { + return x.PrivateKey } return nil } -func (m *SigningInput) GetScripts() map[string][]byte { - if m != nil { - return m.Scripts +func (x *SigningInput) GetScripts() map[string][]byte { + if x != nil { + return x.Scripts } return nil } -func (m *SigningInput) GetUtxo() []*UnspentTransaction { - if m != nil { - return m.Utxo +func (x *SigningInput) GetUtxo() []*UnspentTransaction { + if x != nil { + return x.Utxo } return nil } -func (m *SigningInput) GetUseMaxAmount() bool { - if m != nil { - return m.UseMaxAmount +func (x *SigningInput) GetUseMaxAmount() bool { + if x != nil { + return x.UseMaxAmount } return false } -func (m *SigningInput) GetCoinType() uint32 { - if m != nil { - return m.CoinType +func (x *SigningInput) GetCoinType() uint32 { + if x != nil { + return x.CoinType + } + return 0 +} + +func (x *SigningInput) GetPlan() *TransactionPlan { + if x != nil { + return x.Plan + } + return nil +} + +func (x *SigningInput) GetLockTime() uint32 { + if x != nil { + return x.LockTime } return 0 } -func (m *SigningInput) GetPlan() *TransactionPlan { - if m != nil { - return m.Plan +func (x *SigningInput) GetOutputOpReturn() []byte { + if x != nil { + return x.OutputOpReturn + } + return nil +} + +func (x *SigningInput) GetExtraOutputs() []*OutputAddress { + if x != nil { + return x.ExtraOutputs } return nil } +func (x *SigningInput) GetUseMaxUtxo() bool { + if x != nil { + return x.UseMaxUtxo + } + return false +} + +func (x *SigningInput) GetDisableDustFilter() bool { + if x != nil { + return x.DisableDustFilter + } + return false +} + // Describes a preliminary transaction plan. type TransactionPlan struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Amount to be received at the other end. Amount int64 `protobuf:"varint,1,opt,name=amount,proto3" json:"amount,omitempty"` // Maximum available amount. @@ -455,206 +619,669 @@ type TransactionPlan struct { // Selected unspent transaction outputs. Utxos []*UnspentTransaction `protobuf:"bytes,5,rep,name=utxos,proto3" json:"utxos,omitempty"` // Zcash branch id - BranchId []byte `protobuf:"bytes,6,opt,name=branch_id,json=branchId,proto3" json:"branch_id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + BranchId []byte `protobuf:"bytes,6,opt,name=branch_id,json=branchId,proto3" json:"branch_id,omitempty"` + // Optional error + Error common.SigningError `protobuf:"varint,7,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` + // Optional zero-amount, OP_RETURN output + OutputOpReturn []byte `protobuf:"bytes,8,opt,name=output_op_return,json=outputOpReturn,proto3" json:"output_op_return,omitempty"` +} + +func (x *TransactionPlan) Reset() { + *x = TransactionPlan{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } } -func (m *TransactionPlan) Reset() { *m = TransactionPlan{} } -func (m *TransactionPlan) String() string { return proto.CompactTextString(m) } -func (*TransactionPlan) ProtoMessage() {} -func (*TransactionPlan) Descriptor() ([]byte, []int) { - return fileDescriptor_9fbb050c4cb9ab40, []int{6} +func (x *TransactionPlan) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *TransactionPlan) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TransactionPlan.Unmarshal(m, b) -} -func (m *TransactionPlan) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TransactionPlan.Marshal(b, m, deterministic) -} -func (m *TransactionPlan) XXX_Merge(src proto.Message) { - xxx_messageInfo_TransactionPlan.Merge(m, src) -} -func (m *TransactionPlan) XXX_Size() int { - return xxx_messageInfo_TransactionPlan.Size(m) -} -func (m *TransactionPlan) XXX_DiscardUnknown() { - xxx_messageInfo_TransactionPlan.DiscardUnknown(m) +func (*TransactionPlan) ProtoMessage() {} + +func (x *TransactionPlan) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) } -var xxx_messageInfo_TransactionPlan proto.InternalMessageInfo +// Deprecated: Use TransactionPlan.ProtoReflect.Descriptor instead. +func (*TransactionPlan) Descriptor() ([]byte, []int) { + return file_Bitcoin_proto_rawDescGZIP(), []int{7} +} -func (m *TransactionPlan) GetAmount() int64 { - if m != nil { - return m.Amount +func (x *TransactionPlan) GetAmount() int64 { + if x != nil { + return x.Amount } return 0 } -func (m *TransactionPlan) GetAvailableAmount() int64 { - if m != nil { - return m.AvailableAmount +func (x *TransactionPlan) GetAvailableAmount() int64 { + if x != nil { + return x.AvailableAmount } return 0 } -func (m *TransactionPlan) GetFee() int64 { - if m != nil { - return m.Fee +func (x *TransactionPlan) GetFee() int64 { + if x != nil { + return x.Fee } return 0 } -func (m *TransactionPlan) GetChange() int64 { - if m != nil { - return m.Change +func (x *TransactionPlan) GetChange() int64 { + if x != nil { + return x.Change } return 0 } -func (m *TransactionPlan) GetUtxos() []*UnspentTransaction { - if m != nil { - return m.Utxos +func (x *TransactionPlan) GetUtxos() []*UnspentTransaction { + if x != nil { + return x.Utxos } return nil } -func (m *TransactionPlan) GetBranchId() []byte { - if m != nil { - return m.BranchId +func (x *TransactionPlan) GetBranchId() []byte { + if x != nil { + return x.BranchId + } + return nil +} + +func (x *TransactionPlan) GetError() common.SigningError { + if x != nil { + return x.Error + } + return common.SigningError(0) +} + +func (x *TransactionPlan) GetOutputOpReturn() []byte { + if x != nil { + return x.OutputOpReturn } return nil } // Transaction signing output. type SigningOutput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + // Resulting transaction. Note that the amount may be different than the requested amount to account for fees and available funds. Transaction *Transaction `protobuf:"bytes,1,opt,name=transaction,proto3" json:"transaction,omitempty"` // Signed and encoded transaction bytes. Encoded []byte `protobuf:"bytes,2,opt,name=encoded,proto3" json:"encoded,omitempty"` // Transaction id TransactionId string `protobuf:"bytes,3,opt,name=transaction_id,json=transactionId,proto3" json:"transaction_id,omitempty"` - // Optional error message - Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // Optional error + Error common.SigningError `protobuf:"varint,4,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` + // error description + ErrorMessage string `protobuf:"bytes,5,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (x *SigningOutput) Reset() { + *x = SigningOutput{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SigningOutput) String() string { + return protoimpl.X.MessageStringOf(x) } -func (m *SigningOutput) Reset() { *m = SigningOutput{} } -func (m *SigningOutput) String() string { return proto.CompactTextString(m) } -func (*SigningOutput) ProtoMessage() {} +func (*SigningOutput) ProtoMessage() {} + +func (x *SigningOutput) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SigningOutput.ProtoReflect.Descriptor instead. func (*SigningOutput) Descriptor() ([]byte, []int) { - return fileDescriptor_9fbb050c4cb9ab40, []int{7} + return file_Bitcoin_proto_rawDescGZIP(), []int{8} +} + +func (x *SigningOutput) GetTransaction() *Transaction { + if x != nil { + return x.Transaction + } + return nil } -func (m *SigningOutput) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SigningOutput.Unmarshal(m, b) +func (x *SigningOutput) GetEncoded() []byte { + if x != nil { + return x.Encoded + } + return nil } -func (m *SigningOutput) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SigningOutput.Marshal(b, m, deterministic) + +func (x *SigningOutput) GetTransactionId() string { + if x != nil { + return x.TransactionId + } + return "" } -func (m *SigningOutput) XXX_Merge(src proto.Message) { - xxx_messageInfo_SigningOutput.Merge(m, src) + +func (x *SigningOutput) GetError() common.SigningError { + if x != nil { + return x.Error + } + return common.SigningError(0) } -func (m *SigningOutput) XXX_Size() int { - return xxx_messageInfo_SigningOutput.Size(m) + +func (x *SigningOutput) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" } -func (m *SigningOutput) XXX_DiscardUnknown() { - xxx_messageInfo_SigningOutput.DiscardUnknown(m) + +type HashPublicKey struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + /// Pre-image data hash that will be used for signing + DataHash []byte `protobuf:"bytes,1,opt,name=data_hash,json=dataHash,proto3" json:"data_hash,omitempty"` + /// public key hash used for signing + PublicKeyHash []byte `protobuf:"bytes,2,opt,name=public_key_hash,json=publicKeyHash,proto3" json:"public_key_hash,omitempty"` } -var xxx_messageInfo_SigningOutput proto.InternalMessageInfo +func (x *HashPublicKey) Reset() { + *x = HashPublicKey{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HashPublicKey) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HashPublicKey) ProtoMessage() {} -func (m *SigningOutput) GetTransaction() *Transaction { - if m != nil { - return m.Transaction +func (x *HashPublicKey) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HashPublicKey.ProtoReflect.Descriptor instead. +func (*HashPublicKey) Descriptor() ([]byte, []int) { + return file_Bitcoin_proto_rawDescGZIP(), []int{9} +} + +func (x *HashPublicKey) GetDataHash() []byte { + if x != nil { + return x.DataHash } return nil } -func (m *SigningOutput) GetEncoded() []byte { - if m != nil { - return m.Encoded +func (x *HashPublicKey) GetPublicKeyHash() []byte { + if x != nil { + return x.PublicKeyHash } return nil } -func (m *SigningOutput) GetTransactionId() string { - if m != nil { - return m.TransactionId +/// Transaction pre-signing output +type PreSigningOutput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + /// hash, public key list + HashPublicKeys []*HashPublicKey `protobuf:"bytes,1,rep,name=hash_public_keys,json=hashPublicKeys,proto3" json:"hash_public_keys,omitempty"` + /// error code, 0 is ok, other codes will be treated as errors + Error common.SigningError `protobuf:"varint,2,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` + /// error description + ErrorMessage string `protobuf:"bytes,3,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (x *PreSigningOutput) Reset() { + *x = PreSigningOutput{} + if protoimpl.UnsafeEnabled { + mi := &file_Bitcoin_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) } - return "" } -func (m *SigningOutput) GetError() string { - if m != nil { - return m.Error +func (x *PreSigningOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PreSigningOutput) ProtoMessage() {} + +func (x *PreSigningOutput) ProtoReflect() protoreflect.Message { + mi := &file_Bitcoin_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PreSigningOutput.ProtoReflect.Descriptor instead. +func (*PreSigningOutput) Descriptor() ([]byte, []int) { + return file_Bitcoin_proto_rawDescGZIP(), []int{10} +} + +func (x *PreSigningOutput) GetHashPublicKeys() []*HashPublicKey { + if x != nil { + return x.HashPublicKeys + } + return nil +} + +func (x *PreSigningOutput) GetError() common.SigningError { + if x != nil { + return x.Error + } + return common.SigningError(0) +} + +func (x *PreSigningOutput) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage } return "" } -func init() { - proto.RegisterType((*Transaction)(nil), "TW.Bitcoin.Proto.Transaction") - proto.RegisterType((*TransactionInput)(nil), "TW.Bitcoin.Proto.TransactionInput") - proto.RegisterType((*OutPoint)(nil), "TW.Bitcoin.Proto.OutPoint") - proto.RegisterType((*TransactionOutput)(nil), "TW.Bitcoin.Proto.TransactionOutput") - proto.RegisterType((*UnspentTransaction)(nil), "TW.Bitcoin.Proto.UnspentTransaction") - proto.RegisterType((*SigningInput)(nil), "TW.Bitcoin.Proto.SigningInput") - proto.RegisterMapType((map[string][]byte)(nil), "TW.Bitcoin.Proto.SigningInput.ScriptsEntry") - proto.RegisterType((*TransactionPlan)(nil), "TW.Bitcoin.Proto.TransactionPlan") - proto.RegisterType((*SigningOutput)(nil), "TW.Bitcoin.Proto.SigningOutput") -} - -func init() { proto.RegisterFile("Bitcoin.proto", fileDescriptor_9fbb050c4cb9ab40) } - -var fileDescriptor_9fbb050c4cb9ab40 = []byte{ - // 715 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0xdb, 0x6a, 0xdb, 0x4c, - 0x10, 0x46, 0x96, 0xe3, 0xc3, 0xf8, 0x10, 0x67, 0xf9, 0x0f, 0xfa, 0x13, 0xc2, 0xef, 0xaa, 0x29, - 0xb8, 0x14, 0x7c, 0x91, 0x52, 0x1a, 0x0c, 0xa5, 0x24, 0x90, 0x42, 0x28, 0x25, 0x66, 0xe3, 0xd2, - 0x4b, 0xb1, 0x96, 0xb6, 0xf1, 0x36, 0xca, 0xae, 0x2a, 0xad, 0x5c, 0xfb, 0xa2, 0x2f, 0xd0, 0x17, - 0xe9, 0x5b, 0xf4, 0x35, 0xfa, 0x32, 0xbd, 0x28, 0x7b, 0x90, 0x23, 0xdb, 0x34, 0xe4, 0xca, 0xfb, - 0x0d, 0x33, 0xdf, 0xec, 0xf7, 0xcd, 0xac, 0x05, 0x9d, 0x33, 0x26, 0x43, 0xc1, 0xf8, 0x30, 0x49, - 0x85, 0x14, 0xa8, 0x37, 0xf9, 0x30, 0x2c, 0x22, 0x63, 0x15, 0xf1, 0x7f, 0x38, 0xd0, 0x9a, 0xa4, - 0x84, 0x67, 0x24, 0x94, 0x4c, 0x70, 0xe4, 0x41, 0x7d, 0x4e, 0xd3, 0x8c, 0x09, 0xee, 0x39, 0x7d, - 0x67, 0xb0, 0x87, 0x0b, 0x88, 0xf6, 0xa1, 0x11, 0x8b, 0xf0, 0x66, 0xc2, 0x6e, 0xa9, 0x57, 0xe9, - 0x3b, 0x83, 0x0e, 0x5e, 0x61, 0x34, 0x82, 0x1a, 0xe3, 0x49, 0x2e, 0x33, 0xcf, 0xed, 0xbb, 0x83, - 0xd6, 0xb1, 0x3f, 0xdc, 0x6c, 0x34, 0x2c, 0x35, 0xb9, 0x50, 0xa9, 0xd8, 0x56, 0xa0, 0x57, 0x50, - 0x17, 0xb9, 0xd4, 0xc5, 0x55, 0x5d, 0xfc, 0xf8, 0xde, 0xe2, 0x4b, 0x9d, 0x8b, 0x8b, 0x1a, 0xff, - 0x9b, 0x03, 0xbd, 0x4d, 0x6e, 0x74, 0x06, 0xdd, 0x24, 0xa5, 0x73, 0x26, 0xf2, 0xcc, 0xe4, 0x6b, - 0x31, 0xad, 0xe3, 0xfd, 0x6d, 0xea, 0xcb, 0x5c, 0x8e, 0x05, 0xe3, 0x12, 0x6f, 0x54, 0x28, 0xbd, - 0x19, 0xfd, 0x9c, 0x53, 0x1e, 0xae, 0xf4, 0x16, 0x18, 0xfd, 0x03, 0xb5, 0x2c, 0x4c, 0x59, 0x22, - 0x3d, 0xb7, 0xef, 0x0c, 0xda, 0xd8, 0x22, 0x7f, 0x0c, 0x8d, 0x82, 0x0f, 0x21, 0xa8, 0xce, 0x48, - 0x36, 0xd3, 0x9d, 0xdb, 0x58, 0x9f, 0xd1, 0x5f, 0xb0, 0xc3, 0x78, 0x44, 0x17, 0x96, 0xd0, 0x80, - 0xb5, 0x4e, 0xee, 0x7a, 0x27, 0xff, 0x14, 0xf6, 0xb6, 0xc4, 0x2b, 0x9a, 0x39, 0x89, 0x73, 0xaa, - 0xb9, 0x5d, 0x6c, 0x40, 0xe9, 0x52, 0x95, 0xb5, 0x4b, 0x7d, 0x05, 0xf4, 0x9e, 0x67, 0x09, 0xe5, - 0xb2, 0x3c, 0xe8, 0x97, 0xd0, 0x14, 0xb9, 0x0c, 0x12, 0x75, 0xd7, 0x07, 0xb8, 0xd3, 0x10, 0x85, - 0xae, 0x3f, 0xb4, 0x51, 0x71, 0x72, 0x2b, 0x72, 0x6e, 0x3c, 0x71, 0xb1, 0x45, 0xfe, 0x2f, 0x17, - 0xda, 0x57, 0xec, 0x9a, 0x33, 0x7e, 0x6d, 0x86, 0x73, 0x00, 0x4d, 0x65, 0x46, 0x20, 0x97, 0x89, - 0x51, 0xd0, 0xc1, 0x0d, 0x15, 0x98, 0x2c, 0x13, 0x5a, 0x62, 0xa9, 0x94, 0x59, 0xd0, 0x7f, 0xd0, - 0x98, 0x2e, 0x25, 0x0d, 0x3e, 0x52, 0x6a, 0xf9, 0xeb, 0x0a, 0xbf, 0xa1, 0x14, 0x1d, 0x02, 0x48, - 0x11, 0x90, 0x28, 0x4a, 0x69, 0xa6, 0x76, 0xc8, 0x19, 0x34, 0x71, 0x53, 0x8a, 0x53, 0x13, 0x40, - 0x4f, 0xa0, 0x1b, 0xce, 0x08, 0xbf, 0xa6, 0xab, 0x94, 0x1d, 0x9d, 0xd2, 0x31, 0xd1, 0x22, 0xed, - 0x7f, 0x68, 0x25, 0x29, 0x9b, 0x13, 0x49, 0x83, 0x1b, 0xba, 0xf4, 0xa0, 0xef, 0x0e, 0xda, 0x18, - 0x6c, 0xe8, 0x2d, 0x5d, 0xa2, 0x73, 0xa8, 0x1b, 0xa5, 0x99, 0xd7, 0xd2, 0x7b, 0xfa, 0x6c, 0xdb, - 0xae, 0xb2, 0xce, 0xe1, 0x95, 0xc9, 0x3e, 0xe7, 0x32, 0x5d, 0xe2, 0xa2, 0x16, 0x9d, 0x40, 0x35, - 0x97, 0x0b, 0xe1, 0xb5, 0x35, 0xc7, 0xd1, 0x36, 0xc7, 0xf6, 0xac, 0xb0, 0xae, 0x40, 0x47, 0xd0, - 0xcd, 0x33, 0x1a, 0xdc, 0x92, 0x45, 0x60, 0x2d, 0xea, 0xf4, 0x9d, 0x41, 0x03, 0xb7, 0xf3, 0x8c, - 0xbe, 0x23, 0x8b, 0x53, 0x63, 0xd4, 0x01, 0x34, 0x15, 0x99, 0x71, 0xb7, 0x6b, 0xdc, 0x55, 0x01, - 0xed, 0xee, 0x0b, 0xa8, 0x26, 0x31, 0xe1, 0xde, 0xae, 0x9e, 0xf7, 0xa3, 0x7b, 0x1f, 0xda, 0x38, - 0x26, 0x1c, 0xeb, 0xf4, 0xfd, 0x11, 0xb4, 0xcb, 0x62, 0x50, 0x0f, 0x5c, 0xe5, 0x91, 0xa3, 0x7d, - 0x54, 0xc7, 0xbb, 0x8d, 0x34, 0x3b, 0x61, 0xc0, 0xa8, 0x72, 0xe2, 0xf8, 0x3f, 0x1d, 0xd8, 0xdd, - 0x60, 0x2d, 0x0d, 0xd9, 0x59, 0x1b, 0xf2, 0x53, 0xe8, 0x91, 0x39, 0x61, 0x31, 0x99, 0xc6, 0x34, - 0x58, 0x5b, 0x83, 0xdd, 0x55, 0xdc, 0xca, 0xec, 0x81, 0x7b, 0xb7, 0x0a, 0xea, 0xa8, 0x48, 0xcd, - 0x44, 0xf5, 0x0a, 0xb8, 0xd8, 0x22, 0x34, 0x82, 0x1d, 0x65, 0x9f, 0x1a, 0xfb, 0xc3, 0x1d, 0x37, - 0x25, 0xca, 0xcc, 0x69, 0x4a, 0x78, 0x38, 0x0b, 0x58, 0xe4, 0xd5, 0xb4, 0xb4, 0x86, 0x09, 0x5c, - 0x44, 0xfe, 0x77, 0x07, 0x3a, 0x76, 0xe0, 0xf6, 0x5d, 0xbe, 0x86, 0x96, 0xbc, 0x23, 0xb1, 0xaf, - 0xea, 0xf0, 0x5e, 0x97, 0x71, 0xb9, 0x42, 0xfd, 0xfb, 0x52, 0x1e, 0x8a, 0x88, 0x46, 0xd6, 0xc8, - 0x02, 0xaa, 0x2d, 0x2e, 0x25, 0xaa, 0xeb, 0xb8, 0x66, 0x8b, 0x4b, 0xd1, 0x8b, 0x48, 0xcd, 0x81, - 0xa6, 0xa9, 0x48, 0xed, 0x33, 0x30, 0xe0, 0xec, 0x5f, 0xf8, 0xfb, 0x0b, 0x89, 0x63, 0x2a, 0x87, - 0xa1, 0x48, 0xe9, 0xf0, 0x13, 0x67, 0xe6, 0x7b, 0x30, 0xad, 0xe9, 0x9f, 0xe7, 0xbf, 0x03, 0x00, - 0x00, 0xff, 0xff, 0x53, 0xd6, 0xbb, 0x06, 0x27, 0x06, 0x00, 0x00, +var File_Bitcoin_proto protoreflect.FileDescriptor + +var file_Bitcoin_proto_rawDesc = []byte{ + 0x0a, 0x0d, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x10, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0xbe, 0x01, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x11, + 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x6f, 0x63, + 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x6c, 0x6f, 0x63, + 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, + 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x73, 0x12, 0x3d, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, + 0x22, 0x8a, 0x01, 0x0a, 0x10, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, + 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0e, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x6f, 0x75, 0x73, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x65, 0x71, + 0x75, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x22, 0x50, 0x0a, + 0x08, 0x4f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x14, 0x0a, + 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x22, + 0x41, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x22, 0x7d, 0x0a, 0x12, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, 0x74, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x09, 0x6f, 0x75, 0x74, 0x5f, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x54, 0x57, + 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4f, + 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x08, 0x6f, 0x75, 0x74, 0x50, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x22, 0x46, 0x0a, 0x0d, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xdb, 0x05, 0x0a, 0x0c, 0x53, 0x69, + 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x61, + 0x73, 0x68, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x68, + 0x61, 0x73, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x62, 0x79, 0x74, 0x65, 0x5f, 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x07, 0x62, 0x79, 0x74, 0x65, 0x46, 0x65, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x74, 0x6f, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, + 0x79, 0x12, 0x45, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x12, 0x38, 0x0a, 0x04, 0x75, 0x74, 0x78, 0x6f, + 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, + 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x6e, + 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x75, 0x74, + 0x78, 0x6f, 0x12, 0x24, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x75, 0x73, 0x65, 0x4d, + 0x61, 0x78, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x69, 0x6e, + 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x63, 0x6f, 0x69, + 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x35, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, + 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x1b, 0x0a, 0x09, + 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x08, 0x6c, 0x6f, 0x63, 0x6b, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x5f, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x18, 0x0d, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4f, 0x70, 0x52, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x12, 0x44, 0x0a, 0x0d, 0x65, 0x78, 0x74, 0x72, 0x61, 0x5f, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x54, 0x57, 0x2e, + 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x52, 0x0c, 0x65, 0x78, 0x74, + 0x72, 0x61, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x73, 0x65, + 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0a, 0x75, 0x73, 0x65, 0x4d, 0x61, 0x78, 0x55, 0x74, 0x78, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x64, + 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x75, 0x73, 0x74, 0x5f, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, + 0x65, 0x44, 0x75, 0x73, 0x74, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x3a, 0x0a, 0x0c, 0x53, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xb6, 0x02, 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x61, 0x6d, 0x6f, + 0x75, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x61, + 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x10, + 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x66, 0x65, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x06, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x3a, 0x0a, 0x05, 0x75, 0x74, 0x78, 0x6f, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, + 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x6e, 0x73, 0x70, 0x65, + 0x6e, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x75, + 0x74, 0x78, 0x6f, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x69, + 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x49, + 0x64, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x28, 0x0a, 0x10, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x5f, 0x6f, 0x70, 0x5f, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x0e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4f, 0x70, 0x52, 0x65, 0x74, 0x75, 0x72, 0x6e, + 0x22, 0xeb, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x12, 0x3f, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, + 0x63, 0x6f, 0x69, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x12, 0x25, 0x0a, + 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x54, + 0x0a, 0x0d, 0x48, 0x61, 0x73, 0x68, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, + 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, 0x12, 0x26, 0x0a, 0x0f, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x48, 0x61, 0x73, 0x68, 0x22, 0xb7, 0x01, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x53, 0x69, 0x67, 0x6e, + 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x49, 0x0a, 0x10, 0x68, 0x61, 0x73, + 0x68, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x54, 0x57, 0x2e, 0x42, 0x69, 0x74, 0x63, 0x6f, 0x69, 0x6e, + 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x68, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x73, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x58, + 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, + 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x75, + 0x74, 0x78, 0x6f, 0x5f, 0x63, 0x6f, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_Bitcoin_proto_rawDescOnce sync.Once + file_Bitcoin_proto_rawDescData = file_Bitcoin_proto_rawDesc +) + +func file_Bitcoin_proto_rawDescGZIP() []byte { + file_Bitcoin_proto_rawDescOnce.Do(func() { + file_Bitcoin_proto_rawDescData = protoimpl.X.CompressGZIP(file_Bitcoin_proto_rawDescData) + }) + return file_Bitcoin_proto_rawDescData +} + +var file_Bitcoin_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_Bitcoin_proto_goTypes = []interface{}{ + (*Transaction)(nil), // 0: TW.Bitcoin.Proto.Transaction + (*TransactionInput)(nil), // 1: TW.Bitcoin.Proto.TransactionInput + (*OutPoint)(nil), // 2: TW.Bitcoin.Proto.OutPoint + (*TransactionOutput)(nil), // 3: TW.Bitcoin.Proto.TransactionOutput + (*UnspentTransaction)(nil), // 4: TW.Bitcoin.Proto.UnspentTransaction + (*OutputAddress)(nil), // 5: TW.Bitcoin.Proto.OutputAddress + (*SigningInput)(nil), // 6: TW.Bitcoin.Proto.SigningInput + (*TransactionPlan)(nil), // 7: TW.Bitcoin.Proto.TransactionPlan + (*SigningOutput)(nil), // 8: TW.Bitcoin.Proto.SigningOutput + (*HashPublicKey)(nil), // 9: TW.Bitcoin.Proto.HashPublicKey + (*PreSigningOutput)(nil), // 10: TW.Bitcoin.Proto.PreSigningOutput + nil, // 11: TW.Bitcoin.Proto.SigningInput.ScriptsEntry + (common.SigningError)(0), // 12: TW.Common.Proto.SigningError +} +var file_Bitcoin_proto_depIdxs = []int32{ + 1, // 0: TW.Bitcoin.Proto.Transaction.inputs:type_name -> TW.Bitcoin.Proto.TransactionInput + 3, // 1: TW.Bitcoin.Proto.Transaction.outputs:type_name -> TW.Bitcoin.Proto.TransactionOutput + 2, // 2: TW.Bitcoin.Proto.TransactionInput.previousOutput:type_name -> TW.Bitcoin.Proto.OutPoint + 2, // 3: TW.Bitcoin.Proto.UnspentTransaction.out_point:type_name -> TW.Bitcoin.Proto.OutPoint + 11, // 4: TW.Bitcoin.Proto.SigningInput.scripts:type_name -> TW.Bitcoin.Proto.SigningInput.ScriptsEntry + 4, // 5: TW.Bitcoin.Proto.SigningInput.utxo:type_name -> TW.Bitcoin.Proto.UnspentTransaction + 7, // 6: TW.Bitcoin.Proto.SigningInput.plan:type_name -> TW.Bitcoin.Proto.TransactionPlan + 5, // 7: TW.Bitcoin.Proto.SigningInput.extra_outputs:type_name -> TW.Bitcoin.Proto.OutputAddress + 4, // 8: TW.Bitcoin.Proto.TransactionPlan.utxos:type_name -> TW.Bitcoin.Proto.UnspentTransaction + 12, // 9: TW.Bitcoin.Proto.TransactionPlan.error:type_name -> TW.Common.Proto.SigningError + 0, // 10: TW.Bitcoin.Proto.SigningOutput.transaction:type_name -> TW.Bitcoin.Proto.Transaction + 12, // 11: TW.Bitcoin.Proto.SigningOutput.error:type_name -> TW.Common.Proto.SigningError + 9, // 12: TW.Bitcoin.Proto.PreSigningOutput.hash_public_keys:type_name -> TW.Bitcoin.Proto.HashPublicKey + 12, // 13: TW.Bitcoin.Proto.PreSigningOutput.error:type_name -> TW.Common.Proto.SigningError + 14, // [14:14] is the sub-list for method output_type + 14, // [14:14] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name +} + +func init() { file_Bitcoin_proto_init() } +func file_Bitcoin_proto_init() { + if File_Bitcoin_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_Bitcoin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionInput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OutPoint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionOutput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UnspentTransaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*OutputAddress); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SigningInput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TransactionPlan); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SigningOutput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HashPublicKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Bitcoin_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PreSigningOutput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_Bitcoin_proto_rawDesc, + NumEnums: 0, + NumMessages: 12, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_Bitcoin_proto_goTypes, + DependencyIndexes: file_Bitcoin_proto_depIdxs, + MessageInfos: file_Bitcoin_proto_msgTypes, + }.Build() + File_Bitcoin_proto = out.File + file_Bitcoin_proto_rawDesc = nil + file_Bitcoin_proto_goTypes = nil + file_Bitcoin_proto_depIdxs = nil } diff --git a/samples/go/protos/common/Common.pb.go b/samples/go/protos/common/Common.pb.go new file mode 100644 index 00000000000..bc50f992214 --- /dev/null +++ b/samples/go/protos/common/Common.pb.go @@ -0,0 +1,236 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.19.2 +// source: Common.proto + +package common + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type SigningError int32 + +const ( + SigningError_OK SigningError = 0 // OK + // chain-generic, generic + SigningError_Error_general SigningError = 1 + SigningError_Error_internal SigningError = 2 + // chain-generic, input + SigningError_Error_low_balance SigningError = 3 + SigningError_Error_zero_amount_requested SigningError = 4 // Requested amount is zero + SigningError_Error_missing_private_key SigningError = 5 + SigningError_Error_invalid_private_key SigningError = 15 + SigningError_Error_invalid_address SigningError = 16 + SigningError_Error_invalid_utxo SigningError = 17 + SigningError_Error_invalid_utxo_amount SigningError = 18 + // chain-generic, fee + SigningError_Error_wrong_fee SigningError = 6 + // chain-generic, signing + SigningError_Error_signing SigningError = 7 + SigningError_Error_tx_too_big SigningError = 8 // [NEO] Transaction too big, fee in GAS needed or try send by parts + // UTXO-chain specific, inputs + SigningError_Error_missing_input_utxos SigningError = 9 // No UTXOs provided [BTC] + SigningError_Error_not_enough_utxos SigningError = 10 // Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out) [BTC] + // UTXO-chain specific, script + SigningError_Error_script_redeem SigningError = 11 // [BTC] Missing redeem script + SigningError_Error_script_output SigningError = 12 // [BTC] Invalid output script + SigningError_Error_script_witness_program SigningError = 13 // [BTC] Unrecognized witness program + SigningError_Error_invalid_memo SigningError = 14 // e.g. [XRP] Invalid tag + SigningError_Error_input_parse SigningError = 19 // e.g. Invalid input data + SigningError_Error_no_support_n2n SigningError = 20 // e.g. Not support n2n transaction + SigningError_Error_signatures_count SigningError = 21 // Incorrect count of signatures passed to compile + SigningError_Error_invalid_params SigningError = 22 // Incorrect parameters +) + +// Enum value maps for SigningError. +var ( + SigningError_name = map[int32]string{ + 0: "OK", + 1: "Error_general", + 2: "Error_internal", + 3: "Error_low_balance", + 4: "Error_zero_amount_requested", + 5: "Error_missing_private_key", + 15: "Error_invalid_private_key", + 16: "Error_invalid_address", + 17: "Error_invalid_utxo", + 18: "Error_invalid_utxo_amount", + 6: "Error_wrong_fee", + 7: "Error_signing", + 8: "Error_tx_too_big", + 9: "Error_missing_input_utxos", + 10: "Error_not_enough_utxos", + 11: "Error_script_redeem", + 12: "Error_script_output", + 13: "Error_script_witness_program", + 14: "Error_invalid_memo", + 19: "Error_input_parse", + 20: "Error_no_support_n2n", + 21: "Error_signatures_count", + 22: "Error_invalid_params", + } + SigningError_value = map[string]int32{ + "OK": 0, + "Error_general": 1, + "Error_internal": 2, + "Error_low_balance": 3, + "Error_zero_amount_requested": 4, + "Error_missing_private_key": 5, + "Error_invalid_private_key": 15, + "Error_invalid_address": 16, + "Error_invalid_utxo": 17, + "Error_invalid_utxo_amount": 18, + "Error_wrong_fee": 6, + "Error_signing": 7, + "Error_tx_too_big": 8, + "Error_missing_input_utxos": 9, + "Error_not_enough_utxos": 10, + "Error_script_redeem": 11, + "Error_script_output": 12, + "Error_script_witness_program": 13, + "Error_invalid_memo": 14, + "Error_input_parse": 19, + "Error_no_support_n2n": 20, + "Error_signatures_count": 21, + "Error_invalid_params": 22, + } +) + +func (x SigningError) Enum() *SigningError { + p := new(SigningError) + *p = x + return p +} + +func (x SigningError) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SigningError) Descriptor() protoreflect.EnumDescriptor { + return file_Common_proto_enumTypes[0].Descriptor() +} + +func (SigningError) Type() protoreflect.EnumType { + return &file_Common_proto_enumTypes[0] +} + +func (x SigningError) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SigningError.Descriptor instead. +func (SigningError) EnumDescriptor() ([]byte, []int) { + return file_Common_proto_rawDescGZIP(), []int{0} +} + +var File_Common_proto protoreflect.FileDescriptor + +var file_Common_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, + 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2a, + 0xd1, 0x04, 0x0a, 0x0c, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x10, 0x02, 0x12, + 0x15, 0x0a, 0x11, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c, 0x6f, 0x77, 0x5f, 0x62, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x10, 0x03, 0x12, 0x1f, 0x0a, 0x1b, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, + 0x7a, 0x65, 0x72, 0x6f, 0x5f, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x65, 0x64, 0x10, 0x04, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x5f, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, + 0x5f, 0x6b, 0x65, 0x79, 0x10, 0x05, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, + 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x10, 0x0f, 0x12, 0x19, 0x0a, 0x15, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x10, + 0x12, 0x16, 0x0a, 0x12, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x10, 0x11, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x5f, 0x61, + 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0x12, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x72, 0x72, 0x6f, 0x72, + 0x5f, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x5f, 0x66, 0x65, 0x65, 0x10, 0x06, 0x12, 0x11, 0x0a, 0x0d, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x10, 0x07, 0x12, + 0x14, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x74, 0x78, 0x5f, 0x74, 0x6f, 0x6f, 0x5f, + 0x62, 0x69, 0x67, 0x10, 0x08, 0x12, 0x1d, 0x0a, 0x19, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, + 0x69, 0x73, 0x73, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x75, 0x74, 0x78, + 0x6f, 0x73, 0x10, 0x09, 0x12, 0x1a, 0x0a, 0x16, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6e, 0x6f, + 0x74, 0x5f, 0x65, 0x6e, 0x6f, 0x75, 0x67, 0x68, 0x5f, 0x75, 0x74, 0x78, 0x6f, 0x73, 0x10, 0x0a, + 0x12, 0x17, 0x0a, 0x13, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x5f, 0x72, 0x65, 0x64, 0x65, 0x65, 0x6d, 0x10, 0x0b, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x10, 0x0c, 0x12, 0x20, 0x0a, 0x1c, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x5f, 0x77, 0x69, 0x74, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, + 0x61, 0x6d, 0x10, 0x0d, 0x12, 0x16, 0x0a, 0x12, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, 0x6e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x10, 0x0e, 0x12, 0x15, 0x0a, 0x11, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x10, 0x13, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6e, 0x6f, 0x5f, + 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x32, 0x6e, 0x10, 0x14, 0x12, 0x1a, 0x0a, + 0x16, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x73, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0x15, 0x12, 0x18, 0x0a, 0x14, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x10, 0x16, 0x42, 0x55, 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x6a, 0x6e, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x3c, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, + 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x69, 0x6e, 0x74, + 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_Common_proto_rawDescOnce sync.Once + file_Common_proto_rawDescData = file_Common_proto_rawDesc +) + +func file_Common_proto_rawDescGZIP() []byte { + file_Common_proto_rawDescOnce.Do(func() { + file_Common_proto_rawDescData = protoimpl.X.CompressGZIP(file_Common_proto_rawDescData) + }) + return file_Common_proto_rawDescData +} + +var file_Common_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_Common_proto_goTypes = []interface{}{ + (SigningError)(0), // 0: TW.Common.Proto.SigningError +} +var file_Common_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_Common_proto_init() } +func file_Common_proto_init() { + if File_Common_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_Common_proto_rawDesc, + NumEnums: 1, + NumMessages: 0, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_Common_proto_goTypes, + DependencyIndexes: file_Common_proto_depIdxs, + EnumInfos: file_Common_proto_enumTypes, + }.Build() + File_Common_proto = out.File + file_Common_proto_rawDesc = nil + file_Common_proto_goTypes = nil + file_Common_proto_depIdxs = nil +} diff --git a/samples/go/protos/common/TransactionCompiler.pb.go b/samples/go/protos/common/TransactionCompiler.pb.go new file mode 100644 index 00000000000..8baf5d4f998 --- /dev/null +++ b/samples/go/protos/common/TransactionCompiler.pb.go @@ -0,0 +1,188 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.19.2 +// source: TransactionCompiler.proto + +package common + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +/// Transaction pre-signing output +type PreSigningOutput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + /// Pre-image data hash that will be used for signing + DataHash []byte `protobuf:"bytes,1,opt,name=data_hash,json=dataHash,proto3" json:"data_hash,omitempty"` + /// Pre-image data + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + /// error code, 0 is ok, other codes will be treated as errors + Error SigningError `protobuf:"varint,3,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` + /// error code description + ErrorMessage string `protobuf:"bytes,4,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (x *PreSigningOutput) Reset() { + *x = PreSigningOutput{} + if protoimpl.UnsafeEnabled { + mi := &file_TransactionCompiler_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PreSigningOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PreSigningOutput) ProtoMessage() {} + +func (x *PreSigningOutput) ProtoReflect() protoreflect.Message { + mi := &file_TransactionCompiler_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PreSigningOutput.ProtoReflect.Descriptor instead. +func (*PreSigningOutput) Descriptor() ([]byte, []int) { + return file_TransactionCompiler_proto_rawDescGZIP(), []int{0} +} + +func (x *PreSigningOutput) GetDataHash() []byte { + if x != nil { + return x.DataHash + } + return nil +} + +func (x *PreSigningOutput) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *PreSigningOutput) GetError() SigningError { + if x != nil { + return x.Error + } + return SigningError_OK +} + +func (x *PreSigningOutput) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +var File_TransactionCompiler_proto protoreflect.FileDescriptor + +var file_TransactionCompiler_proto_rawDesc = []byte{ + 0x0a, 0x19, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6d, + 0x70, 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x13, 0x54, 0x57, 0x2e, + 0x54, 0x78, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9d, + 0x01, 0x0a, 0x10, 0x50, 0x72, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x68, 0x61, 0x73, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x64, 0x61, 0x74, 0x61, 0x48, 0x61, 0x73, 0x68, + 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, + 0x6f, 0x72, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x55, + 0x0a, 0x15, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, + 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_TransactionCompiler_proto_rawDescOnce sync.Once + file_TransactionCompiler_proto_rawDescData = file_TransactionCompiler_proto_rawDesc +) + +func file_TransactionCompiler_proto_rawDescGZIP() []byte { + file_TransactionCompiler_proto_rawDescOnce.Do(func() { + file_TransactionCompiler_proto_rawDescData = protoimpl.X.CompressGZIP(file_TransactionCompiler_proto_rawDescData) + }) + return file_TransactionCompiler_proto_rawDescData +} + +var file_TransactionCompiler_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_TransactionCompiler_proto_goTypes = []interface{}{ + (*PreSigningOutput)(nil), // 0: TW.TxCompiler.Proto.PreSigningOutput + (SigningError)(0), // 1: TW.Common.Proto.SigningError +} +var file_TransactionCompiler_proto_depIdxs = []int32{ + 1, // 0: TW.TxCompiler.Proto.PreSigningOutput.error:type_name -> TW.Common.Proto.SigningError + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_TransactionCompiler_proto_init() } +func file_TransactionCompiler_proto_init() { + if File_TransactionCompiler_proto != nil { + return + } + file_Common_proto_init() + if !protoimpl.UnsafeEnabled { + file_TransactionCompiler_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PreSigningOutput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_TransactionCompiler_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_TransactionCompiler_proto_goTypes, + DependencyIndexes: file_TransactionCompiler_proto_depIdxs, + MessageInfos: file_TransactionCompiler_proto_msgTypes, + }.Build() + File_TransactionCompiler_proto = out.File + file_TransactionCompiler_proto_rawDesc = nil + file_TransactionCompiler_proto_goTypes = nil + file_TransactionCompiler_proto_depIdxs = nil +} diff --git a/samples/go/protos/ethereum/Ethereum.pb.go b/samples/go/protos/ethereum/Ethereum.pb.go new file mode 100644 index 00000000000..14707736bce --- /dev/null +++ b/samples/go/protos/ethereum/Ethereum.pb.go @@ -0,0 +1,1122 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.0 +// protoc v3.19.2 +// source: Ethereum.proto + +package ethereum + +import ( + common "tw/protos/common" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type TransactionMode int32 + +const ( + TransactionMode_Legacy TransactionMode = 0 // Legacy transaction, pre-EIP2718/EIP1559; for fee gasPrice/gasLimit is used + TransactionMode_Enveloped TransactionMode = 1 // Enveloped transaction EIP2718 (with type 0x2), fee is according to EIP1559 (base fee, inclusion fee, ...) +) + +// Enum value maps for TransactionMode. +var ( + TransactionMode_name = map[int32]string{ + 0: "Legacy", + 1: "Enveloped", + } + TransactionMode_value = map[string]int32{ + "Legacy": 0, + "Enveloped": 1, + } +) + +func (x TransactionMode) Enum() *TransactionMode { + p := new(TransactionMode) + *p = x + return p +} + +func (x TransactionMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (TransactionMode) Descriptor() protoreflect.EnumDescriptor { + return file_Ethereum_proto_enumTypes[0].Descriptor() +} + +func (TransactionMode) Type() protoreflect.EnumType { + return &file_Ethereum_proto_enumTypes[0] +} + +func (x TransactionMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use TransactionMode.Descriptor instead. +func (TransactionMode) EnumDescriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{0} +} + +// Transaction (transfer, smart contract call, ...) +type Transaction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to TransactionOneof: + // *Transaction_Transfer_ + // *Transaction_Erc20Transfer + // *Transaction_Erc20Approve + // *Transaction_Erc721Transfer + // *Transaction_Erc1155Transfer + // *Transaction_ContractGeneric_ + TransactionOneof isTransaction_TransactionOneof `protobuf_oneof:"transaction_oneof"` +} + +func (x *Transaction) Reset() { + *x = Transaction{} + if protoimpl.UnsafeEnabled { + mi := &file_Ethereum_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction) ProtoMessage() {} + +func (x *Transaction) ProtoReflect() protoreflect.Message { + mi := &file_Ethereum_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction.ProtoReflect.Descriptor instead. +func (*Transaction) Descriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{0} +} + +func (m *Transaction) GetTransactionOneof() isTransaction_TransactionOneof { + if m != nil { + return m.TransactionOneof + } + return nil +} + +func (x *Transaction) GetTransfer() *Transaction_Transfer { + if x, ok := x.GetTransactionOneof().(*Transaction_Transfer_); ok { + return x.Transfer + } + return nil +} + +func (x *Transaction) GetErc20Transfer() *Transaction_ERC20Transfer { + if x, ok := x.GetTransactionOneof().(*Transaction_Erc20Transfer); ok { + return x.Erc20Transfer + } + return nil +} + +func (x *Transaction) GetErc20Approve() *Transaction_ERC20Approve { + if x, ok := x.GetTransactionOneof().(*Transaction_Erc20Approve); ok { + return x.Erc20Approve + } + return nil +} + +func (x *Transaction) GetErc721Transfer() *Transaction_ERC721Transfer { + if x, ok := x.GetTransactionOneof().(*Transaction_Erc721Transfer); ok { + return x.Erc721Transfer + } + return nil +} + +func (x *Transaction) GetErc1155Transfer() *Transaction_ERC1155Transfer { + if x, ok := x.GetTransactionOneof().(*Transaction_Erc1155Transfer); ok { + return x.Erc1155Transfer + } + return nil +} + +func (x *Transaction) GetContractGeneric() *Transaction_ContractGeneric { + if x, ok := x.GetTransactionOneof().(*Transaction_ContractGeneric_); ok { + return x.ContractGeneric + } + return nil +} + +type isTransaction_TransactionOneof interface { + isTransaction_TransactionOneof() +} + +type Transaction_Transfer_ struct { + Transfer *Transaction_Transfer `protobuf:"bytes,1,opt,name=transfer,proto3,oneof"` +} + +type Transaction_Erc20Transfer struct { + Erc20Transfer *Transaction_ERC20Transfer `protobuf:"bytes,2,opt,name=erc20_transfer,json=erc20Transfer,proto3,oneof"` +} + +type Transaction_Erc20Approve struct { + Erc20Approve *Transaction_ERC20Approve `protobuf:"bytes,3,opt,name=erc20_approve,json=erc20Approve,proto3,oneof"` +} + +type Transaction_Erc721Transfer struct { + Erc721Transfer *Transaction_ERC721Transfer `protobuf:"bytes,4,opt,name=erc721_transfer,json=erc721Transfer,proto3,oneof"` +} + +type Transaction_Erc1155Transfer struct { + Erc1155Transfer *Transaction_ERC1155Transfer `protobuf:"bytes,5,opt,name=erc1155_transfer,json=erc1155Transfer,proto3,oneof"` +} + +type Transaction_ContractGeneric_ struct { + ContractGeneric *Transaction_ContractGeneric `protobuf:"bytes,6,opt,name=contract_generic,json=contractGeneric,proto3,oneof"` +} + +func (*Transaction_Transfer_) isTransaction_TransactionOneof() {} + +func (*Transaction_Erc20Transfer) isTransaction_TransactionOneof() {} + +func (*Transaction_Erc20Approve) isTransaction_TransactionOneof() {} + +func (*Transaction_Erc721Transfer) isTransaction_TransactionOneof() {} + +func (*Transaction_Erc1155Transfer) isTransaction_TransactionOneof() {} + +func (*Transaction_ContractGeneric_) isTransaction_TransactionOneof() {} + +// Input data necessary to create a signed transaction. +// Legacy and EIP2718/EIP1559 transactions supported, see TransactionMode. +type SigningInput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Chain identifier (256-bit number) + ChainId []byte `protobuf:"bytes,1,opt,name=chain_id,json=chainId,proto3" json:"chain_id,omitempty"` + // Nonce (256-bit number) + Nonce []byte `protobuf:"bytes,2,opt,name=nonce,proto3" json:"nonce,omitempty"` + // Transaction version selector: Legacy or enveloped, has impact on fee structure. + // Default is Legacy (value 0) + TxMode TransactionMode `protobuf:"varint,3,opt,name=tx_mode,json=txMode,proto3,enum=TW.Ethereum.Proto.TransactionMode" json:"tx_mode,omitempty"` + // Gas price (256-bit number) + // Relevant for legacy transactions only (disregarded for enveloped/EIP1559) + GasPrice []byte `protobuf:"bytes,4,opt,name=gas_price,json=gasPrice,proto3" json:"gas_price,omitempty"` + // Gas limit (256-bit number) + GasLimit []byte `protobuf:"bytes,5,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty"` + // Maxinmum optional inclusion fee (aka tip) (256-bit number) + // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) + MaxInclusionFeePerGas []byte `protobuf:"bytes,6,opt,name=max_inclusion_fee_per_gas,json=maxInclusionFeePerGas,proto3" json:"max_inclusion_fee_per_gas,omitempty"` + // Maxinmum fee (256-bit number) + // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) + MaxFeePerGas []byte `protobuf:"bytes,7,opt,name=max_fee_per_gas,json=maxFeePerGas,proto3" json:"max_fee_per_gas,omitempty"` + // Recipient's address. + ToAddress string `protobuf:"bytes,8,opt,name=to_address,json=toAddress,proto3" json:"to_address,omitempty"` + // Private key. + PrivateKey []byte `protobuf:"bytes,9,opt,name=private_key,json=privateKey,proto3" json:"private_key,omitempty"` + Transaction *Transaction `protobuf:"bytes,10,opt,name=transaction,proto3" json:"transaction,omitempty"` +} + +func (x *SigningInput) Reset() { + *x = SigningInput{} + if protoimpl.UnsafeEnabled { + mi := &file_Ethereum_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SigningInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SigningInput) ProtoMessage() {} + +func (x *SigningInput) ProtoReflect() protoreflect.Message { + mi := &file_Ethereum_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SigningInput.ProtoReflect.Descriptor instead. +func (*SigningInput) Descriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{1} +} + +func (x *SigningInput) GetChainId() []byte { + if x != nil { + return x.ChainId + } + return nil +} + +func (x *SigningInput) GetNonce() []byte { + if x != nil { + return x.Nonce + } + return nil +} + +func (x *SigningInput) GetTxMode() TransactionMode { + if x != nil { + return x.TxMode + } + return TransactionMode_Legacy +} + +func (x *SigningInput) GetGasPrice() []byte { + if x != nil { + return x.GasPrice + } + return nil +} + +func (x *SigningInput) GetGasLimit() []byte { + if x != nil { + return x.GasLimit + } + return nil +} + +func (x *SigningInput) GetMaxInclusionFeePerGas() []byte { + if x != nil { + return x.MaxInclusionFeePerGas + } + return nil +} + +func (x *SigningInput) GetMaxFeePerGas() []byte { + if x != nil { + return x.MaxFeePerGas + } + return nil +} + +func (x *SigningInput) GetToAddress() string { + if x != nil { + return x.ToAddress + } + return "" +} + +func (x *SigningInput) GetPrivateKey() []byte { + if x != nil { + return x.PrivateKey + } + return nil +} + +func (x *SigningInput) GetTransaction() *Transaction { + if x != nil { + return x.Transaction + } + return nil +} + +// Transaction signing output. +type SigningOutput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Signed and encoded transaction bytes. + Encoded []byte `protobuf:"bytes,1,opt,name=encoded,proto3" json:"encoded,omitempty"` + V []byte `protobuf:"bytes,2,opt,name=v,proto3" json:"v,omitempty"` + R []byte `protobuf:"bytes,3,opt,name=r,proto3" json:"r,omitempty"` + S []byte `protobuf:"bytes,4,opt,name=s,proto3" json:"s,omitempty"` + // The payload part, supplied in the input or assembled from input parameters + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` + /// error code, 0 is ok, other codes will be treated as errors + Error common.SigningError `protobuf:"varint,6,opt,name=error,proto3,enum=TW.Common.Proto.SigningError" json:"error,omitempty"` + /// error code description + ErrorMessage string `protobuf:"bytes,7,opt,name=error_message,json=errorMessage,proto3" json:"error_message,omitempty"` +} + +func (x *SigningOutput) Reset() { + *x = SigningOutput{} + if protoimpl.UnsafeEnabled { + mi := &file_Ethereum_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SigningOutput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SigningOutput) ProtoMessage() {} + +func (x *SigningOutput) ProtoReflect() protoreflect.Message { + mi := &file_Ethereum_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SigningOutput.ProtoReflect.Descriptor instead. +func (*SigningOutput) Descriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{2} +} + +func (x *SigningOutput) GetEncoded() []byte { + if x != nil { + return x.Encoded + } + return nil +} + +func (x *SigningOutput) GetV() []byte { + if x != nil { + return x.V + } + return nil +} + +func (x *SigningOutput) GetR() []byte { + if x != nil { + return x.R + } + return nil +} + +func (x *SigningOutput) GetS() []byte { + if x != nil { + return x.S + } + return nil +} + +func (x *SigningOutput) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +func (x *SigningOutput) GetError() common.SigningError { + if x != nil { + return x.Error + } + return common.SigningError(0) +} + +func (x *SigningOutput) GetErrorMessage() string { + if x != nil { + return x.ErrorMessage + } + return "" +} + +// Native coin transfer transaction +type Transaction_Transfer struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Amount to send in wei (256-bit number) + Amount []byte `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` + // Optional payload data + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Transaction_Transfer) Reset() { + *x = Transaction_Transfer{} + if protoimpl.UnsafeEnabled { + mi := &file_Ethereum_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction_Transfer) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction_Transfer) ProtoMessage() {} + +func (x *Transaction_Transfer) ProtoReflect() protoreflect.Message { + mi := &file_Ethereum_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction_Transfer.ProtoReflect.Descriptor instead. +func (*Transaction_Transfer) Descriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *Transaction_Transfer) GetAmount() []byte { + if x != nil { + return x.Amount + } + return nil +} + +func (x *Transaction_Transfer) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +// ERC20 token transfer transaction +type Transaction_ERC20Transfer struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + To string `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"` + // Amount to send (256-bit number) + Amount []byte `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount,omitempty"` +} + +func (x *Transaction_ERC20Transfer) Reset() { + *x = Transaction_ERC20Transfer{} + if protoimpl.UnsafeEnabled { + mi := &file_Ethereum_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction_ERC20Transfer) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction_ERC20Transfer) ProtoMessage() {} + +func (x *Transaction_ERC20Transfer) ProtoReflect() protoreflect.Message { + mi := &file_Ethereum_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction_ERC20Transfer.ProtoReflect.Descriptor instead. +func (*Transaction_ERC20Transfer) Descriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{0, 1} +} + +func (x *Transaction_ERC20Transfer) GetTo() string { + if x != nil { + return x.To + } + return "" +} + +func (x *Transaction_ERC20Transfer) GetAmount() []byte { + if x != nil { + return x.Amount + } + return nil +} + +// ERC20 approve transaction +type Transaction_ERC20Approve struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Spender string `protobuf:"bytes,1,opt,name=spender,proto3" json:"spender,omitempty"` + // Amount to send (256-bit number) + Amount []byte `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount,omitempty"` +} + +func (x *Transaction_ERC20Approve) Reset() { + *x = Transaction_ERC20Approve{} + if protoimpl.UnsafeEnabled { + mi := &file_Ethereum_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction_ERC20Approve) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction_ERC20Approve) ProtoMessage() {} + +func (x *Transaction_ERC20Approve) ProtoReflect() protoreflect.Message { + mi := &file_Ethereum_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction_ERC20Approve.ProtoReflect.Descriptor instead. +func (*Transaction_ERC20Approve) Descriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{0, 2} +} + +func (x *Transaction_ERC20Approve) GetSpender() string { + if x != nil { + return x.Spender + } + return "" +} + +func (x *Transaction_ERC20Approve) GetAmount() []byte { + if x != nil { + return x.Amount + } + return nil +} + +// ERC721 NFT transfer transaction +type Transaction_ERC721Transfer struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` + // ID of the token (256-bit number) + TokenId []byte `protobuf:"bytes,3,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` +} + +func (x *Transaction_ERC721Transfer) Reset() { + *x = Transaction_ERC721Transfer{} + if protoimpl.UnsafeEnabled { + mi := &file_Ethereum_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction_ERC721Transfer) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction_ERC721Transfer) ProtoMessage() {} + +func (x *Transaction_ERC721Transfer) ProtoReflect() protoreflect.Message { + mi := &file_Ethereum_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction_ERC721Transfer.ProtoReflect.Descriptor instead. +func (*Transaction_ERC721Transfer) Descriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{0, 3} +} + +func (x *Transaction_ERC721Transfer) GetFrom() string { + if x != nil { + return x.From + } + return "" +} + +func (x *Transaction_ERC721Transfer) GetTo() string { + if x != nil { + return x.To + } + return "" +} + +func (x *Transaction_ERC721Transfer) GetTokenId() []byte { + if x != nil { + return x.TokenId + } + return nil +} + +// ERC1155 NFT transfer transaction +type Transaction_ERC1155Transfer struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"` + To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` + // ID of the token (256-bit number) + TokenId []byte `protobuf:"bytes,3,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + // The amount of tokens being transferred + Value []byte `protobuf:"bytes,4,opt,name=value,proto3" json:"value,omitempty"` + Data []byte `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Transaction_ERC1155Transfer) Reset() { + *x = Transaction_ERC1155Transfer{} + if protoimpl.UnsafeEnabled { + mi := &file_Ethereum_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction_ERC1155Transfer) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction_ERC1155Transfer) ProtoMessage() {} + +func (x *Transaction_ERC1155Transfer) ProtoReflect() protoreflect.Message { + mi := &file_Ethereum_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction_ERC1155Transfer.ProtoReflect.Descriptor instead. +func (*Transaction_ERC1155Transfer) Descriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{0, 4} +} + +func (x *Transaction_ERC1155Transfer) GetFrom() string { + if x != nil { + return x.From + } + return "" +} + +func (x *Transaction_ERC1155Transfer) GetTo() string { + if x != nil { + return x.To + } + return "" +} + +func (x *Transaction_ERC1155Transfer) GetTokenId() []byte { + if x != nil { + return x.TokenId + } + return nil +} + +func (x *Transaction_ERC1155Transfer) GetValue() []byte { + if x != nil { + return x.Value + } + return nil +} + +func (x *Transaction_ERC1155Transfer) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +// Generic smart contract transaction +type Transaction_ContractGeneric struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Amount to send in wei (256-bit number) + Amount []byte `protobuf:"bytes,1,opt,name=amount,proto3" json:"amount,omitempty"` + // Contract call payload data + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (x *Transaction_ContractGeneric) Reset() { + *x = Transaction_ContractGeneric{} + if protoimpl.UnsafeEnabled { + mi := &file_Ethereum_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction_ContractGeneric) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction_ContractGeneric) ProtoMessage() {} + +func (x *Transaction_ContractGeneric) ProtoReflect() protoreflect.Message { + mi := &file_Ethereum_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction_ContractGeneric.ProtoReflect.Descriptor instead. +func (*Transaction_ContractGeneric) Descriptor() ([]byte, []int) { + return file_Ethereum_proto_rawDescGZIP(), []int{0, 5} +} + +func (x *Transaction_ContractGeneric) GetAmount() []byte { + if x != nil { + return x.Amount + } + return nil +} + +func (x *Transaction_ContractGeneric) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +var File_Ethereum_proto protoreflect.FileDescriptor + +var file_Ethereum_proto_rawDesc = []byte{ + 0x0a, 0x0e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x11, 0x54, 0x57, 0x2e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x50, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x0c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0xe7, 0x07, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x45, 0x0a, 0x08, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x54, 0x57, 0x2e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x48, 0x00, 0x52, 0x08, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x55, 0x0a, 0x0e, 0x65, 0x72, 0x63, 0x32, + 0x30, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2c, 0x2e, 0x54, 0x57, 0x2e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x45, 0x52, 0x43, 0x32, 0x30, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x0d, 0x65, 0x72, 0x63, 0x32, 0x30, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, + 0x52, 0x0a, 0x0d, 0x65, 0x72, 0x63, 0x32, 0x30, 0x5f, 0x61, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x54, 0x57, 0x2e, 0x45, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x52, 0x43, 0x32, 0x30, 0x41, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x72, 0x63, 0x32, 0x30, 0x41, 0x70, 0x70, 0x72, + 0x6f, 0x76, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x65, 0x72, 0x63, 0x37, 0x32, 0x31, 0x5f, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x54, + 0x57, 0x2e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x52, 0x43, + 0x37, 0x32, 0x31, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0e, 0x65, + 0x72, 0x63, 0x37, 0x32, 0x31, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x5b, 0x0a, + 0x10, 0x65, 0x72, 0x63, 0x31, 0x31, 0x35, 0x35, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, + 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x54, 0x57, 0x2e, 0x45, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x45, 0x52, 0x43, 0x31, 0x31, 0x35, 0x35, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x48, 0x00, 0x52, 0x0f, 0x65, 0x72, 0x63, 0x31, 0x31, + 0x35, 0x35, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x5b, 0x0a, 0x10, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x54, 0x57, 0x2e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x69, 0x63, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x1a, 0x36, 0x0a, 0x08, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x66, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, + 0x37, 0x0a, 0x0d, 0x45, 0x52, 0x43, 0x32, 0x30, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x40, 0x0a, 0x0c, 0x45, 0x52, 0x43, 0x32, + 0x30, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x76, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x4f, 0x0a, 0x0e, 0x45, 0x52, + 0x43, 0x37, 0x32, 0x31, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, + 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, + 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x07, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x64, 0x1a, 0x7a, 0x0a, 0x0f, 0x45, + 0x52, 0x43, 0x31, 0x31, 0x35, 0x35, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x12, 0x12, + 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, + 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x74, 0x6f, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3d, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x6d, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x61, 0x6d, 0x6f, 0x75, + 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42, 0x13, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x99, 0x03, 0x0a, 0x0c, + 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x19, 0x0a, 0x08, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x3b, 0x0a, + 0x07, 0x74, 0x78, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, + 0x2e, 0x54, 0x57, 0x2e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, + 0x64, 0x65, 0x52, 0x06, 0x74, 0x78, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, + 0x73, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x67, + 0x61, 0x73, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x19, 0x6d, 0x61, 0x78, 0x5f, 0x69, 0x6e, 0x63, 0x6c, + 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x61, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x6d, 0x61, 0x78, 0x49, 0x6e, 0x63, 0x6c, + 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x65, 0x65, 0x50, 0x65, 0x72, 0x47, 0x61, 0x73, 0x12, 0x25, + 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x5f, 0x66, 0x65, 0x65, 0x5f, 0x70, 0x65, 0x72, 0x5f, 0x67, 0x61, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6d, 0x61, 0x78, 0x46, 0x65, 0x65, 0x50, + 0x65, 0x72, 0x47, 0x61, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x6f, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x40, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x54, 0x57, 0x2e, + 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc1, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, + 0x69, 0x6e, 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x63, + 0x6f, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x6e, 0x63, 0x6f, + 0x64, 0x65, 0x64, 0x12, 0x0c, 0x0a, 0x01, 0x76, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, + 0x76, 0x12, 0x0c, 0x0a, 0x01, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x72, 0x12, + 0x0c, 0x0a, 0x01, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x01, 0x73, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x1d, 0x2e, 0x54, 0x57, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2a, 0x2c, 0x0a, 0x0f, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x0a, + 0x0a, 0x06, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x6e, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x64, 0x10, 0x01, 0x42, 0x57, 0x0a, 0x15, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x6a, 0x6e, 0x69, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, + 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x2d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x2f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_Ethereum_proto_rawDescOnce sync.Once + file_Ethereum_proto_rawDescData = file_Ethereum_proto_rawDesc +) + +func file_Ethereum_proto_rawDescGZIP() []byte { + file_Ethereum_proto_rawDescOnce.Do(func() { + file_Ethereum_proto_rawDescData = protoimpl.X.CompressGZIP(file_Ethereum_proto_rawDescData) + }) + return file_Ethereum_proto_rawDescData +} + +var file_Ethereum_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_Ethereum_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_Ethereum_proto_goTypes = []interface{}{ + (TransactionMode)(0), // 0: TW.Ethereum.Proto.TransactionMode + (*Transaction)(nil), // 1: TW.Ethereum.Proto.Transaction + (*SigningInput)(nil), // 2: TW.Ethereum.Proto.SigningInput + (*SigningOutput)(nil), // 3: TW.Ethereum.Proto.SigningOutput + (*Transaction_Transfer)(nil), // 4: TW.Ethereum.Proto.Transaction.Transfer + (*Transaction_ERC20Transfer)(nil), // 5: TW.Ethereum.Proto.Transaction.ERC20Transfer + (*Transaction_ERC20Approve)(nil), // 6: TW.Ethereum.Proto.Transaction.ERC20Approve + (*Transaction_ERC721Transfer)(nil), // 7: TW.Ethereum.Proto.Transaction.ERC721Transfer + (*Transaction_ERC1155Transfer)(nil), // 8: TW.Ethereum.Proto.Transaction.ERC1155Transfer + (*Transaction_ContractGeneric)(nil), // 9: TW.Ethereum.Proto.Transaction.ContractGeneric + (common.SigningError)(0), // 10: TW.Common.Proto.SigningError +} +var file_Ethereum_proto_depIdxs = []int32{ + 4, // 0: TW.Ethereum.Proto.Transaction.transfer:type_name -> TW.Ethereum.Proto.Transaction.Transfer + 5, // 1: TW.Ethereum.Proto.Transaction.erc20_transfer:type_name -> TW.Ethereum.Proto.Transaction.ERC20Transfer + 6, // 2: TW.Ethereum.Proto.Transaction.erc20_approve:type_name -> TW.Ethereum.Proto.Transaction.ERC20Approve + 7, // 3: TW.Ethereum.Proto.Transaction.erc721_transfer:type_name -> TW.Ethereum.Proto.Transaction.ERC721Transfer + 8, // 4: TW.Ethereum.Proto.Transaction.erc1155_transfer:type_name -> TW.Ethereum.Proto.Transaction.ERC1155Transfer + 9, // 5: TW.Ethereum.Proto.Transaction.contract_generic:type_name -> TW.Ethereum.Proto.Transaction.ContractGeneric + 0, // 6: TW.Ethereum.Proto.SigningInput.tx_mode:type_name -> TW.Ethereum.Proto.TransactionMode + 1, // 7: TW.Ethereum.Proto.SigningInput.transaction:type_name -> TW.Ethereum.Proto.Transaction + 10, // 8: TW.Ethereum.Proto.SigningOutput.error:type_name -> TW.Common.Proto.SigningError + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_Ethereum_proto_init() } +func file_Ethereum_proto_init() { + if File_Ethereum_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_Ethereum_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Ethereum_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SigningInput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Ethereum_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SigningOutput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Ethereum_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction_Transfer); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Ethereum_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction_ERC20Transfer); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Ethereum_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction_ERC20Approve); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Ethereum_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction_ERC721Transfer); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Ethereum_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction_ERC1155Transfer); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_Ethereum_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction_ContractGeneric); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_Ethereum_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Transaction_Transfer_)(nil), + (*Transaction_Erc20Transfer)(nil), + (*Transaction_Erc20Approve)(nil), + (*Transaction_Erc721Transfer)(nil), + (*Transaction_Erc1155Transfer)(nil), + (*Transaction_ContractGeneric_)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_Ethereum_proto_rawDesc, + NumEnums: 1, + NumMessages: 9, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_Ethereum_proto_goTypes, + DependencyIndexes: file_Ethereum_proto_depIdxs, + EnumInfos: file_Ethereum_proto_enumTypes, + MessageInfos: file_Ethereum_proto_msgTypes, + }.Build() + File_Ethereum_proto = out.File + file_Ethereum_proto_rawDesc = nil + file_Ethereum_proto_goTypes = nil + file_Ethereum_proto_depIdxs = nil +} diff --git a/samples/go/sample/external_signing.go b/samples/go/sample/external_signing.go new file mode 100644 index 00000000000..636f56dbc0d --- /dev/null +++ b/samples/go/sample/external_signing.go @@ -0,0 +1,240 @@ +package sample + +import ( + "encoding/hex" + "fmt" + "math/big" + "tw/core" + "tw/protos/binance" + "tw/protos/bitcoin" + "tw/protos/common" + "tw/protos/ethereum" + + "google.golang.org/protobuf/proto" +) + +func ExternalSigningDemo() { + fmt.Println("") + SignExternalBinanceDemo() + SignExternalEthereumDemo() + SignExternalBitcoinDemo() +} + +func SignExternalBinanceDemo() { + fmt.Println("==> Signing with External Signature - Binance Demo") + + coin := core.CoinTypeBinance + + fmt.Println("\n==> Step 1: Prepare transaction input (protobuf)") + txInputData := core.BuildInput( + coin, + "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", // from + "bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5", // to + "1", // amount + "BNB", // asset + "", // memo + "", // chainId + ) + fmt.Println("txInputData len: ", len(txInputData)) + + fmt.Println("\n==> Step 2: Obtain preimage hash") + hashes := core.PreImageHashes(coin, txInputData) + fmt.Println("hash(es): ", len(hashes), hex.EncodeToString(hashes)) + + var preSigningOutput common.PreSigningOutput + proto.Unmarshal(hashes, &preSigningOutput) + + fmt.Println("\n==> Step 3: Compile transaction info") + // Simulate signature, normally obtained from signature server + signature, _ := hex.DecodeString("1b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679") + publicKey, _ := hex.DecodeString("026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502") + txOutput := core.CompileWithSignatures(coin, txInputData, [][]byte{signature}, [][]byte{publicKey}) + + var output binance.SigningOutput + proto.Unmarshal(txOutput, &output) + fmt.Println("final txOutput proto: ", len(txOutput)) + fmt.Println("output.encoded: ", len(output.Encoded), hex.EncodeToString(output.Encoded)) + + fmt.Println("\n==> Double check signature validity (result should be true)") + verifyRes := core.PublicKeyVerify(publicKey, core.PublicKeyTypeSECP256k1, signature, preSigningOutput.DataHash) + fmt.Println(verifyRes) + + fmt.Println("") +} + +func SignExternalEthereumDemo() { + fmt.Println("==> Signing with External Signature - Ethereum Demo") + + coin := core.CoinTypeEthereum + + fmt.Println("\n==> Step 1: Prepare transaction input (protobuf)") + txInputData := core.BuildInput( + coin, + "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", // from + "0x3535353535353535353535353535353535353535", // to + "1000000000000000000", // amount + "ETH", // asset + "", // memo + "", // chainId + ) + fmt.Println("txInputData len: ", len(txInputData)) + + // Set a few other values + var input ethereum.SigningInput + proto.Unmarshal(txInputData, &input) + input.Nonce = big.NewInt(11).Bytes() + input.GasPrice = big.NewInt(20000000000).Bytes() + input.GasLimit = big.NewInt(21000).Bytes() + input.TxMode = ethereum.TransactionMode_Legacy + txInputData2, _ := proto.Marshal(&input) + fmt.Println("txInputData len: ", len(txInputData2)) + + fmt.Println("\n==> Step 2: Obtain preimage hash") + hashes := core.PreImageHashes(coin, txInputData2) + fmt.Println("hash(es): ", len(hashes), hex.EncodeToString(hashes)) + + var preSigningOutput common.PreSigningOutput + proto.Unmarshal(hashes, &preSigningOutput) + + fmt.Println("\n==> Step 3: Compile transaction info") + // Simulate signature, normally obtained from signature server + signature, _ := hex.DecodeString("360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07b53bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c9796677900") + publicKey, _ := hex.DecodeString("044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a") + txOutput := core.CompileWithSignatures(coin, txInputData2, [][]byte{signature}, [][]byte{publicKey}) + + fmt.Println("final txOutput proto: ", len(txOutput)) + var output ethereum.SigningOutput + _ = proto.Unmarshal(txOutput, &output) + fmt.Println("output.encoded: ", len(output.Encoded), hex.EncodeToString(output.Encoded)) + + fmt.Println("\n==> Double check signature validity (result should be true)") + verifyRes := core.PublicKeyVerify(publicKey, core.PublicKeyTypeSECP256k1Extended, signature, preSigningOutput.DataHash) + fmt.Println(verifyRes) + + fmt.Println("") +} + +func SignExternalBitcoinDemo() { + fmt.Println("==> Signing with External Signature - Bitcoin Demo") + + fmt.Println("\n==> Step 1: Prepare transaction input (protobuf)") + + revUtxoHash0, _ := hex.DecodeString("07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa8") + revUtxoHash1, _ := hex.DecodeString("d6892a5aa54e3b8fe430efd23f49a8950733aaa9d7c915d9989179f48dd1905e") + revUtxoHash2, _ := hex.DecodeString("6021efcf7555f90627364339fc921139dd40a06ccb2cb2a2a4f8f4ea7a2dc74d") + inPubKey0, _ := hex.DecodeString("024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382") + inPubKey1, _ := hex.DecodeString("0217142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc") + inPubKeyHash0, _ := hex.DecodeString("bd92088bb7e82d611a9b94fbb74a0908152b784f") + inPubKeyHash1, _ := hex.DecodeString("6641abedacf9483b793afe1718689cc9420bbb1c") + ownAddress := "bc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z00ppggv" + + // Input UTXO infos + type UtxoInfo struct { + revUtxoHash []byte + publicKey []byte + address string + amount int + index int + } + + utxoInfos := [...]UtxoInfo{ + // first + {revUtxoHash0, inPubKey0, ownAddress, 600000, 0}, + // second UTXO, with same pubkey + {revUtxoHash1, inPubKey0, ownAddress, 500000, 1}, + // third UTXO, with different pubkey + {revUtxoHash2, inPubKey1, "bc1qveq6hmdvl9yrk7f6lct3s6yue9pqhwcuxedggg", 400000, 0}, + } + + // Signature infos, indexed by pubkeyhash+hash + type SignatureInfo struct { + signature []byte + publicKey []byte + } + inSig0, _ := hex.DecodeString("304402201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f2a40") + inSig1, _ := hex.DecodeString("3044022041294880caa09bb1b653775310fcdd1458da6b8e7d7fae34e37966414fe115820220646397c9d2513edc5974ecc336e9b287de0cdf071c366f3b3dc3ff309213e4e4") + inSig2, _ := hex.DecodeString("30440220764e3d5b3971c4b3e70b23fb700a7462a6fe519d9830e863a1f8388c402ad0b102207e777f7972c636961f92375a2774af3b7a2a04190251bbcb31d19c70927952dc") + signatureInfos := map[string]SignatureInfo{ + hex.EncodeToString(inPubKeyHash0) + "+" + "a296bead4172007be69b21971a790e076388666c162a9505698415f1b003ebd7": {inSig0, inPubKey0}, + hex.EncodeToString(inPubKeyHash1) + "+" + "505f527f00e15fcc5a2d2416c9970beb57dfdfaca99e572a01f143b24dd8fab6": {inSig1, inPubKey1}, + hex.EncodeToString(inPubKeyHash0) + "+" + "60ed6e9371e5ddc72fd88e46a12cb2f68516ebd307c0fd31b1b55cf767272101": {inSig2, inPubKey0}, + } + + coin := core.CoinTypeBitcoin + + // Setup input for Plan + input := bitcoin.SigningInput{ + HashType: uint32(core.BitcoinSigHashTypeAll), + Amount: 1200000, + ByteFee: 1, + ToAddress: "bc1q2dsdlq3343vk29runkgv4yc292hmq53jedfjmp", + ChangeAddress: ownAddress, + Utxo: []*bitcoin.UnspentTransaction{}, + CoinType: uint32(coin), + Scripts: map[string][]byte{}, + } + + // process UTXOs + for i := 0; i < len(utxoInfos); i++ { + address := utxoInfos[i].address + fmt.Println("utxo", i, ": ", address, utxoInfos[i].amount) + + lockScript := core.BitcoinScriptLockScriptForAddress(address, coin) + fmt.Println(" lockScript: ", hex.EncodeToString(lockScript)) + keyHash := core.BitcoinScriptMatchPayToWitnessPublicKeyHash(lockScript) + fmt.Println(" keyHash: ", hex.EncodeToString(keyHash)) + redeemScript := core.BitcoinScriptBuildPayToPublicKeyHash(keyHash) + fmt.Println(" redeemScript: ", hex.EncodeToString(redeemScript)) + input.Scripts[hex.EncodeToString(keyHash)] = redeemScript + + utxo := bitcoin.UnspentTransaction{ + OutPoint: &bitcoin.OutPoint{ + Hash: utxoInfos[i].revUtxoHash, + Index: uint32(utxoInfos[i].index), + Sequence: 4294967295, + }, + Amount: int64(utxoInfos[i].amount), + Script: lockScript, + } + input.Utxo = append(input.Utxo, &utxo) + } + + txInputData, _ := proto.Marshal(&input) + fmt.Println("txInputData len: ", len(txInputData)) + + fmt.Println("\n==> Step 2: Obtain preimage hashes") + hashes := core.PreImageHashes(coin, txInputData) + + var preSigningOutput bitcoin.PreSigningOutput + proto.Unmarshal(hashes, &preSigningOutput) + fmt.Println("hashes+pubkeyhashes: ", len(preSigningOutput.HashPublicKeys)) + for _, h := range preSigningOutput.HashPublicKeys { + fmt.Println(" ", hex.EncodeToString(h.DataHash), hex.EncodeToString(h.PublicKeyHash)) + } + + fmt.Println("\n==> Step 3: Compile transaction info") + // Simulate signature, normally obtained from signature server + signatureVec := [][]byte{} + pubkeyVec := [][]byte{} + for _, h := range preSigningOutput.HashPublicKeys { + preImageHash := h.DataHash + pubkeyHash := h.PublicKeyHash + key := hex.EncodeToString(pubkeyHash) + "+" + hex.EncodeToString(preImageHash) + sigInfo := signatureInfos[key] + + signatureVec = append(signatureVec, sigInfo.signature) + pubkeyVec = append(pubkeyVec, sigInfo.publicKey) + + // Verify signature (pubkey & hash & signature) + verifyRes := core.PublicKeyVerifyAsDER(sigInfo.publicKey, core.PublicKeyTypeSECP256k1, sigInfo.signature, preImageHash) + fmt.Println("signature verification:", verifyRes) + } + txOutput := core.CompileWithSignatures(coin, txInputData, signatureVec, pubkeyVec) + + fmt.Println("final txOutput proto: ", len(txOutput)) + var output bitcoin.SigningOutput + _ = proto.Unmarshal(txOutput, &output) + fmt.Println("output.encoded: ", len(output.Encoded), hex.EncodeToString(output.Encoded)) + + fmt.Println("") +} diff --git a/samples/go/types/twdata.go b/samples/go/types/twdata.go index f1195643570..dcb7bdc95f8 100644 --- a/samples/go/types/twdata.go +++ b/samples/go/types/twdata.go @@ -1,13 +1,13 @@ package types // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include import "C" import ( - "unsafe" "encoding/hex" + "unsafe" ) // C.TWData -> Go byte[] diff --git a/samples/go/types/twstring.go b/samples/go/types/twstring.go index de9fbb5eb2e..3c5b2668c64 100644 --- a/samples/go/types/twstring.go +++ b/samples/go/types/twstring.go @@ -1,7 +1,7 @@ package types // #cgo CFLAGS: -I../../../include -// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lc++ -lm +// #cgo LDFLAGS: -L../../../build -L../../../build/trezor-crypto -lTrustWalletCore -lprotobuf -lTrezorCrypto -lstdc++ -lm // #include import "C" diff --git a/samples/node/index.ts b/samples/node/index.ts new file mode 100644 index 00000000000..450a4bdaaa3 --- /dev/null +++ b/samples/node/index.ts @@ -0,0 +1,39 @@ +#!/usr/bin/env ts-node + +const { initWasm, TW, KeyStore } = require("@trustwallet/wallet-core"); + +async function main() { + const start = new Date().getTime(); + console.log(`Initializing Wasm...`); + const core = await initWasm(); + const { CoinType, HexCoding, HDWallet, AnyAddress } = core; + console.log(`Done in ${new Date().getTime() - start} ms`); + + const wallet = HDWallet.create(256, ""); + const mnemonic = wallet.mnemonic(); + const key = wallet.getKeyForCoin(CoinType.ethereum); + const pubKey = key.getPublicKeySecp256k1(false); + const address = AnyAddress.createWithPublicKey(pubKey, CoinType.ethereum); + const storage = new KeyStore.FileSystemStorage("/tmp"); + const keystore = new KeyStore.Default(core, storage); + + const storedWallet = await keystore.import(mnemonic, "Coolw", "password", [ + CoinType.ethereum, + ]); + + console.log(`Create wallet: ${mnemonic}`); + console.log(`Get Ethereum public key: ${HexCoding.encode(pubKey.data())}`); + console.log(`Get Ethereum address: ${address.description()}`); + console.log(`CoinType.ethereum.value = ${CoinType.ethereum.value}`); + console.log("Ethereum protobuf models: \n", TW.Ethereum); + console.log("Keystore JSON: \n", JSON.stringify(storedWallet, null, 2)); + + await keystore.delete(storedWallet.id, "password"); + + wallet.delete(); + key.delete(); + pubKey.delete(); + address.delete(); +} + +main(); diff --git a/samples/node/package-lock.json b/samples/node/package-lock.json new file mode 100644 index 00000000000..e0a6b4d8d20 --- /dev/null +++ b/samples/node/package-lock.json @@ -0,0 +1,546 @@ +{ + "name": "node-sample", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "node-sample", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@trustwallet/wallet-core": "3.0.4" + }, + "devDependencies": { + "@types/node": "^10.9.1", + "ts-node": "^10.9.1", + "typescript": "^4.8.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@trustwallet/wallet-core": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@trustwallet/wallet-core/-/wallet-core-3.0.4.tgz", + "integrity": "sha512-FrIVEwRmUYFuwU9IoXg0J8fngeW7nlJZZAsrnOWba2g/yGWZl4FQ4z87MEQ5ROOGW9jJzsdWG4PRZffvYzhqsg==", + "dependencies": { + "protobufjs": ">=6.11.3" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/protobufjs": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/protobufjs/node_modules/@types/node": { + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "@trustwallet/wallet-core": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@trustwallet/wallet-core/-/wallet-core-3.0.4.tgz", + "integrity": "sha512-FrIVEwRmUYFuwU9IoXg0J8fngeW7nlJZZAsrnOWba2g/yGWZl4FQ4z87MEQ5ROOGW9jJzsdWG4PRZffvYzhqsg==", + "requires": { + "protobufjs": ">=6.11.3" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "protobufjs": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" + } + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "typescript": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/samples/node/package.json b/samples/node/package.json new file mode 100644 index 00000000000..c194836a001 --- /dev/null +++ b/samples/node/package.json @@ -0,0 +1,19 @@ +{ + "name": "node-sample", + "version": "1.0.0", + "description": "", + "main": "index.ts", + "scripts": { + "start": "ts-node index.ts" + }, + "author": "", + "license": "MIT", + "dependencies": { + "@trustwallet/wallet-core": "3.0.4" + }, + "devDependencies": { + "@types/node": "^10.9.1", + "ts-node": "^10.9.1", + "typescript": "^4.8.3" + } +} diff --git a/samples/node/tsconfig.json b/samples/node/tsconfig.json new file mode 100644 index 00000000000..d1395c05628 --- /dev/null +++ b/samples/node/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es2017", + "lib": [ + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/samples/osx/cocoapods/Podfile b/samples/osx/cocoapods/Podfile index b3a50e2550b..d071a6b7c05 100644 --- a/samples/osx/cocoapods/Podfile +++ b/samples/osx/cocoapods/Podfile @@ -3,5 +3,6 @@ platform :osx, '10.14' target 'WalletCoreExample' do use_frameworks! pod 'TrustWalletCore' + pod 'BigInt' end diff --git a/samples/osx/cocoapods/Podfile.lock b/samples/osx/cocoapods/Podfile.lock index 796e8f34d88..bfe2d08725c 100644 --- a/samples/osx/cocoapods/Podfile.lock +++ b/samples/osx/cocoapods/Podfile.lock @@ -1,24 +1,28 @@ PODS: - - SwiftProtobuf (1.16.0) - - TrustWalletCore (2.6.7): - - TrustWalletCore/Core (= 2.6.7) - - TrustWalletCore/Core (2.6.7): + - BigInt (5.2.0) + - SwiftProtobuf (1.19.0) + - TrustWalletCore (2.9.5): + - TrustWalletCore/Core (= 2.9.5) + - TrustWalletCore/Core (2.9.5): - TrustWalletCore/Types - - TrustWalletCore/Types (2.6.7): + - TrustWalletCore/Types (2.9.5): - SwiftProtobuf DEPENDENCIES: + - BigInt - TrustWalletCore SPEC REPOS: trunk: + - BigInt - SwiftProtobuf - TrustWalletCore SPEC CHECKSUMS: - SwiftProtobuf: 4e16842b83c6fda06b10fac50d73b3f1fce8ab7b - TrustWalletCore: 1409475008901d761effd92fe6c3d6be6c83ae3a + BigInt: f668a80089607f521586bbe29513d708491ef2f7 + SwiftProtobuf: 6ef3f0e422ef90d6605ca20b21a94f6c1324d6b3 + TrustWalletCore: 52ec9b10c17c8d7443e848ff0e6adac4d39be9f0 -PODFILE CHECKSUM: 68848e868fc9571d319a35d139d7901079a7fe36 +PODFILE CHECKSUM: 5fb79210604aff833a8320c937ea3849c2d9ac06 -COCOAPODS: 1.10.1 +COCOAPODS: 1.11.3 diff --git a/samples/osx/cocoapods/WalletCoreExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/samples/osx/cocoapods/WalletCoreExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/samples/osx/cocoapods/WalletCoreExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/samples/osx/cocoapods/WalletCoreExample/ViewController.swift b/samples/osx/cocoapods/WalletCoreExample/ViewController.swift index 22c7eb87acc..bb130d940b6 100644 --- a/samples/osx/cocoapods/WalletCoreExample/ViewController.swift +++ b/samples/osx/cocoapods/WalletCoreExample/ViewController.swift @@ -6,6 +6,7 @@ import Cocoa import WalletCore +import BigInt class ViewController: NSViewController { @@ -27,13 +28,13 @@ class ViewController: NSViewController { let dummyReceiverAddress = "0xC37054b3b48C3317082E7ba872d7753D13da4986" let signerInput = EthereumSigningInput.with { $0.chainID = Data(hexString: "01")! - $0.gasPrice = Data(hexString: "d693a400")! // decimal 3600000000 - $0.gasLimit = Data(hexString: "5208")! // decimal 21000 + $0.gasPrice = BigInt(3600000000).magnitude.serialize() + $0.gasLimit = BigInt(21000).magnitude.serialize() $0.toAddress = dummyReceiverAddress $0.transaction = EthereumTransaction.with { $0.transfer = EthereumTransaction.Transfer.with { - $0.amount = Data(hexString: "0348bca5a16000")! + $0.amount = BigInt(0.0009244*1000000000000000000).magnitude.serialize() } } $0.privateKey = secretPrivateKeyEth.data diff --git a/samples/typescript/devconsole.ts/.gitignore b/samples/typescript/devconsole.ts/.gitignore new file mode 100644 index 00000000000..83dbaee65a8 --- /dev/null +++ b/samples/typescript/devconsole.ts/.gitignore @@ -0,0 +1,3 @@ +node_modules +lib + diff --git a/samples/typescript/devconsole.ts/README.md b/samples/typescript/devconsole.ts/README.md new file mode 100644 index 00000000000..40ed8e5045d --- /dev/null +++ b/samples/typescript/devconsole.ts/README.md @@ -0,0 +1,34 @@ +# Devconsole.ts + +`Devconsole.ts` is a command-line utility to quickly work with `wallet-core` library. +It is based on `node` `typescript` console, and uses the WASM version of `wallet-core`. + +Type `help()` after starting it. + +Some wallet-core namespaces are exposed, and methods can be called directly. + +Note that auto-completion works in the console. + +## Build: + +``` +cd samples/typescript/devconsole.ts +npm install +npm run build +npm run start +``` + +## Sample usage: + +``` +> help() +This is an interactive typescript shell, to work with wallet-core (wasm) +You can use: +... +> w1 = HDWallet.create(256, '') +W {} +> w1.mnemonic() +'round profit immense ... sniff' +> w1.getAddressForCoin(CoinType.cosmos) +'cosmos1kgd25tvkz3jtuee4cl796trc2s0hjsfgqmhmku' +``` diff --git a/samples/typescript/devconsole.ts/package-lock.json b/samples/typescript/devconsole.ts/package-lock.json new file mode 100644 index 00000000000..50046ba9ab4 --- /dev/null +++ b/samples/typescript/devconsole.ts/package-lock.json @@ -0,0 +1,1491 @@ +{ + "name": "cli-wasm", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "cli-wasm", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "@trustwallet/wallet-core": "3.0.4", + "chalk": "^4.1.2", + "clear": "^0.1.0", + "figlet": "^1.5.2", + "inquirer": "^8.2.4" + }, + "bin": { + "cli": "index.ts" + }, + "devDependencies": { + "@types/node": "^18.7.15", + "ts-node": "^10.9.1", + "typescript": "^4.8.2" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@trustwallet/wallet-core": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@trustwallet/wallet-core/-/wallet-core-3.0.4.tgz", + "integrity": "sha512-FrIVEwRmUYFuwU9IoXg0J8fngeW7nlJZZAsrnOWba2g/yGWZl4FQ4z87MEQ5ROOGW9jJzsdWG4PRZffvYzhqsg==", + "dependencies": { + "protobufjs": ">=6.11.3" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.7.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz", + "integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==" + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/clear": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/clear/-/clear-0.1.0.tgz", + "integrity": "sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw==", + "engines": { + "node": "*" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figlet": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz", + "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", + "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protobufjs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.0.tgz", + "integrity": "sha512-rCuxKlh0UQKSMjrpIcTLbR5TtGQ52cgs1a5nUoPBAKOccdPblN67BJtjrbtudUJK6HmBvUdsmymyYOzO7lxZEA==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rxjs": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "@trustwallet/wallet-core": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@trustwallet/wallet-core/-/wallet-core-3.0.4.tgz", + "integrity": "sha512-FrIVEwRmUYFuwU9IoXg0J8fngeW7nlJZZAsrnOWba2g/yGWZl4FQ4z87MEQ5ROOGW9jJzsdWG4PRZffvYzhqsg==", + "requires": { + "protobufjs": ">=6.11.3" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/node": { + "version": "18.7.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz", + "integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==" + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "clear": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/clear/-/clear-0.1.0.tgz", + "integrity": "sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw==" + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==" + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==" + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "requires": { + "clone": "^1.0.2" + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "figlet": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.2.tgz", + "integrity": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==" + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "long": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.0.tgz", + "integrity": "sha512-9RTUNjK60eJbx3uz+TEGF7fUr29ZDxR5QzXcyDpeSfeH28S9ycINflOgOlppit5U+4kNTe83KQnMEerw7GmE8w==" + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" + }, + "protobufjs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.1.0.tgz", + "integrity": "sha512-rCuxKlh0UQKSMjrpIcTLbR5TtGQ52cgs1a5nUoPBAKOccdPblN67BJtjrbtudUJK6HmBvUdsmymyYOzO7lxZEA==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "rxjs": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==" + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" + }, + "typescript": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "requires": { + "defaults": "^1.0.3" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/samples/typescript/devconsole.ts/package.json b/samples/typescript/devconsole.ts/package.json new file mode 100644 index 00000000000..7ca581061d5 --- /dev/null +++ b/samples/typescript/devconsole.ts/package.json @@ -0,0 +1,28 @@ +{ + "name": "cli-wasm", + "version": "0.0.1", + "description": "CLI utility for Wallet Core, through WASM", + "main": "index.ts", + "bin": { + "cli": "index.ts" + }, + "scripts": { + "start": "ts-node src/index.ts", + "build": "tsc -p .", + "test": "cat test/data/privpubkey.testinput.txt | ts-node src/index.ts" + }, + "author": "Trust Wallet", + "license": "MIT", + "dependencies": { + "@trustwallet/wallet-core": "3.0.4", + "chalk": "^4.1.2", + "clear": "^0.1.0", + "figlet": "^1.5.2", + "inquirer": "^8.2.4" + }, + "devDependencies": { + "@types/node": "^18.7.15", + "ts-node": "^10.9.1", + "typescript": "^4.8.2" + } +} diff --git a/samples/typescript/devconsole.ts/samplescripts/privkeysign.sampleinput.txt b/samples/typescript/devconsole.ts/samplescripts/privkeysign.sampleinput.txt new file mode 100644 index 00000000000..05a6b7a776f --- /dev/null +++ b/samples/typescript/devconsole.ts/samplescripts/privkeysign.sampleinput.txt @@ -0,0 +1,12 @@ +// Create a private key, sign a message, derive public key from private key, verify signature + +privKeyData = '0xafeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5' +messageDigest = '0001020304050607080910111213141519171819202122232425262728293031' + +privkey = PrivateKey.createWithData(HexCoding.decode(privKeyData)); HexCoding.encode(privkey.data()) + +sig1 = privkey.sign(HexCoding.decode(messageDigest), Curve.secp256k1); HexCoding.encode(sig1) + +pubkey = privkey.getPublicKeySecp256k1(true); HexCoding.encode(pubkey.data()) + +verifyRes = pubkey.verify(sig1, HexCoding.decode(messageDigest)) diff --git a/samples/typescript/devconsole.ts/samplescripts/protoeth.sampleinput.txt b/samples/typescript/devconsole.ts/samplescripts/protoeth.sampleinput.txt new file mode 100644 index 00000000000..422d16881d4 --- /dev/null +++ b/samples/typescript/devconsole.ts/samplescripts/protoeth.sampleinput.txt @@ -0,0 +1,26 @@ +// test signing eip1559 erc20 transfer tx + +input = TW.Ethereum.Proto.SigningInput.create({ + toAddress: "0x6b175474e89094c44da98b954eedeac495271d0f", + chainId: Buffer.from("01", "hex"), + nonce: Buffer.from("00", "hex"), + txMode: TW.Ethereum.Proto.TransactionMode.Enveloped, + maxInclusionFeePerGas: Buffer.from("0077359400", "hex"), + maxFeePerGas: Buffer.from("00b2d05e00", "hex"), + gasLimit: Buffer.from("0130B9", "hex"), + transaction: TW.Ethereum.Proto.Transaction.create({ + erc20Transfer: TW.Ethereum.Proto.Transaction.ERC20Transfer.create({ + to: "0x5322b34c88ed0691971bf52a7047448f0f4efc84", + amount: Buffer.from("1bc16d674ec80000", "hex"), + }), + }), + privateKey: HexCoding.decode( + "0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151" + ), + }); + +encoded = TW.Ethereum.Proto.SigningInput.encode(input).finish(); HexCoding.encode(encoded) + +outputData = AnySigner.sign(encoded, CoinType.ethereum); HexCoding.encode(outputData) + +output = TW.Ethereum.Proto.SigningOutput.decode(outputData); HexCoding.encode(output.encoded) diff --git a/samples/typescript/devconsole.ts/samplescripts/solanaaddress.sampleinput.txt b/samples/typescript/devconsole.ts/samplescripts/solanaaddress.sampleinput.txt new file mode 100644 index 00000000000..c2796a598b8 --- /dev/null +++ b/samples/typescript/devconsole.ts/samplescripts/solanaaddress.sampleinput.txt @@ -0,0 +1,13 @@ +// Derive a Solana address directly and through privKey-pubKey + +coin = CoinType.solana + +wallet = HDWallet.createWithMnemonic('ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal', '') + +address = wallet.getAddressForCoin(coin) + +pk = wallet.getKeyForCoin(coin); HexCoding.encode(pk.data()) +pubkey = pk.getPublicKeyEd25519(); HexCoding.encode(pubkey.data()) +a1 = AnyAddress.createWithPublicKey(pubkey, coin); a1.description() + +address === a1.description() diff --git a/samples/typescript/devconsole.ts/samplescripts/wallets.sampleinput.txt b/samples/typescript/devconsole.ts/samplescripts/wallets.sampleinput.txt new file mode 100644 index 00000000000..415993f3d4e --- /dev/null +++ b/samples/typescript/devconsole.ts/samplescripts/wallets.sampleinput.txt @@ -0,0 +1,51 @@ +// Delete all wallets to start with empty state (commented out not to accidentally remove data) +//await walletsDeleteAll('deleteall') + +// create a wallet +await walletCreate(256, 'First') +await walletsList() +await walletDump() + +// add an extra coin to it +await walletAddCoin(WC.CoinType.solana) +await walletDump() + +// Create a second wallet by mnemonic import +await walletImport('ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal', 'Imported') +await walletsList() +await walletDump() + +// Load first ... +await walletsList() +await walletLoad(0) +await walletDump() + +// ... then second +await walletsList() +await walletLoad(1) +await walletDump() + +// retrieve private key and verify that it derives the same address +coin = WC.CoinType.bitcoin +privkey = wallets.wallet.wallet.privateKey(coin, 'devconsole.ts') +pubkey = privkey.getPublicKeySecp256k1(true); HexCoding.encode(pubkey.data()) +addrDerived = AnyAddress.createWithPublicKey(pubkey, coin).description() +addrStored = wallets.wallet.wallet.account(0).address() +addrDerived === addrStored + +// create another wallet +await walletCreate(256, 'Another') +await walletsList() +await walletDump() + +// Delete it +await walletDelete('delete') +await walletsList() +await walletDump() + +await walletLoad(0) +await walletDump() + +// Delete all wallets (commented out not to accidentally remove data) +//await walletDeleteAll('deleteall') +//await walletsList() diff --git a/samples/typescript/devconsole.ts/src/index.ts b/samples/typescript/devconsole.ts/src/index.ts new file mode 100644 index 00000000000..f08c79bd6c8 --- /dev/null +++ b/samples/typescript/devconsole.ts/src/index.ts @@ -0,0 +1,366 @@ +#!/usr/bin/env ts-node + +const chalk = require('chalk'); +const clear = require('clear'); +const figlet = require('figlet'); +const repl = require('node:repl'); +const { initWasm, TW, KeyStore } = require("@trustwallet/wallet-core"); +const fs = require("fs"); + +function enumerateNamespaces(topLevelNamespace: any) { + const exceptions: string[] = ['AsciiToString', 'ExitStatus', 'ENV', 'ERRNO_CODES', 'ERRNO_MESSAGES', 'DNS', 'Protocols', 'Sockets', 'UNWIND_CACHE', 'PATH', 'PATH_FS', 'SYSCALLS', 'JSEvents', 'JSEvents_requestFullscreen', 'JSEvents_resizeCanvasForFullscreen', 'ExceptionInfo', 'Browser', 'FS', 'MEMFS', 'TTY', 'PIPEFS', 'SOCKFS', 'RegisteredClass', 'Emval']; + var ns : string[] = []; + for (var member in topLevelNamespace) { + if (typeof topLevelNamespace[member] == 'function' || typeof topLevelNamespace[member] == 'object') { + ns.push(member); + } + } + return ns + .filter(n => { var firstLetter = n[0]; return (firstLetter >= 'A' && firstLetter <= 'Z'); }) + .filter(n => !n.includes('Error')) + .filter(n => !n.startsWith('HEAP')) + .filter(n => !n.startsWith('UTF')) + .filter(n => !n.startsWith('FS_')) + .filter(n => !n.startsWith('ClassHandle')) + .filter(n => !n.startsWith('RegisteredPointer')) + .filter(n => !exceptions.includes(n)); +} + +async function main() { + // Wrapper for a wallet stored in keystore + class StoredKeyWallet { + wallet: any; // StoredKey + constructor(w: any) { + this.wallet = w; + } + status(): string { + return `Wallet '${this.wallet.name()}', with ${this.wallet.accountCount()} addresses`; + } + dump(): void { + console.error(`Wallet '${this.wallet.name()}', ${this.wallet.isMnemonic() ? 'type mnemonic' : ''}:`); + + if (this.wallet.accountCount() == 0) { + console.log('No addresses'); + } else { + console.log('Addresses:'); + const n = this.wallet.accountCount(); + for (var i = 0; i < n; ++i) { + const a = this.wallet.account(i); + console.log(` ${WC.CoinTypeConfiguration.getSymbol(a.coin())}: ${a.address()}`); + } + } + } + } + + // Handles several wallets, with keystore + class TestWalletManager { + // single opened StoredKeyWallet instance + wallet: StoredKeyWallet | null = null; + StoreFolderPath = "/tmp/devconsole.ts"; + StoreFixedPassword = "devconsole.ts"; + HDWalletPassord = ''; + DefaultCoins: any[] = []; + keystore: any = null; + // wallets available in storage + storeWallets: any[] = []; + + init(): void { + this.DefaultCoins = [WC.CoinType.bitcoin, WC.CoinType.ethereum, WC.CoinType.binance]; + this.keystore = null; + // check and try to create store folder + if (!fs.existsSync(this.StoreFolderPath)) { + // try to create, ignore error + fs.mkdirSync(this.StoreFolderPath); + } + // check again + if (!fs.existsSync(this.StoreFolderPath)) { + console.log(`ERROR: Could not open/create storage folder! Wallet storage will not work. '${this.StoreFolderPath}'`); + return; + } + const storage = new KeyStore.FileSystemStorage(this.StoreFolderPath); + this.keystore = new KeyStore.Default(WC, storage); + } + async refreshStoreWallets(): Promise { + if (!this.keystore) { return; } + process.stdout.write(`Refreshing wallets list ... `); + try { + this.storeWallets = await this.keystore.loadAll(); + console.log(`found ${this.storeWallets.length} wallets (dir: ${this.StoreFolderPath})`); + } catch (err) { + console.log(`Exception: ${err}`); + } + } + async list(): Promise { + if (!this.keystore) { + console.log(`Keystore not available!`); + return; + } + await this.refreshStoreWallets(); + if (!this.storeWallets || this.storeWallets.length == 0) { + console.log("No wallets found in storage."); + } else { + let idx = 0; + console.log(`Found ${this.storeWallets.length} wallets in storage, use walletLoad(id) to load:`); + this.storeWallets.forEach(w => console.log(`${idx++}: ${w.name} \t${w.activeAccounts.length} addrs \t${w.id}`)); + } + } + async load(index: number) { + this.wallet = null; + if (!this.keystore) { return; } + try { + await this.refreshStoreWallets(); + if (index >= this.storeWallets.length) { + console.log(`Index out of range, max ${this.storeWallets.length}`); + await walletsList(); + return; + } + const id = this.storeWallets[index].id; + process.stdout.write(`Loading wallet ${index} ${id} ... `); + const wallet = await this.keystore.load(id); + console.log("done"); + this.wallet = new StoredKeyWallet(this.keystore.mapStoredKey(wallet)); + this.status(); + } catch (err) { + console.log(`Exception: ${err}`); + } + } + async loadAWallet() { + if (this.storeWallets.length > 0) { + await this.load(0); + } + } + autoFillName(name: string): string { + return (name === '') ? `Wallet${(this.storeWallets.length + 1).toString()}` : name; + } + async create(strength: number, name: string): Promise { + this.wallet = null; + if (!this.keystore) { return null; } + try { + if (name === '') { name = this.autoFillName(name); } + const hdWallet = WC.HDWallet.create(strength, this.HDWalletPassord); + const storedKey = WC.StoredKey.importHDWallet(hdWallet.mnemonic(), name, Buffer.from(this.StoreFixedPassword), this.DefaultCoins[0]); + this.DefaultCoins.forEach((coin) => { + storedKey.accountForCoin(coin, hdWallet); + }); + let wallet = this.keystore.mapWallet(storedKey); + await this.keystore.importWallet(wallet); + this.wallet = new StoredKeyWallet(storedKey); + console.log(`Wallet ${name} created, mnemonic: ${hdWallet.mnemonic()}`); + this.status(); + return this.wallet; + } catch (err) { + console.log(`Exception: ${err}`); + return null; + } + } + async import(mnemonic: string, name: string): Promise { + this.wallet = null; + if (!this.keystore) { return null; } + try { + if (!WC.Mnemonic.isValid(mnemonic)) { + console.error(`Mnemonic is not valid ${mnemonic}`); + return null; + } + if (name === '') { name = this.autoFillName(name); } + // There are two ways to do this here: + // 1. Create HDWallet with mnemonic, create StoroedKey with it (add coins), then map to a KeyStore wallet and import it into the storage, or + // 2. Import in KeyStore directly (using mnemonic), and obtain StoredKey from it + let wallet = await this.keystore.import(mnemonic, name, this.StoreFixedPassword, this.DefaultCoins); + this.wallet = new StoredKeyWallet(this.keystore.mapStoredKey(wallet)); + console.log(`Wallet ${name} imported, mnemonic: ${mnemonic}`); + this.status(); + return this.wallet; + } catch (err) { + console.log(`Exception: ${err}`); + return null; + } + } + async addCoin(coin: string): Promise { + if (this.wallet == null) { + console.error('No wallet open, see walletCreate() / walletLoad() / walletsList()'); + return null; + } + if (!this.keystore) { return null; } + const wallet = await this.keystore.addAccounts(this.wallet?.wallet.identifier(), this.StoreFixedPassword, [coin]); + this.wallet = new StoredKeyWallet(this.keystore.mapStoredKey(wallet)); + return this.wallet; + } + // Delete loaded wallet + async delete(param: string): Promise { + if (!this.wallet) { + console.log(`No wallet loaded`); + return; + } + if (!this.keystore) { return; } + try { + if (param !== 'delete') { + console.log(`Are you sure? Invoke with 'delete' parameter!`); + return; + } + const name = this.wallet.wallet.name(); + const id = this.wallet.wallet.identifier(); + await this.keystore.delete(id, this.StoreFixedPassword); + this.wallet = null; + console.log(`Wallet '${name}' ${id} deleted.`); + await this.refreshStoreWallets(); + } catch (err) { + console.log(`Exception: ${err}`); + } + } + async deleteAll(param: string): Promise { + await this.refreshStoreWallets(); + if (this.storeWallets.length == 0) { + console.log(`No wallets found`); + return; + } + if (!this.keystore) { return; } + try { + if (param !== 'deleteall') { + console.log(`Are you sure? Invoke with 'deleteall' parameter!`); + return; + } + while (this.storeWallets.length > 0) { + const id = this.storeWallets[0].id; + await this.keystore.delete(id, this.StoreFixedPassword); + await this.refreshStoreWallets(); + } + this.wallet = null; + console.log(`All wallets deleted.`) + } catch (err) { + console.log(`Exception: ${err}`); + } + } + status(): void { + if (this.wallet === null) { + console.error(`No wallet open, ${this.storeWallets.length} available; see walletCreate() / walletLoad() / walletsList()`); + } else { + console.error(`Wallet open: ${this.wallet.status()}`); + } + } + dump(): void { + this.status(); + if (this.wallet) { + this.wallet.dump(); + } + } + }; + + // single global TestWalletManager instance + let wallets: TestWalletManager = new TestWalletManager(); + + async function walletsList() { await wallets.list(); } + async function walletLoad(index: number) { await wallets.load(index); } + async function walletsDeleteAll(param: string) { await wallets.deleteAll(param); } + async function walletCreate(strength: number = 256, name: string = ''): Promise { return await wallets.create(strength, name); } + async function walletImport(mnemonic: string, name: string = ''): Promise { return await wallets.import(mnemonic, name); } + async function walletAddCoin(coin: string): Promise { return await wallets.addCoin(coin); } + function walletDump(): void { wallets.dump(); } + async function walletDelete(param: string) { await wallets.delete(param); } + + function help(): void { + console.log('This is an interactive typescript shell, to work with wallet-core (wasm)'); + console.log('You can use:'); + console.log(); + console.log("- Any typescript commands EXAMPLES"); + console.log(" 'wallet-core'.length"); + console.log(" x = 5; ++x"); + console.log(); + console.log('- Any method from Wallet-Core, through various namespaces, see full list below. Some examples:'); + console.log(" CoinType CoinType.bitcoin.value"); + console.log(" HDWallet wallet = HDWallet.create(256, ''); wallet.mnemonic()"); + console.log(" PrivateKey pk = PrivateKey.createWithData(HexCoding.decode('afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5'))"); + console.log(" PublicKey pubkey = PublicKey.createWithData(HexCoding.decode('0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1'), PublicKeyType.secp256k1)"); + console.log(" AnyAddress AnyAddress.createWithString('EQCTVra3xPXenA1wNFMba2taTsc9XMdfCWC7FJpGXjk7', CoinType.solana).description()"); + console.log(" CoinTypeConfiguration CoinTypeConfiguration.getSymbol(CoinType.bitcoin)"); + console.log(" Mnemonmic Mnemonic.isValidWord('acid')"); + console.log(" HexCoding HexCoding.encode(HexCoding.decode('01ab02'))"); + console.log(); + console.log("- Convenience methods:"); + console.log(" walletCreate([strength], [name]) walletCreate(128)"); + console.log(" walletImport(mnemonic, [name]) walletImport('ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal', 'Test1')"); + console.log(" walletDump() walletDump()"); + console.log(" walletAddCoin(symbol) walletAddCoin(CoinType.solana)"); + console.log(" walletsList() walletsList()"); + console.log(" walletLoad() walletLoad(0)"); + console.log(" walletDelete() walletDelete('delete')"); + console.log(" walletsDeleteAll() walletsDeleteAll('deleteall')"); + console.log(); + console.log("See examples in samplescripts folder, e.g.: cat samplescripts/protoeth.sampleinput.txt | npm run start"); + console.log(); + console.log("A longer example:"); + console.log(" coin = CoinType.solana"); + console.log(" wallet = HDWallet.createWithMnemonic('ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal', '')"); + console.log(" wallet.getAddressForCoin(coin)"); + console.log(" pk = wallet.getKeyForCoin(coin)"); + console.log(" HexCoding.encode(pk.data())"); + console.log(" pubkey = pk.getPublicKeyEd25519()"); + console.log(" HexCoding.encode(pubkey.data())"); + console.log(" a1 = AnyAddress.createWithPublicKey(pubkey, coin)"); + console.log(" a1.description()"); + console.log(); + console.log("Full list of exposed Wallet-core namespaces:"); + console.log(enumerateNamespaces(WC).sort().join(', ')); + console.log(enumerateNamespaces(TW).sort().map(n => 'TW.' + n).join(', ')); + console.log(); + } + + function exit(code: number): void { + process.exit(code); + } + + clear(); + console.log( + chalk.blue( + figlet.textSync('wallet-core', { horizontalLayout: 'full' }) + ) + ); + console.log(chalk.blue(' devconsole.ts, wasm lib')); + console.log(); + + process.stdout.write("Initializing WalletCore library ..."); + const WC = await initWasm(); + console.log(` done.`); + console.log(); + console.log(`Type 'help()' for help`); + console.log(`Press Ctrl-C twice to exit`); + console.log(chalk.red(`This is a test tool, DO NOT USE WITH REAL FUNDS!`)); + console.log(); + + wallets.init(); + await wallets.refreshStoreWallets(); + await wallets.loadAWallet(); + + const local = repl.start('> '); + + // Expose WC namespaces, as top-level + var nss = enumerateNamespaces(WC); + nss.forEach(ns => local.context[ns] = WC[ns]); + // also all under WC + local.context.WC = WC; + // Expose TW namespaces (under TW) + local.context.TW = TW; + // Expose KeyStore namespace (under KeyStore) + local.context.KeyStore = KeyStore; + + // Expose more stuff; utilities + local.context.help = help; + local.context.exit = exit; + local.context.walletsList = walletsList; + local.context.walletLoad = walletLoad; + local.context.walletsDeleteAll = walletsDeleteAll; + local.context.walletCreate = walletCreate; + local.context.walletImport = walletImport; + local.context.walletAddCoin = walletAddCoin; + local.context.walletDump = walletDump; + local.context.walletDelete = walletDelete; + local.context.wallets = wallets; + local.context.wallet = wallets.wallet; + local.context.coin = WC.CoinType.bitcoin; + + local.on('exit', () => { + console.log('Bye!'); + process.exit(); + }); +} + +main(); diff --git a/samples/typescript/devconsole.ts/test/data/privpubkey.testinput.txt b/samples/typescript/devconsole.ts/test/data/privpubkey.testinput.txt new file mode 100644 index 00000000000..8431062b58b --- /dev/null +++ b/samples/typescript/devconsole.ts/test/data/privpubkey.testinput.txt @@ -0,0 +1,26 @@ +// Create a private key, sign a message, derive public key from private key, verify signature +privKeyData = '0xafeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5' +pubKeyData = '0x0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1' +messageDigest = '0001020304050607080910111213141519171819202122232425262728293031' +signature = '0x673b54a91d87cfb9389e54cc55b1a9343a6eb9f2ea1f449cb19a248a86bb904c1efb109f8cf655c4f510211053c1696e52c75843c5c803fa1b78fe0c263f468201' + +testAssert = function(expression, text) { + if (!expression) { console.log('ERROR: ', text); exit(1); } + return 'ok: ' + text; +} + +privkey = PrivateKey.createWithData(HexCoding.decode(privKeyData)) +HexCoding.encode(privkey.data()) +testAssert(HexCoding.encode(privkey.data()) == privKeyData, 'private key match') + +HexCoding.encode(sig1 = privkey.sign(HexCoding.decode(messageDigest), Curve.secp256k1)) +testAssert(HexCoding.encode(sig1) == signature, 'signature match') + +pubkey = privkey.getPublicKeySecp256k1(true) +HexCoding.encode(pubkey.data()) +testAssert(HexCoding.encode(pubkey.data()) == pubKeyData, 'pubkey match') + +verifyRes = pubkey.verify(sig1, HexCoding.decode(messageDigest)) +testAssert(verifyRes, 'signature verification') + +exit(0) diff --git a/samples/typescript/devconsole.ts/tsconfig.json b/samples/typescript/devconsole.ts/tsconfig.json new file mode 100644 index 00000000000..70198268800 --- /dev/null +++ b/samples/typescript/devconsole.ts/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "lib": [ + "es6", + "es2015", + "dom" + ], + "declaration": true, + "outDir": "lib", + "rootDir": "src", + "strict": true, + "types": [ + "node" + ], + "esModuleInterop": true, + "resolveJsonModule": true + } +} \ No newline at end of file diff --git a/samples/wasm/.gitignore b/samples/wasm/.gitignore new file mode 100644 index 00000000000..8fa7eb9d5df --- /dev/null +++ b/samples/wasm/.gitignore @@ -0,0 +1,2 @@ +*.js +*.wasm diff --git a/samples/wasm/README.md b/samples/wasm/README.md new file mode 100644 index 00000000000..010195fdb38 --- /dev/null +++ b/samples/wasm/README.md @@ -0,0 +1,42 @@ +# Wasm Sample + +## DISCLAIMER + +> This is a sample html for demonstration purpose only, +> do not use it with real addresses, real transactions, or real funds. +> Use it at your own risk. + +## Prerequisites + +1. Everything you need to build the wallet core: https://developer.trustwallet.com/wallet-core/developing-the-library/building +2. Install [emsdk](https://emscripten.org/docs/getting_started/downloads.html) +- - python3 +- - cmake +- - run `tools/install-dependencies` if you just cloned this repo +- - run `tools/install-wasm-dependencies` +3. node.js + +## Building and Running + +- run `tools/generate-files` +- run `tools/wasm-build` +- cd `wasm` +- - run `npm install` +- - run `npm run codegen:js-browser` +- - run `npm run copy:wasm-sample` +- cd `samples/wasm` +- run `python3 -m http.server 8000` +- open web browser and navigate to `http://127.0.0.1:8000` + +## Notes + +This sample also demonstrates how to use protobuf.js models in html directly, `core_proto.js` is built by command defined in `wasm/package.json` + +```shell +npm run codegen:js-browser +``` + +For modern javaScript sample, please check out: + +- React: https://github.com/hewigovens/wallet-core-react +- Flutter: https://github.com/iampawan/Wallet-Core-Web/tree/dev diff --git a/samples/wasm/index.html b/samples/wasm/index.html new file mode 100644 index 00000000000..13a6298ef1c --- /dev/null +++ b/samples/wasm/index.html @@ -0,0 +1,1319 @@ + + + + + + Emscripten-Generated Code + + + + + image/svg+xml + + +
+
Downloading...
+ + + Resize canvas + Lock/hide mouse pointer     + + + + +
+ +
+ + +
+ +
+ + + + + + + + + diff --git a/src/Aeternity/Address.cpp b/src/Aeternity/Address.cpp index 101ba527854..b3d0ecc64fb 100644 --- a/src/Aeternity/Address.cpp +++ b/src/Aeternity/Address.cpp @@ -7,10 +7,8 @@ #include "Address.h" #include "Identifiers.h" #include -#include -#include -using namespace TW::Aeternity; +namespace TW::Aeternity { /// Determines whether a string makes a valid address. bool Address::isValid(const std::string& string) { @@ -25,7 +23,7 @@ bool Address::isValid(const std::string& string) { } /// Initializes an address from a public key. -Address::Address(const PublicKey &publicKey) { +Address::Address(const PublicKey& publicKey) { if (publicKey.type != TWPublicKeyTypeED25519) { throw std::invalid_argument("Invalid public key type"); } @@ -56,3 +54,5 @@ bool Address::checkPayload(const std::string& payload) { unsigned long base58 = Base58::bitcoin.decodeCheck(payload).size(); return base58 == size; } + +} // namespace TW::Aeternity diff --git a/src/Aeternity/Address.h b/src/Aeternity/Address.h index 07e9b732939..3733a44e93d 100644 --- a/src/Aeternity/Address.h +++ b/src/Aeternity/Address.h @@ -4,6 +4,8 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#pragma once + #include #include diff --git a/src/Aeternity/Entry.cpp b/src/Aeternity/Entry.cpp index 97d72906e63..cab15ca9d26 100644 --- a/src/Aeternity/Entry.cpp +++ b/src/Aeternity/Entry.cpp @@ -9,19 +9,21 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Aeternity; using namespace std; +namespace TW::Aeternity { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Aeternity diff --git a/src/Aeternity/Entry.h b/src/Aeternity/Entry.h index 3418220ea7d..b344b0ebb1f 100644 --- a/src/Aeternity/Entry.h +++ b/src/Aeternity/Entry.h @@ -12,12 +12,11 @@ namespace TW::Aeternity { /// Entry point for implementation of Aeternity coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeAeternity}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Aeternity diff --git a/src/Aeternity/Signer.cpp b/src/Aeternity/Signer.cpp index 7c9adf1fafa..ff27db8d81a 100644 --- a/src/Aeternity/Signer.cpp +++ b/src/Aeternity/Signer.cpp @@ -5,7 +5,6 @@ // file LICENSE at the root of the source code distribution tree. #include "Signer.h" -#include "Address.h" #include "Base58.h" #include "Base64.h" #include "HexCoding.h" @@ -14,9 +13,10 @@ #include using namespace TW; -using namespace TW::Aeternity; -Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { +namespace TW::Aeternity { + +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); std::string sender_id = input.from_address(); std::string recipient_id = input.to_address(); @@ -29,7 +29,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { /// implementation copied from /// https://github.com/aeternity/aepp-sdk-go/blob/07aa8a77e5/aeternity/helpers.go#L367 -Proto::SigningOutput Signer::sign(const TW::PrivateKey &privateKey, Transaction &transaction) { +Proto::SigningOutput Signer::sign(const TW::PrivateKey& privateKey, Transaction& transaction) { auto txRlp = transaction.encode(); /// append networkId and txRaw @@ -86,4 +86,6 @@ std::string Signer::encodeBase64WithChecksum(const std::string& prefix, const TW append(data, checksumPart); return prefix + TW::Base64::encode(data); -} \ No newline at end of file +} + +} // namespace TW::Aeternity diff --git a/src/Aeternity/Signer.h b/src/Aeternity/Signer.h index dd289e5487d..d356375055a 100644 --- a/src/Aeternity/Signer.h +++ b/src/Aeternity/Signer.h @@ -4,6 +4,8 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#pragma once + #include "Transaction.h" #include "../proto/Aeternity.pb.h" #include @@ -11,14 +13,14 @@ namespace TW::Aeternity { class Signer { - public: +public: /// Signs a Proto::SigningInput transaction static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; /// Signs the given transaction. - static Proto::SigningOutput sign(const PrivateKey &privateKey, Transaction &transaction); + static Proto::SigningOutput sign(const PrivateKey& privateKey, Transaction& transaction); - private: +private: static const uint8_t checkSumSize = 4; static Data buildRlpTxRaw(Data& txRaw, Data& sigRaw); diff --git a/src/Aeternity/Transaction.cpp b/src/Aeternity/Transaction.cpp index c06d74d7efe..7ed0515b97b 100644 --- a/src/Aeternity/Transaction.cpp +++ b/src/Aeternity/Transaction.cpp @@ -10,8 +10,7 @@ #include #include -using namespace TW; -using namespace TW::Aeternity; +namespace TW::Aeternity { /// RLP returns a byte serialized representation Data Transaction::encode() { @@ -46,3 +45,5 @@ TW::Data Transaction::encodeSafeZero(uint256_t value) { } return Ethereum::RLP::encode(value); } + +} // namespace TW::Aeternity diff --git a/src/Aeternity/Transaction.h b/src/Aeternity/Transaction.h index 6bace4629b8..a58db36f651 100644 --- a/src/Aeternity/Transaction.h +++ b/src/Aeternity/Transaction.h @@ -53,7 +53,7 @@ class Transaction { //// see https://github.com/aeternity/protocol/blob/epoch-v0.22.0/serializations.md#the-id-type static Data buildTag(const std::string& address); - /// Awternity network does not accept zero int values as rlp param, + /// Aeternity network does not accept zero int values as rlp param, /// instead empty byte array should be encoded /// see https://forum.aeternity.com/t/invalid-tx-error-on-mainnet-goggle-says-it-looks-good/4118/5?u=defuera static Data encodeSafeZero(uint256_t value); diff --git a/src/Aion/Address.cpp b/src/Aion/Address.cpp index 58872b004d1..411a1b97d9e 100644 --- a/src/Aion/Address.cpp +++ b/src/Aion/Address.cpp @@ -5,10 +5,9 @@ // file LICENSE at the root of the source code distribution tree. #include "Address.h" -#include "../Hash.h" #include "../HexCoding.h" -using namespace TW::Aion; +namespace TW::Aion { bool Address::isValid(const std::string& string) { const auto data = parse_hex(string); @@ -40,3 +39,5 @@ Address::Address(const PublicKey& publicKey) { std::string Address::string() const { return "0x" + hex(bytes); } + +} // namespace TW::Aion diff --git a/src/Aion/Entry.cpp b/src/Aion/Entry.cpp index c30ee168d7a..f32ef1e3491 100644 --- a/src/Aion/Entry.cpp +++ b/src/Aion/Entry.cpp @@ -9,19 +9,28 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Aion; +using namespace TW; using namespace std; +namespace TW::Aion { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Address(address); + return {addr.bytes.begin(), addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Aion diff --git a/src/Aion/Entry.h b/src/Aion/Entry.h index a369e5b39ea..1ff2ed22dc1 100644 --- a/src/Aion/Entry.h +++ b/src/Aion/Entry.h @@ -12,12 +12,12 @@ namespace TW::Aion { /// Entry point for implementation of Aion coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeAion}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Aion diff --git a/src/Aion/RLP.h b/src/Aion/RLP.h index b713b79c47a..55330d37a5a 100644 --- a/src/Aion/RLP.h +++ b/src/Aion/RLP.h @@ -17,7 +17,7 @@ namespace TW::Aion { -/// Aion's RLP encoging for long numbers +/// Aion's RLP encoding for long numbers /// https://github.com/aionnetwork/aion/issues/680 struct RLP { static Data encodeLong(boost::multiprecision::uint128_t l) noexcept { diff --git a/src/Aion/Signer.cpp b/src/Aion/Signer.cpp index a4a3a4adf14..01c90385d91 100644 --- a/src/Aion/Signer.cpp +++ b/src/Aion/Signer.cpp @@ -5,13 +5,12 @@ // file LICENSE at the root of the source code distribution tree. #include "Signer.h" - -#include "../Hash.h" #include "../uint256.h" #include using namespace TW; -using namespace TW::Aion; + +namespace TW::Aion { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { using boost::multiprecision::uint128_t; @@ -46,3 +45,5 @@ void Signer::sign(const PrivateKey& privateKey, Transaction& transaction) noexce transaction.signature = result; } + +} // namespace TW::Aion diff --git a/src/Aion/Signer.h b/src/Aion/Signer.h index 68a95d79628..76d5e97d36b 100644 --- a/src/Aion/Signer.h +++ b/src/Aion/Signer.h @@ -7,7 +7,7 @@ #pragma once #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/Aion.pb.h" diff --git a/src/Aion/Transaction.cpp b/src/Aion/Transaction.cpp index 017dbe96896..c108f0d4b37 100644 --- a/src/Aion/Transaction.cpp +++ b/src/Aion/Transaction.cpp @@ -4,14 +4,14 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "RLP.h" #include "Transaction.h" -#include "../Ethereum/RLP.h" +#include "RLP.h" using namespace TW; -using namespace TW::Aion; using boost::multiprecision::uint128_t; +namespace TW::Aion { + Data Transaction::encode() const noexcept { auto encoded = Data(); append(encoded, Ethereum::RLP::encode(nonce)); @@ -27,3 +27,5 @@ Data Transaction::encode() const noexcept { } return Ethereum::RLP::encodeList(encoded); } + +} // namespace TW::Aion diff --git a/src/Aion/Transaction.h b/src/Aion/Transaction.h index dc975dcaac9..b5e54bc4d85 100644 --- a/src/Aion/Transaction.h +++ b/src/Aion/Transaction.h @@ -7,7 +7,7 @@ #pragma once #include "Address.h" -#include "../Data.h" +#include "Data.h" #include diff --git a/src/Algorand/Address.cpp b/src/Algorand/Address.cpp index 3e2b8d3b1fa..b0e4ede7229 100644 --- a/src/Algorand/Address.cpp +++ b/src/Algorand/Address.cpp @@ -1,17 +1,15 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "Address.h" -#include "../HexCoding.h" -#include "../Hash.h" #include "../Base32.h" #include -using namespace TW::Algorand; +namespace TW::Algorand { bool Address::isValid(const std::string& string) { if (string.size() != encodedSize) { @@ -63,3 +61,5 @@ std::string Address::string() const { std::string encoded = Base32::encode(data); return encoded; } + +} // namespace TW::Algorand diff --git a/src/Algorand/Address.h b/src/Algorand/Address.h index 93f82788676..dc4af6666fe 100644 --- a/src/Algorand/Address.h +++ b/src/Algorand/Address.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/Algorand/AssetTransfer.cpp b/src/Algorand/AssetTransfer.cpp index eecb4cf8335..3f4c1a4eccd 100644 --- a/src/Algorand/AssetTransfer.cpp +++ b/src/Algorand/AssetTransfer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,8 +7,7 @@ #include "AssetTransfer.h" #include "BinaryCoding.h" -using namespace TW; -using namespace TW::Algorand; +namespace TW::Algorand { Data AssetTransfer::serialize() const { Data data; @@ -59,3 +58,5 @@ Data AssetTransfer::serialize() const { return data; } + +} // namespace TW::Algorand diff --git a/src/Algorand/AssetTransfer.h b/src/Algorand/AssetTransfer.h index cdc5bab9c2b..63b4b51fc86 100644 --- a/src/Algorand/AssetTransfer.h +++ b/src/Algorand/AssetTransfer.h @@ -4,9 +4,11 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#pragma once + #include "Address.h" #include "BaseTransaction.h" -#include "../Data.h" +#include "Data.h" #include "../proto/Algorand.pb.h" namespace TW::Algorand { diff --git a/src/Algorand/BaseTransaction.h b/src/Algorand/BaseTransaction.h index 1db984c5a3d..232c5ed2b00 100644 --- a/src/Algorand/BaseTransaction.h +++ b/src/Algorand/BaseTransaction.h @@ -12,7 +12,8 @@ namespace TW::Algorand { class BaseTransaction { - public: +public: + virtual ~BaseTransaction() noexcept = default; virtual Data serialize() const = 0; virtual Data serialize(const Data& signature) const { /* Algorand transaction and signature are encoded with msgpack: diff --git a/src/Algorand/BinaryCoding.h b/src/Algorand/BinaryCoding.h index a05ba376981..ee4220d46b3 100644 --- a/src/Algorand/BinaryCoding.h +++ b/src/Algorand/BinaryCoding.h @@ -11,10 +11,8 @@ namespace TW::Algorand { -#ifndef _MSC_VER #pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" #pragma GCC diagnostic ignored "-Wunused-function" -#endif static inline void encodeString(std::string string, Data& data) { // encode string header diff --git a/src/Algorand/Entry.cpp b/src/Algorand/Entry.cpp index 29f9809955f..2f090b1ca5b 100644 --- a/src/Algorand/Entry.cpp +++ b/src/Algorand/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,23 +9,24 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Algorand; -using namespace std; +namespace TW::Algorand { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +std::string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +} // namespace TW::Algorand diff --git a/src/Algorand/Entry.h b/src/Algorand/Entry.h index df752e2e42c..a2e2548839b 100644 --- a/src/Algorand/Entry.h +++ b/src/Algorand/Entry.h @@ -12,14 +12,13 @@ namespace TW::Algorand { /// Entry point for implementation of Algorand coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeAlgorand}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Algorand diff --git a/src/Algorand/OptInAssetTransaction.cpp b/src/Algorand/OptInAssetTransaction.cpp index f83df502f78..e62488d48e1 100644 --- a/src/Algorand/OptInAssetTransaction.cpp +++ b/src/Algorand/OptInAssetTransaction.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,8 +7,7 @@ #include "OptInAssetTransaction.h" #include "BinaryCoding.h" -using namespace TW; -using namespace TW::Algorand; +namespace TW::Algorand { Data OptInAssetTransaction::serialize() const { Data data; @@ -56,3 +55,5 @@ Data OptInAssetTransaction::serialize() const { return data; } + +} // namespace TW::Algorand diff --git a/src/Algorand/OptInAssetTransaction.h b/src/Algorand/OptInAssetTransaction.h index 2f9b7c55394..49d2a219213 100644 --- a/src/Algorand/OptInAssetTransaction.h +++ b/src/Algorand/OptInAssetTransaction.h @@ -4,9 +4,13 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#pragma once + +#include + #include "Address.h" #include "BaseTransaction.h" -#include "../Data.h" +#include "Data.h" #include "../proto/Algorand.pb.h" namespace TW::Algorand { @@ -29,7 +33,7 @@ class OptInAssetTransaction: public BaseTransaction { : address(address), fee(fee) , assetId(assetId), firstRound(firstRound) , lastRound(lastRound), note(note) - , type(type), genesisId(genesisId) + , type(std::move(type)), genesisId(genesisId) , genesisHash(genesisHash) {} public: diff --git a/src/Algorand/Signer.cpp b/src/Algorand/Signer.cpp index f979b24fdf9..cfb615def50 100644 --- a/src/Algorand/Signer.cpp +++ b/src/Algorand/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,14 +11,13 @@ #include -using namespace TW; -using namespace TW::Algorand; +namespace TW::Algorand { const Data TRANSACTION_TAG = {84, 88}; const std::string TRANSACTION_PAY = "pay"; const std::string ASSET_TRANSACTION = "axfer"; -Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto protoOutput = Proto::SigningOutput(); auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); auto pubkey = key.getPublicKey(TWPublicKeyTypeED25519); @@ -35,7 +34,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { auto to = Address(message.to_address()); auto transaction = Transfer(from, to, fee, message.amount(), firstRound, - lastRound, note, TRANSACTION_PAY, genesisId, genesisHash); + lastRound, note, TRANSACTION_PAY, genesisId, genesisHash); auto signature = sign(key, transaction); auto serialized = transaction.BaseTransaction::serialize(signature); protoOutput.set_encoded(serialized.data(), serialized.size()); @@ -45,8 +44,8 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { auto transaction = AssetTransfer(from, to, fee, message.amount(), - message.asset_id(), firstRound, lastRound, note, - ASSET_TRANSACTION,genesisId, genesisHash); + message.asset_id(), firstRound, lastRound, note, + ASSET_TRANSACTION, genesisId, genesisHash); auto signature = sign(key, transaction); auto serialized = transaction.BaseTransaction::serialize(signature); protoOutput.set_encoded(serialized.data(), serialized.size()); @@ -55,12 +54,12 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { auto transaction = OptInAssetTransaction(from, fee, message.asset_id(), firstRound, lastRound, note, - ASSET_TRANSACTION,genesisId, genesisHash); + ASSET_TRANSACTION, genesisId, genesisHash); auto signature = sign(key, transaction); auto serialized = transaction.BaseTransaction::serialize(signature); protoOutput.set_encoded(serialized.data(), serialized.size()); } - + return protoOutput; } @@ -76,5 +75,7 @@ Data Signer::sign(const PrivateKey& privateKey, const BaseTransaction& transacti append(data, TRANSACTION_TAG); append(data, transaction.serialize()); auto signature = privateKey.sign(data, TWCurveED25519); - return Data(signature.begin(), signature.end()); + return {signature.begin(), signature.end()}; } + +} // namespace TW::Algorand diff --git a/src/Algorand/Signer.h b/src/Algorand/Signer.h index 7942022585c..4f8ef6a2730 100644 --- a/src/Algorand/Signer.h +++ b/src/Algorand/Signer.h @@ -10,7 +10,7 @@ #include "OptInAssetTransaction.h" #include "Transfer.h" -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" namespace TW::Algorand { diff --git a/src/Algorand/Transfer.cpp b/src/Algorand/Transfer.cpp index b4db29f5d3f..dd470ddae22 100644 --- a/src/Algorand/Transfer.cpp +++ b/src/Algorand/Transfer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,10 +6,8 @@ #include "Transfer.h" #include "BinaryCoding.h" -#include "../HexCoding.h" -using namespace TW; -using namespace TW::Algorand; +namespace TW::Algorand { Data Transfer::serialize() const { /* Algorand transaction is encoded with msgpack @@ -34,11 +32,17 @@ Data Transfer::serialize() const { // note is optional size += 1; } + // don't encode 0 amount + if (amount == 0) { + size -= 1; + } data.push_back(0x80 + size); // encode fields one by one (sorted by name) - encodeString("amt", data); - encodeNumber(amount, data); + if (amount > 0) { + encodeString("amt", data); + encodeNumber(amount, data); + } encodeString("fee", data); encodeNumber(fee, data); @@ -70,3 +74,5 @@ Data Transfer::serialize() const { encodeString(type, data); return data; } + +} // namespace TW::Algorand diff --git a/src/Algorand/Transfer.h b/src/Algorand/Transfer.h index 29b4f6169e5..422ae4d2e4d 100644 --- a/src/Algorand/Transfer.h +++ b/src/Algorand/Transfer.h @@ -6,9 +6,11 @@ #pragma once +#include + #include "Address.h" #include "BaseTransaction.h" -#include "../Data.h" +#include "Data.h" #include "../proto/Algorand.pb.h" namespace TW::Algorand { @@ -32,7 +34,7 @@ class Transfer : public BaseTransaction { : from(from) , to(to) , fee(fee), amount(amount) , firstRound(firstRound), lastRound(lastRound) - , note(note), type(type) + , note(note), type(std::move(type)) , genesisId(genesisIdg), genesisHash(genesisHash) {} public: diff --git a/src/AnyAddress.cpp b/src/AnyAddress.cpp new file mode 100644 index 00000000000..9bb265cc041 --- /dev/null +++ b/src/AnyAddress.cpp @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "AnyAddress.h" + +#include +#include "Coin.h" + +namespace TW { + +Data AnyAddress::getData() const { + return TW::addressToData(coin, address); +} + +AnyAddress* AnyAddress::createAddress(const std::string& address, enum TWCoinType coin, const std::string& hrp) { + auto normalized = TW::normalizeAddress(coin, address, hrp); + if (normalized.empty()) { + return nullptr; + } + + return new AnyAddress{.address = std::move(normalized), .coin = coin}; +} + +AnyAddress* AnyAddress::createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp) { + auto derivedAddress = TW::deriveAddress(coin, publicKey, TWDerivationDefault, hrp); + return new AnyAddress{.address = std::move(derivedAddress), .coin = coin}; +} + +} // namespace TW diff --git a/src/AnyAddress.h b/src/AnyAddress.h new file mode 100644 index 00000000000..7bf773eaf07 --- /dev/null +++ b/src/AnyAddress.h @@ -0,0 +1,41 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" +#include "PublicKey.h" + +#include +#include +#include +#include + +namespace TW { + +class AnyAddress { +public: + std::string address; + + enum TWCoinType coin; + + static AnyAddress* createAddress(const std::string& address, enum TWCoinType coin, const std::string& hrp = ""); + static AnyAddress* createAddress(const PublicKey& publicKey, enum TWCoinType coin, const std::string& hrp = ""); + + Data getData() const; +}; + +inline bool operator==(const AnyAddress& lhs, const AnyAddress& rhs) { + return lhs.address == rhs.address && lhs.coin == rhs.coin; +} + +} // namespace TW + +/// Wrapper for C interface. +struct TWAnyAddress { + // Pointer to the underlying implementation + TW::AnyAddress* impl; +}; diff --git a/src/Aptos/Address.cpp b/src/Aptos/Address.cpp new file mode 100644 index 00000000000..30c1b587872 --- /dev/null +++ b/src/Aptos/Address.cpp @@ -0,0 +1,88 @@ +// Copyright © 2017-2022 Trust Wallet. +// Author: Clement Doumergue +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Address.h" +#include "HexCoding.h" + +namespace { + +std::string normalize(const std::string& string, std::size_t hexLen) { + std::string hexStr((TW::Aptos::Address::size * 2) - hexLen, '0'); + hexStr.append(string); + return hexStr; +} + +} // namespace + +namespace TW::Aptos { + +bool Address::isValid(const std::string& string) { + auto address = string; + if (address.starts_with("0x")) { + address = address.substr(2); + if (std::size_t hexLen = address.size(); hexLen < Address::size * 2) { + address = normalize(address, hexLen); + } + } + if (address.size() != 2 * Address::size) { + return false; + } + const auto data = parse_hex(address); + return isValid(data); +} + +Address::Address(const std::string& string) { + if (!isValid(string)) { + throw std::invalid_argument("Invalid address string"); + } + auto hexFunctor = [&string]() { + if (std::size_t hexLen = string.size() - 2; string.starts_with("0x") && hexLen < Address::size * 2) { + //! We have specific address like 0x1, padding it. + return parse_hex(normalize(string.substr(2), hexLen)); + } else { + return parse_hex(string); + } + }; + + const auto data = hexFunctor(); + std::copy(data.begin(), data.end(), bytes.begin()); +} + +Address::Address(const Data& data) { + if (!isValid(data)) { + throw std::invalid_argument("Invalid address data"); + } + std::copy(data.begin(), data.end(), bytes.begin()); +} + +Address::Address(const PublicKey& publicKey) { + if (publicKey.type != TWPublicKeyTypeED25519) { + throw std::invalid_argument("Invalid public key type"); + } + auto key_data = publicKey.bytes; + append(key_data, 0x00); + const auto data = Hash::sha3_256(key_data); + std::copy(data.begin(), data.end(), bytes.begin()); +} + +std::string Address::string(bool withPrefix) const { + std::string output = withPrefix ? "0x" : ""; + return output + hex(bytes); +} + +std::string Address::shortString() const { + std::string s = hex(bytes); + s.erase(0, s.find_first_not_of('0')); + return s; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, Address addr) noexcept { + stream.add_bytes(addr.bytes.begin(), addr.bytes.end()); + return stream; +} + +} // namespace TW::Aptos diff --git a/src/Aptos/Address.h b/src/Aptos/Address.h new file mode 100644 index 00000000000..45fe27aa011 --- /dev/null +++ b/src/Aptos/Address.h @@ -0,0 +1,59 @@ +// Copyright © 2017-2022 Trust Wallet. +// Author: Clement Doumergue +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "BCS.h" +#include "Data.h" +#include "../PublicKey.h" + +#include + +namespace TW::Aptos { + +class Address { +public: + static constexpr size_t size = 32; + + std::array bytes; + + /// Determines whether a collection of bytes makes a valid address. + static bool isValid(const Data& data) { return data.size() == size; } + + /// Determines whether a string makes a valid address. + static bool isValid(const std::string& string); + + /// Initializes an Aptos address with a string representation. + explicit Address(const std::string& string); + + /// Initializes an Aptos address with a collection of bytes + explicit Address(const Data& data); + + /// Initializes an Aptos address with a public key. + explicit Address(const PublicKey& publicKey); + + /// Constructor that allow factory programming; + Address() noexcept = default; + + /// Returns a string representation of the address. + [[nodiscard]] std::string string(bool withPrefix = true) const; + + /// Returns a short string representation of the address. E.G 0x1; + [[nodiscard]] std::string shortString() const; +}; + +constexpr inline bool operator==(const Address& lhs, const Address& rhs) noexcept { + return lhs.bytes == rhs.bytes; +} + +inline const Address gAddressZero = Address("0x0"); +inline const Address gAddressOne = Address("0x1"); +inline const Address gAddressThree = Address("0x3"); + +BCS::Serializer& operator<<(BCS::Serializer& stream, Address) noexcept; + +} // namespace TW::Aptos diff --git a/src/Aptos/Entry.cpp b/src/Aptos/Entry.cpp new file mode 100644 index 00000000000..4c299f946a2 --- /dev/null +++ b/src/Aptos/Entry.cpp @@ -0,0 +1,26 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Entry.h" + +#include "Address.h" +#include "Signer.h" + +namespace TW::Aptos { + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { + return Address::isValid(address); +} + +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { + return Address(publicKey).string(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +} // namespace TW::Aptos diff --git a/src/Aptos/Entry.h b/src/Aptos/Entry.h new file mode 100644 index 00000000000..0fd1ab1d008 --- /dev/null +++ b/src/Aptos/Entry.h @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../CoinEntry.h" + +namespace TW::Aptos { + +/// Entry point for implementation of Aptos coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry final : public CoinEntry { +public: + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; +}; + +} // namespace TW::Aptos diff --git a/src/Aptos/MoveTypes.cpp b/src/Aptos/MoveTypes.cpp new file mode 100644 index 00000000000..4df1a932c5f --- /dev/null +++ b/src/Aptos/MoveTypes.cpp @@ -0,0 +1,152 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include + +namespace TW::Aptos { + +Aptos::ModuleId::ModuleId(Address accountAddress, Identifier name) noexcept + : mAccountAddress(accountAddress), mName(std::move(name)) { +} + +Data ModuleId::accessVector() const noexcept { + BCS::Serializer serializer; + serializer << static_cast(gCodeTag) << mAccountAddress << mName; + return serializer.bytes; +} + +std::string ModuleId::string() const noexcept { + std::stringstream ss; + ss << mAccountAddress.string() << "::" << mName; + return ss.str(); +} + +std::string ModuleId::shortString() const noexcept { + std::stringstream ss; + ss << "0x" << mAccountAddress.shortString() << "::" << mName; + return ss.str(); +} + +#ifdef _MSC_VER +#pragma optimize( "", off ) +#endif +Data StructTag::serialize(bool withResourceTag) const noexcept { + BCS::Serializer serializer; + if (withResourceTag) + { + serializer << gResourceTag; + } + serializer << mAccountAddress << mModule << mName << mTypeParams; + return serializer.bytes; +} +#ifdef _MSC_VER +#pragma optimize("", on) +#endif + +StructTag::StructTag(Address accountAddress, Identifier module, Identifier name, std::vector typeParams) noexcept + : mAccountAddress(accountAddress), mModule(std::move(module)), mName(std::move(name)), mTypeParams(std::move(typeParams)) { +} +std::string StructTag::string() const noexcept { + std::stringstream ss; + ss << "0x" << mAccountAddress.shortString() << "::" << mModule << "::" << mName; + if (!mTypeParams.empty()) { + ss << "<"; + ss << TypeTagToString(*mTypeParams.begin()); + std::for_each(begin(mTypeParams) + 1, end(mTypeParams), [&ss](auto&& cur) { + ss << ", " << TypeTagToString(cur); + }); + ss << ">"; + } + return ss.str(); +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, Bool) noexcept { + stream << Bool::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, U8) noexcept { + stream << U8::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, U64) noexcept { + stream << U64::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, U128) noexcept { + stream << U128::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, TAddress) noexcept { + stream << TAddress::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept { + stream << TSigner::value; + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const StructTag& st) noexcept { + auto res = st.serialize(); + stream.add_bytes(begin(res), end(res)); + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& st) noexcept { + stream << TStructTag::value; + auto res = st.st.serialize(false); + stream.add_bytes(begin(res), end(res)); + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept { + stream << Vector::value; + for (auto&& cur: t.tags) { + stream << cur; + } + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept { + std::visit([&stream](auto&& arg) { stream << arg; }, t.tags); + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept { + stream << module.address() << module.name(); + return stream; +} +std::string TypeTagToString(const TypeTag& typeTag) noexcept { + auto visit_functor = [](const TypeTag::TypeTagVariant& value) -> std::string { + if (std::holds_alternative(value)) { + return "bool"; + } else if (std::holds_alternative(value)) { + return "u8"; + } else if (std::holds_alternative(value)) { + return "u64"; + } else if (std::holds_alternative(value)) { + return "u128"; + } else if (std::holds_alternative(value)) { + return "address"; + } else if (std::holds_alternative(value)) { + return "signer"; + } else if (auto* vectorData = std::get_if(&value); vectorData != nullptr && !vectorData->tags.empty()) { + std::stringstream ss; + ss << "vector<" << TypeTagToString(*vectorData->tags.begin()) << ">"; + return ss.str(); + } else if (auto* structData = std::get_if(&value); structData) { + return structData->string(); + } else if (auto* tStructData = std::get_if(&value); tStructData) { + return tStructData->st.string(); + } else { + return ""; + } + }; + + return std::visit(visit_functor, typeTag.tags); +} + +} // namespace TW::Aptos diff --git a/src/Aptos/MoveTypes.h b/src/Aptos/MoveTypes.h new file mode 100644 index 00000000000..c4360afcd83 --- /dev/null +++ b/src/Aptos/MoveTypes.h @@ -0,0 +1,107 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Aptos/Address.h" +#include "BCS.h" +#include + +namespace TW::Aptos { + +constexpr std::uint8_t gCodeTag{0}; +constexpr std::uint8_t gResourceTag{1}; +using Identifier = std::string; + +class ModuleId { +public: + ///< Constructor + ModuleId(Address accountAddress, Identifier name) noexcept; + + ///< Getters + [[nodiscard]] const std::string& name() const noexcept { return mName; } + [[nodiscard]] const Address& address() const noexcept { return mAccountAddress; } + [[nodiscard]] Data accessVector() const noexcept; + [[nodiscard]] std::string string() const noexcept; + [[nodiscard]] std::string shortString() const noexcept; + +private: + Address mAccountAddress; + Identifier mName; +}; + +inline ModuleId gAptosAccountModule{gAddressOne, "aptos_account"}; +inline ModuleId gAptosCoinModule{gAddressOne, "coin"}; +inline ModuleId gAptosManagedCoinsModule{gAddressOne, "managed_coin"}; +inline ModuleId gAptosTokenTransfersModule{gAddressThree, "token_transfers"}; + +BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept; + +struct TypeTag; + +struct Bool { + static constexpr std::uint8_t value = 0; +}; +struct U8 { + static constexpr std::uint8_t value = 1; +}; +struct U64 { + static constexpr std::uint8_t value = 2; +}; +struct U128 { + static constexpr std::uint8_t value = 3; +}; +struct TAddress { + static constexpr std::uint8_t value = 4; +}; +struct TSigner { + static constexpr std::uint8_t value = 5; +}; +struct Vector { + static constexpr std::uint8_t value = 6; + std::vector tags; +}; + +class StructTag { +public: + explicit StructTag(Address accountAddress, Identifier module, Identifier name, std::vector typeParams) noexcept; + [[nodiscard]] Data serialize(bool withResourceTag = true) const noexcept; + [[nodiscard]] ModuleId moduleID() const noexcept { return {mAccountAddress, mName}; }; + [[nodiscard]] std::string string() const noexcept; + +private: + Address mAccountAddress; + Identifier mModule; + Identifier mName; + std::vector mTypeParams; +}; + +// C++ limitation, the first StructTag will serialize with ResourceTag, the inner one will use the value 7 instead. Tweaking by wrapping the struct +struct TStructTag { + static constexpr std::uint8_t value = 7; + StructTag st; +}; + +struct TypeTag { + using TypeTagVariant = std::variant; + TypeTagVariant tags; +}; + +std::string TypeTagToString(const TypeTag& typeTag) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const StructTag& st) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, Bool) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, U8) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, U64) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, U128) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, TAddress) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& t) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept; +static const TypeTag gTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(gAddressOne, "aptos_coin", "AptosCoin", {})})}; +static const TypeTag gOfferNftTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(gAddressThree, "token_transfers", "offer_script", {})})}; + +} // namespace TW::Aptos diff --git a/src/Aptos/Signer.cpp b/src/Aptos/Signer.cpp new file mode 100644 index 00000000000..9c15ea200af --- /dev/null +++ b/src/Aptos/Signer.cpp @@ -0,0 +1,208 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Signer.h" +#include "Address.h" +#include "Hash.h" +#include "MoveTypes.h" +#include "TransactionBuilder.h" +#include "TransactionPayload.h" + +namespace { +template +void serializeToArgs(std::vector& args, T&& toSerialize) { + TW::BCS::Serializer serializer; + serializer << std::forward(toSerialize); + args.emplace_back(serializer.bytes); +} +} // namespace + +namespace TW::Aptos { + +template +std::pair, nlohmann::json> commonTransferPayload(const TPayload& input) { + std::vector args; + serializeToArgs(args, Address(input.to())); + serializeToArgs(args, input.amount()); + nlohmann::json argsJson = nlohmann::json::array({input.to(), std::to_string(input.amount())}); + return std::make_pair(args, argsJson); +} + +TransactionPayload transferPayload(const Proto::SigningInput& input) { + auto&& [args, argsJson] = commonTransferPayload(input.transfer()); + TransactionPayload payload = EntryFunction(gAptosAccountModule, "transfer", {}, args, argsJson); + return payload; +} + +TransactionPayload createAccountPayload(const Proto::SigningInput& input) { + std::vector args; + serializeToArgs(args, Address(input.create_account().auth_key())); + nlohmann::json argsJson = nlohmann::json::array({input.create_account().auth_key()}); + TransactionPayload payload = EntryFunction(gAptosAccountModule, "create_account", {}, args, argsJson); + return payload; +} + +TransactionPayload claimNftPayload(const Proto::ClaimNftMessage& msg) { + std::vector args; + serializeToArgs(args, Address(msg.sender())); + serializeToArgs(args, Address(msg.creator())); + serializeToArgs(args, msg.collectionname()); + serializeToArgs(args, msg.name()); + serializeToArgs(args, msg.property_version()); + // clang-format off + nlohmann::json argsJson = nlohmann::json::array( + { + msg.sender(), + msg.creator(), + msg.collectionname(), + msg.name(), + std::to_string(msg.property_version()), + }); + // clang-format on + TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "claim_script", {}, args, argsJson); + return payload; +} + +TransactionPayload nftOfferPayload(const Proto::OfferNftMessage& msg) { + std::vector args; + serializeToArgs(args, Address(msg.receiver())); + serializeToArgs(args, Address(msg.creator())); + serializeToArgs(args, msg.collectionname()); + serializeToArgs(args, msg.name()); + serializeToArgs(args, msg.property_version()); + serializeToArgs(args, msg.amount()); + // clang-format off + nlohmann::json argsJson = nlohmann::json::array( + { + msg.receiver(), + msg.creator(), + msg.collectionname(), + msg.name(), + std::to_string(msg.property_version()), + std::to_string(msg.amount()) + }); + // clang-format on + TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "offer_script", {}, args, argsJson); + return payload; +} + +TransactionPayload cancelNftOfferPayload(const Proto::CancelOfferNftMessage& msg) { + std::vector args; + serializeToArgs(args, Address(msg.receiver())); + serializeToArgs(args, Address(msg.creator())); + serializeToArgs(args, msg.collectionname()); + serializeToArgs(args, msg.name()); + serializeToArgs(args, msg.property_version()); + // clang-format off + nlohmann::json argsJson = nlohmann::json::array( + { + msg.receiver(), + msg.creator(), + msg.collectionname(), + msg.name(), + std::to_string(msg.property_version()), + }); + // clang-format on + TransactionPayload payload = EntryFunction(gAptosTokenTransfersModule, "cancel_offer_script", {}, args, argsJson); + return payload; +} + +TransactionPayload tokenTransferPayload(const Proto::SigningInput& input) { + + auto&& [args, argsJson] = commonTransferPayload(input.token_transfer()); + auto& function = input.token_transfer().function(); + TypeTag tokenTransferTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), + function.module(), function.name(), {})})}; + TransactionPayload payload = EntryFunction(gAptosCoinModule, "transfer", {tokenTransferTag}, args, argsJson); + return payload; +} + +TransactionPayload registerTokenPayload(const Proto::SigningInput& input) { + + auto& function = input.register_token().function(); + TypeTag tokenRegisterTag = {TypeTag::TypeTagVariant(TStructTag{.st = StructTag(Address(function.account_address()), + function.module(), function.name(), {})})}; + TransactionPayload payload = EntryFunction(gAptosManagedCoinsModule, "register", {tokenRegisterTag}, {}); + return payload; +} + +Proto::SigningOutput blindSign(const Proto::SigningInput& input) { + auto output = Proto::SigningOutput(); + BCS::Serializer serializer; + auto encodedCall = parse_hex(input.any_encoded()); + serializer.add_bytes(begin(encodedCall), end(encodedCall)); + auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); + auto signature = privateKey.sign(encodedCall, TWCurveED25519); + auto pubKeyData = privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes; + output.set_raw_txn(encodedCall.data(), encodedCall.size()); + output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); + output.mutable_authenticator()->set_signature(signature.data(), signature.size()); + serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; + output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); + + // clang-format off + nlohmann::json json = { + {"type", "ed25519_signature"}, + {"public_key", hexEncoded(pubKeyData)}, + {"signature", hexEncoded(signature)} + }; + // clang-format on + + output.set_json(json.dump()); + return output; +} + +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) { + auto protoOutput = Proto::SigningOutput(); + if (!input.any_encoded().empty()) { + return blindSign(input); + } + auto nftPayloadFunctor = [](const Proto::NftMessage& nftMessage) { + switch (nftMessage.nft_transaction_payload_case()) { + case Proto::NftMessage::kOfferNft: + return nftOfferPayload(nftMessage.offer_nft()); + case Proto::NftMessage::kCancelOfferNft: + return cancelNftOfferPayload(nftMessage.cancel_offer_nft()); + case Proto::NftMessage::kClaimNft: + return claimNftPayload(nftMessage.claim_nft()); + case Proto::NftMessage::NFT_TRANSACTION_PAYLOAD_NOT_SET: + throw std::runtime_error("Nft message payload not set"); + } + }; + auto payloadFunctor = [&input, &nftPayloadFunctor]() { + switch (input.transaction_payload_case()) { + case Proto::SigningInput::kTransfer: { + return transferPayload(input); + } + case Proto::SigningInput::kTokenTransfer: { + return tokenTransferPayload(input); + } + case Proto::SigningInput::kNftMessage: { + return nftPayloadFunctor(input.nft_message()); + } + case Proto::SigningInput::kCreateAccount: { + return createAccountPayload(input); + } + case Proto::SigningInput::kRegisterToken: { + return registerTokenPayload(input); + } + case Proto::SigningInput::TRANSACTION_PAYLOAD_NOT_SET: + throw std::runtime_error("Transaction payload should be set"); + } + }; + TransactionBuilder::builder() + .sender(Address(input.sender())) + .sequenceNumber(input.sequence_number()) + .payload(payloadFunctor()) + .maxGasAmount(input.max_gas_amount()) + .gasUnitPrice(input.gas_unit_price()) + .expirationTimestampSecs(input.expiration_timestamp_secs()) + .chainId(static_cast(input.chain_id())) + .sign(input, protoOutput); + return protoOutput; +} + +} // namespace TW::Aptos diff --git a/src/Aptos/Signer.h b/src/Aptos/Signer.h new file mode 100644 index 00000000000..ad1f09ffdd8 --- /dev/null +++ b/src/Aptos/Signer.h @@ -0,0 +1,27 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" +#include "../PrivateKey.h" +#include "../proto/Aptos.pb.h" + +namespace TW::Aptos { + +inline const Data gAptosSalt = data("APTOS::RawTransaction"); + +/// Helper class that performs Aptos transaction signing. +class Signer { +public: + /// Hide default constructor + Signer() = delete; + + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input); +}; + +} // namespace TW::Aptos diff --git a/src/Aptos/TransactionBuilder.h b/src/Aptos/TransactionBuilder.h new file mode 100644 index 00000000000..20f37a591ff --- /dev/null +++ b/src/Aptos/TransactionBuilder.h @@ -0,0 +1,100 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "HexCoding.h" +#include "TransactionPayload.h" +#include + +namespace TW::Aptos { + +class TransactionBuilder { +public: + TransactionBuilder() noexcept = default; + + static TransactionBuilder builder() noexcept { return {}; } + + TransactionBuilder& sender(Address sender) noexcept { + mSender = sender; + return *this; + } + + TransactionBuilder& sequenceNumber(std::uint64_t sequenceNumber) noexcept { + mSequenceNumber = sequenceNumber; + return *this; + } + + TransactionBuilder& payload(TransactionPayload payload) noexcept { + mPayload = std::move(payload); + return *this; + } + + TransactionBuilder& maxGasAmount(std::uint64_t maxGasAmount) noexcept { + mMaxGasAmount = maxGasAmount; + return *this; + } + + TransactionBuilder& gasUnitPrice(std::uint64_t gasUnitPrice) noexcept { + mGasUnitPrice = gasUnitPrice; + return *this; + } + + TransactionBuilder& expirationTimestampSecs(std::uint64_t expirationTimestampSecs) noexcept { + mExpirationTimestampSecs = expirationTimestampSecs; + return *this; + } + + TransactionBuilder& chainId(std::uint8_t chainId) noexcept { + mChainId = chainId; + return *this; + } + + TransactionBuilder& sign(const Proto::SigningInput& input, Proto::SigningOutput& output) noexcept { + BCS::Serializer serializer; + serializer << mSender << mSequenceNumber << mPayload << mMaxGasAmount << mGasUnitPrice << mExpirationTimestampSecs << mChainId; + auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); + output.set_raw_txn(serializer.bytes.data(), serializer.bytes.size()); + auto msgToSign = TW::Hash::sha3_256(gAptosSalt.data(), gAptosSalt.size()); + append(msgToSign, serializer.bytes); + auto signature = privateKey.sign(msgToSign, TWCurveED25519); + auto pubKeyData = privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes; + output.mutable_authenticator()->set_public_key(pubKeyData.data(), pubKeyData.size()); + output.mutable_authenticator()->set_signature(signature.data(), signature.size()); + serializer << BCS::uleb128{.value = 0} << pubKeyData << signature; + output.set_encoded(serializer.bytes.data(), serializer.bytes.size()); + + // https://fullnode.devnet.aptoslabs.com/v1/spec#/operations/submit_transaction + // clang-format off + nlohmann::json json = { + {"sender", mSender.string()}, + {"sequence_number", std::to_string(mSequenceNumber)}, + {"max_gas_amount", std::to_string(mMaxGasAmount)}, + {"gas_unit_price", std::to_string(mGasUnitPrice)}, + {"expiration_timestamp_secs", std::to_string(mExpirationTimestampSecs)}, + {"payload", payloadToJson(mPayload)}, + {"signature", { + {"type", "ed25519_signature"}, + {"public_key", hexEncoded(pubKeyData)}, + {"signature", hexEncoded(signature)}} + } + }; + // clang-format on + output.set_json(json.dump()); + return *this; + } + +private: + Address mSender{}; + std::uint64_t mSequenceNumber{}; + TransactionPayload mPayload{}; + std::uint64_t mMaxGasAmount{}; + std::uint64_t mGasUnitPrice{}; + std::uint64_t mExpirationTimestampSecs{}; + std::uint8_t mChainId{}; +}; + +} // namespace TW::Aptos diff --git a/src/Aptos/TransactionPayload.cpp b/src/Aptos/TransactionPayload.cpp new file mode 100644 index 00000000000..1e5b188e290 --- /dev/null +++ b/src/Aptos/TransactionPayload.cpp @@ -0,0 +1,57 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include + +namespace TW::Aptos { + +EntryFunction::EntryFunction(ModuleId module, Identifier function, std::vector tyArgs, std::vector args, nlohmann::json jsonArgs) noexcept + : mModule(std::move(module)), mFunction(std::move(function)), mTyArgs(std::move(tyArgs)), mArgs(std::move(args)), mJsonArgs(std::move(jsonArgs)) { +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const EntryFunction& entryFunction) noexcept { + stream << entryFunction.module() << entryFunction.function() << entryFunction.tyArgs() << entryFunction.args(); + return stream; +} + +nlohmann::json payloadToJson(const TransactionPayload& payload) { + auto visit_functor = [](const TransactionPayload& value) -> nlohmann::json { + if (auto* entryFunction = std::get_if(&value); entryFunction) { + return entryFunction->json(); + } else { + return {}; + } + }; + + return std::visit(visit_functor, payload); +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, [[maybe_unused]] const Script& script) noexcept { + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, [[maybe_unused]] const ModuleBundle& moduleBundle) noexcept { + return stream; +} + +nlohmann::json EntryFunction::json() const noexcept { + nlohmann::json tyArgsJson = nlohmann::json::array(); + for (auto&& cur : mTyArgs) { + tyArgsJson.emplace_back(TypeTagToString(cur)); + } + // clang-format off + nlohmann::json out = { + {"type", "entry_function_payload"}, + {"function", mModule.shortString() + "::" + mFunction}, + {"type_arguments", tyArgsJson}, + {"arguments", mJsonArgs.empty() ? nlohmann::json::array() : mJsonArgs} + }; + // clang-format on + return out; +} + +} // namespace TW::Aptos diff --git a/src/Aptos/TransactionPayload.h b/src/Aptos/TransactionPayload.h new file mode 100644 index 00000000000..e52124f84ff --- /dev/null +++ b/src/Aptos/TransactionPayload.h @@ -0,0 +1,46 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include +#include + +namespace TW::Aptos { + +/// Call a Move entry function. +class EntryFunction { +public: + explicit EntryFunction(ModuleId module, Identifier function, std::vector tyArgs, std::vector args, nlohmann::json jsonArgs = {}) noexcept; + [[nodiscard]] const ModuleId& module() const noexcept { return mModule; } + [[nodiscard]] const Identifier& function() const noexcept { return mFunction; } + [[nodiscard]] const std::vector& tyArgs() const noexcept { return mTyArgs; } + [[nodiscard]] const std::vector& args() const noexcept { return mArgs; } + [[nodiscard]] nlohmann::json json() const noexcept; + +private: + ModuleId mModule; + Identifier mFunction; + std::vector mTyArgs; + std::vector mArgs; + nlohmann::json mJsonArgs; +}; + + +class Script { +}; + +class ModuleBundle { +}; + +BCS::Serializer& operator<<(BCS::Serializer& stream, const EntryFunction& entryFunction) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const Script& script) noexcept; +BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleBundle& moduleBundle) noexcept; +using TransactionPayload = std::variant; +nlohmann::json payloadToJson(const TransactionPayload& payload); + +} // namespace TW::Aptos diff --git a/src/BCS.cpp b/src/BCS.cpp new file mode 100644 index 00000000000..1959ba83079 --- /dev/null +++ b/src/BCS.cpp @@ -0,0 +1,42 @@ +// Copyright © 2017-2022 Trust Wallet. +// Created by Clément Doumergue + +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "BCS.h" + +namespace TW::BCS { + +Serializer& operator<<(Serializer& stream, std::byte b) noexcept { + stream.add_byte(b); + return stream; +} + +Serializer& operator<<(Serializer& stream, uleb128 t) noexcept { + integral auto value = t.value; + + while (value >= 0x80) { + // Add the 7 lowest bits of data and set highest bit to 1 + stream << static_cast((value & 0x7f) | 0x80); + value >>= 7; + } + + // Add the remaining bits of data (highest bit is already 0 at this point) + stream << static_cast(value); + return stream; +} + +Serializer& operator<<(Serializer& stream, std::string_view sv) noexcept { + stream << uleb128{static_cast(sv.size())}; + stream.add_bytes(sv.begin(), sv.end()); + return stream; +} + +Serializer& operator<<(Serializer& stream, std::nullopt_t) noexcept { + stream << false; + return stream; +} + +} // namespace TW::BCS diff --git a/src/BCS.h b/src/BCS.h new file mode 100644 index 00000000000..5d795089649 --- /dev/null +++ b/src/BCS.h @@ -0,0 +1,222 @@ +// Copyright © 2017-2022 Trust Wallet. +// Created by Clément Doumergue + +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "Data.h" +#include "concepts/tw_concepts.h" + +namespace TW::BCS { + +/// Implementation of BCS encoding (as specified by the Diem project, see github.com/diem/bcs#detailed-specifications) + +struct Serializer { + Data bytes; + + void add_byte(std::byte b) noexcept { + bytes.emplace_back(static_cast(b)); + } + + template + void add_bytes(Iterator first, Iterator last) noexcept { + std::transform(first, last, std::back_inserter(bytes), [](auto&& c) { + return static_cast(c); + }); + } + + void clear() noexcept { + bytes.clear(); + } +}; + +struct uleb128 { + uint32_t value; +}; + +namespace details { + +template +concept aggregate_struct = std::is_class_v> && std::is_aggregate_v>; + +template +concept map_container = requires(T t) { + typename T::key_type; + typename T::mapped_type; + { std::declval().size() } -> std::same_as; + }; + +template + requires integral || + floating_point || + std::same_as || + std::same_as || + std::same_as || + aggregate_struct || + map_container +struct is_serializable { + static constexpr auto value = true; +}; + +template +struct is_serializable> { + static constexpr auto value = is_serializable::value; +}; + +template +struct is_serializable> { + static constexpr auto value = (is_serializable::value && ...); +}; + +template +struct is_serializable> { + static constexpr auto value = is_serializable::value && is_serializable::value; +}; + +template +struct is_serializable> { + static constexpr auto value = is_serializable::value; +}; + +template +struct is_serializable> { + static constexpr auto value = (is_serializable::value && ...); +}; + +template +Serializer& serialize_integral_impl(Serializer& stream, T t, std::index_sequence) noexcept { + const char* bytes = reinterpret_cast(&t); + // Add each byte in little-endian order + return (stream << ... << static_cast(bytes[Is])); +} + +template +Serializer& serialize_tuple_impl(Serializer& stream, const T& t, std::index_sequence) noexcept { + return (stream << ... << std::get(t)); +} + +template +struct dependent_false { + static constexpr auto value = false; +}; + +template +constexpr auto to_tuple(T&& t) { + if constexpr (std::is_empty_v) { + return std::make_tuple(); + } else if constexpr (requires { [&t] { auto&& [a0] = t; }; }) { + auto&& [a0] = std::forward(t); + return std::make_tuple(a0); + } else if constexpr (requires { [&t] { auto&& [a0, a1] = t; }; }) { + auto&& [a0, a1] = std::forward(t); + return std::make_tuple(a0, a1); + } else if constexpr (requires { [&t] { auto&& [a0, a1, a2] = t; }; }) { + auto&& [a0, a1, a2] = std::forward(t); + return std::make_tuple(a0, a1, a2); + } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3] = t; }; }) { + auto&& [a0, a1, a2, a3] = std::forward(t); + return std::make_tuple(a0, a1, a2, a3); + } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3, a4] = t; }; }) { + auto&& [a0, a1, a2, a3, a4] = std::forward(t); + return std::make_tuple(a0, a1, a2, a3, a4); + } else if constexpr (requires { [&t] { auto&& [a0, a1, a2, a3, a4, a5] = t; }; }) { + auto&& [a0, a1, a2, a3, a4, a5] = std::forward(t); + return std::make_tuple(a0, a1, a2, a3, a4, a5); + } else { + static_assert(dependent_false::value, "the structure has more than 6 members"); + } +} + +template +Serializer& serialize_struct_impl(Serializer& stream, const T& t) noexcept { + return stream << to_tuple(t); +} +} // namespace details + +template +concept PrimitiveSerializable = details::is_serializable::value; + +template +concept CustomSerializable = requires(T t) { + { std::declval() << t } -> std::same_as; + }; + +template +concept Serializable = PrimitiveSerializable || CustomSerializable; + +Serializer& operator<<(Serializer& stream, std::byte b) noexcept; + +template +Serializer& operator<<(Serializer& stream, T t) noexcept { + return details::serialize_integral_impl(stream, t, std::make_index_sequence{}); +} + +Serializer& operator<<(Serializer& stream, uleb128 t) noexcept; + +Serializer& operator<<(Serializer& stream, std::string_view sv) noexcept; + +template +Serializer& operator<<(Serializer& stream, const std::optional o) noexcept { + if (o.has_value()) { + stream << true; + stream << o.value(); + } else { + stream << false; + } + return stream; +} + +Serializer& operator<<(Serializer& stream, std::nullopt_t) noexcept; + +template +Serializer& operator<<(Serializer& stream, const std::tuple& t) noexcept { + return details::serialize_tuple_impl(stream, t, std::make_index_sequence{}); +} + +template +Serializer& operator<<(Serializer& stream, const std::pair& t) noexcept { + return details::serialize_tuple_impl(stream, t, std::make_index_sequence<2>{}); +} + +template +Serializer& operator<<(Serializer& stream, const T& t) noexcept { + return details::serialize_struct_impl(stream, t); +} + +template +Serializer& operator<<(Serializer& stream, const std::vector& t) noexcept { + stream << uleb128{static_cast(t.size())}; + for (auto&& cur: t) { + stream << cur; + } + return stream; +} + +template +Serializer& operator<<(Serializer& stream, const std::variant& t) noexcept { + stream << uleb128{static_cast(t.index())}; + std::visit([&stream](auto&& value) { stream << value; }, t); + return stream; +} + +template +Serializer& operator<<(Serializer& stream, const T& t) noexcept { + stream << uleb128{static_cast(t.size())}; + for (auto&& [k, v] : t) { + stream << std::make_tuple(k, v); + } + return stream; +} + +} // namespace TW::BCS diff --git a/src/Base32.h b/src/Base32.h index 78f9717d5f9..7c9f5e9cb3e 100644 --- a/src/Base32.h +++ b/src/Base32.h @@ -20,11 +20,13 @@ inline bool decode(const std::string& encoded_in, Data& decoded_out, const char* size_t inLen = encoded_in.size(); // obtain output length first size_t outLen = base32_decoded_length(inLen); + //win #ifdef _MSC_VER uint8_t *buf = (uint8_t *)_alloca(outLen); #else uint8_t buf[outLen]; #endif + if (alphabet_in == nullptr) { alphabet_in = BASE32_ALPHABET_RFC4648; } @@ -43,11 +45,12 @@ inline std::string encode(const Data& val, const char* alphabet = nullptr) { size_t inLen = val.size(); // obtain output length first, reserve for terminator size_t outLen = base32_encoded_length(inLen) + 1; + //win #ifdef _MSC_VER char *buf = (char *)_alloca(outLen); #else char buf[outLen]; -#endif +#endif if (alphabet == nullptr) { alphabet = BASE32_ALPHABET_RFC4648; } diff --git a/src/Base58.cpp b/src/Base58.cpp index c83c690dada..61ed8d4600b 100644 --- a/src/Base58.cpp +++ b/src/Base58.cpp @@ -67,7 +67,7 @@ Data Base58::decodeCheck(const char* begin, const char* end, Hash::Hasher hasher } // re-calculate the checksum, ensure it matches the included 4-byte checksum - auto hash = hasher(result.data(), result.size() - 4); + auto hash = Hash::hash(hasher, result.data(), result.size() - 4); if (!std::equal(hash.begin(), hash.begin() + 4, result.end() - 4)) { return {}; } @@ -144,7 +144,7 @@ Data Base58::decode(const char* begin, const char* end) const { std::string Base58::encodeCheck(const byte* begin, const byte* end, Hash::Hasher hasher) const { // add 4-byte hash check to the end Data dataWithCheck(begin, end); - auto hash = hasher(begin, end - begin); + auto hash = Hash::hash(hasher, begin, end - begin); dataWithCheck.insert(dataWithCheck.end(), hash.begin(), hash.begin() + 4); return encode(dataWithCheck); } diff --git a/src/Base58.h b/src/Base58.h index 9bcc2df7aa9..d5e231d307d 100644 --- a/src/Base58.h +++ b/src/Base58.h @@ -34,12 +34,12 @@ class Base58 { : digits(digits), characterMap(characterMap) {} /// Decodes a base 58 string verifying the checksum, returns empty on failure. - Data decodeCheck(const std::string& string, Hash::Hasher hasher = Hash::sha256d) const { + Data decodeCheck(const std::string& string, Hash::Hasher hasher = Hash::HasherSha256d) const { return decodeCheck(string.data(), string.data() + string.size(), hasher); } /// Decodes a base 58 string verifying the checksum, returns empty on failure. - Data decodeCheck(const char* begin, const char* end, Hash::Hasher hasher = Hash::sha256d) const; + Data decodeCheck(const char* begin, const char* end, Hash::Hasher hasher = Hash::HasherSha256d) const; /// Decodes a base 58 string into `result`, returns `false` on failure. Data decode(const std::string& string) const { @@ -51,12 +51,12 @@ class Base58 { /// Encodes data as a base 58 string with a checksum. template - std::string encodeCheck(const T& data, Hash::Hasher hasher = Hash::sha256d) const { + std::string encodeCheck(const T& data, Hash::Hasher hasher = Hash::HasherSha256d) const { return encodeCheck(data.data(), data.data() + data.size(), hasher); } /// Encodes data as a base 58 string with a checksum. - std::string encodeCheck(const byte* pbegin, const byte* pend, Hash::Hasher hasher = Hash::sha256d) const; + std::string encodeCheck(const byte* pbegin, const byte* pend, Hash::Hasher hasher = Hash::HasherSha256d) const; /// Encodes data as a base 58 string. template diff --git a/src/Base58Address.h b/src/Base58Address.h index c294f364161..ee66c495a93 100644 --- a/src/Base58Address.h +++ b/src/Base58Address.h @@ -80,7 +80,7 @@ class Base58Address { if (publicKey.type != TWPublicKeyTypeSECP256k1) { throw std::invalid_argument("Bitcoin::Address needs a compressed SECP256k1 public key."); } - const auto data = publicKey.hash(prefix, Hash::sha256ripemd); + const auto data = publicKey.hash(prefix, Hash::HasherSha256ripemd); std::copy(data.begin(), data.end(), bytes.begin()); } diff --git a/src/Base64.cpp b/src/Base64.cpp index 6f5aeb44969..cf4605592ad 100644 --- a/src/Base64.cpp +++ b/src/Base64.cpp @@ -63,15 +63,9 @@ void convertToBase64Url(string& b) { } Data decodeBase64Url(const string& val) { - Data bytes; - try { - return decode(val); - } catch (const exception& ex) { - // 2nd try: Base64URL format (replaced by '-' and '_' by '+' and '/' ) - string base64Url = val; - convertFromBase64Url(base64Url); - return decode(base64Url); - } + string base64Url = val; + convertFromBase64Url(base64Url); + return decode(base64Url); } string encodeBase64Url(const Data& val) { diff --git a/src/Base64.h b/src/Base64.h index 089e1c21a93..5bc20e6fc70 100644 --- a/src/Base64.h +++ b/src/Base64.h @@ -20,7 +20,7 @@ std::string encode(const Data& val); // Base64Url format uses '-' and '_' as the two special characters, Base64 uses '+'and '/'. Data decodeBase64Url(const std::string& val); -// Encode bytes into Base64Url string (uses '-' and '_' as pecial characters) +// Encode bytes into Base64Url string (uses '-' and '_' as special characters) std::string encodeBase64Url(const Data& val); } // namespace TW::Base64 diff --git a/src/Bech32.cpp b/src/Bech32.cpp index f1c6699eba6..a309f45c2a2 100644 --- a/src/Bech32.cpp +++ b/src/Bech32.cpp @@ -14,9 +14,11 @@ // Bech32M variant also supported (BIP350) // Max length of 90 constraint is extended here to 120 for other usages -using namespace TW::Bech32; + using namespace TW; +namespace TW::Bech32 { + namespace { /** The Bech32 character set for encoding. */ @@ -26,15 +28,14 @@ const char* charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; constexpr std::array charset_rev = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, - -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, - 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, - 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}; + -1, -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, + -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, + 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, + 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1}; const uint32_t BECH32_XOR_CONST = 0x01; const uint32_t BECH32M_XOR_CONST = 0x2bc830a3; - /** Find the polynomial with value coefficients mod the generator as 30-bit. */ uint32_t polymod(const Data& values) { uint32_t chk = 1; @@ -105,7 +106,7 @@ Data create_checksum(const std::string& hrp, const Data& values, ChecksumVariant } // namespace /** Encode a Bech32 string. */ -std::string Bech32::encode(const std::string& hrp, const Data& values, ChecksumVariant variant) { +std::string encode(const std::string& hrp, const Data& values, ChecksumVariant variant) { Data checksum = create_checksum(hrp, values, variant); Data combined = values; append(combined, checksum); @@ -118,7 +119,7 @@ std::string Bech32::encode(const std::string& hrp, const Data& values, ChecksumV } /** Decode a Bech32 string. */ -std::tuple Bech32::decode(const std::string& str) { +std::tuple decode(const std::string& str) { if (str.length() > 120 || str.length() < 2) { // too long or too short return std::make_tuple(std::string(), Data(), None); @@ -141,7 +142,7 @@ std::tuple Bech32::decode(const std::string& ok = false; } size_t pos = str.rfind('1'); - if (ok && pos != str.npos && pos >= 1 && pos + 7 <= str.size()) { + if (ok && pos != std::string::npos && pos >= 1 && pos + 7 <= str.size()) { Data values; values.resize(str.size() - 1 - pos); for (size_t i = 0; i < str.size() - 1 - pos; ++i) { @@ -164,3 +165,5 @@ std::tuple Bech32::decode(const std::string& } return std::make_tuple(std::string(), Data(), None); } + +} // namespace TW::Bech32 diff --git a/src/Bech32.h b/src/Bech32.h index 60037fc5794..f9f8311a786 100644 --- a/src/Bech32.h +++ b/src/Bech32.h @@ -5,6 +5,8 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#pragma once + #include "Data.h" #include diff --git a/src/Bech32Address.cpp b/src/Bech32Address.cpp index d1c36c013b6..da1c7af32a5 100644 --- a/src/Bech32Address.cpp +++ b/src/Bech32Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -55,38 +55,16 @@ bool Bech32Address::decode(const std::string& addr, Bech32Address& obj_out, cons return true; } -Bech32Address::Bech32Address(const std::string& hrp, HasherType hasher, const PublicKey& publicKey) +Bech32Address::Bech32Address(const std::string& hrp, Hash::Hasher hasher, const PublicKey& publicKey) : hrp(hrp) { - switch (hasher) { - case HASHER_SHA2_RIPEMD: - { - auto key = Data(20); - ecdsa_get_pubkeyhash(publicKey.compressed().bytes.data(), HASHER_SHA2_RIPEMD, key.data()); - setKey(key); - } - break; - - case HASHER_SHA2: - { - const auto hash = Hash::sha256(publicKey.bytes); - auto key = Data(20); - std::copy(hash.end() - 20, hash.end(), key.begin()); - setKey(key); - } - break; - - case HASHER_SHA3K: - { - const auto hash = publicKey.hash({}, static_cast(Hash::keccak256), true); - auto key = Data(20); - std::copy(hash.end() - 20, hash.end(), key.begin()); - setKey(key); - } - break; - - default: - throw std::invalid_argument("Invalid HasherType in Bech32Address"); + bool skipTypeByte = false; + // Extended-key / keccak-hash skips first byte (Evmos) + if (publicKey.type == TWPublicKeyTypeSECP256k1Extended || hasher == Hash::HasherKeccak256) { + skipTypeByte = true; } + const auto hash = publicKey.hash({}, hasher, skipTypeByte); + auto key = subData(hash, hash.size() - 20, 20); + setKey(key); } std::string Bech32Address::string() const { diff --git a/src/Bech32Address.h b/src/Bech32Address.h index 23b4c06a640..23a88d6a23f 100644 --- a/src/Bech32Address.h +++ b/src/Bech32Address.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,7 +8,7 @@ #include "Data.h" #include "PublicKey.h" -#include +#include "Hash.h" #include #include @@ -45,7 +45,7 @@ class Bech32Address { Bech32Address(const std::string& hrp, const Data& keyHash) : hrp(std::move(hrp)), keyHash(std::move(keyHash)) {} /// Initialization from public key --> chain specific hash methods - Bech32Address(const std::string& hrp, HasherType hasher, const PublicKey& publicKey); + Bech32Address(const std::string& hrp, Hash::Hasher hasher, const PublicKey& publicKey); void setHrp(const std::string& hrp_in) { hrp = std::move(hrp_in); } void setKey(const Data& keyHash_in) { keyHash = std::move(keyHash_in); } diff --git a/src/Binance/Address.cpp b/src/Binance/Address.cpp index 481f2eda9ca..67f5bab7559 100644 --- a/src/Binance/Address.cpp +++ b/src/Binance/Address.cpp @@ -1,5 +1,5 @@ // Copyright © 2017 Pieter Wuille -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,19 +10,24 @@ #include #include -using namespace TW::Binance; +namespace TW::Binance { -const std::string Address::hrp = HRP_BINANCE; +const std::string Address::_hrp = HRP_BINANCE; const std::string Address::hrpValidator = "bva"; +const std::vector validHrps = {Address::_hrp, Address::hrpValidator, "bnbp", "bvap", "bca", "bcap"}; bool Address::isValid(const std::string& addr) { - std::vector hrps = {hrp, hrpValidator, "bnbp", "bvap", "bca", "bcap"}; - bool result = false; - for (auto& hrp : hrps) { - result = Bech32Address::isValid(addr, hrp); - if (result) { - break; + Address addrNotUsed; + return decode(addr, addrNotUsed); +} + +bool Address::decode(const std::string& addr, Address& obj_out) { + for (const auto& hrp : validHrps) { + if (Bech32Address::decode(addr, obj_out, hrp)) { + return true; } } - return result; + return false; } + +} // namespace TW::Binance diff --git a/src/Binance/Address.h b/src/Binance/Address.h index 2fb7c1fab0d..0735d5db0cf 100644 --- a/src/Binance/Address.h +++ b/src/Binance/Address.h @@ -12,25 +12,23 @@ namespace TW::Binance { -/// Binance address is a Bech32Address, with "bnb" prefix and HASHER_SHA2_RIPEMD hash +/// Binance address is a Bech32Address, with "bnb" prefix and sha256ripemd hash class Address: public Bech32Address { public: - static const std::string hrp; // HRP_BINANCE + static const std::string _hrp; // HRP_BINANCE static const std::string hrpValidator; // HRP_BINANCE static bool isValid(const std::string& addr); - Address() : Bech32Address(hrp) {} + Address() : Bech32Address(_hrp) {} /// Initializes an address with a key hash. - Address(const Data& keyHash) : Bech32Address(hrp, keyHash) {} + Address(const Data& keyHash) : Bech32Address(_hrp, keyHash) {} /// Initializes an address with a public key. - Address(const PublicKey& publicKey) : Bech32Address(hrp, HASHER_SHA2_RIPEMD, publicKey) {} + Address(const PublicKey& publicKey) : Bech32Address(_hrp, Hash::HasherSha256ripemd, publicKey) {} - static bool decode(const std::string& addr, Address& obj_out) { - return Bech32Address::decode(addr, obj_out, hrp); - } + static bool decode(const std::string& addr, Address& obj_out); }; } // namespace TW::Binance diff --git a/src/Binance/Entry.cpp b/src/Binance/Entry.cpp index 3688f01ec81..48d39fc64c2 100644 --- a/src/Binance/Entry.cpp +++ b/src/Binance/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,24 +6,105 @@ #include "Entry.h" +#include "../proto/TransactionCompiler.pb.h" #include "Address.h" #include "Signer.h" -using namespace TW::Binance; -using namespace std; +namespace TW::Binance { -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + Address addr; + if (!Address::decode(address, addr)) { + return {}; + } + return addr.getKeyHash(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +std::string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { + return txCompilerTemplate( + txInputData, [](const auto& input, auto& output) { + Signer signer(input); + + auto preImageHash = signer.preImageHash(); + auto preImage = signer.signaturePreimage(); + output.set_data_hash(preImageHash.data(), preImageHash.size()); + output.set_data(preImage.data(), preImage.size()); + }); +} + +void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const { + dataOut = txCompilerTemplate( + txInputData, [&](const auto& input, auto& output) { + if (signatures.size() == 0 || publicKeys.size() == 0) { + output.set_error(Common::Proto::Error_invalid_params); + output.set_error_message("empty signatures or publickeys"); + return; + } + if (signatures.size() > 1 || publicKeys.size() > 1) { + output.set_error(Common::Proto::Error_no_support_n2n); + output.set_error_message(Common::Proto::SigningError_Name(Common::Proto::Error_no_support_n2n)); + return; + } + output = Signer(input).compile(signatures[0], publicKeys[0]); + }); +} + +Data Entry::buildTransactionInput([[maybe_unused]] TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId) const { + auto input = Proto::SigningInput(); + input.set_chain_id(chainId); + input.set_account_number(0); + input.set_sequence(0); + input.set_source(0); + input.set_memo(memo); + // do not set private_key! + input.set_private_key(""); + + auto& order = *input.mutable_send_order(); + + Address fromAddress; + if (!Address::decode(from, fromAddress)) { + throw std::invalid_argument("Invalid from address"); + } + const auto fromKeyhash = fromAddress.getKeyHash(); + Address toAddress; + if (!Address::decode(to, toAddress)) { + throw std::invalid_argument("Invalid to address"); + } + const auto toKeyhash = toAddress.getKeyHash(); + + { + auto* sendOrderInputs = order.add_inputs(); + sendOrderInputs->set_address(fromKeyhash.data(), fromKeyhash.size()); + auto* inputCoin = sendOrderInputs->add_coins(); + inputCoin->set_denom(asset); + inputCoin->set_amount(static_cast(amount)); + } + { + auto* output = order.add_outputs(); + output->set_address(toKeyhash.data(), toKeyhash.size()); + auto* outputCoin = output->add_coins(); + outputCoin->set_denom(asset); + outputCoin->set_amount(static_cast(amount)); + } + + const auto txInputData = data(input.SerializeAsString()); + return txInputData; +} + +} // namespace TW::Binance diff --git a/src/Binance/Entry.h b/src/Binance/Entry.h index d9bf47e8bde..da927b68166 100644 --- a/src/Binance/Entry.h +++ b/src/Binance/Entry.h @@ -12,14 +12,18 @@ namespace TW::Binance { /// Binance entry dispatcher. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeBinance}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + + Data preImageHashes(TWCoinType coin, const Data& txInputData) const; + void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const; + Data buildTransactionInput(TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId) const; }; } // namespace TW::Binance diff --git a/src/Binance/Serialization.cpp b/src/Binance/Serialization.cpp index 2869341207b..cdf0b83bb58 100644 --- a/src/Binance/Serialization.cpp +++ b/src/Binance/Serialization.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,9 +11,7 @@ #include "Ethereum/Address.h" #include "../HexCoding.h" -using namespace TW; -using namespace TW::Binance; -using namespace google::protobuf; +namespace TW::Binance { using json = nlohmann::json; @@ -27,7 +25,7 @@ static inline std::string validatorAddress(const std::string& bytes) { return Bech32Address(Address::hrpValidator, data).string(); } -json Binance::signatureJSON(const Proto::SigningInput& input) { +json signatureJSON(const Proto::SigningInput& input) { json j; j["account_number"] = std::to_string(input.account_number()); j["chain_id"] = input.chain_id(); @@ -39,7 +37,7 @@ json Binance::signatureJSON(const Proto::SigningInput& input) { return j; } -json Binance::orderJSON(const Proto::SigningInput& input) { +json orderJSON(const Proto::SigningInput& input) { json j; if (input.has_trade_order()) { j["id"] = input.trade_order().id(); @@ -98,7 +96,7 @@ json Binance::orderJSON(const Proto::SigningInput& input) { j["type"] = "cosmos-sdk/MsgSideChainDelegate"; j["value"] = { {"delegator_addr", addressString(input.side_delegate_order().delegator_addr())}, - {"validator_addr",validatorAddress(input.side_delegate_order().validator_addr())}, + {"validator_addr", validatorAddress(input.side_delegate_order().validator_addr())}, {"delegation", tokenJSON(input.side_delegate_order().delegation(), true)}, {"side_chain_id", input.side_delegate_order().chain_id()}, }; @@ -131,7 +129,7 @@ json Binance::orderJSON(const Proto::SigningInput& input) { j["description"] = input.time_relock_order().description(); // if amount is empty or omitted, set null to avoid signature verification error j["amount"] = nullptr; - if (amount.size() > 0) { + if (!amount.empty()) { j["amount"] = tokensJSON(amount); } j["lock_time"] = input.time_relock_order().lock_time(); @@ -142,30 +140,26 @@ json Binance::orderJSON(const Proto::SigningInput& input) { return j; } -json Binance::inputsJSON(const Proto::SendOrder& order) { +json inputsJSON(const Proto::SendOrder& order) { json j = json::array(); for (auto& input : order.inputs()) { - j.push_back({ - {"address", addressString(input.address())}, - {"coins", tokensJSON(input.coins())} - }); + j.push_back({{"address", addressString(input.address())}, + {"coins", tokensJSON(input.coins())}}); } return j; } -json Binance::outputsJSON(const Proto::SendOrder& order) { +json outputsJSON(const Proto::SendOrder& order) { json j = json::array(); for (auto& output : order.outputs()) { - j.push_back({ - {"address", addressString(output.address())}, - {"coins", tokensJSON(output.coins())} - }); + j.push_back({{"address", addressString(output.address())}, + {"coins", tokensJSON(output.coins())}}); } return j; } -json Binance::tokenJSON(const Proto::SendOrder_Token& token, bool stringAmount) { - json j = { {"denom", token.denom()} }; +json tokenJSON(const Proto::SendOrder_Token& token, bool stringAmount) { + json j = {{"denom", token.denom()}}; if (stringAmount) { j["amount"] = std::to_string(token.amount()); } else { @@ -174,10 +168,12 @@ json Binance::tokenJSON(const Proto::SendOrder_Token& token, bool stringAmount) return j; } -json Binance::tokensJSON(const RepeatedPtrField& tokens) { +json tokensJSON(const google::protobuf::RepeatedPtrField& tokens) { json j = json::array(); for (auto& token : tokens) { j.push_back(tokenJSON(token)); } return j; } + +} // namespace TW::Binance diff --git a/src/Binance/Signer.cpp b/src/Binance/Signer.cpp index 03736f2dc07..5a06be40181 100644 --- a/src/Binance/Signer.cpp +++ b/src/Binance/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,7 +6,6 @@ #include "Signer.h" #include "Serialization.h" -#include "../Hash.h" #include "../HexCoding.h" #include "../PrivateKey.h" @@ -16,8 +15,7 @@ #include -using namespace TW; -using namespace TW::Binance; +namespace TW::Binance { // Message prefixes // see https://docs.binance.org/api-reference/transactions.html#amino-types @@ -65,10 +63,32 @@ Data Signer::build() const { } Data Signer::sign() const { + auto hash = preImageHash(); auto key = PrivateKey(input.private_key()); - auto hash = Hash::sha256(signaturePreimage()); auto signature = key.sign(hash, TWCurveSECP256k1); - return Data(signature.begin(), signature.end() - 1); + return {signature.begin(), signature.end() - 1}; +} + +Data Signer::preImageHash() const { + return Hash::sha256(signaturePreimage()); +} + +Proto::SigningOutput Signer::compile(const Data& signature, const PublicKey& publicKey) const { + // validate public key + if (publicKey.type != TWPublicKeyTypeSECP256k1) { + throw std::invalid_argument("Invalid public key"); + } + { + // validate correctness of signature + const auto hash = this->preImageHash(); + if (!publicKey.verify(signature, hash)) { + throw std::invalid_argument("Invalid signature/hash/publickey combination"); + } + } + const auto encoded = encodeTransaction(encodeSignature(signature, publicKey)); + auto output = Proto::SigningOutput(); + output.set_encoded(encoded.data(), encoded.size()); + return output; } std::string Signer::signaturePreimage() const { @@ -157,7 +177,10 @@ Data Signer::encodeOrder() const { Data Signer::encodeSignature(const Data& signature) const { auto key = PrivateKey(input.private_key()); auto publicKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); + return encodeSignature(signature, publicKey); +} +Data Signer::encodeSignature(const Data& signature, const PublicKey& publicKey) const { auto encodedPublicKey = pubKeyPrefix; encodedPublicKey.insert(encodedPublicKey.end(), static_cast(publicKey.bytes.size())); encodedPublicKey.insert(encodedPublicKey.end(), publicKey.bytes.begin(), publicKey.bytes.end()); @@ -190,5 +213,7 @@ Data Signer::aminoWrap(const std::string& raw, const Data& typePrefix, bool pref cos.WriteRaw(raw.data(), static_cast(raw.size())); } - return Data(msg.begin(), msg.end()); + return {msg.begin(), msg.end()}; } + +} // namespace TW::Binance diff --git a/src/Binance/Signer.h b/src/Binance/Signer.h index 1539c48d7b7..d66b42bf774 100644 --- a/src/Binance/Signer.h +++ b/src/Binance/Signer.h @@ -7,9 +7,11 @@ #pragma once #include "Data.h" +#include "PublicKey.h" #include "../proto/Binance.pb.h" #include +#include namespace TW::Binance { @@ -24,7 +26,7 @@ class Signer { Proto::SigningInput input; /// Initializes a transaction signer. - explicit Signer(const Proto::SigningInput& input) : input(input) {} + explicit Signer(Proto::SigningInput input) : input(std::move(input)) {} /// Builds a signed transaction. /// @@ -38,11 +40,15 @@ class Signer { /// error. TW::Data sign() const; - private: + TW::Data preImageHash() const; + Proto::SigningOutput compile(const Data& signature, const PublicKey& publicKey) const; std::string signaturePreimage() const; + + private: TW::Data encodeTransaction(const TW::Data& signature) const; TW::Data encodeOrder() const; TW::Data encodeSignature(const TW::Data& signature) const; + TW::Data encodeSignature(const TW::Data& signature, const PublicKey& publicKey) const; TW::Data aminoWrap(const std::string& raw, const TW::Data& typePrefix, bool isPrefixLength) const; }; diff --git a/src/BinaryCoding.h b/src/BinaryCoding.h index 233acfaf7e3..88c11ebab31 100644 --- a/src/BinaryCoding.h +++ b/src/BinaryCoding.h @@ -14,6 +14,7 @@ #include #include +//win #ifdef _MSC_VER #define _Nonnull #endif @@ -66,12 +67,12 @@ uint8_t varIntSize(uint64_t value); /// being encoded. It produces fewer bytes for smaller numbers as opposed to a /// fixed-size encoding. Little endian byte order is used. /// -/// @returns the number of bytes written. +/// \returns the number of bytes written. uint8_t encodeVarInt(uint64_t size, std::vector& data); /// Decodes an integer as a variable-length integer. See encodeVarInt(). /// -/// @returns a tuple with a success indicator and the decoded integer. +/// \returns a tuple with a success indicator and the decoded integer. std::tuple decodeVarInt(const Data& in, size_t& indexInOut); /// Encodes a 16-bit big-endian value into the provided buffer. @@ -113,7 +114,7 @@ uint64_t decode64BE(const uint8_t* _Nonnull src); void encodeString(const std::string& str, std::vector& data); /// Decodes an ASCII string prefixed by its length (varInt) -/// @returns a tuple with a success indicator and the decoded string. +/// \returns a tuple with a success indicator and the decoded string. std::tuple decodeString(const Data& in, size_t& indexInOut); } // namespace TW diff --git a/src/Bitcoin/Address.cpp b/src/Bitcoin/Address.cpp deleted file mode 100644 index 5aa19d29eab..00000000000 --- a/src/Bitcoin/Address.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Address.h" - -#include "../Base58.h" - -using namespace TW::Bitcoin; diff --git a/src/Bitcoin/Address.h b/src/Bitcoin/Address.h index 9ab00322ed8..48ae90a8760 100644 --- a/src/Bitcoin/Address.h +++ b/src/Bitcoin/Address.h @@ -7,7 +7,7 @@ #pragma once #include "../Base58Address.h" -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/Bitcoin/Amount.h b/src/Bitcoin/Amount.h index 873b9ba871d..d286003d75f 100644 --- a/src/Bitcoin/Amount.h +++ b/src/Bitcoin/Amount.h @@ -15,15 +15,4 @@ namespace TW::Bitcoin { /// Amount in satoshis (can be negative) using Amount = int64_t; -/// One bitcoin in satoshis -static const Amount coin = 100000000; - -/// Maxximum valid amount in satoshis. -static const Amount maxAmount = 21000000 * coin; - -/// Detemines if the provided value is a valid amount. -inline bool isValidAmount(const Amount& amount) { - return (amount >= 0 && amount <= maxAmount); -} - } // namespace TW::Bitcoin diff --git a/src/Bitcoin/CashAddress.cpp b/src/Bitcoin/CashAddress.cpp index f95ef8e8df6..86852f0f472 100644 --- a/src/Bitcoin/CashAddress.cpp +++ b/src/Bitcoin/CashAddress.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,67 +12,78 @@ #include #include -#include +#include -using namespace TW::Bitcoin; - -/// Cash address human-readable part -static const std::string cashHRP = "bitcoincash"; +namespace TW::Bitcoin { /// From https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md +namespace { -static const uint8_t p2khVersion = 0x00; -static const uint8_t p2shVersion = 0x08; +enum class Version : uint8_t { + p2kh = 0x00, + p2sh = 0x08 +}; +constexpr size_t maxHRPSize{20}; +constexpr size_t maxDataSize{104}; -static constexpr size_t maxHRPSize = 20; -static constexpr size_t maxDataSize = 104; +} // namespace -bool CashAddress::isValid(const std::string& string) { - auto withPrefix = string; - if (string.size() < cashHRP.size() || !std::equal(cashHRP.begin(), cashHRP.end(), string.begin())) { - withPrefix = cashHRP + ":" + string; - } +namespace details { - std::array hrp = {0}; - std::array data; - size_t dataLen; - if (cash_decode(hrp.data(), data.data(), &dataLen, withPrefix.c_str()) == 0 || dataLen != CashAddress::size) { - return false; +inline std::string buildPrefix(const std::string& hrp, const std::string& string) noexcept { + if (string.size() < hrp.size() || !std::equal(hrp.begin(), hrp.end(), string.begin())) { + return hrp + ":" + string; } - if (std::strncmp(hrp.data(), cashHRP.c_str(), std::min(cashHRP.size(), maxHRPSize)) != 0) { - return false; - } - return true; + return string; } -CashAddress::CashAddress(const std::string& string) { - auto withPrefix = string; - if (!std::equal(cashHRP.begin(), cashHRP.end(), string.begin())) { - withPrefix = cashHRP + ":" + string; +inline void determinePrefix(TW::Data& data) noexcept { + auto& prefix = data.front(); + switch (static_cast(prefix)) { + case Version::p2kh: + prefix = TW::p2pkhPrefix(TWCoinTypeBitcoinCash); + break; + case Version::p2sh: + prefix = TW::p2shPrefix(TWCoinTypeBitcoinCash); + break; } +} - std::array hrp; - std::array data; +} // namespace details + +bool CashAddress::isValid(const std::string& hrp, const std::string& string) noexcept { + const auto withPrefix = details::buildPrefix(hrp, string); + std::array decodedHRP{0}; + std::array data{}; size_t dataLen; - auto success = cash_decode(hrp.data(), data.data(), &dataLen, withPrefix.c_str()) != 0; - if (!success || std::strncmp(hrp.data(), cashHRP.c_str(), std::min(cashHRP.size(), maxHRPSize)) != 0 || dataLen != CashAddress::size) { - throw std::invalid_argument("Invalid address string"); - } - std::copy(data.begin(), data.begin() + dataLen, bytes.begin()); + const bool decodeValid = + cash_decode(decodedHRP.data(), data.data(), &dataLen, withPrefix.c_str()) != 0 && + dataLen == CashAddress::size; + return decodeValid && + std::string(decodedHRP.data()).compare(0, std::min(hrp.size(), maxHRPSize), hrp) == 0; } -CashAddress::CashAddress(const Data& data) { - if (!isValid(data)) { - throw std::invalid_argument("Invalid address key data"); +CashAddress::CashAddress(const std::string& hrp, const std::string& string) + : hrp(hrp) { + const auto withPrefix = details::buildPrefix(hrp, string); + std::array decodedHRP{}; + std::array data{}; + size_t dataLen; + bool success = cash_decode(decodedHRP.data(), data.data(), &dataLen, withPrefix.c_str()) != 0; + if (!success || + std::string(decodedHRP.data()).compare(0, std::min(hrp.size(), maxHRPSize), hrp) != 0 || + dataLen != CashAddress::size) { + throw std::invalid_argument("Invalid address string"); } - std::copy(data.begin(), data.end(), bytes.begin()); + std::copy(data.begin(), data.begin() + dataLen, bytes.begin()); } -CashAddress::CashAddress(const PublicKey& publicKey) { +CashAddress::CashAddress(std::string hrp, const PublicKey& publicKey) + : hrp(std::move(hrp)) { if (publicKey.type != TWPublicKeyTypeSECP256k1) { throw std::invalid_argument("CashAddress needs a compressed SECP256k1 public key."); } - std::array payload; + std::array payload{}; payload[0] = 0; ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, payload.data() + 1); @@ -83,21 +94,26 @@ CashAddress::CashAddress(const PublicKey& publicKey) { } } -std::string CashAddress::string() const { - std::array result; - cash_encode(result.data(), cashHRP.c_str(), bytes.data(), CashAddress::size); +std::string CashAddress::string() const noexcept { + std::array result{}; + cash_encode(result.data(), hrp.c_str(), bytes.data(), CashAddress::size); return result.data(); } -Address CashAddress::legacyAddress() const { +Address CashAddress::legacyAddress() const noexcept { Data result(Address::size); size_t outlen = 0; cash_data_to_addr(result.data(), &outlen, bytes.data(), CashAddress::size); assert(outlen == 21 && "Invalid length"); - if (result[0] == p2khVersion) { - result[0] = TW::p2pkhPrefix(TWCoinTypeBitcoinCash); - } else if (result[0] == p2shVersion) { - result[0] = TW::p2shPrefix(TWCoinTypeBitcoinCash); - } + details::determinePrefix(result); return Address(result); } + +Data CashAddress::getData() const { + Data data(Address::size); + size_t outlen = 0; + cash_data_to_addr(data.data(), &outlen, bytes.data(), CashAddress::size); + return data; +} + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/CashAddress.h b/src/Bitcoin/CashAddress.h index 089e6cb265d..1ad14b9213f 100644 --- a/src/Bitcoin/CashAddress.h +++ b/src/Bitcoin/CashAddress.h @@ -7,22 +7,30 @@ #pragma once #include "Address.h" +#include "Data.h" #include "../PublicKey.h" -#include "../Data.h" + +#include #include #include namespace TW::Bitcoin { +inline const std::string gBitcoinCashHrp{HRP_BITCOINCASH}; +inline const std::string gECashHrp{HRP_ECASH}; + class CashAddress { - public: +public: /// Number of bytes in an address. - static const size_t size = 34; + static constexpr size_t size{34}; /// Address data consisting of a prefix byte followed by the public key /// hash. - std::array bytes; + std::array bytes{}; + + /// Cash address human-readable part + const std::string hrp; /// Determines whether a collection of bytes makes a valid address. template @@ -31,26 +39,47 @@ class CashAddress { } /// Determines whether a string makes a valid address. - static bool isValid(const std::string& string); + static bool isValid(const std::string& hrp, const std::string& string) noexcept; /// Initializes a address with a string representation. - explicit CashAddress(const std::string& string); - - /// Initializes a address with a collection of bytes. - explicit CashAddress(const Data& data); + explicit CashAddress(const std::string& hrp, const std::string& string); /// Initializes a address with a public key. - explicit CashAddress(const PublicKey& publicKey); + explicit CashAddress(std::string hrp, const PublicKey& publicKey); /// Returns a string representation of the address. - std::string string() const; + [[nodiscard]] std::string string() const noexcept; /// Returns the legacy address representation. - Address legacyAddress() const; + [[nodiscard]] Address legacyAddress() const noexcept; + + Data getData() const; }; -inline bool operator==(const CashAddress& lhs, const CashAddress& rhs) { - return lhs.bytes == rhs.bytes; -} +class BitcoinCashAddress : public CashAddress { +public: + /// Initializes an address with a string representation. + explicit BitcoinCashAddress(const std::string& string) : CashAddress(gBitcoinCashHrp, string) {} + + /// Initializes an address with a public key. + explicit BitcoinCashAddress(const PublicKey& publicKey) : CashAddress(gBitcoinCashHrp, publicKey) {} + + static bool isValid(const std::string& string) noexcept { + return CashAddress::isValid(gBitcoinCashHrp, string); + } +}; + +class ECashAddress : public CashAddress { +public: + /// Initializes an address with a string representation. + explicit ECashAddress(const std::string& string) : CashAddress(gECashHrp, string) {} + + /// Initializes an address with a public key. + explicit ECashAddress(const PublicKey& publicKey) : CashAddress(gECashHrp, publicKey) {} + + static bool isValid(const std::string& string) noexcept { + return CashAddress::isValid(gECashHrp, string); + } +}; } // namespace TW::Bitcoin diff --git a/src/Bitcoin/Entry.cpp b/src/Bitcoin/Entry.cpp index 877e6e3da93..36ee93a94f6 100644 --- a/src/Bitcoin/Entry.cpp +++ b/src/Bitcoin/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,77 +11,178 @@ #include "SegwitAddress.h" #include "Signer.h" -using namespace TW::Bitcoin; -using namespace std; +namespace TW::Bitcoin { -bool Entry::validateAddress(TWCoinType coin, const string& address, byte p2pkh, byte p2sh, const char* hrp) const { +bool Entry::validateAddress(TWCoinType coin, const std::string& address, byte p2pkh, byte p2sh, + const char* hrp) const { switch (coin) { - case TWCoinTypeBitcoin: - case TWCoinTypeDigiByte: - case TWCoinTypeLitecoin: - case TWCoinTypeMonacoin: - case TWCoinTypeQtum: - case TWCoinTypeViacoin: - case TWCoinTypeBitcoinGold: - return SegwitAddress::isValid(address, hrp) - || Address::isValid(address, {{p2pkh}, {p2sh}}); - - case TWCoinTypeBitcoinCash: - return CashAddress::isValid(address) - || Address::isValid(address, {{p2pkh}, {p2sh}}); - - case TWCoinTypeDash: - case TWCoinTypeDogecoin: - case TWCoinTypeRavencoin: - case TWCoinTypeZcoin: - default: - return Address::isValid(address, {{p2pkh}, {p2sh}}); + case TWCoinTypeBitcoin: + case TWCoinTypeDigiByte: + case TWCoinTypeLitecoin: + case TWCoinTypeMonacoin: + case TWCoinTypeQtum: + case TWCoinTypeViacoin: + case TWCoinTypeBitcoinGold: + return SegwitAddress::isValid(address, hrp) || Address::isValid(address, {{p2pkh}, {p2sh}}); + + case TWCoinTypeBitcoinCash: + return BitcoinCashAddress::isValid(address) || Address::isValid(address, {{p2pkh}, {p2sh}}); + + case TWCoinTypeECash: + return ECashAddress::isValid(address) || Address::isValid(address, {{p2pkh}, {p2sh}}); + + case TWCoinTypeDash: + case TWCoinTypeDogecoin: + case TWCoinTypeRavencoin: + case TWCoinTypeFiro: + default: + return Address::isValid(address, {{p2pkh}, {p2sh}}); } } -string Entry::normalizeAddress(TWCoinType coin, const string& address) const { +std::string Entry::normalizeAddress(TWCoinType coin, const std::string& address) const { switch (coin) { - case TWCoinTypeBitcoinCash: - // normalized with bitcoincash: prefix - if (CashAddress::isValid(address)) { - return CashAddress(address).string(); - } else { - return std::string(address); - } + case TWCoinTypeBitcoinCash: + // normalized with bitcoincash: prefix + if (BitcoinCashAddress::isValid(address)) { + return BitcoinCashAddress(address).string(); + } + return address; - default: - // no change - return address; + case TWCoinTypeECash: + // normalized with ecash: prefix + if (ECashAddress::isValid(address)) { + return ECashAddress(address).string(); + } + return address; + + default: + // no change + return address; } } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, byte p2pkh, const char* hrp) const { +std::string Entry::deriveAddress(TWCoinType coin, TWDerivation derivation, const PublicKey& publicKey, + byte p2pkh, const char* hrp) const { switch (coin) { - case TWCoinTypeBitcoin: - case TWCoinTypeDigiByte: - case TWCoinTypeLitecoin: - case TWCoinTypeViacoin: - case TWCoinTypeBitcoinGold: - return SegwitAddress(publicKey, 0, hrp).string(); - - case TWCoinTypeBitcoinCash: - return CashAddress(publicKey).string(); - - case TWCoinTypeDash: - case TWCoinTypeDogecoin: - case TWCoinTypeMonacoin: - case TWCoinTypeQtum: - case TWCoinTypeRavencoin: - case TWCoinTypeZcoin: - default: + case TWCoinTypeBitcoin: + case TWCoinTypeLitecoin: + switch (derivation) { + case TWDerivationBitcoinLegacy: + case TWDerivationLitecoinLegacy: return Address(publicKey, p2pkh).string(); + + case TWDerivationBitcoinTestnet: + return SegwitAddress::createTestnetFromPublicKey(publicKey).string(); + + case TWDerivationBitcoinSegwit: + case TWDerivationDefault: + default: + return SegwitAddress(publicKey, hrp).string(); + } + + case TWCoinTypeDigiByte: + case TWCoinTypeViacoin: + case TWCoinTypeBitcoinGold: + return SegwitAddress(publicKey, hrp).string(); + + case TWCoinTypeBitcoinCash: + return BitcoinCashAddress(publicKey).string(); + + case TWCoinTypeECash: + return ECashAddress(publicKey).string(); + + case TWCoinTypeDash: + case TWCoinTypeDogecoin: + case TWCoinTypeMonacoin: + case TWCoinTypeQtum: + case TWCoinTypeRavencoin: + case TWCoinTypeFiro: + default: + return Address(publicKey, p2pkh).string(); + } +} + +template +inline Data cashAddressToData(const CashAddress&& addr) { + return subData(addr.getData(), 1); +} + +Data Entry::addressToData(TWCoinType coin, const std::string& address) const { + switch (coin) { + case TWCoinTypeBitcoin: + case TWCoinTypeBitcoinGold: + case TWCoinTypeDigiByte: + case TWCoinTypeGroestlcoin: + case TWCoinTypeLitecoin: + case TWCoinTypeViacoin: { + const auto decoded = SegwitAddress::decode(address); + if (!std::get<2>(decoded)) { + return {}; + } + return std::get<0>(decoded).witnessProgram; + } + + case TWCoinTypeBitcoinCash: + return cashAddressToData(BitcoinCashAddress(address)); + + case TWCoinTypeECash: + return cashAddressToData(ECashAddress(address)); + + case TWCoinTypeDash: + case TWCoinTypeDogecoin: + case TWCoinTypeMonacoin: + case TWCoinTypeQtum: + case TWCoinTypeRavencoin: + case TWCoinTypeFiro: { + const auto addr = Address(address); + return {addr.bytes.begin() + 1, addr.bytes.end()}; + } + + default: + return {}; } } -void Entry::sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const Data& dataIn, Data& dataOut) const { signTemplate(dataIn, dataOut); } -void Entry::plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const { +void Entry::plan([[maybe_unused]] TWCoinType coin, const Data& dataIn, Data& dataOut) const { planTemplate(dataIn, dataOut); } + +Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { + return txCompilerTemplate( + txInputData, [](auto&& input, auto&& output) { output = Signer::preImageHashes(input); }); +} + +void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, + const std::vector& publicKeys, Data& dataOut) const { + auto txCompilerFunctor = [&signatures, &publicKeys](auto&& input, auto&& output) noexcept { + if (signatures.empty() || publicKeys.empty()) { + output.set_error(Common::Proto::Error_invalid_params); + output.set_error_message("empty signatures or publickeys"); + return; + } + + if (signatures.size() != publicKeys.size()) { + output.set_error(Common::Proto::Error_invalid_params); + output.set_error_message("signatures size and publickeys size not equal"); + return; + } + + HashPubkeyList externalSignatures; + auto insertFunctor = [](auto&& signature, auto&& pubkey) noexcept { + return std::make_pair(signature, pubkey.bytes); + }; + transform(begin(signatures), end(signatures), begin(publicKeys), + back_inserter(externalSignatures), insertFunctor); + output = Signer::sign(input, externalSignatures); + }; + + dataOut = txCompilerTemplate(txInputData, + txCompilerFunctor); +} + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/Entry.h b/src/Bitcoin/Entry.h index ccdd4f4932e..713f97685d2 100644 --- a/src/Bitcoin/Entry.h +++ b/src/Bitcoin/Entry.h @@ -11,30 +11,27 @@ namespace TW::Bitcoin { /// Bitcoin entry dispatcher. -/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific +/// includes in this file +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { - return { - TWCoinTypeBitcoin, - TWCoinTypeBitcoinCash, - TWCoinTypeBitcoinGold, - TWCoinTypeDash, - TWCoinTypeDigiByte, - TWCoinTypeDogecoin, - TWCoinTypeLitecoin, - TWCoinTypeMonacoin, - TWCoinTypeQtum, - TWCoinTypeRavencoin, - TWCoinTypeViacoin, - TWCoinTypeZcoin, - }; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, + const char* hrp) const; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, + const char* hrp) const { + return deriveAddress(coin, TWDerivationDefault, publicKey, p2pkh, hrp); } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + std::string deriveAddress(TWCoinType coin, TWDerivation derivation, const PublicKey& publicKey, + TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + + Data preImageHashes(TWCoinType coin, const Data& txInputData) const; + void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, + const std::vector& publicKeys, Data& dataOut) const; + // Note: buildTransactionInput is not implemented for Binance chain with UTXOs }; } // namespace TW::Bitcoin diff --git a/src/Bitcoin/FeeCalculator.cpp b/src/Bitcoin/FeeCalculator.cpp index 06a40facdc0..ffae2669621 100644 --- a/src/Bitcoin/FeeCalculator.cpp +++ b/src/Bitcoin/FeeCalculator.cpp @@ -12,25 +12,33 @@ using namespace TW; namespace TW::Bitcoin { -int64_t LinearFeeCalculator::calculate(int64_t inputs, int64_t outputs, int64_t byteFee) const { - const auto txsize = int64_t(std::ceil(bytesPerInput * (double)inputs + bytesPerOutput * (double)outputs + bytesBase)); +constexpr double gDecredBytesPerInput{166}; +constexpr double gDecredBytesPerOutput{38}; +constexpr double gDecredBytesBase{12}; + +int64_t LinearFeeCalculator::calculate(int64_t inputs, int64_t outputs, + int64_t byteFee) const noexcept { + const auto txsize = + static_cast(std::ceil(bytesPerInput * static_cast(inputs) + + bytesPerOutput * static_cast(outputs) + bytesBase)); return txsize * byteFee; } -int64_t LinearFeeCalculator::calculateSingleInput(int64_t byteFee) const { - return int64_t(std::ceil(bytesPerInput)) * byteFee; // std::ceil(101.25) = 102 +int64_t LinearFeeCalculator::calculateSingleInput(int64_t byteFee) const noexcept { + return static_cast(std::ceil(bytesPerInput)) * byteFee; // std::ceil(101.25) = 102 } class DecredFeeCalculator : public LinearFeeCalculator { public: - DecredFeeCalculator(): LinearFeeCalculator(166, 38, 12) {} + constexpr DecredFeeCalculator() noexcept + : LinearFeeCalculator(gDecredBytesPerInput, gDecredBytesPerOutput, gDecredBytesBase) {} }; -DefaultFeeCalculator defaultFeeCalculator; -DecredFeeCalculator decredFeeCalculator; -SegwitFeeCalculator segwitFeeCalculator; +static constexpr DefaultFeeCalculator defaultFeeCalculator{}; +static constexpr DecredFeeCalculator decredFeeCalculator{}; +static constexpr SegwitFeeCalculator segwitFeeCalculator{}; -FeeCalculator& getFeeCalculator(TWCoinType coinType) { +const FeeCalculator& getFeeCalculator(TWCoinType coinType) noexcept { switch (coinType) { case TWCoinTypeDecred: return decredFeeCalculator; diff --git a/src/Bitcoin/FeeCalculator.h b/src/Bitcoin/FeeCalculator.h index 62d1e1504be..49b76b28a75 100644 --- a/src/Bitcoin/FeeCalculator.h +++ b/src/Bitcoin/FeeCalculator.h @@ -10,11 +10,20 @@ namespace TW::Bitcoin { +inline constexpr double gDefaultBytesPerInput{148}; +inline constexpr double gDefaultBytesPerOutput{34}; +inline constexpr double gDefaultBytesBase{10}; +inline constexpr double gSegwitBytesPerInput{101.25}; +inline constexpr double gSegwitBytesPerOutput{31}; +inline constexpr double gSegwitBytesBase{gDefaultBytesBase}; + /// Interface for transaction fee calculator. class FeeCalculator { public: - virtual int64_t calculate(int64_t inputs, int64_t outputs, int64_t byteFee) const = 0; - virtual int64_t calculateSingleInput(int64_t byteFee) const = 0; + virtual ~FeeCalculator() noexcept = default; + [[nodiscard]] virtual int64_t calculate(int64_t inputs, int64_t outputs, + int64_t byteFee) const noexcept = 0; + [[nodiscard]] virtual int64_t calculateSingleInput(int64_t byteFee) const noexcept = 0; }; /// Generic fee calculator with linear input and output size, and a fix size @@ -23,36 +32,43 @@ class LinearFeeCalculator : public FeeCalculator { const double bytesPerInput; const double bytesPerOutput; const double bytesBase; - LinearFeeCalculator(double bytesPerInput, double bytesPerOutput, double bytesBase) - :bytesPerInput(bytesPerInput), bytesPerOutput(bytesPerOutput), bytesBase(bytesBase) {} + explicit constexpr LinearFeeCalculator(double bytesPerInput, double bytesPerOutput, + double bytesBase) noexcept + : bytesPerInput(bytesPerInput), bytesPerOutput(bytesPerOutput), bytesBase(bytesBase) {} - virtual int64_t calculate(int64_t inputs, int64_t outputs, int64_t byteFee) const override; - virtual int64_t calculateSingleInput(int64_t byteFee) const override; + [[nodiscard]] int64_t calculate(int64_t inputs, int64_t outputs, + int64_t byteFee) const noexcept override; + [[nodiscard]] int64_t calculateSingleInput(int64_t byteFee) const noexcept override; }; /// Constant fee calculator class ConstantFeeCalculator : public FeeCalculator { public: const int64_t fee; - ConstantFeeCalculator(int64_t fee) : fee(fee) {} + explicit constexpr ConstantFeeCalculator(int64_t fee) noexcept : fee(fee) {} - virtual int64_t calculate(int64_t inputs, int64_t outputs, int64_t byteFee) const override { return fee; } - virtual int64_t calculateSingleInput(int64_t byteFee) const override { return 0; } + [[nodiscard]] int64_t calculate([[maybe_unused]] int64_t inputs, [[maybe_unused]] int64_t outputs, + [[maybe_unused]] int64_t byteFee) const noexcept final { + return fee; + } + [[nodiscard]] int64_t calculateSingleInput([[maybe_unused]] int64_t byteFee) const noexcept final { return 0; } }; /// Default Bitcoin transaction fee calculator, non-segwit. class DefaultFeeCalculator : public LinearFeeCalculator { public: - DefaultFeeCalculator(): LinearFeeCalculator(148, 34, 10) {} + constexpr DefaultFeeCalculator() noexcept + : LinearFeeCalculator(gDefaultBytesPerInput, gDefaultBytesPerOutput, gDefaultBytesBase) {} }; /// Bitcoin Segwit transaction fee calculator class SegwitFeeCalculator : public LinearFeeCalculator { public: - SegwitFeeCalculator(): LinearFeeCalculator(101.25, 31, 10) {} + constexpr SegwitFeeCalculator() noexcept + : LinearFeeCalculator(gSegwitBytesPerInput, gSegwitBytesPerOutput, gSegwitBytesBase) {} }; /// Return the fee calculator for the given coin. -FeeCalculator& getFeeCalculator(TWCoinType coinType); +const FeeCalculator& getFeeCalculator(TWCoinType coinType) noexcept; } // namespace TW::Bitcoin diff --git a/src/Bitcoin/InputSelector.cpp b/src/Bitcoin/InputSelector.cpp index f643394abf4..9564e67fa20 100644 --- a/src/Bitcoin/InputSelector.cpp +++ b/src/Bitcoin/InputSelector.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,33 +9,36 @@ #include "UTXO.h" #include +#include #include -using namespace TW; -using namespace TW::Bitcoin; - +namespace TW::Bitcoin { template -uint64_t InputSelector::sum(const std::vector& amounts) { +uint64_t InputSelector::sum(const std::vector& amounts) noexcept { uint64_t sum = 0; - for(auto& i: amounts) { + for (auto& i : amounts) { sum += i.amount; } return sum; } template -std::vector InputSelector::filterOutDust(const std::vector& inputs, int64_t byteFee) { +std::vector +InputSelector::filterOutDust(const std::vector& inputs, + int64_t byteFee) noexcept { auto inputFeeLimit = static_cast(feeCalculator.calculateSingleInput(byteFee)); return filterThreshold(inputs, inputFeeLimit); } // Filters utxos that are dust template -std::vector InputSelector::filterThreshold(const std::vector& inputs, uint64_t minimumAmount) { +std::vector +InputSelector::filterThreshold(const std::vector& inputs, + uint64_t minimumAmount) noexcept { std::vector filtered; - for (auto& i: inputs) { - if (i.amount > minimumAmount) { + for (auto& i : inputs) { + if (static_cast(i.amount) > minimumAmount) { filtered.push_back(i); } } @@ -48,9 +51,10 @@ std::vector InputSelector::filterThreshold(const // [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], // [7, 8, 9]] template -static inline std::vector> slice(const std::vector& inputs, size_t sliceSize) { +static inline std::vector> +slice(const std::vector& inputs, size_t sliceSize) { std::vector> slices; - for (auto i = 0; i <= inputs.size() - sliceSize; ++i) { + for (auto i = 0ul; i <= inputs.size() - sliceSize; ++i) { slices.emplace_back(); slices[i].reserve(sliceSize); for (auto j = i; j < i + sliceSize; j++) { @@ -61,40 +65,41 @@ static inline std::vector> slice(const std::vector -std::vector InputSelector::select(int64_t targetValue, int64_t byteFee, int64_t numOutputs) { +std::vector +InputSelector::select(uint64_t targetValue, uint64_t byteFee, uint64_t numOutputs) { // if target value is zero, no UTXOs are needed if (targetValue == 0) { return {}; } // total values of utxos should be greater than targetValue - if (inputs.empty() || sum(inputs) < targetValue) { + if (_inputs.empty() || sum(_inputs) < targetValue) { return {}; } - assert(inputs.size() >= 1); + assert(_inputs.size() >= 1); - // definitions for the following caluculation + // definitions for the following calculation const auto doubleTargetValue = targetValue * 2; // Get all possible utxo selections up to a maximum size, sort by total amount, increasing - std::vector sorted = inputs; + std::vector sorted = _inputs; std::sort(sorted.begin(), sorted.end(), - [](const TypeWithAmount& lhs, const TypeWithAmount& rhs) { - return lhs.amount < rhs.amount; - }); + [](const TypeWithAmount& lhs, const TypeWithAmount& rhs) { + return lhs.amount < rhs.amount; + }); // Precompute maximum amount possible to obtain with given number of inputs const auto n = sorted.size(); std::vector maxWithXInputs = std::vector(); maxWithXInputs.push_back(0); int64_t maxSum = 0; - for (auto i = 0; i < n; ++i) { + for (auto i = 0ul; i < n; ++i) { maxSum += sorted[n - 1 - i].amount; maxWithXInputs.push_back(maxSum); } // difference from 2x targetValue - auto distFrom2x = [doubleTargetValue](int64_t val) -> int64_t { + auto distFrom2x = [doubleTargetValue](uint64_t val) -> uint64_t { if (val > doubleTargetValue) { return val - doubleTargetValue; } @@ -118,15 +123,16 @@ std::vector InputSelector::select(int64_t target slices.erase( std::remove_if(slices.begin(), slices.end(), - [targetWithFeeAndDust](const std::vector& slice) { - return sum(slice) < targetWithFeeAndDust; - }), + [targetWithFeeAndDust](const std::vector& slice) { + return sum(slice) < targetWithFeeAndDust; + }), slices.end()); if (!slices.empty()) { std::sort(slices.begin(), slices.end(), - [distFrom2x](const std::vector& lhs, const std::vector& rhs) { - return distFrom2x(sum(lhs)) < distFrom2x(sum(rhs)); - }); + [distFrom2x](const std::vector& lhs, + const std::vector& rhs) { + return distFrom2x(sum(lhs)) < distFrom2x(sum(rhs)); + }); return filterOutDust(slices.front(), byteFee); } } @@ -140,12 +146,11 @@ std::vector InputSelector::select(int64_t target continue; } auto slices = slice(sorted, static_cast(numInputs)); - slices.erase( - std::remove_if(slices.begin(), slices.end(), - [targetWithFee](const std::vector& slice) { - return sum(slice) < targetWithFee; - }), - slices.end()); + slices.erase(std::remove_if(slices.begin(), slices.end(), + [targetWithFee](const std::vector& slice) { + return sum(slice) < targetWithFee; + }), + slices.end()); if (!slices.empty()) { return filterOutDust(slices.front(), byteFee); } @@ -155,25 +160,30 @@ std::vector InputSelector::select(int64_t target } template -std::vector InputSelector::selectSimple(int64_t targetValue, int64_t byteFee, int64_t numOutputs) { +std::vector InputSelector::selectSimple(int64_t targetValue, + int64_t byteFee, + int64_t numOutputs) { // if target value is zero, no UTXOs are needed if (targetValue == 0) { return {}; } - if (inputs.empty()) { + if (_inputs.empty()) { return {}; } - assert(inputs.size() >= 1); + assert(_inputs.size() >= 1); - // target value is larger that original, but not by a factor of 2 (optioized for large UTXO cases) - const auto increasedTargetValue = (uint64_t)((double)targetValue * 1.1 + feeCalculator.calculate(inputs.size(), numOutputs, byteFee) + 1000); + // target value is larger that original, but not by a factor of 2 (optimized for large UTXO + // cases) + const auto increasedTargetValue = + (uint64_t)((double)targetValue * 1.1 + + feeCalculator.calculate(_inputs.size(), numOutputs, byteFee) + 1000); const int64_t dustThreshold = feeCalculator.calculateSingleInput(byteFee); // Go through inputs in a single pass, in the order they appear, no optimization uint64_t sum = 0; std::vector selected; - for (auto& input: inputs) { + for (auto& input : _inputs) { if (input.amount <= dustThreshold) { continue; // skip dust } @@ -190,9 +200,12 @@ std::vector InputSelector::selectSimple(int64_t } template -std::vector InputSelector::selectMaxAmount(int64_t byteFee) { - return filterOutDust(inputs, byteFee); +std::vector +InputSelector::selectMaxAmount(int64_t byteFee) noexcept { + return filterOutDust(_inputs, byteFee); } // Explicitly instantiate template class Bitcoin::InputSelector; + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/InputSelector.h b/src/Bitcoin/InputSelector.h index 027be21f4c2..73298a69729 100644 --- a/src/Bitcoin/InputSelector.h +++ b/src/Bitcoin/InputSelector.h @@ -14,37 +14,46 @@ namespace TW::Bitcoin { -template // TypeWithAmount has to have a uint64_t amount +template // TypeWithAmount has to have an uint64_t amount class InputSelector { - public: +public: /// Selects unspent transactions to use given a target transaction value, using complete logic. /// - /// \returns the list of indices of selected inputs, or an empty list if there are insufficient funds. - std::vector select(int64_t targetValue, int64_t byteFee, int64_t numOutputs = 2); + /// \returns the list of indices of selected inputs, or an empty list if there are insufficient + /// funds. + std::vector select(uint64_t targetValue, uint64_t byteFee, + uint64_t numOutputs = 2); /// Selects unspent transactions to use given a target transaction value; /// Simplified version suitable for large number of inputs /// - /// \returns the list of indices of selected inputs, or an empty list if there are insufficient funds. - std::vector selectSimple(int64_t targetValue, int64_t byteFee, int64_t numOutputs = 2); + /// \returns the list of indices of selected inputs, or an empty list if there are insufficient + /// funds. + std::vector selectSimple(int64_t targetValue, int64_t byteFee, + int64_t numOutputs = 2); - /// Selects UTXOs for max amount; select all except those which would reduce output (dust). Return indIces. - /// One output and no change is assumed. - std::vector selectMaxAmount(int64_t byteFee); + /// Selects UTXOs for max amount; select all except those which would reduce output (dust). + /// Return indices. One output and no change is assumed. + std::vector selectMaxAmount(int64_t byteFee) noexcept; /// Construct, using provided feeCalculator (see getFeeCalculator()). - explicit InputSelector(const std::vector& inputs, const FeeCalculator& feeCalculator) : inputs(inputs), feeCalculator(feeCalculator) {} - InputSelector(const std::vector& inputs) : InputSelector(inputs, getFeeCalculator(TWCoinTypeBitcoin)) {} + explicit InputSelector(const std::vector& inputs, + const FeeCalculator& feeCalculator) noexcept + : _inputs(inputs), feeCalculator(feeCalculator) {} + explicit InputSelector(const std::vector& inputs) noexcept + : InputSelector(inputs, getFeeCalculator(TWCoinTypeBitcoin)) {} /// Sum of input amounts - static uint64_t sum(const std::vector& amounts); + static uint64_t sum(const std::vector& amounts) noexcept; /// Filters out utxos that are dust - std::vector filterOutDust(const std::vector& inputs, int64_t byteFee); - /// Filters out inputs below (or equal) a certain threshold limit - std::vector filterThreshold(const std::vector& inputs, uint64_t minimumAmount); - - private: - const std::vector inputs; + inline std::vector filterOutDust(const std::vector& inputsIn, + int64_t byteFee) noexcept; + /// Filters out inputsIn below (or equal) a certain threshold limit + inline std::vector filterThreshold(const std::vector& inputsIn, + uint64_t minimumAmount) noexcept; + +private: + const std::vector _inputs; const FeeCalculator& feeCalculator; }; diff --git a/src/Bitcoin/MessageSigner.cpp b/src/Bitcoin/MessageSigner.cpp new file mode 100644 index 00000000000..21f3ca90de7 --- /dev/null +++ b/src/Bitcoin/MessageSigner.cpp @@ -0,0 +1,111 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "MessageSigner.h" +#include "Address.h" + +#include "Base64.h" +#include "BinaryCoding.h" +#include "Coin.h" +#include "Data.h" +#include "HexCoding.h" + +using namespace TW; + +namespace TW::Bitcoin { + +// lenght-encode a message string +Data messageToData(const std::string& message) { + Data d; + TW::encodeVarInt(message.size(), d); + TW::append(d, TW::data(message)); + return d; +} + +// append prefix and length-encode message string +Data messageToFullData(const std::string& message) { + Data d = messageToData(MessageSigner::MessagePrefix); + TW::append(d, messageToData(message)); + return d; +} + +Data MessageSigner::messageToHash(const std::string& message) { + Data d = messageToFullData(message); + return Hash::sha256d(d.data(), d.size()); +} + +std::string MessageSigner::signMessage(const PrivateKey& privateKey, const std::string& address, const std::string& message, bool compressed) { + if (!Address::isValid(address)) { + throw std::invalid_argument("Address is not valid (legacy) address"); + } + std::string addrFromKey; + if (compressed) { + const auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + addrFromKey = Address(pubKey, TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); + } else { + const auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + const auto keyHash = pubKey.hash(Data{TW::p2pkhPrefix(TWCoinTypeBitcoin)}, Hash::HasherSha256ripemd); + addrFromKey = Address(keyHash).string(); + } + if (addrFromKey != address) { + throw std::invalid_argument("Address does not match key"); + } + const auto messageHash = messageToHash(message); + const auto signature = privateKey.sign(messageHash, TWCurveSECP256k1); + + // The V value: add 31 (or 27 for compressed), and move to the first byte + const byte v = signature[SignatureRSLength] + PublicKey::SignatureVOffset + (compressed ? 4ul : 0ul); + auto sigAdjusted = Data{v}; + TW::append(sigAdjusted, TW::subData(signature, 0, SignatureRSLength)); + return Base64::encode(sigAdjusted); +} + +std::string MessageSigner::recoverAddressFromMessage(const std::string& message, const Data& signature) { + if (signature.size() < SignatureRSVLength) { + throw std::invalid_argument("signature too short"); + } + const auto messageHash = MessageSigner::messageToHash(message); + auto recId = signature[0]; + auto compressed = false; + if (recId >= PublicKey::SignatureVOffset + 4) { + recId -= 4; + compressed = true; + } + if (recId >= PublicKey::SignatureVOffset) { + recId -= PublicKey::SignatureVOffset; + } + + const auto publicKeyRecovered = PublicKey::recoverRaw(TW::subData(signature, 1), recId, messageHash); + + if (!compressed) { + // uncompressed public key + const auto keyHash = publicKeyRecovered.hash(Data{TW::p2pkhPrefix(TWCoinTypeBitcoin)}, Hash::HasherSha256ripemd); + return Bitcoin::Address(keyHash).string(); + } + // compressed + const auto publicKeyRecoveredCompressed = publicKeyRecovered.compressed(); + return Bitcoin::Address(publicKeyRecoveredCompressed, TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); +} + +bool MessageSigner::verifyMessage(const std::string& address, const std::string& message, const std::string& signature) noexcept { + try { + const auto signatureData = Base64::decode(signature); + return verifyMessage(address, message, signatureData); + } catch (...) { + return false; + } +} + +/// May throw +bool MessageSigner::verifyMessage(const std::string& address, const std::string& message, const Data& signature) { + if (!Bitcoin::Address::isValid(address)) { + throw std::invalid_argument("Input address invalid, must be valid legacy"); + } + const auto addressRecovered = recoverAddressFromMessage(message, signature); + return (addressRecovered == address); +} + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/MessageSigner.h b/src/Bitcoin/MessageSigner.h new file mode 100644 index 00000000000..6779bfaf52d --- /dev/null +++ b/src/Bitcoin/MessageSigner.h @@ -0,0 +1,61 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" +#include "PrivateKey.h" + +#include + +namespace TW::Bitcoin { + +/// Class for message signing and verification. +/// +/// Bitcoin Core and some other wallets support a message signing & verification format, to create a proof (a signature) +/// that someone has access to the private keys of a specific address. +/// This feature currently works on old legacy addresses only. +class MessageSigner { + public: + /// Sign a message. + /// privateKey: the private key used for signing + /// address: the address that matches the privateKey, must be a legacy address (P2PKH) + /// message: A custom message which is input to the signing. + /// compressed: True by default, as addresses are generated from the hash of the compressed public key. + /// However, in some instances key hash is generated from the hash of the extended public key, + /// that's also supported here as well for compatibility. + /// Returns the signature, Base64-encoded. + /// Throws on invalid input. + static std::string signMessage(const PrivateKey& privateKey, const std::string& address, const std::string& message, bool compressed = true); + + /// Verify signature for a message. + /// address: address to use, only legacy is supported + /// message: the message signed (without prefix) + /// signature: in Base64-encoded form. + /// Returns false on any invalid input (does not throw). + static bool verifyMessage(const std::string& address, const std::string& message, const std::string& signature) noexcept; + + /// Verify signature for a message. + /// Address: address to use, only legacy is supported + /// message: the message signed (without prefix) + /// signature: in binary form. + /// May throw + static bool verifyMessage(const std::string& address, const std::string& message, const Data& signature); + + /// Recover address from signature and message. May throw. + static std::string recoverAddressFromMessage(const std::string& message, const Data& signature); + + /// Append prefix and compute hash for a message + static Data messageToHash(const std::string& message); + + static constexpr auto MessagePrefix = "Bitcoin Signed Message:\n"; + static const byte DigestLength = 32; + static const byte SignatureRSLength = 64; + static constexpr byte SignatureRSVLength = SignatureRSLength + 1; + static const byte VOffset = 27; +}; + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/OpCodes.h b/src/Bitcoin/OpCodes.h index f3a4405d697..fa797be4b19 100644 --- a/src/Bitcoin/OpCodes.h +++ b/src/Bitcoin/OpCodes.h @@ -9,139 +9,139 @@ enum OpCode { // push value OP_0 = 0x00, - OP_FALSE = OP_0, + OP_FALSE [[maybe_unused]] = OP_0, OP_PUSHDATA1 = 0x4c, OP_PUSHDATA2 = 0x4d, OP_PUSHDATA4 = 0x4e, - OP_1NEGATE = 0x4f, - OP_RESERVED = 0x50, + OP_1NEGATE [[maybe_unused]] = 0x4f, + OP_RESERVED [[maybe_unused]] = 0x50, OP_1 = 0x51, - OP_TRUE = OP_1, - OP_2 = 0x52, + OP_TRUE [[maybe_unused]] = OP_1, + OP_2 [[maybe_unused]] = 0x52, OP_3 = 0x53, - OP_4 = 0x54, - OP_5 = 0x55, - OP_6 = 0x56, - OP_7 = 0x57, - OP_8 = 0x58, + OP_4 [[maybe_unused]] = 0x54, + OP_5 [[maybe_unused]] = 0x55, + OP_6 [[maybe_unused]] = 0x56, + OP_7 [[maybe_unused]] = 0x57, + OP_8 [[maybe_unused]] = 0x58, OP_9 = 0x59, - OP_10 = 0x5a, - OP_11 = 0x5b, - OP_12 = 0x5c, - OP_13 = 0x5d, - OP_14 = 0x5e, - OP_15 = 0x5f, + OP_10 [[maybe_unused]] = 0x5a, + OP_11 [[maybe_unused]] = 0x5b, + OP_12 [[maybe_unused]] = 0x5c, + OP_13 [[maybe_unused]] = 0x5d, + OP_14 [[maybe_unused]] = 0x5e, + OP_15 [[maybe_unused]] = 0x5f, OP_16 = 0x60, // control - OP_NOP = 0x61, - OP_VER = 0x62, - OP_IF = 0x63, - OP_NOTIF = 0x64, - OP_VERIF = 0x65, - OP_VERNOTIF = 0x66, - OP_ELSE = 0x67, - OP_ENDIF = 0x68, - OP_VERIFY = 0x69, + OP_NOP [[maybe_unused]] = 0x61, + OP_VER [[maybe_unused]] = 0x62, + OP_IF [[maybe_unused]] = 0x63, + OP_NOTIF [[maybe_unused]] = 0x64, + OP_VERIF [[maybe_unused]] = 0x65, + OP_VERNOTIF [[maybe_unused]] = 0x66, + OP_ELSE [[maybe_unused]] = 0x67, + OP_ENDIF [[maybe_unused]] = 0x68, + OP_VERIFY [[maybe_unused]] = 0x69, OP_RETURN = 0x6a, // stack ops - OP_TOALTSTACK = 0x6b, - OP_FROMALTSTACK = 0x6c, - OP_2DROP = 0x6d, - OP_2DUP = 0x6e, - OP_3DUP = 0x6f, - OP_2OVER = 0x70, - OP_2ROT = 0x71, - OP_2SWAP = 0x72, - OP_IFDUP = 0x73, - OP_DEPTH = 0x74, - OP_DROP = 0x75, + OP_TOALTSTACK [[maybe_unused]] = 0x6b, + OP_FROMALTSTACK [[maybe_unused]] = 0x6c, + OP_2DROP [[maybe_unused]] = 0x6d, + OP_2DUP [[maybe_unused]] = 0x6e, + OP_3DUP [[maybe_unused]] = 0x6f, + OP_2OVER [[maybe_unused]] = 0x70, + OP_2ROT [[maybe_unused]] = 0x71, + OP_2SWAP [[maybe_unused]] = 0x72, + OP_IFDUP [[maybe_unused]] = 0x73, + OP_DEPTH [[maybe_unused]] = 0x74, + OP_DROP [[maybe_unused]] = 0x75, OP_DUP = 0x76, - OP_NIP = 0x77, - OP_OVER = 0x78, - OP_PICK = 0x79, - OP_ROLL = 0x7a, - OP_ROT = 0x7b, - OP_SWAP = 0x7c, - OP_TUCK = 0x7d, + OP_NIP [[maybe_unused]] = 0x77, + OP_OVER [[maybe_unused]] = 0x78, + OP_PICK [[maybe_unused]] = 0x79, + OP_ROLL [[maybe_unused]] = 0x7a, + OP_ROT [[maybe_unused]] = 0x7b, + OP_SWAP [[maybe_unused]] = 0x7c, + OP_TUCK [[maybe_unused]] = 0x7d, // splice ops - OP_CAT = 0x7e, - OP_SUBSTR = 0x7f, - OP_LEFT = 0x80, - OP_RIGHT = 0x81, - OP_SIZE = 0x82, + OP_CAT [[maybe_unused]] = 0x7e, + OP_SUBSTR [[maybe_unused]] = 0x7f, + OP_LEFT [[maybe_unused]] = 0x80, + OP_RIGHT [[maybe_unused]] = 0x81, + OP_SIZE [[maybe_unused]] = 0x82, // bit logic - OP_INVERT = 0x83, - OP_AND = 0x84, - OP_OR = 0x85, - OP_XOR = 0x86, + OP_INVERT [[maybe_unused]] = 0x83, + OP_AND [[maybe_unused]] = 0x84, + OP_OR [[maybe_unused]] = 0x85, + OP_XOR [[maybe_unused]] = 0x86, OP_EQUAL = 0x87, OP_EQUALVERIFY = 0x88, - OP_RESERVED1 = 0x89, - OP_RESERVED2 = 0x8a, + OP_RESERVED1 [[maybe_unused]] = 0x89, + OP_RESERVED2 [[maybe_unused]] = 0x8a, // numeric - OP_1ADD = 0x8b, - OP_1SUB = 0x8c, - OP_2MUL = 0x8d, - OP_2DIV = 0x8e, - OP_NEGATE = 0x8f, - OP_ABS = 0x90, - OP_NOT = 0x91, - OP_0NOTEQUAL = 0x92, + OP_1ADD [[maybe_unused]] = 0x8b, + OP_1SUB [[maybe_unused]] = 0x8c, + OP_2MUL [[maybe_unused]] = 0x8d, + OP_2DIV [[maybe_unused]] = 0x8e, + OP_NEGATE [[maybe_unused]] = 0x8f, + OP_ABS [[maybe_unused]] = 0x90, + OP_NOT [[maybe_unused]] = 0x91, + OP_0NOTEQUAL [[maybe_unused]] = 0x92, - OP_ADD = 0x93, - OP_SUB = 0x94, - OP_MUL = 0x95, - OP_DIV = 0x96, - OP_MOD = 0x97, - OP_LSHIFT = 0x98, - OP_RSHIFT = 0x99, + OP_ADD [[maybe_unused]] = 0x93, + OP_SUB [[maybe_unused]] = 0x94, + OP_MUL [[maybe_unused]] = 0x95, + OP_DIV [[maybe_unused]] = 0x96, + OP_MOD [[maybe_unused]] = 0x97, + OP_LSHIFT [[maybe_unused]] = 0x98, + OP_RSHIFT [[maybe_unused]] = 0x99, - OP_BOOLAND = 0x9a, - OP_BOOLOR = 0x9b, - OP_NUMEQUAL = 0x9c, - OP_NUMEQUALVERIFY = 0x9d, - OP_NUMNOTEQUAL = 0x9e, - OP_LESSTHAN = 0x9f, - OP_GREATERTHAN = 0xa0, - OP_LESSTHANOREQUAL = 0xa1, - OP_GREATERTHANOREQUAL = 0xa2, - OP_MIN = 0xa3, - OP_MAX = 0xa4, + OP_BOOLAND [[maybe_unused]] = 0x9a, + OP_BOOLOR [[maybe_unused]] = 0x9b, + OP_NUMEQUAL [[maybe_unused]] = 0x9c, + OP_NUMEQUALVERIFY [[maybe_unused]] = 0x9d, + OP_NUMNOTEQUAL [[maybe_unused]] = 0x9e, + OP_LESSTHAN [[maybe_unused]] = 0x9f, + OP_GREATERTHAN [[maybe_unused]] = 0xa0, + OP_LESSTHANOREQUAL [[maybe_unused]] = 0xa1, + OP_GREATERTHANOREQUAL [[maybe_unused]] = 0xa2, + OP_MIN [[maybe_unused]] = 0xa3, + OP_MAX [[maybe_unused]] = 0xa4, - OP_WITHIN = 0xa5, + OP_WITHIN [[maybe_unused]] = 0xa5, // crypto - OP_RIPEMD160 = 0xa6, - OP_SHA1 = 0xa7, - OP_SHA256 = 0xa8, + OP_RIPEMD160 [[maybe_unused]] = 0xa6, + OP_SHA1 [[maybe_unused]] = 0xa7, + OP_SHA256 [[maybe_unused]] = 0xa8, OP_HASH160 = 0xa9, - OP_HASH256 = 0xaa, - OP_CODESEPARATOR = 0xab, + OP_HASH256 [[maybe_unused]] = 0xaa, + OP_CODESEPARATOR [[maybe_unused]] = 0xab, OP_CHECKSIG = 0xac, - OP_CHECKSIGVERIFY = 0xad, + OP_CHECKSIGVERIFY [[maybe_unused]] = 0xad, OP_CHECKMULTISIG = 0xae, - OP_CHECKMULTISIGVERIFY = 0xaf, + OP_CHECKMULTISIGVERIFY [[maybe_unused]] = 0xaf, // expansion - OP_NOP1 = 0xb0, + OP_NOP1 [[maybe_unused]] = 0xb0, OP_CHECKLOCKTIMEVERIFY = 0xb1, - OP_NOP2 = OP_CHECKLOCKTIMEVERIFY, + OP_NOP2 [[maybe_unused]] = OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY = 0xb2, - OP_NOP3 = OP_CHECKSEQUENCEVERIFY, - OP_NOP4 = 0xb3, - OP_NOP5 = 0xb4, - OP_NOP6 = 0xb5, - OP_NOP7 = 0xb6, - OP_NOP8 = 0xb7, - OP_NOP9 = 0xb8, - OP_NOP10 = 0xb9, + OP_NOP3 [[maybe_unused]] = OP_CHECKSEQUENCEVERIFY, + OP_NOP4 [[maybe_unused]] = 0xb3, + OP_NOP5 [[maybe_unused]] = 0xb4, + OP_NOP6 [[maybe_unused]] = 0xb5, + OP_NOP7 [[maybe_unused]] = 0xb6, + OP_NOP8 [[maybe_unused]] = 0xb7, + OP_NOP9 [[maybe_unused]] = 0xb8, + OP_NOP10 [[maybe_unused]] = 0xb9, - OP_INVALIDOPCODE = 0xff, + OP_INVALIDOPCODE [[maybe_unused]] = 0xff, }; static inline bool TWOpCodeIsSmallInteger(uint8_t opcode) { diff --git a/src/Bitcoin/OutPoint.cpp b/src/Bitcoin/OutPoint.cpp index 611c837003c..922f23d35de 100644 --- a/src/Bitcoin/OutPoint.cpp +++ b/src/Bitcoin/OutPoint.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,10 +8,13 @@ #include "../BinaryCoding.h" -using namespace TW::Bitcoin; +namespace TW::Bitcoin { -void OutPoint::encode(Data& data) const { +void OutPoint::encode(Data& data) const noexcept { std::copy(std::begin(hash), std::end(hash), std::back_inserter(data)); encode32LE(index, data); // sequence is encoded in TransactionInputs } + +} // namespace TW::Bitcoin + diff --git a/src/Bitcoin/OutPoint.h b/src/Bitcoin/OutPoint.h index eb5e61f496d..0abed5a31a1 100644 --- a/src/Bitcoin/OutPoint.h +++ b/src/Bitcoin/OutPoint.h @@ -6,7 +6,8 @@ #pragma once -#include "../Data.h" +#include "algorithm/to_array.h" +#include "Data.h" #include "../proto/Bitcoin.pb.h" #include @@ -16,49 +17,32 @@ namespace TW::Bitcoin { /// Bitcoin transaction out-point reference. -class OutPoint { - public: +struct OutPoint { /// The hash of the referenced transaction. std::array hash; /// The index of the specific output in the transaction. uint32_t index; - /// Sequence number, matches sequence from Proto::OutPoint (not always used, see also TransactionInput.sequence) + /// Sequence number, matches sequence from Proto::OutPoint (not always used, see also + /// TransactionInput.sequence) uint32_t sequence; - OutPoint() = default; + OutPoint() noexcept = default; /// Initializes an out-point reference with hash, index. template - OutPoint(const T& h, uint32_t index, uint32_t sequence = 0 ) { - std::copy(std::begin(h), std::end(h), hash.begin()); - this->index = index; - this->sequence = sequence; - } + OutPoint(const T& h, uint32_t index, uint32_t sequence = 0) noexcept + : hash(to_array(h)), index(index), sequence(sequence) {} /// Initializes an out-point from a Protobuf out-point. - OutPoint(const Proto::OutPoint& other) { + OutPoint(const Proto::OutPoint& other) noexcept + : OutPoint(other.hash(), other.index(), other.sequence()) { assert(other.hash().size() == 32); - std::copy(other.hash().begin(), other.hash().end(), hash.begin()); - index = other.index(); - sequence = other.sequence(); } /// Encodes the out-point into the provided buffer. - void encode(Data& data) const; - - friend bool operator<(const OutPoint& a, const OutPoint& b) { - int cmp = std::memcmp(a.hash.data(), b.hash.data(), 32); - return cmp < 0 || (cmp == 0 && a.index < b.index); - } - - friend bool operator==(const OutPoint& a, const OutPoint& b) { - int cmp = std::memcmp(a.hash.data(), b.hash.data(), 32); - return (cmp == 0 && a.index == b.index); - } - - friend bool operator!=(const OutPoint& a, const OutPoint& b) { return !(a == b); } + void encode(Data& data) const noexcept; Proto::OutPoint proto() const { auto op = Proto::OutPoint(); diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp index ca991bc4eed..7000814c396 100644 --- a/src/Bitcoin/Script.cpp +++ b/src/Bitcoin/Script.cpp @@ -1,35 +1,26 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Script.h" - #include "Address.h" #include "CashAddress.h" +#include "OpCodes.h" +#include "Script.h" #include "SegwitAddress.h" -#include "../Base58.h" -#include "../Coin.h" - #include "../BinaryCoding.h" -#include "../Data.h" +#include "../Coin.h" #include "../Decred/Address.h" #include "../Groestlcoin/Address.h" -#include "../Hash.h" -#include "../PublicKey.h" #include "../Zcash/TAddress.h" -#include "OpCodes.h" - #include #include #include -#include -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { Data Script::hash() const { return Hash::ripemd(Hash::sha256(bytes)); @@ -148,8 +139,8 @@ bool Script::matchMultisig(std::vector& keys, int& required) const { return false; } - auto expectedCount = decodeNumber(opcode); - if (keys.size() != expectedCount || expectedCount < required) { + std::size_t expectedCount = decodeNumber(opcode); + if (keys.size() != expectedCount || expectedCount < static_cast(required)) { return false; } if (it + 1 != bytes.size()) { @@ -237,7 +228,7 @@ Script Script::buildPayToScriptHash(const Data& scriptHash) { return script; } -Script Script::buildPayToWitnessProgram(const Data& program) { +Script Script::buildPayToV0WitnessProgram(const Data& program) { assert(program.size() == 20 || program.size() == 32); Script script; script.bytes.push_back(OP_0); @@ -249,12 +240,32 @@ Script Script::buildPayToWitnessProgram(const Data& program) { Script Script::buildPayToWitnessPublicKeyHash(const Data& hash) { assert(hash.size() == 20); - return Script::buildPayToWitnessProgram(hash); + return Script::buildPayToV0WitnessProgram(hash); } Script Script::buildPayToWitnessScriptHash(const Data& scriptHash) { assert(scriptHash.size() == 32); - return Script::buildPayToWitnessProgram(scriptHash); + return Script::buildPayToV0WitnessProgram(scriptHash); +} + +Script Script::buildPayToV1WitnessProgram(const Data& publicKey) { + assert(publicKey.size() == 32); + Script script; + script.bytes.push_back(OP_1); + script.bytes.push_back(static_cast(publicKey.size())); + append(script.bytes, publicKey); + assert(script.bytes.size() == 34); + return script; +} + +Script Script::buildOpReturnScript(const Data& data) { + static const size_t MaxOpReturnLength = 64; + Script script; + script.bytes.push_back(OP_RETURN); + size_t size = std::min(data.size(), MaxOpReturnLength); + script.bytes.push_back(static_cast(size)); + script.bytes.insert(script.bytes.end(), data.begin(), data.begin() + size); + return script; } void Script::encode(Data& data) const { @@ -281,22 +292,31 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c return buildPayToScriptHash(data); } } else if (SegwitAddress::isValid(string)) { - auto result = SegwitAddress::decode(string); + const auto result = SegwitAddress::decode(string); // address starts with bc/ltc - auto program = std::get<0>(result).witnessProgram; - return buildPayToWitnessProgram(program); - } else if (CashAddress::isValid(string)) { - auto address = CashAddress(string); + const auto address = std::get<0>(result); + if (address.witnessVersion == 0) { + return buildPayToV0WitnessProgram(address.witnessProgram); + } + if (address.witnessVersion == 1 && address.witnessProgram.size() == 32) { + return buildPayToV1WitnessProgram(address.witnessProgram); + } + } else if (BitcoinCashAddress::isValid(string)) { + auto address = BitcoinCashAddress(string); auto bitcoinAddress = address.legacyAddress(); return lockScriptForAddress(bitcoinAddress.string(), TWCoinTypeBitcoinCash); } else if (Decred::Address::isValid(string)) { - auto bytes = Base58::bitcoin.decodeCheck(string, Hash::blake256d); + auto bytes = Base58::bitcoin.decodeCheck(string, Hash::HasherBlake256d); if (bytes[1] == TW::p2pkhPrefix(TWCoinTypeDecred)) { return buildPayToPublicKeyHash(Data(bytes.begin() + 2, bytes.end())); } if (bytes[1] == TW::p2shPrefix(TWCoinTypeDecred)) { return buildPayToScriptHash(Data(bytes.begin() + 2, bytes.end())); } + } else if (ECashAddress::isValid(string)) { + auto address = ECashAddress(string); + auto bitcoinAddress = address.legacyAddress(); + return lockScriptForAddress(bitcoinAddress.string(), TWCoinTypeECash); } else if (Groestlcoin::Address::isValid(string)) { auto address = Groestlcoin::Address(string); auto data = Data(); @@ -321,3 +341,5 @@ Script Script::lockScriptForAddress(const std::string& string, enum TWCoinType c } return {}; } + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/Script.h b/src/Bitcoin/Script.h index 5890a24093f..39cc5c874fc 100644 --- a/src/Bitcoin/Script.h +++ b/src/Bitcoin/Script.h @@ -6,12 +6,13 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "OpCodes.h" #include #include +#include #include #include @@ -29,8 +30,8 @@ class Script { template Script(It begin, It end) : bytes(begin, end) {} - /// Initializaes a script with a collection of raw bytes by moving. - explicit Script(const Data& bytes) : bytes(bytes) {} + /// Initializes a script with a collection of raw bytes by moving. + explicit Script(Data bytes) : bytes(std::move(bytes)) {} /// Whether the script is empty. bool empty() const { return bytes.empty(); } @@ -47,7 +48,7 @@ class Script { /// Determines whether this is a pay-to-witness-public-key-hash (P2WPKH) script. bool isPayToWitnessPublicKeyHash() const; - /// Determines whether this is a witness programm script. + /// Determines whether this is a witness program script. bool isWitnessProgram() const; /// Matches the script to a pay-to-public-key (P2PK) script. @@ -69,7 +70,7 @@ class Script { bool matchMultisig(std::vector& publicKeys, int& required) const; /// Builds a pay-to-public-key (P2PK) script from a public key. - static Script buildPayToPublicKey(const Data& publickKey); + static Script buildPayToPublicKey(const Data& publicKey); /// Builds a pay-to-public-key-hash (P2PKH) script from a public key hash. static Script buildPayToPublicKeyHash(const Data& hash); @@ -77,9 +78,6 @@ class Script { /// Builds a pay-to-script-hash (P2SH) script from a script hash. static Script buildPayToScriptHash(const Data& scriptHash); - /// Builds a pay-to-witness-program script, P2WSH or P2WPKH. - static Script buildPayToWitnessProgram(const Data& program); - /// Builds a pay-to-witness-public-key-hash (P2WPKH) script from a public /// key hash. static Script buildPayToWitnessPublicKeyHash(const Data& hash); @@ -87,6 +85,15 @@ class Script { /// Builds a pay-to-witness-script-hash (P2WSH) script from a script hash. static Script buildPayToWitnessScriptHash(const Data& scriptHash); + /// Builds a V0 pay-to-witness-program script, P2WSH or P2WPKH. + static Script buildPayToV0WitnessProgram(const Data& program); + + /// Builds a V1 pay-to-witness-program script, P2TR (from a 32-byte Schnorr public key). + static Script buildPayToV1WitnessProgram(const Data& publicKey); + + /// Builds an OP_RETURN script with given data + static Script buildOpReturnScript(const Data& data); + /// Builds a appropriate lock script for the given /// address. static Script lockScriptForAddress(const std::string& address, enum TWCoinType coin); diff --git a/src/Bitcoin/SegwitAddress.cpp b/src/Bitcoin/SegwitAddress.cpp index a658da281cd..4e40ce06ab7 100644 --- a/src/Bitcoin/SegwitAddress.cpp +++ b/src/Bitcoin/SegwitAddress.cpp @@ -1,5 +1,5 @@ // Copyright © 2017 Pieter Wuille -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,9 +9,8 @@ #include "../Bech32.h" #include -#include -using namespace TW::Bitcoin; +namespace TW::Bitcoin { bool SegwitAddress::isValid(const std::string& string) { return std::get<2>(decode(string)); @@ -31,8 +30,8 @@ bool SegwitAddress::isValid(const std::string& string, const std::string& hrp) { return true; } -SegwitAddress::SegwitAddress(const PublicKey& publicKey, int witver, std::string hrp) - : hrp(std::move(hrp)), witnessVersion(witver), witnessProgram() { +SegwitAddress::SegwitAddress(const PublicKey& publicKey, std::string hrp) + : hrp(std::move(hrp)), witnessVersion(0), witnessProgram() { if (publicKey.type != TWPublicKeyTypeSECP256k1) { throw std::invalid_argument("SegwitAddress needs a compressed SECP256k1 public key."); } @@ -73,10 +72,10 @@ std::tuple SegwitAddress::decode(const std::st std::string SegwitAddress::string() const { Data enc; - enc.push_back(static_cast(witnessVersion)); + enc.push_back(witnessVersion); Bech32::convertBits<8, 5, true>(enc, witnessProgram); Bech32::ChecksumVariant variant = Bech32::ChecksumVariant::Bech32; - if (witnessVersion== 0) { + if (witnessVersion == 0) { variant = Bech32::ChecksumVariant::Bech32; } else if (witnessVersion >= 1) { variant = Bech32::ChecksumVariant::Bech32M; @@ -103,3 +102,5 @@ std::pair SegwitAddress::fromRaw(const std::string& hrp, co return std::make_pair(SegwitAddress(hrp, data[0], conv), true); } + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/SegwitAddress.h b/src/Bitcoin/SegwitAddress.h index 68ad227e05a..54803acb7ba 100644 --- a/src/Bitcoin/SegwitAddress.h +++ b/src/Bitcoin/SegwitAddress.h @@ -7,7 +7,7 @@ #pragma once #include "../PublicKey.h" -#include "../Data.h" +#include "Data.h" #include #include @@ -26,11 +26,14 @@ class SegwitAddress { std::string hrp; /// Witness program version. - int witnessVersion; + byte witnessVersion; /// Witness program. Data witnessProgram; + // Prefix for Bitcoin Testnet Segwit addresses + static constexpr auto TestnetPrefix = "tb"; + /// Determines whether a string makes a valid Bech32 address. static bool isValid(const std::string& string); @@ -40,11 +43,15 @@ class SegwitAddress { /// Initializes a Bech32 address with a human-readable part, a witness /// version, and a witness program. - SegwitAddress(std::string hrp, int witver, Data witprog) + SegwitAddress(std::string hrp, byte witver, Data witprog) : hrp(std::move(hrp)), witnessVersion(witver), witnessProgram(std::move(witprog)) {} - /// Initializes a Bech32 address with a public key and a HRP prefix. - SegwitAddress(const PublicKey& publicKey, int witver, std::string hrp); + /// Initializes a segwit-version-0 Bech32 address with a public key and a HRP prefix. + /// Taproot (v>=1) is not supported by this method. + SegwitAddress(const PublicKey& publicKey, std::string hrp); + + /// Create a testnet address + static SegwitAddress createTestnetFromPublicKey(const PublicKey& publicKey) { return SegwitAddress(publicKey, TestnetPrefix); } /// Decodes a SegWit address. /// diff --git a/src/Bitcoin/SigHashType.h b/src/Bitcoin/SigHashType.h index b0540187b00..72329e6a477 100644 --- a/src/Bitcoin/SigHashType.h +++ b/src/Bitcoin/SigHashType.h @@ -17,9 +17,10 @@ static const uint32_t SigHashMask = 0x1f; // Return the default HashType for the given coin, such as TWBitcoinSigHashTypeAll. inline enum TWBitcoinSigHashType hashTypeForCoin(enum TWCoinType coinType) { - // set fork hash type for BCH + // set fork hash type for BCH and XEC switch (coinType) { case TWCoinTypeBitcoinCash: + case TWCoinTypeECash: return (TWBitcoinSigHashType)((uint32_t)TWBitcoinSigHashTypeAll | (uint32_t)TWBitcoinSigHashTypeFork); case TWCoinTypeBitcoinGold: return (TWBitcoinSigHashType)((uint32_t)TWBitcoinSigHashTypeAll | (uint32_t)TWBitcoinSigHashTypeForkBTG); diff --git a/src/Bitcoin/SignatureBuilder.cpp b/src/Bitcoin/SignatureBuilder.cpp index 85398a95016..fe2bb1f6ac3 100644 --- a/src/Bitcoin/SignatureBuilder.cpp +++ b/src/Bitcoin/SignatureBuilder.cpp @@ -1,26 +1,21 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "SignatureBuilder.h" - #include "SigHashType.h" #include "TransactionInput.h" #include "TransactionOutput.h" -#include "InputSelector.h" #include "../BinaryCoding.h" -#include "../Hash.h" #include "../HexCoding.h" - #include "../Groestlcoin/Transaction.h" #include "../Zcash/Transaction.h" #include "../Zcash/TransactionBuilder.h" -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { template Result SignatureBuilder::sign() { @@ -28,23 +23,23 @@ Result SignatureBuilder:: // plan with error, fail return Result::failure(plan.error); } - if (transaction.inputs.size() == 0 || plan.utxos.size() == 0) { + if (_transaction.inputs.size() == 0 || plan.utxos.size() == 0) { return Result::failure(Common::Proto::Error_missing_input_utxos); } - transactionToSign = transaction; + transactionToSign = _transaction; transactionToSign.inputs.clear(); - std::copy(std::begin(transaction.inputs), std::end(transaction.inputs), + std::copy(std::begin(_transaction.inputs), std::end(_transaction.inputs), std::back_inserter(transactionToSign.inputs)); const auto hashSingle = hashTypeIsSingle(input.hashType); - for (auto i = 0; i < plan.utxos.size(); i++) { + for (auto i = 0ul; i < plan.utxos.size(); i++) { // Only sign TWBitcoinSigHashTypeSingle if there's a corresponding output - if (hashSingle && i >= transaction.outputs.size()) { + if (hashSingle && i >= _transaction.outputs.size()) { continue; } auto& utxo = plan.utxos[i]; - if (i < transaction.inputs.size()) { + if (i < _transaction.inputs.size()) { auto result = sign(utxo.script, i, utxo); if (!result) { return Result::failure(result.error()); @@ -62,8 +57,8 @@ Result SignatureBuilder:: template Result SignatureBuilder::sign(Script script, size_t index, - const UTXO& utxo) { - assert(index < transaction.inputs.size()); + const UTXO& utxo) { + assert(index < _transaction.inputs.size()); Script redeemScript; std::vector results; @@ -71,7 +66,7 @@ Result SignatureBuilder::sign(Sc uint32_t signatureVersion = [this]() { if ((input.hashType & TWBitcoinSigHashTypeFork) != 0) { return WITNESS_V0; - } + } return BASE; }(); auto result = signStep(script, index, utxo, signatureVersion); @@ -80,15 +75,15 @@ Result SignatureBuilder::sign(Sc } results = result.payload(); assert(results.size() >= 1); - auto txin = transaction.inputs[index]; + auto txin = _transaction.inputs[index]; if (script.isPayToScriptHash()) { script = Script(results[0]); - auto result = signStep(script, index, utxo, signatureVersion); - if (!result) { - return Result::failure(result.error()); + auto signStepResult = signStep(script, index, utxo, signatureVersion); + if (!signStepResult) { + return Result::failure(signStepResult.error()); } - results = result.payload(); + results = signStepResult.payload(); results.push_back(script.bytes); redeemScript = script; } @@ -97,20 +92,20 @@ Result SignatureBuilder::sign(Sc Data data; if (script.matchPayToWitnessPublicKeyHash(data)) { auto witnessScript = Script::buildPayToPublicKeyHash(results[0]); - auto result = signStep(witnessScript, index, utxo, WITNESS_V0); - if (!result) { - return Result::failure(result.error()); + auto _result = signStep(witnessScript, index, utxo, WITNESS_V0); + if (!_result) { + return Result::failure(_result.error()); } - witnessStack = result.payload(); + witnessStack = _result.payload(); results.clear(); } else if (script.matchPayToWitnessScriptHash(data)) { auto witnessScript = Script(results[0]); - auto result = signStep(witnessScript, index, utxo, WITNESS_V0); - if (!result) { - return Result::failure(result.error()); + auto _result = signStep(witnessScript, index, utxo, WITNESS_V0); + if (!_result) { + return Result::failure(_result.error()); } - witnessStack = result.payload(); - witnessStack.push_back(move(witnessScript.bytes)); + witnessStack = _result.payload(); + witnessStack.push_back(std::move(witnessScript.bytes)); results.clear(); } else if (script.isWitnessProgram()) { // Error: Unrecognized witness program. @@ -129,7 +124,7 @@ Result SignatureBuilder::sign(Sc template Result, Common::Proto::SigningError> SignatureBuilder::signStep( - Script script, size_t index, const UTXO& utxo, uint32_t version) const { + Script script, size_t index, const UTXO& utxo, uint32_t version) { Data data; std::vector keys; @@ -156,22 +151,22 @@ Result, Common::Proto::SigningError> SignatureBuilder, Common::Proto::SigningError>::success({data}); } if (script.isWitnessProgram()) { - // Error: Invalid sutput script + // Error: Invalid output script return Result, Common::Proto::SigningError>::failure(Common::Proto::Error_script_output); } if (script.matchMultisig(keys, required)) { auto results = std::vector{{}}; // workaround CHECKMULTISIG bug for (auto& pubKey : keys) { - if (results.size() >= required + 1) { + if (results.size() >= required + 1ul) { break; } auto keyHash = Hash::ripemd(Hash::sha256(pubKey)); auto pair = keyPairForPubKeyHash(keyHash); - if (!pair.has_value() && !estimationMode) { + if (!pair.has_value() && signingMode == SigningMode_Normal) { // Error: missing key return Result, Common::Proto::SigningError>::failure(Common::Proto::Error_missing_private_key); } - auto signature = createSignature(transactionToSign, script, pair, index, utxo.amount, version); + auto signature = createSignature(transactionToSign, script, keyHash, pair, index, utxo.amount, version); if (signature.empty()) { // Error: Failed to sign return Result, Common::Proto::SigningError>::failure(Common::Proto::Error_signing); @@ -184,11 +179,11 @@ Result, Common::Proto::SigningError> SignatureBuilder, Common::Proto::SigningError>::failure(Common::Proto::Error_missing_private_key); } - auto signature = createSignature(transactionToSign, script, pair, index, utxo.amount, version); + auto signature = createSignature(transactionToSign, script, keyHash, pair, index, utxo.amount, version); if (signature.empty()) { // Error: Failed to sign return Result, Common::Proto::SigningError>::failure(Common::Proto::Error_signing); @@ -196,22 +191,35 @@ Result, Common::Proto::SigningError> SignatureBuilder, Common::Proto::SigningError>::success({signature}); } if (script.matchPayToPublicKeyHash(data)) { + // obtain public key auto pair = keyPairForPubKeyHash(data); - if (!pair.has_value() && !estimationMode) { - // Error: Missing keys - return Result, Common::Proto::SigningError>::failure(Common::Proto::Error_missing_private_key); + Data pubkey; + if (!pair.has_value()) { + if (signingMode == SigningMode_SizeEstimationOnly || signingMode == SigningMode_HashOnly) { + // estimation mode, key is missing: use placeholder for public key + pubkey = Data(PublicKey::secp256k1Size); + } else if (signingMode == SigningMode_External) { + size_t _index = hashesForSigning.size(); + if (!externalSignatures.has_value() || externalSignatures.value().size() <= _index) { + // Error: no or not enough signatures provided + return Result, Common::Proto::SigningError>::failure(Common::Proto::Error_signing); + } + pubkey = std::get<1>(externalSignatures.value()[_index]); + } else { + // Error: Missing keys + return Result, Common::Proto::SigningError>::failure(Common::Proto::Error_missing_private_key); + } + } else { + pubkey = std::get<1>(pair.value()).bytes; } - auto signature = createSignature(transactionToSign, script, pair, index, utxo.amount, version); + assert(!pubkey.empty()); + + auto signature = createSignature(transactionToSign, script, data, pair, index, utxo.amount, version); if (signature.empty()) { // Error: Failed to sign return Result, Common::Proto::SigningError>::failure(Common::Proto::Error_signing); } - if (!pair.has_value() && estimationMode) { - // estimation mode, key is missing: use placeholder for public key - return Result, Common::Proto::SigningError>::success({signature, Data(PublicKey::secp256k1Size)}); - } - auto pubkey = std::get<1>(pair.value()); - return Result, Common::Proto::SigningError>::success({signature, pubkey.bytes}); + return Result, Common::Proto::SigningError>::success({signature, pubkey}); } // Error: Invalid output script return Result, Common::Proto::SigningError>::failure(Common::Proto::Error_script_output); @@ -220,21 +228,59 @@ Result, Common::Proto::SigningError> SignatureBuilder Data SignatureBuilder::createSignature( const Transaction& transaction, - const Script& script, + const Script& script, + const Data& publicKeyHash, const std::optional& pair, size_t index, Amount amount, - uint32_t version -) const { - if (estimationMode) { + uint32_t version) { + if (signingMode == SigningMode_SizeEstimationOnly) { // Don't sign, only estimate signature size. It is 71-72 bytes. Return placeholder. return Data(72); } - auto key = std::get<0>(pair.value()); - Data sighash = transaction.getSignatureHash(script, index, input.hashType, amount, - static_cast(version)); - auto pk = PrivateKey(key); - auto sig = pk.signAsDER(sighash, TWCurveSECP256k1); + + const Data sighash = transaction.getSignatureHash(script, index, input.hashType, amount, + static_cast(version)); + + if (signingMode == SigningMode_HashOnly) { + // Don't sign, only store hash-to-be-signed + pubkeyhash. Return placeholder. + hashesForSigning.push_back(std::make_pair(sighash, publicKeyHash)); + return Data(72); + } + + if (signingMode == SigningMode_External) { + // Use externally-provided signature + // Store hash, only for counting + size_t _index = hashesForSigning.size(); + hashesForSigning.push_back(std::make_pair(sighash, publicKeyHash)); + + if (!externalSignatures.has_value() || externalSignatures.value().size() <= _index) { + // Error: no or not enough signatures provided + return {}; + } + + Data externalSignature = std::get<0>(externalSignatures.value()[_index]); + const Data publicKey = std::get<1>(externalSignatures.value()[_index]); + + // Verify provided signature + if (!PublicKey::isValid(publicKey, TWPublicKeyTypeSECP256k1)) { + // Error: invalid public key + return {}; + } + const auto publicKeyObj = PublicKey(publicKey, TWPublicKeyTypeSECP256k1); + if (!publicKeyObj.verifyAsDER(externalSignature, sighash)) { + // Error: Signature does not match publickey+hash + return {}; + } + externalSignature.push_back(static_cast(input.hashType)); + + return externalSignature; + } + + const auto key = std::get<0>(pair.value()); + const auto pk = PrivateKey(key); + + auto sig = pk.signAsDER(sighash); if (!sig.empty()) { sig.push_back(static_cast(input.hashType)); } @@ -293,6 +339,8 @@ Data SignatureBuilder::scriptForScriptHash(const Data& hash) const } // Explicitly instantiate a Signers for compatible transactions. -template class Bitcoin::SignatureBuilder; -template class Bitcoin::SignatureBuilder; -template class Bitcoin::SignatureBuilder; +template class SignatureBuilder; +template class SignatureBuilder; +template class SignatureBuilder; + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/SignatureBuilder.h b/src/Bitcoin/SignatureBuilder.h index 234eac17704..07ad17f6f4c 100644 --- a/src/Bitcoin/SignatureBuilder.h +++ b/src/Bitcoin/SignatureBuilder.h @@ -10,15 +10,28 @@ #include "SigningInput.h" #include "Transaction.h" #include "TransactionInput.h" +#include "Signer.h" #include "../proto/Bitcoin.pb.h" #include "../KeyPair.h" #include "../Result.h" +#include "../PublicKey.h" +#include "../CoinEntry.h" +#include #include #include +#include namespace TW::Bitcoin { +/// Normal and special signature modes +enum SigningMode { + SigningMode_Normal = 0, // normal signing + SigningMode_SizeEstimationOnly, // no signing, only estimate size of the signature + SigningMode_HashOnly, // no signing, only collect hash to be signed + SigningMode_External, // no signing, signatures are provided +}; + /// Class that performs Bitcoin transaction signing. template class SignatureBuilder { @@ -30,18 +43,30 @@ class SignatureBuilder { TransactionPlan plan; /// Transaction being signed. - Transaction transaction; + Transaction _transaction; /// Transaction being signed, with list of signed inputs Transaction transactionToSign; - bool estimationMode = false; + SigningMode signingMode = SigningMode_Normal; + + /// For SigningMode_HashOnly, collect hashes (plus corresponding publickey hashes) here + HashPubkeyList hashesForSigning; + + /// For SigningMode_External, signatures are provided here + std::optional externalSignatures; public: /// Initializes a transaction signer with signing input. /// estimationMode: is set, no real signing is performed, only as much as needed to get the almost-exact signed size - SignatureBuilder(const SigningInput& input, const TransactionPlan& plan, Transaction& transaction, bool estimationMode = false) - : input(input), plan(plan), transaction(transaction), estimationMode(estimationMode) {} + SignatureBuilder( + SigningInput input, + TransactionPlan plan, + Transaction& transaction, + SigningMode signingMode = SigningMode_Normal, + std::optional externalSignatures = {} + ) + : input(std::move(input)), plan(std::move(plan)), _transaction(transaction), signingMode(signingMode), externalSignatures(std::move(externalSignatures)) {} /// Signs the transaction. /// @@ -52,12 +77,16 @@ class SignatureBuilder { // internal, public for testability and Decred static Data pushAll(const std::vector& results); + HashPubkeyList getHashesForSigning() const { return hashesForSigning; } + private: Result sign(Script script, size_t index, const UTXO& utxo); Result, Common::Proto::SigningError> signStep(Script script, size_t index, - const UTXO& utxo, uint32_t version) const; - Data createSignature(const Transaction& transaction, const Script& script, const std::optional&, - size_t index, Amount amount, uint32_t version) const; + const UTXO& utxo, uint32_t version); + + Data createSignature(const Transaction& transaction, const Script& script, + const Data& publicKeyHash, const std::optional& key, + size_t index, Amount amount, uint32_t version); /// Returns the private key for the given public key hash. std::optional keyPairForPubKeyHash(const Data& hash) const; diff --git a/src/Bitcoin/SignatureVersion.h b/src/Bitcoin/SignatureVersion.h index fa3363eec3f..73659efb711 100644 --- a/src/Bitcoin/SignatureVersion.h +++ b/src/Bitcoin/SignatureVersion.h @@ -11,4 +11,4 @@ enum SignatureVersion { BASE, WITNESS_V0 }; -} // TW::Bitcoin namespace +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/Signer.cpp b/src/Bitcoin/Signer.cpp index f0461cfa007..ac00533c2a5 100644 --- a/src/Bitcoin/Signer.cpp +++ b/src/Bitcoin/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,17 +11,18 @@ #include "TransactionBuilder.h" #include "TransactionSigner.h" -using namespace TW; -using namespace TW::Bitcoin; +#include "proto/Common.pb.h" + +namespace TW::Bitcoin { Proto::TransactionPlan Signer::plan(const Proto::SigningInput& input) noexcept { auto plan = TransactionSigner::plan(input); return plan.proto(); } -Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input, std::optional optionalExternalSigs) noexcept { Proto::SigningOutput output; - auto result = TransactionSigner::sign(input); + auto result = TransactionSigner::sign(input, false, optionalExternalSigs); if (!result) { output.set_error(result.error()); return output; @@ -44,3 +45,24 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { output.set_transaction_id(hex(txHash)); return output; } + +Proto::PreSigningOutput Signer::preImageHashes(const Proto::SigningInput& input) noexcept { + Proto::PreSigningOutput output; + auto result = TransactionSigner::preImageHashes(input); + if (!result) { + output.set_error(result.error()); + output.set_error_message(Common::Proto::SigningError_Name(result.error())); + return output; + } + + auto hashList = result.payload(); + auto* hashPubKeys = output.mutable_hash_public_keys(); + for (auto& h : hashList) { + auto* hpk = hashPubKeys->Add(); + hpk->set_data_hash(h.first.data(), h.first.size()); + hpk->set_public_key_hash(h.second.data(), h.second.size()); + } + return output; +} + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/Signer.h b/src/Bitcoin/Signer.h index 81509b49a03..b990d5b7dd2 100644 --- a/src/Bitcoin/Signer.h +++ b/src/Bitcoin/Signer.h @@ -6,9 +6,17 @@ #pragma once #include "../proto/Bitcoin.pb.h" +#include "Data.h" +#include "CoinEntry.h" + +#include +#include +#include namespace TW::Bitcoin { +typedef std::vector> SignaturePubkeyList; + class Signer { public: Signer() = delete; @@ -17,7 +25,10 @@ class Signer { static Proto::TransactionPlan plan(const Proto::SigningInput& input) noexcept; /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; + static Proto::SigningOutput sign(const Proto::SigningInput& input, std::optional optionalExternalSigs = {}) noexcept; + + /// Collect pre-image hashes to be signed + static Proto::PreSigningOutput preImageHashes(const Proto::SigningInput& input) noexcept; }; } // namespace TW::Bitcoin diff --git a/src/Bitcoin/SigningInput.cpp b/src/Bitcoin/SigningInput.cpp index 2ea2d87c374..a244e62952b 100644 --- a/src/Bitcoin/SigningInput.cpp +++ b/src/Bitcoin/SigningInput.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,8 +6,7 @@ #include "SigningInput.h" -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { SigningInput::SigningInput(const Proto::SigningInput& input) { hashType = static_cast(input.hash_type()); @@ -15,19 +14,22 @@ SigningInput::SigningInput(const Proto::SigningInput& input) { byteFee = input.byte_fee(); toAddress = input.to_address(); changeAddress = input.change_address(); - for (auto& key: input.private_key()) { - privateKeys.emplace_back(PrivateKey(key)); + for (auto&& key : input.private_key()) { + privateKeys.emplace_back(key); } - for (auto& script: input.scripts()) { + for (auto&& script : input.scripts()) { scripts[script.first] = Script(script.second.begin(), script.second.end()); } - for (auto& u: input.utxo()) { - utxos.push_back(UTXO(u)); + for (auto&& u : input.utxo()) { + utxos.emplace_back(u); } useMaxAmount = input.use_max_amount(); coinType = static_cast(input.coin_type()); if (input.has_plan()) { plan = TransactionPlan(input.plan()); } + outputOpReturn = data(input.output_op_return()); lockTime = input.lock_time(); } + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/SigningInput.h b/src/Bitcoin/SigningInput.h index 48a69da6719..cc365fd5b49 100644 --- a/src/Bitcoin/SigningInput.h +++ b/src/Bitcoin/SigningInput.h @@ -57,6 +57,8 @@ class SigningInput { // Optional transaction plan std::optional plan; + Data outputOpReturn; + uint32_t lockTime = 0; public: diff --git a/src/Bitcoin/Transaction.cpp b/src/Bitcoin/Transaction.cpp index 8e9b3f5c5c3..152c693d736 100644 --- a/src/Bitcoin/Transaction.cpp +++ b/src/Bitcoin/Transaction.cpp @@ -1,22 +1,19 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "SegwitAddress.h" #include "Transaction.h" +#include "SegwitAddress.h" +#include "SignatureVersion.h" #include "SigHashType.h" -#include "../BinaryCoding.h" -#include "../Hash.h" -#include "../Data.h" -#include "SignatureVersion.h" +#include "../BinaryCoding.h" #include -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { Data Transaction::getPreImage(const Script& scriptCode, size_t index, enum TWBitcoinSigHashType hashType, uint64_t amount) const { @@ -25,7 +22,7 @@ Data Transaction::getPreImage(const Script& scriptCode, size_t index, Data data; // Version - encode32LE(version, data); + encode32LE(_version, data); // Input prevouts (none/all, depending on flags) if ((hashType & TWBitcoinSigHashTypeAnyoneCanPay) == 0) { @@ -36,8 +33,8 @@ Data Transaction::getPreImage(const Script& scriptCode, size_t index, } // Input nSequence (none/all, depending on flags) - if ((hashType & TWBitcoinSigHashTypeAnyoneCanPay) == 0 && - !hashTypeIsSingle(hashType) && !hashTypeIsNone(hashType)) { + if ((hashType & TWBitcoinSigHashTypeAnyoneCanPay) == 0 && !hashTypeIsSingle(hashType) && + !hashTypeIsNone(hashType)) { auto hashSequence = getSequenceHash(); std::copy(std::begin(hashSequence), std::end(hashSequence), std::back_inserter(data)); } else { @@ -46,7 +43,7 @@ Data Transaction::getPreImage(const Script& scriptCode, size_t index, // The input being signed (replacing the scriptSig with scriptCode + amount) // The prevout may already be contained in hashPrevout, and the nSequence - // may already be contain in hashSequence. + // may already be contained in hashSequence. reinterpret_cast(inputs[index].previousOutput).encode(data); scriptCode.encode(data); @@ -106,12 +103,18 @@ Data Transaction::getOutputsHash() const { void Transaction::encode(Data& data, enum SegwitFormatMode segwitFormat) const { bool useWitnessFormat = true; switch (segwitFormat) { - case NonSegwit: useWitnessFormat = false; break; - case IfHasWitness: useWitnessFormat = hasWitness(); break; - case Segwit: useWitnessFormat = true; break; + case NonSegwit: + useWitnessFormat = false; + break; + case IfHasWitness: + useWitnessFormat = hasWitness(); + break; + case Segwit: + useWitnessFormat = true; + break; } - encode32LE(version, data); + encode32LE(_version, data); if (useWitnessFormat) { // Use extended format in case witnesses are to be serialized. @@ -145,18 +148,17 @@ void Transaction::encodeWitness(Data& data) const { } bool Transaction::hasWitness() const { - return std::any_of(inputs.begin(), inputs.end(), [](auto& input) { return !input.scriptWitness.empty(); }); + return std::any_of(inputs.begin(), inputs.end(), [](auto& input) { return !input.scriptWitness.empty(); }); } Data Transaction::getSignatureHash(const Script& scriptCode, size_t index, enum TWBitcoinSigHashType hashType, uint64_t amount, enum SignatureVersion version) const { - switch (version) { - case BASE: + if (version == BASE) { return getSignatureHashBase(scriptCode, index, hashType); - case WITNESS_V0: - return getSignatureHashWitnessV0(scriptCode, index, hashType, amount); } + // version == WITNESS_V0 + return getSignatureHashWitnessV0(scriptCode, index, hashType, amount); } /// Generates the signature hash for Witness version 0 scripts. @@ -175,12 +177,12 @@ Data Transaction::getSignatureHashBase(const Script& scriptCode, size_t index, Data data; - encode32LE(version, data); + encode32LE(_version, data); auto serializedInputCount = (hashType & TWBitcoinSigHashTypeAnyoneCanPay) != 0 ? 1 : inputs.size(); encodeVarInt(serializedInputCount, data); - for (auto subindex = 0; subindex < serializedInputCount; subindex += 1) { + for (auto subindex = 0ul; subindex < serializedInputCount; subindex += 1) { serializeInput(subindex, scriptCode, index, hashType, data); } @@ -188,7 +190,7 @@ Data Transaction::getSignatureHashBase(const Script& scriptCode, size_t index, auto hashSingle = hashTypeIsSingle(hashType); auto serializedOutputCount = hashNone ? 0 : (hashSingle ? index + 1 : outputs.size()); encodeVarInt(serializedOutputCount, data); - for (auto subindex = 0; subindex < serializedOutputCount; subindex += 1) { + for (auto subindex = 0ul; subindex < serializedOutputCount; subindex += 1) { if (hashSingle && subindex != index) { auto output = TransactionOutput(-1, {}); output.encode(data); @@ -236,7 +238,7 @@ void Transaction::serializeInput(size_t subindex, const Script& scriptCode, size Proto::Transaction Transaction::proto() const { auto protoTx = Proto::Transaction(); - protoTx.set_version(version); + protoTx.set_version(_version); protoTx.set_locktime(lockTime); for (const auto& input : inputs) { @@ -256,3 +258,5 @@ Proto::Transaction Transaction::proto() const { return protoTx; } + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/Transaction.h b/src/Bitcoin/Transaction.h index e6cb1276d9a..b45fa14c6ab 100644 --- a/src/Bitcoin/Transaction.h +++ b/src/Bitcoin/Transaction.h @@ -14,7 +14,7 @@ #include "UTXO.h" #include "../PrivateKey.h" #include "../Hash.h" -#include "../Data.h" +#include "Data.h" #include "SignatureVersion.h" #include "../proto/Bitcoin.pb.h" @@ -33,7 +33,7 @@ class TransactionOutputs: public std::vector {}; struct Transaction { public: /// Transaction data format version (note, this is signed) - int32_t version = 1; + int32_t _version = 1; /// The block number or timestamp at which this transaction is unlocked /// @@ -53,7 +53,7 @@ struct Transaction { // List of transaction outputs TransactionOutputs outputs; - TW::Hash::Hasher hasher = TW::Hash::sha256d; + TW::Hash::Hasher hasher = TW::Hash::HasherSha256d; /// Used for diagnostics; store previously estimated virtual size (if any; size in bytes) int previousEstimatedVirtualSize = 0; @@ -61,8 +61,8 @@ struct Transaction { public: Transaction() = default; - Transaction(int32_t version, uint32_t lockTime = 0, TW::Hash::Hasher hasher = TW::Hash::sha256d) - : version(version), lockTime(lockTime), inputs(), outputs(), hasher(hasher) {} + Transaction(int32_t version, uint32_t lockTime = 0, TW::Hash::Hasher hasher = TW::Hash::HasherSha256d) + : _version(version), lockTime(lockTime), inputs(), outputs(), hasher(hasher) {} /// Whether the transaction is empty. bool empty() const { return inputs.empty() && outputs.empty(); } @@ -110,8 +110,3 @@ struct Transaction { }; } // namespace TW::Bitcoin - -/// Wrapper for C interface. -struct TWBitcoinTransaction { - TW::Bitcoin::Transaction impl; -}; diff --git a/src/Bitcoin/TransactionBuilder.cpp b/src/Bitcoin/TransactionBuilder.cpp index a9c07a0e295..9e72dee4f5f 100644 --- a/src/Bitcoin/TransactionBuilder.cpp +++ b/src/Bitcoin/TransactionBuilder.cpp @@ -7,9 +7,9 @@ #include "TransactionBuilder.h" #include "Script.h" #include "TransactionSigner.h" +#include "SignatureBuilder.h" #include "../Coin.h" -#include "../proto/Bitcoin.pb.h" #include #include @@ -48,7 +48,7 @@ int64_t estimateSegwitFee(const FeeCalculator& feeCalculator, const TransactionP auto inputWithPlan = std::move(input); inputWithPlan.plan = plan; - auto result = TransactionSigner::sign(inputWithPlan, true); + auto result = TransactionSigner::sign(inputWithPlan, SigningMode_SizeEstimationOnly); if (!result) { // signing failed; return default simple estimate return estimateSimpleFee(feeCalculator, plan, outputSize, input.byteFee); @@ -77,8 +77,16 @@ int64_t estimateSegwitFee(const FeeCalculator& feeCalculator, const TransactionP return fee; } +int extraOutputCount(const SigningInput& input) { + int count = int(input.outputOpReturn.size() > 0); + return count; +} + TransactionPlan TransactionBuilder::plan(const SigningInput& input) { TransactionPlan plan; + if (input.outputOpReturn.size() > 0) { + plan.outputOpReturn = input.outputOpReturn; + } bool maxAmount = input.useMaxAmount; if (input.amount == 0 && !maxAmount) { @@ -93,23 +101,24 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { // select UTXOs plan.amount = input.amount; - // if amount requested is the same or more than available amount, it cannot be satisifed, but + // if amount requested is the same or more than available amount, it cannot be satisfied, but // treat this case as MaxAmount, and send maximum available (which will be less) - if (!maxAmount && input.amount >= inputSum) { + if (!maxAmount && static_cast(input.amount) >= inputSum) { maxAmount = true; } + auto extraOutputs = extraOutputCount(input); auto output_size = 2; UTXOs selectedInputs; if (!maxAmount) { - output_size = 2; // output + change + output_size = 2 + extraOutputs; // output + change if (input.utxos.size() <= SimpleModeLimit && input.utxos.size() <= MaxUtxosHardLimit) { selectedInputs = inputSelector.select(plan.amount, input.byteFee, output_size); } else { selectedInputs = inputSelector.selectSimple(plan.amount, input.byteFee, output_size); } } else { - output_size = 1; // no change + output_size = 1 + extraOutputs; // output, no change selectedInputs = inputSelector.selectMaxAmount(input.byteFee); } if (selectedInputs.size() <= MaxUtxosHardLimit) { @@ -117,7 +126,7 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { } else { // truncate to limit number of selected UTXOs plan.utxos.clear(); - for (auto i = 0; i < MaxUtxosHardLimit; ++i) { + for (auto i = 0ul; i < MaxUtxosHardLimit; ++i) { plan.utxos.push_back(selectedInputs[i]); } } @@ -141,7 +150,7 @@ TransactionPlan TransactionBuilder::plan(const SigningInput& input) { plan.change = 0; } plan.fee = estimateSegwitFee(feeCalculator, plan, output_size, input); - // If fee is larger then availableAmount (can happen in special maxAmount case), we reduce it (and hope it will go through) + // If fee is larger than availableAmount (can happen in special maxAmount case), we reduce it (and hope it will go through) plan.fee = std::min(plan.availableAmount, plan.fee); assert(plan.fee >= 0 && plan.fee <= plan.availableAmount); diff --git a/src/Bitcoin/TransactionBuilder.h b/src/Bitcoin/TransactionBuilder.h index f5b122346a8..b2c9feeb8c0 100644 --- a/src/Bitcoin/TransactionBuilder.h +++ b/src/Bitcoin/TransactionBuilder.h @@ -45,6 +45,12 @@ class TransactionBuilder { tx.inputs.emplace_back(utxo.outPoint, emptyScript, utxo.outPoint.sequence); } + // Optional OP_RETURN output + if (plan.outputOpReturn.size() > 0) { + auto lockingScriptOpReturn = Script::buildOpReturnScript(plan.outputOpReturn); + tx.outputs.emplace_back(0, lockingScriptOpReturn); + } + return tx; } diff --git a/src/Bitcoin/TransactionInput.cpp b/src/Bitcoin/TransactionInput.cpp index 6d59f5536b8..7c43f07e0aa 100644 --- a/src/Bitcoin/TransactionInput.cpp +++ b/src/Bitcoin/TransactionInput.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,7 +8,7 @@ #include "../BinaryCoding.h" -using namespace TW::Bitcoin; +namespace TW::Bitcoin { void TransactionInput::encode(Data& data) const { auto& outpoint = reinterpret_cast(previousOutput); @@ -24,3 +24,5 @@ void TransactionInput::encodeWitness(Data& data) const { std::copy(std::begin(item), std::end(item), std::back_inserter(data)); } } + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/TransactionInput.h b/src/Bitcoin/TransactionInput.h index 41e33b0cc91..8a9b28399ae 100644 --- a/src/Bitcoin/TransactionInput.h +++ b/src/Bitcoin/TransactionInput.h @@ -8,7 +8,7 @@ #include "OutPoint.h" #include "Script.h" -#include "../Data.h" +#include "Data.h" #include @@ -45,8 +45,3 @@ class TransactionInput { }; } // namespace TW::Bitcoin - -/// Wrapper for C interface. -struct TWBitcoinTransactionInput { - TW::Bitcoin::TransactionInput impl; -}; diff --git a/src/Bitcoin/TransactionOutput.cpp b/src/Bitcoin/TransactionOutput.cpp index e21b9668737..7f36e02fce7 100644 --- a/src/Bitcoin/TransactionOutput.cpp +++ b/src/Bitcoin/TransactionOutput.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,9 +8,11 @@ #include "../BinaryCoding.h" -using namespace TW::Bitcoin; +namespace TW::Bitcoin { void TransactionOutput::encode(Data& data) const { encode64LE(value, data); script.encode(data); } + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/TransactionOutput.h b/src/Bitcoin/TransactionOutput.h index b2264667f56..0a4ac788218 100644 --- a/src/Bitcoin/TransactionOutput.h +++ b/src/Bitcoin/TransactionOutput.h @@ -8,7 +8,7 @@ #include "Amount.h" #include "Script.h" -#include "../Data.h" +#include "Data.h" #include @@ -34,8 +34,3 @@ struct TransactionOutput { }; } // namespace TW::Bitcoin - -/// Wrapper for C interface. -struct TWBitcoinTransactionOutput { - TW::Bitcoin::TransactionOutput impl; -}; diff --git a/src/Bitcoin/TransactionPlan.h b/src/Bitcoin/TransactionPlan.h index 060c4de0852..1225082d307 100644 --- a/src/Bitcoin/TransactionPlan.h +++ b/src/Bitcoin/TransactionPlan.h @@ -8,7 +8,7 @@ #include "Amount.h" #include "UTXO.h" -#include "../Data.h" +#include "Data.h" #include "../proto/Bitcoin.pb.h" namespace TW::Bitcoin { @@ -33,6 +33,8 @@ struct TransactionPlan { /// Zcash branch id Data branchId; + Data outputOpReturn; + Common::Proto::SigningError error = Common::Proto::SigningError::OK; TransactionPlan() = default; @@ -44,6 +46,7 @@ struct TransactionPlan { , change(plan.change()) , utxos(std::vector(plan.utxos().begin(), plan.utxos().end())) , branchId(plan.branch_id().begin(), plan.branch_id().end()) + , outputOpReturn(plan.output_op_return().begin(), plan.output_op_return().end()) , error(plan.error()) {} @@ -57,6 +60,7 @@ struct TransactionPlan { *plan.add_utxos() = utxo.proto(); } plan.set_branch_id(branchId.data(), branchId.size()); + plan.set_output_op_return(outputOpReturn.data(), outputOpReturn.size()); plan.set_error(error); return plan; } diff --git a/src/Bitcoin/TransactionSigner.cpp b/src/Bitcoin/TransactionSigner.cpp index 38a0a672246..0e87be108e8 100644 --- a/src/Bitcoin/TransactionSigner.cpp +++ b/src/Bitcoin/TransactionSigner.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,8 +12,7 @@ #include "../Zcash/Transaction.h" #include "../Zcash/TransactionBuilder.h" -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { template TransactionPlan TransactionSigner::plan(const SigningInput& input) { @@ -21,7 +20,7 @@ TransactionPlan TransactionSigner::plan(const S } template -Result TransactionSigner::sign(const SigningInput& input, bool estimationMode) { +Result TransactionSigner::sign(const SigningInput& input, bool estimationMode, std::optional optionalExternalSigs) { TransactionPlan plan; if (input.plan.has_value()) { plan = input.plan.value(); @@ -29,11 +28,33 @@ Result TransactionSigner(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime); - SignatureBuilder signer(std::move(input), plan, transaction, estimationMode); + SigningMode signingMode = + estimationMode ? SigningMode_SizeEstimationOnly : optionalExternalSigs.has_value() ? SigningMode_External + : SigningMode_Normal; + SignatureBuilder signer(std::move(input), plan, transaction, signingMode, optionalExternalSigs); return signer.sign(); } +template +Result TransactionSigner::preImageHashes(const SigningInput& input) { + TransactionPlan plan; + if (input.plan.has_value()) { + plan = input.plan.value(); + } else { + plan = TransactionBuilder::plan(input); + } + auto transaction = TransactionBuilder::template build(plan, input.toAddress, input.changeAddress, input.coinType, input.lockTime); + SignatureBuilder signer(std::move(input), plan, transaction, SigningMode_HashOnly); + auto signResult = signer.sign(); + if (!signResult) { + return Result::failure(signResult.error()); + } + return Result::success(signer.getHashesForSigning()); +} + // Explicitly instantiate a Signers for compatible transactions. template class Bitcoin::TransactionSigner; template class Bitcoin::TransactionSigner; template class Bitcoin::TransactionSigner; + +} // namespace TW::Bitcoin diff --git a/src/Bitcoin/TransactionSigner.h b/src/Bitcoin/TransactionSigner.h index ab290d1294e..f75c88851cb 100644 --- a/src/Bitcoin/TransactionSigner.h +++ b/src/Bitcoin/TransactionSigner.h @@ -9,9 +9,16 @@ #include "SigningInput.h" #include "Transaction.h" #include "TransactionBuilder.h" +#include "Signer.h" +#include "Data.h" #include "../KeyPair.h" #include "../Result.h" #include "../proto/Bitcoin.pb.h" +#include "../CoinEntry.h" + +#include +#include +#include namespace TW::Bitcoin { @@ -23,7 +30,10 @@ class TransactionSigner { static TransactionPlan plan(const SigningInput& input); // Sign an unsigned transaction. Plan it if needed beforehand. - static Result sign(const SigningInput& input, bool estimationMode = false); + static Result sign(const SigningInput& input, bool estimationMode = false, std::optional optionalExternalSigs = {}); + + /// Collect pre-image hashes to be signed + static Result preImageHashes(const SigningInput& input); }; } // namespace TW::Bitcoin diff --git a/src/Cardano/AddressV2.cpp b/src/Cardano/AddressV2.cpp index 5d26a9c2d7d..6d2140ab82f 100644 --- a/src/Cardano/AddressV2.cpp +++ b/src/Cardano/AddressV2.cpp @@ -1,47 +1,42 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "AddressV2.h" -#include "../Cbor.h" -#include "../Data.h" #include "../Base58.h" +#include "../Cbor.h" #include "../Crc.h" -#include "../HexCoding.h" -#include "../Hash.h" #include -using namespace TW; -using namespace TW::Cardano; -using namespace std; +namespace TW::Cardano { bool AddressV2::parseAndCheck(const std::string& addr, Data& root_out, Data& attrs_out, byte& type_out) { // Decode Bas58, decode payload + crc, decode root, attr Data base58decoded = Base58::bitcoin.decode(addr); - if (base58decoded.size() == 0) { - throw invalid_argument("Invalid address: could not Base58 decode"); + if (base58decoded.empty()) { + throw std::invalid_argument("Invalid address: could not Base58 decode"); } auto elems = Cbor::Decode(base58decoded).getArrayElements(); if (elems.size() < 2) { - throw invalid_argument("Could not parse address payload from CBOR data"); + throw std::invalid_argument("Could not parse address payload from CBOR data"); } auto tag = elems[0].getTagValue(); if (tag != PayloadTag) { - throw invalid_argument("wrong tag value"); + throw std::invalid_argument("wrong tag value"); } Data payload = elems[0].getTagElement().getBytes(); uint64_t crcPresent = (uint32_t)elems[1].getValue(); uint32_t crcComputed = TW::Crc::crc32(payload); if (crcPresent != crcComputed) { - throw invalid_argument("CRC mismatch"); + throw std::invalid_argument("CRC mismatch"); } // parse payload, 3 elements auto payloadElems = Cbor::Decode(payload).getArrayElements(); if (payloadElems.size() < 3) { - throw invalid_argument("Could not parse address root and attrs from CBOR data"); + throw std::invalid_argument("Could not parse address root and attrs from CBOR data"); } root_out = payloadElems[0].getBytes(); attrs_out = payloadElems[1].encoded(); // map, but encoded as bytes @@ -54,10 +49,12 @@ bool AddressV2::isValid(const std::string& string) { Data root; Data attrs; byte type = 0; - if (!parseAndCheck(string, root, attrs, type)) { return false; } + if (!parseAndCheck(string, root, attrs, type)) { + return false; + } // valid return true; - } catch (exception& ex) { + } catch (std::exception& ex) { return false; } } @@ -71,18 +68,18 @@ AddressV2::AddressV2(const std::string& string) { AddressV2::AddressV2(const PublicKey& publicKey) { // input is extended pubkey, 64-byte - if (publicKey.type != TWPublicKeyTypeED25519Extended) { + if (publicKey.type != TWPublicKeyTypeED25519Cardano || publicKey.bytes.size() != PublicKey::cardanoKeySize) { throw std::invalid_argument("Invalid public key type"); } type = 0; // public key - root = keyHash(publicKey.bytes); + root = keyHash(subData(publicKey.bytes, 0, 64)); // address attributes: empty map for V2, for V1 encrypted derivation path Cbor::Encode emptyMap = Cbor::Encode::map({}); attrs = emptyMap.encoded(); } Data AddressV2::getCborData() const { - // put together string represenatation, CBOR representation + // put together string representation, CBOR representation // inner data: pubkey, attrs, type auto cbor1 = Cbor::Encode::array({ Cbor::Encode::bytes(root), @@ -90,8 +87,8 @@ Data AddressV2::getCborData() const { Cbor::Encode::uint(type), }); auto payloadData = cbor1.encoded(); - - // crc checksum + + // crc checksum auto crc = TW::Crc::crc32(payloadData); // second pack: tag, base, crc auto cbor2 = Cbor::Encode::array({ @@ -101,25 +98,29 @@ Data AddressV2::getCborData() const { return cbor2.encoded(); } -string AddressV2::string() const { +std::string AddressV2::string() const { // Base58 encode the CBOR data return Base58::bitcoin.encode(getCborData()); } Data AddressV2::keyHash(const TW::Data& xpub) { - if (xpub.size() != 64) { throw invalid_argument("invalid xbub length"); } - // hash of follwoing Cbor-array: [0, [0, xbub], {} ] + if (xpub.size() != 64) { + throw std::invalid_argument("invalid xpub length"); + } + // hash of following Cbor-array: [0, [0, xpub], {} ] // 3rd entry map is empty map for V2, contains derivation path for V1 + // clang-format off Data cborData = Cbor::Encode::array({ Cbor::Encode::uint(0), - Cbor::Encode::array({ - Cbor::Encode::uint(0), - Cbor::Encode::bytes(xpub) - }), + Cbor::Encode::array({Cbor::Encode::uint(0), + Cbor::Encode::bytes(xpub)}), Cbor::Encode::map({}), }).encoded(); + // clang-format on // SHA3 hash, then blake Data firstHash = Hash::sha3_256(cborData); Data blake = Hash::blake2b(firstHash, 28); return blake; } + +} // namespace TW::Cardano diff --git a/src/Cardano/AddressV2.h b/src/Cardano/AddressV2.h index 0f3d6ef5a62..929a52356f9 100644 --- a/src/Cardano/AddressV2.h +++ b/src/Cardano/AddressV2.h @@ -20,7 +20,7 @@ namespace TW::Cardano { * Derivation is BIP39, default derivation path is "m/44'/1815'/0'/0/0", with last element being the account number. * Curve is ED25519 with special variations, custom logic in HDWallet and TrezorCrypto lib. * Private key is ED25519: 32-byte PK is extended with 32-byte extra extension, and 32-byte chain code. - * Private key is derived from mnemonic raw entropy (not seed, as in other cases); 96-byte secret is generated (pk, extrension, and chain code). + * Private key is derived from mnemonic raw entropy (not seed, as in other cases); 96-byte secret is generated (pk, extension, and chain code). * Public key is 64-byte: the 32-byte ED25519 public key plus the 32-byte chain code of the PK. * Address derivation: Only V2, type 0 (=public key) addresses are generated. * - CBOR binary scheme is used inside addresses. @@ -46,7 +46,7 @@ class AddressV2 { Data attrs; /// Type; 0: public key. - TW::byte type; + TW::byte type{}; static const TW::byte PayloadTag = 24; @@ -77,8 +77,3 @@ inline bool operator==(const AddressV2& lhs, const AddressV2& rhs) { } } // namespace TW::Cardano - -/// Wrapper for C interface. -struct TWCardanoAddressV2 { - TW::Cardano::AddressV2 impl; -}; diff --git a/src/Cardano/AddressV3.cpp b/src/Cardano/AddressV3.cpp index a75e5ce55ac..44c34ab0352 100644 --- a/src/Cardano/AddressV3.cpp +++ b/src/Cardano/AddressV3.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,23 +7,50 @@ #include "AddressV3.h" #include "AddressV2.h" #include -#include "../Data.h" #include "../Bech32.h" #include "../Base32.h" -#include "../Crc.h" #include "../HexCoding.h" -#include "../Hash.h" #include -using namespace TW; -using namespace TW::Cardano; -using namespace std; +namespace TW::Cardano { -bool AddressV3::parseAndCheckV3(const std::string& addr, Discrimination& discrimination, Kind& kind, Data& key1, Data& key2) { +bool AddressV3::checkLength(Kind kind, size_t length) noexcept { + switch (kind) { + case Kind_Base: + return (length == EncodedSize2); + + case Kind_Enterprise: + case Kind_Reward: + return (length == EncodedSize1); + + default: + // accept other types as well + return true; + } +} + +bool AddressV3::parseAndCheckV3(const Data& raw, NetworkId& networkId, Kind& kind, Data& bytes) noexcept { + if (raw.empty()) { + // too short, cannot extract kind and networkId + return false; + } + kind = kindFromFirstByte(raw[0]); + networkId = networkIdFromFirstByte(raw[0]); + if (networkId != Network_Production) { + return false; + } + + bytes = Data(); + std::copy(cbegin(raw) + 1, cend(raw), std::back_inserter(bytes)); + + return checkLength(kind, raw.size()); +} + +bool AddressV3::parseAndCheckV3(const std::string& addr, NetworkId& networkId, Kind& kind, Data& bytes) noexcept { try { auto bech = Bech32::decode(addr); - if (std::get<1>(bech).size() == 0) { + if (std::get<1>(bech).empty()) { // empty Bech data return false; } @@ -33,95 +60,86 @@ bool AddressV3::parseAndCheckV3(const std::string& addr, Discrimination& discrim if (!success) { return false; } - if (conv.size() != 33 && conv.size() != 65) { - return false; - } - discrimination = (Discrimination)((conv[0] & 0b10000000) >> 7); - kind = (Kind)(conv[0] & 0b01111111); - if (kind <= Kind_Sentinel_Low || kind >= Kind_Sentinel_High) { + + if (!parseAndCheckV3(conv, networkId, kind, bytes)) { return false; } - if ((kind == Kind_Group && conv.size() != 65) || - (kind != Kind_Group && conv.size() != 33)) { + + // check prefix + if (const auto expectedHrp = getHrp(kind); !addr.starts_with(expectedHrp)) { return false; } - switch (kind) { - case Kind_Single: - case Kind_Account: - case Kind_Multisig: - assert(conv.size() == 33); - key1 = Data(32); - std::copy(conv.begin() + 1, conv.begin() + 33, key1.begin()); - return true; - - case Kind_Group: - assert(conv.size() == 65); - key1 = Data(32); - key2 = Data(32); - std::copy(conv.begin() + 1, conv.begin() + 33, key1.begin()); - std::copy(conv.begin() + 33, conv.begin() + 65, key2.begin()); - return true; - - default: - return false; - } + return true; } catch (...) { return false; } } bool AddressV3::isValid(const std::string& addr) { - Discrimination discrimination; + NetworkId networkId; Kind kind; - Data key1; - Data key2; - if (parseAndCheckV3(addr, discrimination, kind, key1, key2)) { + Data key; + return parseAndCheckV3(addr, networkId, kind, key); +} + +bool AddressV3::isValidLegacy(const std::string& addr) { + if (isValid(addr)) { return true; } // not V3, try older return AddressV2::isValid(addr); } -AddressV3 AddressV3::createSingle(Discrimination discrimination_in, const Data& spendingKey) { - if (spendingKey.size() != 32) { - throw std::invalid_argument("Wrong spending key size"); +Data blakeHash(Data d) { + assert(d.size() == 32); + return Hash::blake2b(d.data(), d.size(), AddressV3::HashSize); +} + +AddressV3 AddressV3::createBase(NetworkId networkId, const TW::Data& spendingKeyHash, const TW::Data& stakingKeyHash) { + if (spendingKeyHash.size() != HashSize) { + throw std::invalid_argument("Wrong spending key hash size"); + } + if (stakingKeyHash.size() != HashSize) { + throw std::invalid_argument("Wrong spending key hash size"); } auto addr = AddressV3(); - addr.discrimination = discrimination_in; - addr.kind = Kind_Single; - addr.key1 = spendingKey; + addr.networkId = networkId; + addr.kind = Kind_Base; + + addr.bytes = Data(); + append(addr.bytes, spendingKeyHash); + append(addr.bytes, stakingKeyHash); + return addr; } -AddressV3 AddressV3::createGroup(Discrimination discrimination_in, const Data& spendingKey, const Data& groupKey) { - if (spendingKey.size() != 32) { +AddressV3 AddressV3::createBase(NetworkId networkId, const PublicKey& spendingKey, const PublicKey& stakingKey) { + if (spendingKey.bytes.size() != 32) { throw std::invalid_argument("Wrong spending key size"); } - if (groupKey.size() != 32) { - throw std::invalid_argument("Wrong group key size"); + if (stakingKey.bytes.size() != 32) { + throw std::invalid_argument("Wrong spending key size"); } - auto addr = AddressV3(); - addr.discrimination = discrimination_in; - addr.kind = Kind_Group; - addr.key1 = spendingKey; - addr.groupKey = groupKey; - return addr; + + const Data hash1 = blakeHash(spendingKey.bytes); + const Data hash2 = blakeHash(stakingKey.bytes); + return createBase(networkId, hash1, hash2); } -AddressV3 AddressV3::createAccount(Discrimination discrimination_in, const Data& accountKey) { - if (accountKey.size() != 32) { - throw std::invalid_argument("Wrong spending key size"); +AddressV3 AddressV3::createReward(NetworkId networkId, const TW::Data& stakingKeyHash) { + if (stakingKeyHash.size() != HashSize) { + throw std::invalid_argument("Wrong spending key hash size"); } auto addr = AddressV3(); - addr.discrimination = discrimination_in; - addr.kind = Kind_Account; - addr.key1 = accountKey; + addr.networkId = networkId; + addr.kind = Kind_Reward; + addr.bytes = stakingKeyHash; return addr; } AddressV3::AddressV3(const std::string& addr) { - if (parseAndCheckV3(addr, discrimination, kind, key1, groupKey)) { + if (parseAndCheckV3(addr, networkId, kind, bytes)) { // values stored return; } @@ -131,109 +149,80 @@ AddressV3::AddressV3(const std::string& addr) { } AddressV3::AddressV3(const PublicKey& publicKey) { - // input is extended pubkey, 64-byte - if (publicKey.type != TWPublicKeyTypeED25519Extended) { + // input is double extended pubkey + if (publicKey.type != TWPublicKeyTypeED25519Cardano || publicKey.bytes.size() != PublicKey::cardanoKeySize) { throw std::invalid_argument("Invalid public key type"); } - discrimination = Discrim_Test; - kind = Kind_Group; - key1 = Data(32); - groupKey = Data(32); - std::copy(publicKey.bytes.begin(), publicKey.bytes.begin() + 32, key1.begin()); - std::copy(publicKey.bytes.begin() + 32, publicKey.bytes.begin() + 64, groupKey.begin()); + *this = createBase(Network_Production, PublicKey(subData(publicKey.bytes, 0, 32), TWPublicKeyTypeED25519), PublicKey(subData(publicKey.bytes, 64, 32), TWPublicKeyTypeED25519)); } AddressV3::AddressV3(const Data& data) { - // min 4 bytes, 2 prefix + 2 len - if (data.size() < 4) { throw std::invalid_argument("Address data too short"); } - assert(data.size() >= 4); - int index = 0; - discrimination = (Discrimination)data[index++]; - kind = (Kind)data[index++]; - // key1: - byte len1 = data[index++]; - if (data.size() < 4 + len1) { throw std::invalid_argument("Address data too short"); } - assert(data.size() >= 4 + len1); - key1 = Data(len1); - std::copy(data.begin() + index, data.begin() + index + len1, key1.begin()); - index += len1; - // groupKey: - byte len2 = data[index++]; - if (data.size() < 4 + len1 + len2) { throw std::invalid_argument("Address data too short"); } - assert(data.size() >= 4 + len1 + len2); - groupKey = Data(len2); - std::copy(data.begin() + index, data.begin() + index + len2, groupKey.begin()); + parseAndCheckV3(data, networkId, kind, bytes); } AddressV3::AddressV3(const AddressV3& other) = default; -void AddressV3::operator=(const AddressV3& other) -{ - discrimination = other.discrimination; - kind = other.kind; - key1 = other.key1; - groupKey = other.groupKey; - legacyAddressV2 = other.legacyAddressV2; +uint8_t AddressV3::firstByte(NetworkId networkId, Kind kind) { + byte first = (byte)(((byte)kind << 4) + networkId); + return first; } -string AddressV3::string() const { - std::string hrp; +AddressV3::NetworkId AddressV3::networkIdFromFirstByte(uint8_t first) { + return (NetworkId)(first & 0x0F); +} + +AddressV3::Kind AddressV3::kindFromFirstByte(uint8_t first) { + return (Kind)((first & 0xF0) >> 4); +} + +std::string AddressV3::getHrp(Kind kind) noexcept { switch (kind) { - case Kind_Single: - case Kind_Group: - case Kind_Account: - hrp = stringForHRP(TWHRPCardano); break; - default: - hrp = ""; break; + case Kind_Base: + case Kind_Enterprise: + default: + return stringForHRP(TWHRPCardano); + case Kind_Reward: + return "stake"; } +} + +std::string AddressV3::string() const { + const auto hrp = getHrp(kind); return string(hrp); } -string AddressV3::string(const std::string& hrp) const { +std::string AddressV3::string(const std::string& hrp) const { if (legacyAddressV2.has_value()) { return legacyAddressV2->string(); } - byte first = (byte)kind; - if (discrimination == Discrim_Test) first = first | 0b10000000; - Data keys; - TW::append(keys, first); - TW::append(keys, key1); - if (groupKey.size() > 0) { - TW::append(keys, groupKey); - } + const Data raw = data(); // bech Data bech; - if (!Bech32::convertBits<8, 5, true>(bech, keys)) { + if (!Bech32::convertBits<8, 5, true>(bech, raw)) { return ""; } return Bech32::encode(hrp, bech, Bech32::ChecksumVariant::Bech32); } -string AddressV3::stringBase32() const { +Data AddressV3::data() const noexcept { if (legacyAddressV2.has_value()) { - return legacyAddressV2->string(); + return legacyAddressV2->getCborData(); } - byte first = (byte)kind; - if (discrimination == Discrim_Test) first = first | 0b10000000; - Data keys; - TW::append(keys, first); - TW::append(keys, key1); - if (groupKey.size() > 0) { - TW::append(keys, groupKey); - } - std::string base32 = Base32::encode(keys, "abcdefghijklmnopqrstuvwxyz23456789"); - return base32; + const byte first = firstByte(networkId, kind); + Data raw; + TW::append(raw, first); + TW::append(raw, bytes); + return raw; } -Data AddressV3::data() const { - Data data; - TW::append(data, (uint8_t)discrimination); - TW::append(data, (uint8_t)kind); - TW::append(data, (uint8_t)key1.size()); - TW::append(data, key1); - TW::append(data, (uint8_t)groupKey.size()); - TW::append(data, groupKey); - return data; +std::string AddressV3::getStakingAddress() const noexcept { + if (kind != Kind_Base || bytes.size() != (2 * HashSize)) { + return ""; + } + const auto& stakingKeyHash = TW::subData(bytes, HashSize, HashSize); + return createReward(this->networkId, stakingKeyHash).string(); } + +} // namespace TW::Cardano diff --git a/src/Cardano/AddressV3.h b/src/Cardano/AddressV3.h index 863815bae3c..f9580812afe 100644 --- a/src/Cardano/AddressV3.h +++ b/src/Cardano/AddressV3.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -18,42 +18,55 @@ namespace TW::Cardano { /// A Cardano-Shelley address, V3 or V2. class AddressV3 { public: - enum Discrimination: uint8_t { - Discrim_Production = 0, - Discrim_Test = 1, + enum NetworkId: uint8_t { + Network_Test = 0, + Network_Production = 1, }; enum Kind: uint8_t { - Kind_Sentinel_Low = 2, - Kind_Single = 3, - Kind_Group = 4, - Kind_Account = 5, - Kind_Multisig = 6, - Kind_Sentinel_High = 7, + Kind_Base = 0, // spending + staking key + //Kind_Base_Script_Key = 1, + //Kind_Base_Key_Script_Key = 2, + //Kind_Base_Script_Script_Key = 3, + //Kind_Pointer = 4, + //Kind_Pointer_Script = 5, + Kind_Enterprise = 6, // spending key + //Kind_Enterprise_Script = 7, + //Kind_Bootstrap = 8, // Byron + Kind_Reward = 14, // // staking key + //Kind_Reward_Script = 15, }; - Discrimination discrimination; + static const uint8_t HashSize = 28; - Kind kind; + // First byte header (kind, netowrkId) and 2 hashes + static const uint8_t EncodedSize1 = 1 + HashSize; + static const uint8_t EncodedSize2 = 1 + 2 * HashSize; - /// key1: spending key or account key - Data key1; + NetworkId networkId{Network_Production}; - /// group key (in case of Group address, empty otherwise) - Data groupKey; + Kind kind{Kind_Base}; + + /// raw key/hash bytes + Data bytes; /// Used in case of legacy address (V2) std::optional legacyAddressV2; - /// Determines whether a string makes a valid address. + /// Determines whether a string makes a valid address (V3). static bool isValid(const std::string& addr); - /// Create a single spending key address - static AddressV3 createSingle(Discrimination discrimination_in, const TW::Data& spendingKey); - /// Create a group address - static AddressV3 createGroup(Discrimination discrimination_in, const TW::Data& spendingKey, const TW::Data& groupKey); - /// Create an account address - static AddressV3 createAccount(Discrimination discrimination_in, const TW::Data& accountKey); + /// Determines whether a string makes a valid address, V3 or earlier legacy. + static bool isValidLegacy(const std::string& addr); + + /// Create a base address, given public key hashes + static AddressV3 createBase(NetworkId networkId, const TW::Data& spendingKeyHash, const TW::Data& stakingKeyHash); + + /// Create a base address, given public keys + static AddressV3 createBase(NetworkId networkId, const PublicKey& spendingKey, const PublicKey& stakingKey); + + /// Create a staking (reward) address, given a staking key + static AddressV3 createReward(NetworkId networkId, const TW::Data& stakingKeyHash); /// Initializes a Cardano address with a string representation. Throws if invalid. explicit AddressV3(const std::string& addr); @@ -67,32 +80,38 @@ class AddressV3 { /// Copy constructor AddressV3(const AddressV3& other); - void operator=(const AddressV3& other); + AddressV3& operator=(const AddressV3& other) noexcept = default; /// Returns the Bech string representation of the address, with default HRP. std::string string() const; /// Returns the Bech string representation of the address, with given HRP. std::string string(const std::string& hrp) const; - /// Returns the internal Base32 string representation of the address. - std::string stringBase32() const; + /// Hrp of kind + static std::string getHrp(Kind kind) noexcept; + /// Check whether data length is correct + static bool checkLength(Kind kind, size_t length) noexcept; + /// Check validity of binary address. + static bool parseAndCheckV3(const TW::Data& raw, NetworkId& networkId, Kind& kind, TW::Data& bytes) noexcept; /// Check validity and parse elements of a string address. Used internally by isValid and ctor. - static bool parseAndCheckV3(const std::string& addr, Discrimination& discrimination, Kind& kind, TW::Data& key1, TW::Data& key2); + static bool parseAndCheckV3(const std::string& addr, NetworkId& networkId, Kind& kind, TW::Data& bytes) noexcept; /// Return the binary data representation (keys appended, internal format) - Data data() const; + Data data() const noexcept; + /// Return the staking address associated to (contained in) this address. Must be a Base address. Empty string is returned on error. + std::string getStakingAddress() const noexcept; + + // First encoded byte, from networkId and Kind + static uint8_t firstByte(NetworkId networkId, Kind kind); + static NetworkId networkIdFromFirstByte(uint8_t first); + static Kind kindFromFirstByte(uint8_t first); private: - AddressV3() : discrimination(Discrim_Production), kind(Kind_Single) {} + AddressV3() = default; }; inline bool operator==(const AddressV3& lhs, const AddressV3& rhs) { - return lhs.discrimination == rhs.discrimination && lhs.kind == rhs.kind && lhs.key1 == rhs.key1 && lhs.groupKey == rhs.groupKey; + return lhs.networkId == rhs.networkId && lhs.kind == rhs.kind && lhs.bytes == rhs.bytes; } } // namespace TW::Cardano - -/// Wrapper for C interface. -struct TWCardanoAddress { - TW::Cardano::AddressV3 impl; -}; diff --git a/src/Cardano/Entry.cpp b/src/Cardano/Entry.cpp index 0cf599dcc39..154b44ac407 100644 --- a/src/Cardano/Entry.cpp +++ b/src/Cardano/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,23 +7,30 @@ #include "Entry.h" #include "AddressV3.h" -//#include "Signer.h" +#include "Signer.h" -#include - -using namespace TW::Cardano; -using namespace std; +namespace TW::Cardano { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { - return AddressV3::isValid(address); +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { + return AddressV3::isValidLegacy(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return AddressV3(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - // not implemented yet +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + return AddressV3(address).data(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); } + +void Entry::plan([[maybe_unused]] TWCoinType coin, const Data& dataIn, Data& dataOut) const { + planTemplate(dataIn, dataOut); +} + +} // namespace TW::Cardano diff --git a/src/Cardano/Entry.h b/src/Cardano/Entry.h index c30df02bfba..94766b32303 100644 --- a/src/Cardano/Entry.h +++ b/src/Cardano/Entry.h @@ -12,12 +12,13 @@ namespace TW::Cardano { /// Entry point for implementation of Cardano coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeCardano}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Cardano diff --git a/src/Cardano/Signer.cpp b/src/Cardano/Signer.cpp new file mode 100644 index 00000000000..103f57f5e92 --- /dev/null +++ b/src/Cardano/Signer.cpp @@ -0,0 +1,535 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Signer.h" +#include "AddressV3.h" + +#include "Cbor.h" +#include "HexCoding.h" +#include "PrivateKey.h" + +#include +#include +#include +#include +#include + +namespace TW::Cardano { + +static const Data placeholderPrivateKey = parse_hex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"); +static const auto PlaceholderFee = 170000; +static const auto ExtraInputAmount = 500000; + +Proto::SigningOutput Signer::sign() { + // plan if needed + if (input.has_plan()) { + _plan = TransactionPlan::fromProto(input.plan()); + } else { + // no plan supplied, plan it + _plan = doPlan(); + } + + return signWithPlan(); +} + +Common::Proto::SigningError Signer::buildTransactionAux(Transaction& tx, const Proto::SigningInput& input, const TransactionPlan& plan) { + tx = Transaction(); + for (const auto& i : plan.utxos) { + tx.inputs.emplace_back(i.txHash, i.outputIndex); + } + + // Spending output + if (!AddressV3::isValidLegacy(input.transfer_message().to_address())) { + return Common::Proto::Error_invalid_address; + } + const auto toAddress = AddressV3(input.transfer_message().to_address()); + tx.outputs.emplace_back(toAddress.data(), plan.amount, plan.outputTokens); + // Change + bool hasChangeToken = any_of(plan.changeTokens.bundle.begin(), plan.changeTokens.bundle.end(), [](auto&& t) { return t.second.amount > 0; }); + if (plan.change > 0 || hasChangeToken) { + if (!AddressV3::isValidLegacy(input.transfer_message().change_address())) { + return Common::Proto::Error_invalid_address; + } + const auto changeAddress = AddressV3(input.transfer_message().change_address()); + tx.outputs.emplace_back(changeAddress.data(), plan.change, plan.changeTokens); + } + tx.fee = plan.fee; + tx.ttl = input.ttl(); + + if (input.has_register_staking_key()) { + const auto stakingAddress = AddressV3(input.register_staking_key().staking_address()); + // here we need the bare staking key + const auto key = stakingAddress.bytes; + tx.certificates.emplace_back(Certificate{Certificate::SkatingKeyRegistration, {CertificateKey{CertificateKey::AddressKeyHash, key}}, Data()}); + } + if (input.has_delegate()) { + const auto stakingAddress = AddressV3(input.delegate().staking_address()); + // here we need the bare staking key + const auto key = stakingAddress.bytes; + const auto poolId = data(input.delegate().pool_id()); + tx.certificates.emplace_back(Certificate{Certificate::Delegation, {CertificateKey{CertificateKey::AddressKeyHash, key}}, poolId}); + } + if (input.has_withdraw()) { + const auto stakingAddress = AddressV3(input.withdraw().staking_address()); + const auto key = stakingAddress.data(); + const auto amount = input.withdraw().withdraw_amount(); + tx.withdrawals.emplace_back(Withdrawal{key, amount}); + } + if (input.has_deregister_staking_key()) { + const auto stakingAddress = AddressV3(input.deregister_staking_key().staking_address()); + // here we need the bare staking key + const auto key = stakingAddress.bytes; + tx.certificates.emplace_back(Certificate{Certificate::StakingKeyDeregistration, {CertificateKey{CertificateKey::AddressKeyHash, key}}, Data()}); + } + + return Common::Proto::OK; +} + +Data deriveStakingPrivateKey(const Data& privateKeyData) { + if (privateKeyData.size() != PrivateKey::cardanoKeySize) { + return {}; + } + assert(privateKeyData.size() == PrivateKey::cardanoKeySize); + const auto halfSize = PrivateKey::cardanoKeySize / 2; + auto stakingPrivKeyData = TW::subData(privateKeyData, halfSize); + TW::append(stakingPrivKeyData, TW::Data(halfSize)); + return stakingPrivKeyData; +} + +Common::Proto::SigningError Signer::assembleSignatures(std::vector>& signatures, const Proto::SigningInput& input, const TransactionPlan& plan, const Data& txId, bool sizeEstimationOnly) { + signatures.clear(); + // Private keys and corresponding addresses + std::map privateKeys; + for (auto i = 0; i < input.private_key_size(); ++i) { + const auto privateKeyData = data(input.private_key(i)); + if (!PrivateKey::isValid(privateKeyData)) { + return Common::Proto::Error_invalid_private_key; + } + + // Add this private key and associated address + const auto privateKey = PrivateKey(privateKeyData); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + const auto address = AddressV3(publicKey); + privateKeys[address.string()] = privateKeyData; + + // Also add the derived staking private key (the 2nd half) and associated address; because staking keys also need signature + const auto stakingPrivKeyData = deriveStakingPrivateKey(privateKeyData); + if (!stakingPrivKeyData.empty()) { + privateKeys[address.getStakingAddress()] = stakingPrivKeyData; + } + } + + // collect every unique input UTXO address, preserving order + std::vector addresses; + for (auto& u : plan.utxos) { + if (!AddressV3::isValid(u.address)) { + return Common::Proto::Error_invalid_address; + } + addresses.emplace_back(u.address); + } + // Staking key is also an address that needs signature + if (input.has_register_staking_key()) { + addresses.emplace_back(input.register_staking_key().staking_address()); + } + if (input.has_deregister_staking_key()) { + addresses.emplace_back(input.deregister_staking_key().staking_address()); + } + if (input.has_delegate()) { + addresses.emplace_back(input.delegate().staking_address()); + } + if (input.has_withdraw()) { + addresses.emplace_back(input.withdraw().staking_address()); + } + // discard duplicates (std::set, std::copy_if, std::unique does not work well here) + std::vector addressesUnique; + for (auto& a: addresses) { + if (find(addressesUnique.begin(), addressesUnique.end(), a) == addressesUnique.end()) { + addressesUnique.emplace_back(a); + } + } + + // create signature for each address + for (auto& a : addressesUnique) { + const auto privKeyFind = privateKeys.find(a); + Data privateKeyData; + if (privKeyFind != privateKeys.end()) { + privateKeyData = privKeyFind->second; + } else { + // private key not found + if (sizeEstimationOnly) { + privateKeyData = placeholderPrivateKey; + } else { + return Common::Proto::Error_missing_private_key; + } + } + const auto privateKey = PrivateKey(privateKeyData); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + const auto signature = privateKey.sign(txId, TWCurveED25519ExtendedCardano); + // public key (first 32 bytes) and signature (64 bytes) + signatures.emplace_back(subData(publicKey.bytes, 0, 32), signature); + } + + return Common::Proto::OK; +} + +Cbor::Encode cborizeSignatures(const std::vector>& signatures) { + // signatures as Cbor + // clang-format off + std::vector sigsCbor; + for (auto& s : signatures) { + sigsCbor.emplace_back(Cbor::Encode::array({ + Cbor::Encode::bytes(s.first), + Cbor::Encode::bytes(s.second) + })); + } + + // Cbor-encode txAux & signatures + return Cbor::Encode::map({ + std::make_pair( + Cbor::Encode::uint(0), + Cbor::Encode::array(sigsCbor) + ) + }); + // clang-format on +} + +Proto::SigningOutput Signer::signWithPlan() const { + auto ret = Proto::SigningOutput(); + if (_plan.error != Common::Proto::OK) { + // plan has error + ret.set_error(_plan.error); + return ret; + } + + Data encoded; + Data txId; + const auto buildRet = encodeTransaction(encoded, txId, input, _plan); + if (buildRet != Common::Proto::OK) { + ret.set_error(buildRet); + return ret; + } + + ret.set_encoded(std::string(encoded.begin(), encoded.end())); + ret.set_tx_id(std::string(txId.begin(), txId.end())); + ret.set_error(Common::Proto::OK); + + return ret; +} + +Common::Proto::SigningError Signer::encodeTransaction(Data& encoded, Data& txId, const Proto::SigningInput& input, const TransactionPlan& plan, bool sizeEstimationOnly) { + if (plan.error != Common::Proto::OK) { + return plan.error; + } + + Transaction txAux; + const auto buildRet = buildTransactionAux(txAux, input, plan); + if (buildRet != Common::Proto::OK) { + return buildRet; + } + txId = txAux.getId(); + + std::vector> signatures; + const auto sigError = assembleSignatures(signatures, input, plan, txId, sizeEstimationOnly); + if (sigError != Common::Proto::OK) { + return sigError; + } + const auto sigsCbor = cborizeSignatures(signatures); + + // Cbor-encode txAux & signatures + const auto cbor = Cbor::Encode::array({ + // txaux + Cbor::Encode::fromRaw(txAux.encode()), + // signatures + sigsCbor, + // aux data + Cbor::Encode::null(), + }); + encoded = cbor.encoded(); + return Common::Proto::OK; +} + +// Select a subset of inputs, to cover desired coin amount. Simple algorithm: pick the largest ones. +std::vector selectInputsSimpleNative(const std::vector& inputs, Amount amount) { + auto ii = std::vector(inputs); + sort(ii.begin(), ii.end(), [](auto&& t1, auto&& t2) { + return t1.amount > t2.amount; + }); + auto selected = std::vector(); + Amount selectedAmount = 0; + + for (const auto& i : ii) { + selected.emplace_back(i); + selectedAmount += i.amount; + if (selectedAmount >= amount) { + break; + } + } + return selected; +} + +// Select a subset of inputs, to cover desired token amount. Simple algorithm: pick the largest ones. +void selectInputsSimpleToken(const std::vector& inputs, std::string key, const uint256_t& amount, std::vector& selectedInputs) { + auto accumulateFunctor = [key]([[maybe_unused]] auto&& sum, auto&& si) { return si.tokenBundle.getAmount(key); }; + uint256_t selectedAmount = std::accumulate(selectedInputs.begin(), selectedInputs.end(), uint256_t(0), accumulateFunctor); + if (selectedAmount >= amount) { + return; // already covered + } + // sort inputs descending + auto ii = std::vector(inputs); + std::sort(ii.begin(), ii.end(), [key](auto&& t1, auto&& t2) { return t1.tokenBundle.getAmount(key) > t2.tokenBundle.getAmount(key); }); + for (const auto& i : ii) { + if (static_cast(distance(selectedInputs.begin(), find(selectedInputs.begin(), selectedInputs.end(), i))) < selectedInputs.size()) { + // already selected + continue; + } + selectedInputs.emplace_back(i); + selectedAmount += i.amount; + if (selectedAmount >= amount) { + return; + } + } + // not enough +} + +// Select a subset of inputs, to cover desired amount. Simple algorithm: pick the largest ones +std::vector Signer::selectInputsWithTokens(const std::vector& inputs, Amount amount, const TokenBundle& requestedTokens) { + auto selected = selectInputsSimpleNative(inputs, amount); + for (auto&& [_, curAmount] : requestedTokens.bundle) { + selectInputsSimpleToken(inputs, curAmount.key(), curAmount.amount, selected); + } + return selected; +} + +// Create a simple plan, used for estimation +TransactionPlan simplePlan(Amount amount, const TokenBundle& requestedTokens, const std::vector& selectedInputs, bool maxAmount, uint64_t deposit, uint64_t undeposit) { + TransactionPlan plan{.utxos = selectedInputs, .amount = amount, .deposit = deposit, .undeposit = undeposit}; + // Sum availableAmount + plan.availableAmount = 0; + for (auto& u : plan.utxos) { + plan.availableAmount += u.amount; + for (auto && [_, curAmount] : u.tokenBundle.bundle) { + plan.availableTokens.add(curAmount); + } + } + plan.fee = PlaceholderFee; // placeholder value + const auto availAfterDeposit = plan.availableAmount + plan.undeposit - plan.deposit; + // adjust/compute output amount and output tokens + if (!maxAmount) { + // reduce amount if needed + plan.amount = std::max(Amount(0), std::min(plan.amount, availAfterDeposit - plan.fee)); + plan.outputTokens = requestedTokens; + } else { + // max available amount + plan.amount = std::max(Amount(0), availAfterDeposit - plan.fee); + plan.outputTokens = plan.availableTokens; // use all + } + + // compute change + plan.change = availAfterDeposit - (plan.amount + plan.fee); + for (auto iter = plan.availableTokens.bundle.begin(); iter != plan.availableTokens.bundle.end(); ++iter) { + const auto key = iter->second.key(); + const auto changeAmount = iter->second.amount - plan.outputTokens.getAmount(key); + assert(changeAmount >= 0); + plan.changeTokens.bundle[key] = iter->second; + plan.changeTokens.bundle[key].amount = changeAmount; + } + return plan; +} + +uint64_t sumDeposits(const Proto::SigningInput& input) { + uint64_t sum = 0; + if (input.has_register_staking_key()) { + sum += input.register_staking_key().deposit_amount(); + } + if (input.has_delegate()) { + sum += input.delegate().deposit_amount(); + } + return sum; +} + +uint64_t sumUndeposits(const Proto::SigningInput& input) { + uint64_t sum = 0; + if (input.has_deregister_staking_key()) { + sum += input.deregister_staking_key().undeposit_amount(); + } + if (input.has_withdraw()) { + sum += input.withdraw().withdraw_amount(); + } + return sum; +} + +// Estimates size of transaction in bytes. +uint64_t estimateTxSize(const Proto::SigningInput& input, Amount amount, const TokenBundle& requestedTokens, const std::vector& selectedInputs) { + auto inputs = std::vector(); + for (auto i = 0; i < input.utxos_size(); ++i) { + inputs.emplace_back(TxInput::fromProto(input.utxos(i))); + } + const auto deposits = sumDeposits(input); + const uint64_t undeposits = sumUndeposits(input); + const auto _simplePlan = simplePlan(amount, requestedTokens, selectedInputs, input.transfer_message().use_max_amount(), deposits, undeposits); + + Data encoded; + Data txId; + const auto encodeError = Signer::encodeTransaction(encoded, txId, input, _simplePlan, true); + if (encodeError != Common::Proto::OK) { + return 0; + } + + return encoded.size(); +} + +// Compute fee from tx size, with some over-estimation +Amount txFeeFunction(uint64_t txSizeInBytes) { + const double fixedTerm = 155381 + 500; + const double linearTerm = 43.946 + 0.1; + + const auto fee = (Amount)(ceil(fixedTerm + (double)txSizeInBytes * linearTerm)); + return fee; +} + +Amount Signer::estimateFee(const Proto::SigningInput& input, Amount amount, const TokenBundle& requestedTokens, const std::vector& selectedInputs) { + return txFeeFunction(estimateTxSize(input, amount, requestedTokens, selectedInputs)); +} + +TransactionPlan Signer::doPlan() const { + auto plan = TransactionPlan(); + + bool maxAmount = input.transfer_message().use_max_amount(); + if (input.transfer_message().amount() == 0 && !maxAmount && input.transfer_message().token_amount().token_size() == 0) { + plan.error = Common::Proto::Error_zero_amount_requested; + return plan; + } + // Check input UTXOs, process, sum ADA and token amounts + auto utxos = std::vector(); + uint64_t inputSum = 0; + for (auto i = 0; i < input.utxos_size(); ++i) { + const auto& utxo = input.utxos(i); + utxos.emplace_back(TxInput::fromProto(utxo)); + inputSum += utxo.amount(); + } + if (inputSum == 0 || input.utxos_size() == 0) { + plan.error = Common::Proto::Error_missing_input_utxos; + return plan; + } + assert(inputSum > 0); + // adjust inputSum with deposited/undeposited amount + plan.deposit = sumDeposits(input); + plan.undeposit = sumUndeposits(input); + const auto inputSumAfterDeposit = inputSum + plan.undeposit - plan.deposit; + + // Amounts requested + plan.amount = input.transfer_message().amount(); + TokenBundle requestedTokens; + for (auto i = 0; i < input.transfer_message().token_amount().token_size(); ++i) { + const auto token = TokenAmount::fromProto(input.transfer_message().token_amount().token(i)); + requestedTokens.add(token); + } + assert(plan.amount > 0 || maxAmount); + if (requestedTokens.size() > 1) { + // We support transfer of only one coin (for simplicity; inputs may contain more coins which are preserved) + plan.error = Common::Proto::Error_invalid_requested_token_amount; + return plan; + } + + // if amount requested is the same or more than available amount, it cannot be satisfied, but + // treat this case as MaxAmount, and send maximum available (which will be less) + if (!maxAmount && input.transfer_message().amount() >= inputSumAfterDeposit) { + maxAmount = true; + } + + // select UTXOs + if (!maxAmount) { + // aim for larger total input, enough for 4/3 of the target amount plus typical fee plus minimal ADA for change plus some extra + auto targetInputAmount = (plan.amount * 4) / 3 + plan.deposit - plan.undeposit + PlaceholderFee + requestedTokens.minAdaAmount() + ExtraInputAmount; + plan.utxos = selectInputsWithTokens(utxos, targetInputAmount, requestedTokens); + } else { + // maxAmount, select all + plan.utxos = utxos; + } + assert(!plan.utxos.empty()); + + // Sum availableAmount + plan.availableAmount = 0; + for (auto& u : plan.utxos) { + plan.availableAmount += u.amount; + for (auto && [_, curAmount] : u.tokenBundle.bundle) { + plan.availableTokens.add(curAmount); + } + } + if (plan.availableAmount == 0) { + plan.error = Common::Proto::Error_missing_input_utxos; + return plan; + } + assert(plan.availableAmount > 0); + // adjust availableAmount with deposited/undeposited amount + const auto availableAmountAfterDeposit = plan.availableAmount + plan.undeposit - plan.deposit; + + // check that there are enough coins in the inputs + if (plan.amount > availableAmountAfterDeposit) { + plan.error = Common::Proto::Error_low_balance; + return plan; + } + assert(plan.amount <= availableAmountAfterDeposit); + // check that there are enough tokens in the inputs + for (auto && [_, curAmount] : requestedTokens.bundle) { + if (curAmount.amount > plan.availableTokens.getAmount(curAmount.key())) { + plan.error = Common::Proto::Error_low_balance; + return plan; + } + } + + // compute fee + if (input.transfer_message().force_fee() == 0) { + plan.fee = estimateFee(input, plan.amount, requestedTokens, plan.utxos); + } else { + // fee provided, use it (capped) + plan.fee = std::max(Amount(0), std::min(availableAmountAfterDeposit - plan.amount, input.transfer_message().force_fee())); + } + assert(plan.fee >= 0 && plan.fee < availableAmountAfterDeposit); + + // adjust/compute output amount + if (!maxAmount) { + // reduce amount if needed + plan.amount = std::max(Amount(0), std::min(plan.amount, availableAmountAfterDeposit - plan.fee)); + } else { + // max available amount + plan.amount = std::max(Amount(0), availableAmountAfterDeposit - plan.fee); + } + assert(plan.amount >= 0 && plan.amount <= availableAmountAfterDeposit); + + if (plan.amount + plan.fee > availableAmountAfterDeposit) { + plan.error = Common::Proto::Error_low_balance; + return plan; + } + assert(plan.amount + plan.fee <= availableAmountAfterDeposit); + + // compute output token amounts + if (!maxAmount) { + plan.outputTokens = requestedTokens; + } else { + plan.outputTokens = plan.availableTokens; // send all + } + + // compute change + plan.change = availableAmountAfterDeposit - (plan.amount + plan.fee); + for (auto iter = plan.availableTokens.bundle.begin(); iter != plan.availableTokens.bundle.end(); ++iter) { + const auto key = iter->second.key(); + const auto changeAmount = iter->second.amount - plan.outputTokens.getAmount(key); + if (changeAmount > 0) { // omit 0-amount tokens + plan.changeTokens.bundle[key] = iter->second; + plan.changeTokens.bundle[key].amount = changeAmount; + } + } + + assert(plan.change >= 0 && plan.change <= availableAmountAfterDeposit); + assert(!maxAmount || plan.change == 0); // change is 0 in max amount case + assert(plan.amount + plan.change + plan.fee == availableAmountAfterDeposit); + assert(plan.amount + plan.change + plan.fee + plan.deposit == plan.availableAmount + plan.undeposit); + + return plan; +} + +} // namespace TW::Cardano diff --git a/src/Cardano/Signer.h b/src/Cardano/Signer.h new file mode 100644 index 00000000000..01d538a617f --- /dev/null +++ b/src/Cardano/Signer.h @@ -0,0 +1,54 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Transaction.h" +#include "../proto/Cardano.pb.h" +#include "Data.h" + +#include +#include +#include + +namespace TW::Cardano { + +class Signer { +public: + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept { + Signer signer = Signer(input); + return signer.sign(); + } + +public: + Proto::SigningInput input; + TransactionPlan _plan; + + explicit Signer(Proto::SigningInput input): input(std::move(input)) {} + + Proto::SigningOutput sign(); + // Sign using existing plan + Proto::SigningOutput signWithPlan() const; + // Create plan from signing input + TransactionPlan doPlan() const; + /// Returns a transaction plan (utxo selection, fee estimation) + static Proto::TransactionPlan plan(const Proto::SigningInput& input) noexcept { + const auto signer = Signer(input); + const auto plan = signer.doPlan(); + return plan.toProto(); + } + // Build encoded transaction + static Common::Proto::SigningError encodeTransaction(Data& encoded, Data& txId, const Proto::SigningInput& input, const TransactionPlan& plan, bool sizeEstimationOnly = false); + // Build aux transaction object, using input and plan + static Common::Proto::SigningError buildTransactionAux(Transaction& tx, const Proto::SigningInput& input, const TransactionPlan& plan); + static Amount estimateFee(const Proto::SigningInput& input, Amount amount, const TokenBundle& requestedTokens, const std::vector& selectedInputs); + static std::vector selectInputsWithTokens(const std::vector& inputs, Amount amount, const TokenBundle& requestedTokens); + // Build list of public keys + signature + static Common::Proto::SigningError assembleSignatures(std::vector>& signatures, const Proto::SigningInput& input, const TransactionPlan& plan, const Data& txId, bool sizeEstimationOnly = false); +}; + +} // namespace TW::Cardano diff --git a/src/Cardano/Transaction.cpp b/src/Cardano/Transaction.cpp new file mode 100644 index 00000000000..da6a4924572 --- /dev/null +++ b/src/Cardano/Transaction.cpp @@ -0,0 +1,316 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Transaction.h" + +#include "Cbor.h" +#include "Hash.h" +#include "HexCoding.h" + +namespace TW::Cardano { + +TokenAmount TokenAmount::fromProto(const Proto::TokenAmount& proto) { + return {proto.policy_id(), proto.asset_name(), load(proto.amount())}; +} + +Proto::TokenAmount TokenAmount::toProto() const { + Proto::TokenAmount tokenAmount; + tokenAmount.set_policy_id(policyId.data(), policyId.size()); + tokenAmount.set_asset_name(assetName.data(), assetName.size()); + const auto amountData = store(amount); + tokenAmount.set_amount(amountData.data(), amountData.size()); + return tokenAmount; +} + +TokenBundle TokenBundle::fromProto(const Proto::TokenBundle& proto) { + TokenBundle ret; + const auto addFunctor = [&ret](auto&& cur) { ret.add(TokenAmount::fromProto(cur)); }; + std::for_each(std::cbegin(proto.token()), std::cend(proto.token()), addFunctor); + return ret; +} + +Proto::TokenBundle TokenBundle::toProto() const { + Proto::TokenBundle proto; + for (const auto& t : bundle) { + *(proto.add_token()) = t.second.toProto(); + } + return proto; +} + +void TokenBundle::add(const TokenAmount& ta) { + const auto key = ta.key(); + if (auto&& [it, inserted] = bundle.try_emplace(key, ta); !inserted) { + it->second.amount += ta.amount; + } +} + +uint256_t TokenBundle::getAmount(const std::string& key) const { + const auto& findkey = bundle.find(key); + return findkey == bundle.end() ? 0 : findkey->second.amount; +} + +std::unordered_set TokenBundle::getPolicyIds() const { + std::unordered_set policyIds; + std::transform(bundle.cbegin(), bundle.cend(), + std::inserter(policyIds, policyIds.begin()), + [](auto&& cur) { return cur.second.policyId; }); + return policyIds; +} + +std::vector TokenBundle::getByPolicyId(const std::string& policyId) const { + std::vector filtered; + for (auto&& t : bundle) { + if (t.second.policyId == policyId) { + filtered.emplace_back(t.second); + } + } + return filtered; +} + +uint64_t roundupBytesToWords(uint64_t b) { + return ((b + 7) / 8); +} + +const uint64_t TokenBundle::MinUtxoValue = 1000000; + +uint64_t TokenBundle::minAdaAmountHelper(uint64_t numPids, uint64_t numAssets, uint64_t sumAssetNameLengths) { + if (numPids == 0) { + return MinUtxoValue; + } + + static const uint64_t coinSize = 0; + static const uint64_t utxoEntrySizeWithoutVal = 27; + static const uint64_t adaOnlyUTxOSize = utxoEntrySizeWithoutVal + coinSize; // 27 + static const uint64_t pidSize = 28; + + uint64_t sizeB = 6 + roundupBytesToWords((numAssets * 12) + sumAssetNameLengths + (numPids * pidSize)); + return std::max(MinUtxoValue, (MinUtxoValue / adaOnlyUTxOSize) * (utxoEntrySizeWithoutVal + sizeB)); +} + +uint64_t TokenBundle::minAdaAmount() const { + if (size() == 0) { + // ADA only + return MinUtxoValue; + } + + std::unordered_set policyIdRegistry; + std::unordered_set assetNameRegistry; + uint64_t sumAssetNameLengths = 0; + for (const auto& t : bundle) { + policyIdRegistry.emplace(t.second.policyId); + if (t.second.assetName.length() > 0) { + assetNameRegistry.emplace(t.second.assetName); + } + } + auto numPids = uint64_t(policyIdRegistry.size()); + auto numAssets = uint64_t(assetNameRegistry.size()); + for_each(assetNameRegistry.begin(), assetNameRegistry.end(), [&sumAssetNameLengths](auto&& a) { sumAssetNameLengths += a.length(); }); + return minAdaAmountHelper(numPids, numAssets, sumAssetNameLengths); +} + +TxInput TxInput::fromProto(const Cardano::Proto::TxInput& proto) { + auto ret = TxInput(); + ret.txHash = data(proto.out_point().tx_hash()); + ret.outputIndex = proto.out_point().output_index(); + ret.address = proto.address(); + ret.amount = proto.amount(); + for (auto i = 0; i < proto.token_amount_size(); ++i) { + auto ta = TokenAmount::fromProto(proto.token_amount(i)); + ret.tokenBundle.add(ta); + } + return ret; +} + +Proto::TxInput TxInput::toProto() const { + Proto::TxInput txInput; + txInput.mutable_out_point()->set_tx_hash(txHash.data(), txHash.size()); + txInput.mutable_out_point()->set_output_index(outputIndex); + txInput.set_address(address.data(), address.size()); + txInput.set_amount(amount); + for (const auto& token : tokenBundle.bundle) { + *txInput.add_token_amount() = token.second.toProto(); + } + return txInput; +} + +bool operator==(const TxInput& i1, const TxInput& i2) { + return i1.outputIndex == i2.outputIndex && i1.txHash == i2.txHash; +} + +TransactionPlan TransactionPlan::fromProto(const Proto::TransactionPlan& proto) { + auto ret = TransactionPlan(); + ret.availableAmount = proto.available_amount(); + ret.amount = proto.amount(); + ret.fee = proto.fee(); + ret.change = proto.change(); + ret.deposit = proto.deposit(); + ret.undeposit = proto.undeposit(); + for (auto i = 0; i < proto.available_tokens_size(); ++i) { + ret.availableTokens.add(TokenAmount::fromProto(proto.available_tokens(i))); + } + for (auto i = 0; i < proto.output_tokens_size(); ++i) { + ret.outputTokens.add(TokenAmount::fromProto(proto.output_tokens(i))); + } + for (auto i = 0; i < proto.change_tokens_size(); ++i) { + ret.changeTokens.add(TokenAmount::fromProto(proto.change_tokens(i))); + } + for (auto i = 0; i < proto.utxos_size(); ++i) { + ret.utxos.emplace_back(TxInput::fromProto(proto.utxos(i))); + } + ret.error = proto.error(); + return ret; +} + +Proto::TransactionPlan TransactionPlan::toProto() const { + Proto::TransactionPlan plan; + plan.set_available_amount(availableAmount); + plan.set_amount(amount); + plan.set_fee(fee); + plan.set_change(change); + plan.set_deposit(deposit); + plan.set_undeposit(undeposit); + for (const auto& token : availableTokens.bundle) { + *plan.add_available_tokens() = token.second.toProto(); + } + for (const auto& token : outputTokens.bundle) { + *plan.add_output_tokens() = token.second.toProto(); + } + for (const auto& token : changeTokens.bundle) { + *plan.add_change_tokens() = token.second.toProto(); + } + for (const auto& u : utxos) { + *plan.add_utxos() = u.toProto(); + } + plan.set_error(error); + return plan; +} + +Cbor::Encode cborizeInputs(const std::vector& inputs) { + // clang-format off + std::vector ii; + for (const auto& i : inputs) { + ii.emplace_back(Cbor::Encode::array({ + Cbor::Encode::bytes(i.txHash), + Cbor::Encode::uint(i.outputIndex) + })); + } + // clang-format on + return Cbor::Encode::array(ii); +} + +Cbor::Encode cborizeOutputAmounts(const Amount& amount, const TokenBundle& tokenBundle) { + if (tokenBundle.size() == 0) { + // native amount only + return Cbor::Encode::uint(amount); + } + // native and token amounts + // tokens: organized in two levels: by policyId and by assetName + const auto policyIds = tokenBundle.getPolicyIds(); + std::map tokensMap; + for (const auto& policy : policyIds) { + const auto& subTokens = tokenBundle.getByPolicyId(policy); + std::map subTokensMap; + for (const auto& token : subTokens) { + subTokensMap.emplace( + Cbor::Encode::bytes(data(token.assetName)), + Cbor::Encode::uint(uint64_t(token.amount)) // 64 bits + ); + } + tokensMap.emplace( + Cbor::Encode::bytes(parse_hex(policy)), + Cbor::Encode::map(subTokensMap)); + } + // clang-format off + return Cbor::Encode::array({ + Cbor::Encode::uint(amount), + Cbor::Encode::map(tokensMap) + }); + // clang-format on +} + +Cbor::Encode cborizeOutput(const TxOutput& output) { + // clang-format off + return Cbor::Encode::array({ + Cbor::Encode::bytes(output.address), + cborizeOutputAmounts(output.amount, output.tokenBundle) + }); + // clang-format on +} + +Cbor::Encode cborizeOutputs(const std::vector& outputs) { + std::vector oo; + for (const auto& o : outputs) { + oo.emplace_back(cborizeOutput(o)); + } + return Cbor::Encode::array(oo); +} + +Cbor::Encode cborizeCertificateKey(const CertificateKey& certKey) { + std::vector c; + c.emplace_back(Cbor::Encode::uint(static_cast(certKey.type))); + c.emplace_back(Cbor::Encode::bytes(certKey.key)); + return Cbor::Encode::array(c); +} + +Cbor::Encode cborizeCert(const Certificate& cert) { + std::vector c; + c.emplace_back(Cbor::Encode::uint(static_cast(cert.type))); + c.emplace_back(cborizeCertificateKey(cert.certKey)); + if (!cert.poolId.empty()) { + c.emplace_back(Cbor::Encode::bytes(cert.poolId)); + } + return Cbor::Encode::array(c); +} + +Cbor::Encode cborizeCerts(const std::vector& certs) { + std::vector c; + for (const auto& i : certs) { + c.emplace_back(cborizeCert(i)); + } + return Cbor::Encode::array(c); +} + +Cbor::Encode cborizeWithdrawals(const std::vector& withdrawals) { + std::map mapElems; + for (const auto& w : withdrawals) { + mapElems.emplace(Cbor::Encode::bytes(w.stakingKey), Cbor::Encode::uint(w.amount)); + } + return Cbor::Encode::map(mapElems); +} + +Data Transaction::encode() const { + const auto ii = cborizeInputs(inputs); + const auto oo = cborizeOutputs(outputs); + + // Encode elements in a map, with fixed numbers as keys + std::map mapElems = { + std::make_pair(Cbor::Encode::uint(0), ii), + std::make_pair(Cbor::Encode::uint(1), oo), + std::make_pair(Cbor::Encode::uint(2), Cbor::Encode::uint(fee)), + std::make_pair(Cbor::Encode::uint(3), Cbor::Encode::uint(ttl)), + }; + + if (!certificates.empty()) { + mapElems.emplace(Cbor::Encode::uint(4), cborizeCerts(certificates)); + } + if (!withdrawals.empty()) { + mapElems.emplace(Cbor::Encode::uint(5), cborizeWithdrawals(withdrawals)); + } + + Cbor::Encode encode = Cbor::Encode::map(mapElems); + return encode.encoded(); + + // Note: following fields are not included: + // 7 AUXILIARY_DATA_HASH, 8 VALIDITY_INTERVAL_START +} + +Data Transaction::getId() const { + const auto encoded = encode(); + auto hash = Hash::blake2b(encoded, 32); + return hash; +} + +} // namespace TW::Cardano diff --git a/src/Cardano/Transaction.h b/src/Cardano/Transaction.h new file mode 100644 index 00000000000..a8e7e758000 --- /dev/null +++ b/src/Cardano/Transaction.h @@ -0,0 +1,180 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "AddressV3.h" + +#include "Data.h" +#include "uint256.h" +#include "../proto/Cardano.pb.h" +#include "../proto/Common.pb.h" + +#include +#include +#include +#include + +namespace TW::Cardano { + +typedef uint64_t Amount; + +class TokenAmount { +public: + std::string policyId; + std::string assetName; + uint256_t amount; + + TokenAmount() = default; + TokenAmount(std::string policyId, std::string assetName, uint256_t amount) + : policyId(std::move(policyId)), assetName(std::move(assetName)), amount(std::move(amount)) {} + + static TokenAmount fromProto(const Proto::TokenAmount& proto); + Proto::TokenAmount toProto() const; + /// Key used in TokenBundle + std::string key() const { return policyId + "_" + assetName; } +}; + +class TokenBundle { +public: + std::map bundle; + + TokenBundle() = default; + explicit TokenBundle(const std::vector& tokens) { + for (const auto& t : tokens) { + add(t); + } + } + + static TokenBundle fromProto(const Proto::TokenBundle& proto); + Proto::TokenBundle toProto() const; + + void add(const TokenAmount& ta); + uint256_t getAmount(const std::string& key) const; + size_t size() const { return bundle.size(); } + /// Get the unique policyIds, can be the same number as the elements, or less (in case a policyId appears more than once, with different asset names). + std::unordered_set getPolicyIds() const; + /// Filter by policyIds + std::vector getByPolicyId(const std::string& policyId) const; + + // The minimum ADA amount needed for an ADA-only UTXO + static const uint64_t MinUtxoValue; + // The minimum ADA amount needed for a UTXO with this token bundle. See https://docs.cardano.org/native-tokens/minimum-ada-value-requirement + uint64_t minAdaAmount() const; + static uint64_t minAdaAmountHelper(uint64_t numPids, uint64_t numAssets, uint64_t sumAssetNameLengths); +}; + +class OutPoint { +public: + Data txHash; + uint64_t outputIndex{}; + + OutPoint() = default; + OutPoint(Data txHash, uint64_t outputIndex) + : txHash(std::move(txHash)), outputIndex(outputIndex) {} +}; + +class TxInput : public OutPoint { +public: + std::string address; + + /// ADA amount + Amount amount; + + /// Token amounts (optional) + TokenBundle tokenBundle; + + static TxInput fromProto(const Proto::TxInput& proto); + Proto::TxInput toProto() const; +}; + +bool operator==(const TxInput& i1, const TxInput& i2); + +class TxOutput { +public: + Data address; + + /// ADA amount + Amount amount{}; + + /// Token amounts (optional) + TokenBundle tokenBundle; + + TxOutput() = default; + TxOutput(Data address, Amount amount) + : address(std::move(address)), amount(amount) {} + TxOutput(Data address, Amount amount, TokenBundle tokenBundle) + : address(std::move(address)), amount(amount), tokenBundle(std::move(tokenBundle)) {} +}; + +class TransactionPlan { +public: + std::vector utxos; + Amount availableAmount = 0; // total coins in the input utxos + Amount amount = 0; // coins in the output UTXO + Amount fee = 0; // coin amount deducted as fee + Amount change = 0; // coins in the change UTXO + Amount deposit = 0; // coins deposited (going to deposit) in this TX + Amount undeposit = 0; // coins undeposited (returned from deposit) in this TX + TokenBundle availableTokens; // total tokens in the utxos (optional) + TokenBundle outputTokens; // tokens in the output (optional) + TokenBundle changeTokens; // tokens in the change (optional) + Common::Proto::SigningError error = Common::Proto::SigningError::OK; + + static TransactionPlan fromProto(const Proto::TransactionPlan& proto); + Proto::TransactionPlan toProto() const; +}; + +/// A key with a type, used in a Certificate +class CertificateKey { +public: + enum KeyType : uint8_t { + AddressKeyHash = 0, + // ScriptHash = 1, + }; + KeyType type; + Data key; +}; + +/// Certificate, mainly used for staking +class Certificate { +public: + enum CertificateType : uint8_t { + SkatingKeyRegistration = 0, + StakingKeyDeregistration = 1, + Delegation = 2, + // StakePoolRegistration = 3, // not supported + }; + CertificateType type; + CertificateKey certKey; + /// Optional PoolId, used in delegation + Data poolId; +}; + +/// Staking withdrawal +class Withdrawal { +public: + Data stakingKey; + Amount amount; +}; + +class Transaction { +public: + std::vector inputs; + std::vector outputs; + Amount fee; + uint64_t ttl; + std::vector certificates; + std::vector withdrawals; + + // Encode into CBOR binary format + Data encode() const; + + // Derive Transaction ID from hashed encoded data + Data getId() const; +}; + +} // namespace TW::Cardano diff --git a/src/Cbor.cpp b/src/Cbor.cpp index 41e85aa1106..719c173ec83 100644 --- a/src/Cbor.cpp +++ b/src/Cbor.cpp @@ -19,7 +19,7 @@ TW::Data Encode::encoded() const { if (openIndefCount > 0) { throw invalid_argument("CBOR Unclosed indefinite length building"); } - return data; + return _data; } Encode Encode::uint(uint64_t value) { @@ -46,21 +46,21 @@ Encode Encode::array(const vector& elems) { Encode e; auto n = elems.size(); e.appendValue(Decode::MT_array, n); - for (int i = 0; i < n; ++i) { + for (auto i = 0ul; i < n; ++i) { e.append(elems[i].encoded()); } return e; } -Encode Encode::map(const vector>& elems) { - Encode e; +Encode Encode::map(const std::map& elems) { + Encode enc; auto n = elems.size(); - e.appendValue(Decode::MT_map, n); - for (int i = 0; i < n; ++i) { - e.append(elems[i].first.encoded()); - e.append(elems[i].second.encoded()); + enc.appendValue(Decode::MT_map, n); + for (const auto& e: elems) { + enc.append(e.first.encoded()); + enc.append(e.second.encoded()); } - return e; + return enc; } Encode Encode::tag(uint64_t value, const Encode& elem) { @@ -70,6 +70,12 @@ Encode Encode::tag(uint64_t value, const Encode& elem) { return e; } +Encode Encode::null() { + Encode e; + e.appendValue(Decode::MT_special, 0x16); + return e; +} + Encode Encode::indefArray() { Encode e; e.appendIndefinite(Decode::MT_array); @@ -90,7 +96,7 @@ Encode Encode::closeIndefArray() { throw invalid_argument("CBOR Not inside indefinite-length array"); } // add closing break command - TW::append(data, 0xFF); + TW::append(_data, 0xFF); // close counter --openIndefCount; return *this; @@ -123,9 +129,9 @@ Encode Encode::appendValue(byte majorType, uint64_t value) { minorType = 27; } // add bytes - TW::append(data, (byte)((majorType << 5) | (minorType & 0x1F))); + TW::append(_data, (byte)((majorType << 5) | (minorType & 0x1F))); Data valBytes = Data(byteCount - 1); - for (int i = 0; i < valBytes.size(); ++i) { + for (auto i = 0ul; i < valBytes.size(); ++i) { valBytes[valBytes.size() - 1 - i] = (byte)(value & 0xFF); value = value >> 8; } @@ -135,7 +141,7 @@ Encode Encode::appendValue(byte majorType, uint64_t value) { void Encode::appendIndefinite(byte majorType) { byte minorType = 31; - TW::append(data, (byte)((majorType << 5) | (minorType & 0x1F))); + TW::append(_data, (byte)((majorType << 5) | (minorType & 0x1F))); } @@ -273,7 +279,7 @@ uint32_t Decode::getCompoundLength(uint32_t countMultiplier) const { uint32_t count = typeDesc.isIndefiniteValue ? 0 : (uint32_t)(typeDesc.value * countMultiplier); // process elements len += typeDesc.byteCount; - for (int i = 0; i < count || typeDesc.isIndefiniteValue; ++i) { + for (auto i = 0ul; i < count || typeDesc.isIndefiniteValue; ++i) { Decode nextElem = skipClone(len); if (typeDesc.isIndefiniteValue && nextElem.isBreak()) { // end of indefinite-length @@ -298,7 +304,7 @@ vector Decode::getCompoundElements(uint32_t countMultiplier, TW::byte ex uint32_t count = typeDesc.isIndefiniteValue ? 0 : (uint32_t)(typeDesc.value * countMultiplier); // process elements uint32_t idx = typeDesc.byteCount; - for (int i = 0; i < count || typeDesc.isIndefiniteValue; ++i) { + for (auto i = 0ul; i < count || typeDesc.isIndefiniteValue; ++i) { Decode nextElem = skipClone(idx); if (typeDesc.isIndefiniteValue && nextElem.isBreak()) { // end of indefinite-length @@ -308,7 +314,7 @@ vector Decode::getCompoundElements(uint32_t countMultiplier, TW::byte ex if (idx + elemLen > length()) { throw std::invalid_argument("CBOR array data too short"); } - elems.push_back(Decode(data, subStart + idx, elemLen)); + elems.emplace_back(Decode(data, subStart + idx, elemLen)); idx += elemLen; } return elems; @@ -317,7 +323,7 @@ vector Decode::getCompoundElements(uint32_t countMultiplier, TW::byte ex vector> Decode::getMapElements() const { auto elems = getCompoundElements(2, MT_map); vector> map; - for (int i = 0; i < elems.size(); i += 2) { + for (auto i = 0ul; i < elems.size(); i += 2) { map.emplace_back(make_pair(elems[i], elems[i + 1])); } return map; @@ -363,7 +369,7 @@ bool Decode::isValid() const { if (len > subLen) { return false; } auto count = typeDesc.isIndefiniteValue ? 0 : countMultiplier * typeDesc.value; uint32_t idx = typeDesc.byteCount; - for (int i = 0; i < count || typeDesc.isIndefiniteValue; ++i) + for (auto i = 0ul; i < count || typeDesc.isIndefiniteValue; ++i) { Decode nextElem = skipClone(idx); if (typeDesc.isIndefiniteValue && nextElem.isBreak()) { break; } @@ -410,7 +416,7 @@ string Decode::dumpToStringInternal() const { s << "["; } vector elems = getArrayElements(); - for (int i = 0; i < elems.size(); ++i) { + for (auto i = 0ul; i < elems.size(); ++i) { if (i > 0) s << ", "; s << elems[i].dumpToStringInternal(); } @@ -426,7 +432,7 @@ string Decode::dumpToStringInternal() const { s << "{"; } auto elems = getMapElements(); - for (int i = 0; i < elems.size(); ++i) { + for (auto i = 0ul; i < elems.size(); ++i) { if (i > 0) s << ", "; s << elems[i].first.dumpToStringInternal() << ": " << elems[i].second.dumpToStringInternal(); } @@ -443,7 +449,11 @@ string Decode::dumpToStringInternal() const { if (typeDesc.isIndefiniteValue) { // skip break command } else { - s << "spec " << typeDesc.value; + if (typeDesc.value == 0x16) { + s << "null"; + } else { + s << "spec " << typeDesc.value; + } } break; } diff --git a/src/Cbor.h b/src/Cbor.h index 0b6dee6cf62..06ab9f3feca 100644 --- a/src/Cbor.h +++ b/src/Cbor.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include namespace TW::Cbor { @@ -38,9 +40,11 @@ class Encode { /// encode an array of elements (of different types) static Encode array(const std::vector& elems); /// encode a map - static Encode map(const std::vector>& elems); + static Encode map(const std::map& elems); /// encode a tag and following element static Encode tag(uint64_t value, const Encode& elem); + /// encode a null value (special) + static Encode null(); /// Stateful building (for indefinite length) /// Start an indefinite-length array @@ -52,22 +56,28 @@ class Encode { /// Create from raw content, must be valid CBOR data, may throw static Encode fromRaw(const TW::Data& rawData); + const Data& getDataInternal() const { return _data; } private: Encode() {} - Encode(const TW::Data& rawData) : data(rawData) {} + Encode(const TW::Data& rawData) : _data(rawData) {} /// Append types + value, on variable number of bytes (1..8). Return object to support chain syntax. Encode appendValue(byte majorType, uint64_t value); - inline Encode append(const TW::Data& data) { TW::append(this->data, data); return *this; } + inline Encode append(const TW::Data& data) { TW::append(_data, data); return *this; } void appendIndefinite(byte majorType); private: /// Encoded data is stored here, always well-formed, but my be partial. - TW::Data data; + TW::Data _data; /// number of currently open indefinite buildingds (0, 1, or more for nested) int openIndefCount = 0; }; +/// Comparator, needed for map keys +inline bool operator<(const Encode& lhs, const Encode& rhs) { + return lhs.getDataInternal() < rhs.getDataInternal(); +} + /// CBOR Decoder and container for data for decoding. Contains reference to read-only CBOR data. /// See CborTests.cpp for usage. class Decode { diff --git a/src/Coin.cpp b/src/Coin.cpp index d94cf75a7d8..efe10e47e56 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -16,6 +16,7 @@ #include "Aeternity/Entry.h" #include "Aion/Entry.h" #include "Algorand/Entry.h" +#include "Aptos/Entry.h" #include "Binance/Entry.h" #include "Bitcoin/Entry.h" #include "Cardano/Entry.h" @@ -24,6 +25,7 @@ #include "EOS/Entry.h" #include "Elrond/Entry.h" #include "Ethereum/Entry.h" +#include "Everscale/Entry.h" #include "FIO/Entry.h" #include "Filecoin/Entry.h" #include "Groestlcoin/Entry.h" @@ -36,20 +38,21 @@ #include "NULS/Entry.h" #include "Nano/Entry.h" #include "Nebulas/Entry.h" +#include "Nervos/Entry.h" #include "Nimiq/Entry.h" #include "Oasis/Entry.h" #include "Ontology/Entry.h" #include "Polkadot/Entry.h" -#include "Ripple/Entry.h" +#include "Ronin/Entry.h" #include "Solana/Entry.h" #include "Stellar/Entry.h" #include "THORChain/Entry.h" #include "Tezos/Entry.h" #include "Theta/Entry.h" #include "Tron/Entry.h" -#include "TrustWalletCore/TWCoinType.h" #include "VeChain/Entry.h" #include "Waves/Entry.h" +#include "XRP/Entry.h" #include "Zcash/Entry.h" #include "Zilliqa/Entry.h" // end_of_coin_includes_marker_do_not_modify @@ -61,6 +64,7 @@ using namespace std; Aeternity::Entry aeternityDP; Aion::Entry aionDP; Algorand::Entry algorandDP; +Aptos::Entry AptosDP; Binance::Entry binanceDP; Bitcoin::Entry bitcoinDP; Cardano::Entry cardanoDP; @@ -86,6 +90,7 @@ Ontology::Entry ontologyDP; Oasis::Entry oasisDP; Polkadot::Entry polkadotDP; Ripple::Entry rippleDP; +Ronin::Entry roninDP; Solana::Entry solanaDP; Stellar::Entry stellarDP; Tezos::Entry tezosDP; @@ -96,87 +101,58 @@ VeChain::Entry vechainDP; Waves::Entry wavesDP; Zcash::Entry zcashDP; Zilliqa::Entry zilliqaDP; +Nervos::Entry NervosDP; +Everscale::Entry EverscaleDP; // end_of_coin_dipatcher_declarations_marker_do_not_modify CoinEntry* coinDispatcher(TWCoinType coinType) { // switch is preferred instead of a data structure, due to initialization issues CoinEntry* entry = nullptr; - switch (coinType) { + const auto blockchain = TW::blockchain(coinType); + switch (blockchain) { // #coin-list# - case TWCoinTypeAeternity: entry = &aeternityDP; break; - case TWCoinTypeAion: entry = &aionDP; break; - case TWCoinTypeAlgorand: entry = &algorandDP; break; - case TWCoinTypeBinance: entry = &binanceDP; break; - case TWCoinTypeBitcoin: entry = &bitcoinDP; break; - case TWCoinTypeBitcoinCash: entry = &bitcoinDP; break; - case TWCoinTypeBitcoinGold: entry = &bitcoinDP; break; - case TWCoinTypeDash: entry = &bitcoinDP; break; - case TWCoinTypeDigiByte: entry = &bitcoinDP; break; - case TWCoinTypeDogecoin: entry = &bitcoinDP; break; - case TWCoinTypeLitecoin: entry = &bitcoinDP; break; - case TWCoinTypeMonacoin: entry = &bitcoinDP; break; - case TWCoinTypeQtum: entry = &bitcoinDP; break; - case TWCoinTypeRavencoin: entry = &bitcoinDP; break; - case TWCoinTypeViacoin: entry = &bitcoinDP; break; - case TWCoinTypeZcoin: entry = &bitcoinDP; break; - case TWCoinTypeCardano: entry = &cardanoDP; break; - case TWCoinTypeCosmos: entry = &cosmosDP; break; - case TWCoinTypeKava: entry = &cosmosDP; break; - case TWCoinTypeTerra: entry = &cosmosDP; break; - case TWCoinTypeBandChain: entry = &cosmosDP; break; - case TWCoinTypeBluzelle: entry = &cosmosDP; break; - case TWCoinTypeElrond: entry = &elrondDP; break; - case TWCoinTypeEOS: entry = &eosDP; break; - case TWCoinTypeCallisto: entry = ðereumDP; break; - case TWCoinTypeEthereum: entry = ðereumDP; break; - case TWCoinTypeEthereumClassic: entry = ðereumDP; break; - case TWCoinTypeGoChain: entry = ðereumDP; break; - case TWCoinTypePOANetwork: entry = ðereumDP; break; - case TWCoinTypeThunderToken: entry = ðereumDP; break; - case TWCoinTypeTomoChain: entry = ðereumDP; break; - case TWCoinTypeSmartChainLegacy: entry = ðereumDP; break; - case TWCoinTypeSmartChain: entry = ðereumDP; break; - case TWCoinTypeDecred: entry = &decredDP; break; - case TWCoinTypeFilecoin: entry = &filecoinDP; break; - case TWCoinTypeFIO: entry = &fioDP; break; - case TWCoinTypeGroestlcoin: entry = &groestlcoinDP; break; - case TWCoinTypeHarmony: entry = &harmonyDP; break; - case TWCoinTypeICON: entry = &iconDP; break; - case TWCoinTypeIoTeX: entry = &iotexDP; break; - case TWCoinTypeKusama: entry = &kusamaDP; break; - case TWCoinTypeNano: entry = &nanoDP; break; - case TWCoinTypeNEAR: entry = &nearDP; break; - case TWCoinTypeNebulas: entry = &nebulasDP; break; - case TWCoinTypeNEO: entry = &neoDP; break; - case TWCoinTypeNimiq: entry = &nimiqDP; break; - case TWCoinTypeNULS: entry = &nulsDP; break; - case TWCoinTypeOasis: entry = &oasisDP; break; - case TWCoinTypeOntology: entry = &ontologyDP; break; - case TWCoinTypePolkadot: entry = &polkadotDP; break; - case TWCoinTypeXRP: entry = &rippleDP; break; - case TWCoinTypeSolana: entry = &solanaDP; break; - case TWCoinTypeStellar: entry = &stellarDP; break; - case TWCoinTypeKin: entry = &stellarDP; break; - case TWCoinTypeTezos: entry = &tezosDP; break; - case TWCoinTypeTheta: entry = &thetaDP; break; - case TWCoinTypeTHORChain: entry = &thorchainDP; break; - case TWCoinTypeTron: entry = &tronDP; break; - case TWCoinTypeVeChain: entry = &vechainDP; break; - case TWCoinTypeWanchain: entry = ðereumDP; break; - case TWCoinTypeWaves: entry = &wavesDP; break; - case TWCoinTypeZcash: entry = &zcashDP; break; - case TWCoinTypeZelcash: entry = &zcashDP; break; - case TWCoinTypeZilliqa: entry = &zilliqaDP; break; - case TWCoinTypePolygon: entry = ðereumDP; break; - case TWCoinTypeOptimism: entry = ðereumDP; break; - case TWCoinTypeArbitrum: entry = ðereumDP; break; - case TWCoinTypeECOChain: entry = ðereumDP; break; - case TWCoinTypeAvalancheCChain: entry = ðereumDP; break; - case TWCoinTypeXDai: entry = ðereumDP; break; - case TWCoinTypeFantom: entry = ðereumDP; break; - case TWCoinTypeCelo: entry = ðereumDP; break; - case TWCoinTypeRonin: entry = ðereumDP; break; - case TWCoinTypeCryptoOrg: entry = &cosmosDP; break; + case TWBlockchainBitcoin: entry = &bitcoinDP; break; + case TWBlockchainEthereum: entry = ðereumDP; break; + case TWBlockchainVechain: entry = &vechainDP; break; + case TWBlockchainTron: entry = &tronDP; break; + case TWBlockchainIcon: entry = &iconDP; break; + case TWBlockchainBinance: entry = &binanceDP; break; + case TWBlockchainRipple: entry = &rippleDP; break; + case TWBlockchainTezos: entry = &tezosDP; break; + case TWBlockchainNimiq: entry = &nimiqDP; break; + case TWBlockchainStellar: entry = &stellarDP; break; + case TWBlockchainAion: entry = &aionDP; break; + case TWBlockchainCosmos: entry = &cosmosDP; break; + case TWBlockchainTheta: entry = &thetaDP; break; + case TWBlockchainOntology: entry = &ontologyDP; break; + case TWBlockchainZilliqa: entry = &zilliqaDP; break; + case TWBlockchainIoTeX: entry = &iotexDP; break; + case TWBlockchainEOS: entry = &eosDP; break; + case TWBlockchainNano: entry = &nanoDP; break; + case TWBlockchainNULS: entry = &nulsDP; break; + case TWBlockchainWaves: entry = &wavesDP; break; + case TWBlockchainAeternity: entry = &aeternityDP; break; + case TWBlockchainNebulas: entry = &nebulasDP; break; + case TWBlockchainFIO: entry = &fioDP; break; + case TWBlockchainSolana: entry = &solanaDP; break; + case TWBlockchainHarmony: entry = &harmonyDP; break; + case TWBlockchainNEAR: entry = &nearDP; break; + case TWBlockchainAlgorand: entry = &algorandDP; break; + case TWBlockchainPolkadot: entry = &polkadotDP; break; + case TWBlockchainCardano: entry = &cardanoDP; break; + case TWBlockchainNEO: entry = &neoDP; break; + case TWBlockchainFilecoin: entry = &filecoinDP; break; + case TWBlockchainElrondNetwork: entry = &elrondDP; break; + case TWBlockchainOasisNetwork: entry = &oasisDP; break; + case TWBlockchainDecred: entry = &decredDP; break; + case TWBlockchainGroestlcoin: entry = &groestlcoinDP; break; + case TWBlockchainZcash: entry = &zcashDP; break; + case TWBlockchainThorchain: entry = &thorchainDP; break; + case TWBlockchainRonin: entry = &roninDP; break; + case TWBlockchainKusama: entry = &kusamaDP; break; + case TWBlockchainNervos: entry = &NervosDP; break; + case TWBlockchainEverscale: entry = &EverscaleDP; break; + case TWBlockchainAptos: entry = &AptosDP; break; // end_of_coin_dipatcher_switch_marker_do_not_modify default: entry = nullptr; break; @@ -185,10 +161,21 @@ CoinEntry* coinDispatcher(TWCoinType coinType) { return entry; } -bool TW::validateAddress(TWCoinType coin, const std::string& string) { +const Derivation CoinInfo::derivationByName(TWDerivation nameIn) const { + if (nameIn == TWDerivationDefault && derivation.size() > 0) { + return derivation[0]; + } + for (auto deriv : derivation) { + if (deriv.name == nameIn) { + return deriv; + } + } + return Derivation(); +} + +bool TW::validateAddress(TWCoinType coin, const std::string& string, const char* hrp) { auto p2pkh = TW::p2pkhPrefix(coin); auto p2sh = TW::p2shPrefix(coin); - const auto* hrp = stringForHRP(TW::hrp(coin)); // dispatch auto* dispatcher = coinDispatcher(coin); @@ -196,8 +183,14 @@ bool TW::validateAddress(TWCoinType coin, const std::string& string) { return dispatcher->validateAddress(coin, string, p2pkh, p2sh, hrp); } -std::string TW::normalizeAddress(TWCoinType coin, const std::string& address) { - if (!TW::validateAddress(coin, address)) { +bool TW::validateAddress(TWCoinType coin, const std::string& string) { + const auto* hrp = stringForHRP(TW::hrp(coin)); + return TW::validateAddress(coin, string, hrp); +} + +std::string TW::normalizeAddress(TWCoinType coin, const std::string& address, const std::string& hrp) { + const char* rawHrp = hrp.empty() ? stringForHRP(TW::hrp(coin)) : hrp.c_str(); + if (!TW::validateAddress(coin, address, rawHrp)) { // invalid address, not normalizing return ""; } @@ -209,18 +202,29 @@ std::string TW::normalizeAddress(TWCoinType coin, const std::string& address) { } std::string TW::deriveAddress(TWCoinType coin, const PrivateKey& privateKey) { + return TW::deriveAddress(coin, privateKey, TWDerivationDefault); +} + +std::string TW::deriveAddress(TWCoinType coin, const PrivateKey& privateKey, TWDerivation derivation) { auto keyType = TW::publicKeyType(coin); - return TW::deriveAddress(coin, privateKey.getPublicKey(keyType)); + return TW::deriveAddress(coin, privateKey.getPublicKey(keyType), derivation); } -std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey) { +std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const std::string& hrp) { auto p2pkh = TW::p2pkhPrefix(coin); - const auto* hrp = stringForHRP(TW::hrp(coin)); - + const char* hrpRaw = [&hrp, coin]() { + return hrp.empty() ? stringForHRP(TW::hrp(coin)) : hrp.c_str(); + }(); // dispatch auto* dispatcher = coinDispatcher(coin); assert(dispatcher != nullptr); - return dispatcher->deriveAddress(coin, publicKey, p2pkh, hrp); + return dispatcher->deriveAddress(coin, derivation, publicKey, p2pkh, hrpRaw); +} + +Data TW::addressToData(TWCoinType coin, const std::string& address) { + const auto* dispatcher = coinDispatcher(coin); + assert(dispatcher != nullptr); + return dispatcher->addressToData(coin, address); } void TW::anyCoinSign(TWCoinType coinType, const Data& dataIn, Data& dataOut) { @@ -247,6 +251,24 @@ void TW::anyCoinPlan(TWCoinType coinType, const Data& dataIn, Data& dataOut) { dispatcher->plan(coinType, dataIn, dataOut); } +Data TW::anyCoinPreImageHashes(TWCoinType coinType, const Data& txInputData) { + auto* dispatcher = coinDispatcher(coinType); + assert(dispatcher != nullptr); + return dispatcher->preImageHashes(coinType, txInputData); +} + +void TW::anyCoinCompileWithSignatures(TWCoinType coinType, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& txOutputOut) { + auto* dispatcher = coinDispatcher(coinType); + assert(dispatcher != nullptr); + dispatcher->compile(coinType, txInputData, signatures, publicKeys, txOutputOut); +} + +Data TW::anyCoinBuildTransactionInput(TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId) { + auto* dispatcher = coinDispatcher(coinType); + assert(dispatcher != nullptr); + return dispatcher->buildTransactionInput(coinType, from, to, amount, asset, memo, chainId); +} + // Coin info accessors extern const CoinInfo getCoinInfo(TWCoinType coin); // in generated CoinInfoData.cpp file @@ -264,15 +286,31 @@ TWCurve TW::curve(TWCoinType coin) { } TWHDVersion TW::xpubVersion(TWCoinType coin) { - return getCoinInfo(coin).xpubVersion; + return getCoinInfo(coin).defaultDerivation().xpubVersion; } TWHDVersion TW::xprvVersion(TWCoinType coin) { - return getCoinInfo(coin).xprvVersion; + return getCoinInfo(coin).defaultDerivation().xprvVersion; +} + +TWHDVersion TW::xpubVersionDerivation(TWCoinType coin, TWDerivation derivation) { + return getCoinInfo(coin).derivationByName(derivation).xpubVersion; +} + +TWHDVersion TW::xprvVersionDerivation(TWCoinType coin, TWDerivation derivation) { + return getCoinInfo(coin).derivationByName(derivation).xprvVersion; } DerivationPath TW::derivationPath(TWCoinType coin) { - return DerivationPath(getCoinInfo(coin).derivationPath); + return DerivationPath(getCoinInfo(coin).defaultDerivation().path); +} + +DerivationPath TW::derivationPath(TWCoinType coin, TWDerivation derivation) { + return DerivationPath(getCoinInfo(coin).derivationByName(derivation).path); +} + +const char* TW::derivationName(TWCoinType coin, TWDerivation derivation) { + return getCoinInfo(coin).derivationByName(derivation).nameString; } enum TWPublicKeyType TW::publicKeyType(TWCoinType coin) { @@ -295,6 +333,10 @@ enum TWHRP TW::hrp(TWCoinType coin) { return getCoinInfo(coin).hrp; } +const char* TW::chainId(TWCoinType coin) { + return getCoinInfo(coin).chainId; +} + Hash::Hasher TW::publicKeyHasher(TWCoinType coin) { return getCoinInfo(coin).publicKeyHasher; } @@ -303,6 +345,10 @@ Hash::Hasher TW::base58Hasher(TWCoinType coin) { return getCoinInfo(coin).base58Hasher; } +Hash::Hasher TW::addressHasher(TWCoinType coin) { + return getCoinInfo(coin).addressHasher; +} + uint32_t TW::slip44Id(TWCoinType coin) { return getCoinInfo(coin).slip44; } @@ -334,9 +380,3 @@ TWString* _Nonnull TWCoinTypeConfigurationGetID(enum TWCoinType coin) { TWString* _Nonnull TWCoinTypeConfigurationGetName(enum TWCoinType coin) { return TWStringCreateWithUTF8Bytes(getCoinInfo(coin).name); } - -const std::vector TW::getSimilarCoinTypes(TWCoinType coinType) { - const auto* dispatcher = coinDispatcher(coinType); - assert(dispatcher != nullptr); - return dispatcher->coinTypes(); -} diff --git a/src/Coin.h b/src/Coin.h index d1ea0a1213c..aba8e7adef5 100644 --- a/src/Coin.h +++ b/src/Coin.h @@ -11,12 +11,15 @@ #include "DerivationPath.h" #include "PrivateKey.h" #include "PublicKey.h" +#include "uint256.h" +#include "CoinEntry.h" #include #include #include #include #include +#include #include #include @@ -29,8 +32,11 @@ std::vector getCoinTypes(); /// Validates an address for a particular coin. bool validateAddress(TWCoinType coin, const std::string& address); +/// Validates an address for a particular coin. +bool validateAddress(TWCoinType coin, const std::string& address, const char* hrp); + /// Validates and normalizes an address for a particular coin. -std::string normalizeAddress(TWCoinType coin, const std::string& address); +std::string normalizeAddress(TWCoinType coin, const std::string& address, const std::string& hrp = ""); /// Returns the blockchain for a coin type. TWBlockchain blockchain(TWCoinType coin); @@ -41,30 +47,51 @@ TWPurpose purpose(TWCoinType coin); /// Returns the curve that should be used for a coin type. TWCurve curve(TWCoinType coin); -/// Returns the xpub HD version that should be used for a coin type. +/// Returns the default xpub HD version that should be used for a coin type. TWHDVersion xpubVersion(TWCoinType coin); -/// Returns the xprv HD version that should be used for a coin type. +/// Returns the default xprv HD version that should be used for a coin type. TWHDVersion xprvVersion(TWCoinType coin); +/// Returns the xpub HD version for a TWDerivation. +TWHDVersion xpubVersionDerivation(TWCoinType coin, TWDerivation derivation); + +/// Returns the xprv HD version for a TWDerivation. +TWHDVersion xprvVersionDerivation(TWCoinType coin, TWDerivation derivation); + /// Returns the default derivation path for a particular coin. DerivationPath derivationPath(TWCoinType coin); +/// Returns an alternative derivation path for a particular coin, TWDerivationDefault for default. +DerivationPath derivationPath(TWCoinType coin, TWDerivation derivation); + +/// Returns the string name of a derivation for a particular coin. +const char* derivationName(TWCoinType coin, TWDerivation derivation); + /// Returns the public key type for a particular coin. enum TWPublicKeyType publicKeyType(TWCoinType coin); /// Derives the address for a particular coin from the private key. std::string deriveAddress(TWCoinType coin, const PrivateKey& privateKey); -/// Derives the address for a particular coin from the public key. -std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey); +/// Derives the address for a particular coin from the private key, with given derivation. +std::string deriveAddress(TWCoinType coin, const PrivateKey& privateKey, TWDerivation derivation); + +/// Derives the address for a particular coin from the public key, with given derivation. +std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation = TWDerivationDefault, const std::string& hrp = ""); + +/// Returns the binary representation of a string address +Data addressToData(TWCoinType coin, const std::string& address); -/// Hasher for deriving the public key hash. +/// Hasher for deriving the extended public key Hash::Hasher publicKeyHasher(TWCoinType coin); -/// Hasher to use for base 58 checksums. +/// Hasher to use for base 58 checksums in keys (extended private, public) Hash::Hasher base58Hasher(TWCoinType coin); +/// Hasher used inside address generation (hash of public key) +Hash::Hasher addressHasher(TWCoinType coin); + /// Returns static prefix for a coin type. byte staticPrefix(TWCoinType coin); @@ -77,6 +104,9 @@ byte p2shPrefix(TWCoinType coin); /// Returns human readable part for a coin type. enum TWHRP hrp(TWCoinType coin); +/// Returns chain ID. +const char* chainId(TWCoinType coin); + // Note: use output parameter to avoid unneeded copies void anyCoinSign(TWCoinType coinType, const Data& dataIn, Data& dataOut); @@ -88,8 +118,20 @@ bool supportsJSONSigning(TWCoinType coinType); void anyCoinPlan(TWCoinType coinType, const Data& dataIn, Data& dataOut); -// Return coins handled by the same dispatcher as the given coin (mostly for testing) -const std::vector getSimilarCoinTypes(TWCoinType coinType); +Data anyCoinPreImageHashes(TWCoinType coinType, const Data& txInputData); + +void anyCoinCompileWithSignatures(TWCoinType coinType, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& txOutputOut); + +Data anyCoinBuildTransactionInput(TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId); + +// Describes a derivation: path + optional format + optional name +struct Derivation { + TWDerivation name = TWDerivationDefault; + const char* path = ""; + const char* nameString = ""; + TWHDVersion xpubVersion = TWHDVersionNone; + TWHDVersion xprvVersion = TWHDVersionNone; +}; // Contains only simple types. struct CoinInfo { @@ -98,21 +140,27 @@ struct CoinInfo { TWBlockchain blockchain; TWPurpose purpose; TWCurve curve; - TWHDVersion xpubVersion; - TWHDVersion xprvVersion; - const char* derivationPath; + std::vector derivation; TWPublicKeyType publicKeyType; byte staticPrefix; byte p2pkhPrefix; byte p2shPrefix; TWHRP hrp; + const char* chainId; Hash::Hasher publicKeyHasher; Hash::Hasher base58Hasher; + Hash::Hasher addressHasher; const char* symbol; int decimals; const char* explorerTransactionUrl; const char* explorerAccountUrl; uint32_t slip44; + + // returns default derivation + const Derivation defaultDerivation() const { + return (derivation.size() > 0) ? derivation[0] : Derivation(); + } + const Derivation derivationByName(TWDerivation name) const; }; } // namespace TW diff --git a/src/CoinEntry.h b/src/CoinEntry.h index c42223d9d2a..b0ab4bd72d3 100644 --- a/src/CoinEntry.h +++ b/src/CoinEntry.h @@ -7,34 +7,58 @@ #pragma once #include +#include #include "Data.h" #include "PublicKey.h" #include "PrivateKey.h" +#include "proto/Common.pb.h" +#include "uint256.h" #include #include +#include namespace TW { +typedef std::vector> HashPubkeyList; + /// Interface for coin-specific entry, used to dispatch calls to coins /// Implement this for all coins. class CoinEntry { public: - // Report the coin types this implementation is responsible of - virtual const std::vector coinTypes() const = 0; + virtual ~CoinEntry() noexcept = default; virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const = 0; // normalizeAddress is optional, it may leave this default, no-change implementation - virtual std::string normalizeAddress(TWCoinType coin, const std::string& address) const { return address; } + virtual std::string normalizeAddress([[maybe_unused]] TWCoinType coin, const std::string& address) const { return address; } + // Address derivation, default derivation virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const = 0; + // Address derivation, by default invoking default + virtual std::string deriveAddress(TWCoinType coin, [[maybe_unused]] TWDerivation derivation, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const { + return deriveAddress(coin, publicKey, p2pkh, hrp); + } + // Return the binary representation of a string address, used by AnyAddress + // It is optional, if not defined, 'AnyAddress' interface will not support this coin. + virtual Data addressToData([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const std::string& address) const { return {}; } // Signing virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const = 0; virtual bool supportsJSONSigning() const { return false; } // It is optional, Signing JSON input with private key - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const { return ""; } + virtual std::string signJSON([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const std::string& json, [[maybe_unused]] const Data& key) const { return ""; } // Planning, for UTXO chains, in preparation for signing // It is optional, only UTXO chains need it, default impl. leaves empty result. - virtual void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const { return; } + virtual void plan([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const Data& dataIn, [[maybe_unused]] Data& dataOut) const { } + + // Optional method for obtaining hash(es) for signing, needed for external signing. + // It will return a proto object named `PreSigningOutput` which will include hash. + // We provide a default `PreSigningOutput` in TransactionCompiler.proto. + // For some special coins, such as bitcoin, we will create a custom `PreSigningOutput` object in its proto file. + virtual Data preImageHashes([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const Data& txInputData) const { return {}; } + // Optional method for compiling a transaction with externally-supplied signatures & pubkeys. + virtual void compile([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const Data& txInputData, [[maybe_unused]] const std::vector& signatures, [[maybe_unused]] const std::vector& publicKeys, [[maybe_unused]] Data& dataOut) const {} + // Optional helper to prepare a SigningInput from simple parameters. + // Not suitable for UTXO chains. Some parameters, like chain-specific fee/gas paraemters, may need to be set in the SigningInput. + virtual Data buildTransactionInput([[maybe_unused]] TWCoinType coinType, [[maybe_unused]] const std::string& from, [[maybe_unused]] const std::string& to, [[maybe_unused]] const uint256_t& amount, [[maybe_unused]] const std::string& asset, [[maybe_unused]] const std::string& memo, [[maybe_unused]] const std::string& chainId) const { return Data(); } }; // In each coin's Entry.cpp the specific types of the coin are used, this template enforces the Signer implement: @@ -57,4 +81,26 @@ void planTemplate(const Data& dataIn, Data& dataOut) { dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end()); } +// This template will be used for preImageHashes and compile in each coin's Entry.cpp. +// It is a helper function to simplify exception handle. +template +Data txCompilerTemplate(const Data& dataIn, Func&& fnHandler) { + auto input = Input(); + auto output = Output(); + if (!input.ParseFromArray(dataIn.data(), (int)dataIn.size())) { + output.set_error(Common::Proto::Error_input_parse); + output.set_error_message("failed to parse input data"); + return TW::data(output.SerializeAsString());; + } + + try { + // each coin function handler + fnHandler(input, output); + } catch (const std::exception& e) { + output.set_error(Common::Proto::Error_internal); + output.set_error_message(e.what()); + } + return TW::data(output.SerializeAsString()); +} + } // namespace TW diff --git a/src/Cosmos/Address.h b/src/Cosmos/Address.h index c528317f3a5..cb629cfa607 100644 --- a/src/Cosmos/Address.h +++ b/src/Cosmos/Address.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,7 +7,7 @@ #pragma once #include "../Bech32Address.h" -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include "../Coin.h" #include @@ -17,7 +17,7 @@ namespace TW::Cosmos { -/// A Bech32 Cosmos address. Hrp has to be specified (e.g. "cosmos", "terra"...), hash is HASHER_SHA2_RIPEMD. +/// A Bech32 Cosmos address. Hrp has to be specified (e.g. "cosmos", "terra"...), hash is coin-specific (from config, usually sha256ripemd). class Address: public Bech32Address { public: Address() : Bech32Address("") {} @@ -29,10 +29,10 @@ class Address: public Bech32Address { Address(const std::string& hrp, const Data& keyHash) : Bech32Address(hrp, keyHash) {} /// Initializes an address with a public key, with prefix of the given coin. - Address(TWCoinType coin, const PublicKey& publicKey) : Bech32Address(stringForHRP(TW::hrp(coin)), HASHER_SHA2_RIPEMD, publicKey) {} + Address(TWCoinType coin, const PublicKey& publicKey) : Bech32Address(stringForHRP(TW::hrp(coin)), TW::addressHasher(coin), publicKey) {} /// Initializes an address with a public key, with given prefix. - Address(const std::string& hrp, const PublicKey& publicKey) : Bech32Address(hrp, HASHER_SHA2_RIPEMD, publicKey) {} + Address(const std::string& hrp, const PublicKey& publicKey, TWCoinType coin = TWCoinTypeCosmos) : Bech32Address(hrp, TW::addressHasher(coin), publicKey) {} /// Determines whether a string makes a valid Bech32 address, and the HRP matches to the coin. static bool isValid(TWCoinType coin, const std::string& addr) { @@ -40,6 +40,11 @@ class Address: public Bech32Address { return Bech32Address::isValid(addr, hrp); } + /// Determines whether a string makes a valid Bech32 address with the given hrp. + static bool isValid(const std::string& addr, const std::string& hrp) { + return Bech32Address::isValid(addr, hrp); + } + /// Creates an address object from the given string, if valid. Returns success. static bool decode(const std::string& addr, Address& obj_out) { return Bech32Address::decode(addr, obj_out, ""); diff --git a/src/Cosmos/Entry.cpp b/src/Cosmos/Entry.cpp index 8b463c102e7..6cf9026fddd 100644 --- a/src/Cosmos/Entry.cpp +++ b/src/Cosmos/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,23 +9,44 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Cosmos; +using namespace TW; using namespace std; +namespace TW::Cosmos { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char* hrp) const { - return Address::isValid(coin, address); + if (hrpForString(hrp) != TWHRPUnknown) { + return Address::isValid(coin, address); + } + return Address::isValid(address, hrp); } string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char* hrp) const { + if (!std::string(hrp).empty()) { + return Address(hrp, publicKey, coin).string(); + } return Address(coin, publicKey).string(); } +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + Address addr; + if (!Address::decode(address, addr)) { + return Data(); + } + return addr.getKeyHash(); +} + void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); + auto input = Proto::SigningInput(); + input.ParseFromArray(dataIn.data(), (int)dataIn.size()); + auto serializedOut = Signer::sign(input, coin).SerializeAsString(); + dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end()); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { - return Signer::signJSON(json, key); +string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { + return Signer::signJSON(json, key, coin); } + +} // namespace TW::Cosmos diff --git a/src/Cosmos/Entry.h b/src/Cosmos/Entry.h index b98b5b005ef..e5be0796622 100644 --- a/src/Cosmos/Entry.h +++ b/src/Cosmos/Entry.h @@ -12,23 +12,14 @@ namespace TW::Cosmos { /// Entry point for implementation of Cosmos coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry : public CoinEntry { public: - virtual const std::vector coinTypes() const { - return { - TWCoinTypeCosmos, - TWCoinTypeKava, - TWCoinTypeTerra, - TWCoinTypeBandChain, - TWCoinTypeBluzelle, - TWCoinTypeCryptoOrg, - }; - } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const final; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const final; + Data addressToData(TWCoinType coin, const std::string& address) const final; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; + bool supportsJSONSigning() const final { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const override; }; } // namespace TW::Cosmos diff --git a/src/Cosmos/JsonSerialization.cpp b/src/Cosmos/JsonSerialization.cpp new file mode 100644 index 00000000000..bb7e28c98b8 --- /dev/null +++ b/src/Cosmos/JsonSerialization.cpp @@ -0,0 +1,229 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "JsonSerialization.h" +#include "ProtobufSerialization.h" + +#include "../Cosmos/Address.h" +#include "../proto/Cosmos.pb.h" +#include "Base64.h" +#include "PrivateKey.h" + +using namespace TW; + +namespace TW::Cosmos::Json { + +using json = nlohmann::json; +using string = std::string; + +const string TYPE_PREFIX_MSG_SEND = "cosmos-sdk/MsgSend"; +const string TYPE_PREFIX_MSG_DELEGATE = "cosmos-sdk/MsgDelegate"; +const string TYPE_PREFIX_MSG_UNDELEGATE = "cosmos-sdk/MsgUndelegate"; +const string TYPE_PREFIX_MSG_REDELEGATE = "cosmos-sdk/MsgBeginRedelegate"; +const string TYPE_PREFIX_MSG_WITHDRAW_REWARD = "cosmos-sdk/MsgWithdrawDelegationReward"; +const string TYPE_PREFIX_PUBLIC_KEY = "tendermint/PubKeySecp256k1"; +const string TYPE_EVMOS_PREFIX_PUBLIC_KEY = "ethermint/PubKeyEthSecp256k1"; +const string TYPE_PREFIX_WASM_MSG_EXECUTE = "wasm/MsgExecuteContract"; + +static inline std::string coinTypeToPrefixPublicKey(TWCoinType coin) noexcept { + if (coin == TWCoinTypeNativeEvmos) { + return TYPE_EVMOS_PREFIX_PUBLIC_KEY; + } + return TYPE_PREFIX_PUBLIC_KEY; +} + +static string broadcastMode(Proto::BroadcastMode mode) { + switch (mode) { + case Proto::BroadcastMode::BLOCK: + return "block"; + case Proto::BroadcastMode::ASYNC: + return "async"; + default: return "sync"; + } +} + +static json broadcastJSON(json& j, Proto::BroadcastMode mode) { + return { + {"tx", j}, + {"mode", broadcastMode(mode)} + }; +} + +static json amountJSON(const Proto::Amount& amount) { + return { + {"amount", amount.amount()}, + {"denom", amount.denom()} + }; +} + +static json amountsJSON(const ::google::protobuf::RepeatedPtrField& amounts) { + json j = json::array(); + for (auto& amount : amounts) { + j.push_back(amountJSON(amount)); + } + return j; +} + +static json feeJSON(const Proto::Fee& fee) { + json js = json::array(); + + for (auto& amount : fee.amounts()) { + js.push_back(amountJSON(amount)); + } + + return { + {"amount", js}, + {"gas", std::to_string(fee.gas())} + }; +} + +static json messageSend(const Proto::Message_Send& message) { + auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_SEND : message.type_prefix(); + + return { + {"type", typePrefix}, + {"value", { + {"amount", amountsJSON(message.amounts())}, + {"from_address", message.from_address()}, + {"to_address", message.to_address()} + }} + }; +} + +static json messageDelegate(const Proto::Message_Delegate& message) { + auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_DELEGATE : message.type_prefix(); + + return { + {"type", typePrefix}, + {"value", { + {"amount", amountJSON(message.amount())}, + {"delegator_address", message.delegator_address()}, + {"validator_address", message.validator_address()} + }} + }; +} + +static json messageUndelegate(const Proto::Message_Undelegate& message) { + auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_UNDELEGATE : message.type_prefix(); + + return { + {"type", typePrefix}, + {"value", { + {"amount", amountJSON(message.amount())}, + {"delegator_address", message.delegator_address()}, + {"validator_address", message.validator_address()} + }} + }; +} + +static json messageRedelegate(const Proto::Message_BeginRedelegate& message) { + auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_REDELEGATE : message.type_prefix(); + + return { + {"type", typePrefix}, + {"value", { + {"amount", amountJSON(message.amount())}, + {"delegator_address", message.delegator_address()}, + {"validator_src_address", message.validator_src_address()}, + {"validator_dst_address", message.validator_dst_address()}, + }} + }; +} + +static json messageWithdrawReward(const Proto::Message_WithdrawDelegationReward& message) { + auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_WITHDRAW_REWARD : message.type_prefix(); + + return { + {"type", typePrefix}, + {"value", { + {"delegator_address", message.delegator_address()}, + {"validator_address", message.validator_address()} + }} + }; +} + +json messageWasmTerraTransfer(const Proto::Message_WasmTerraExecuteContractTransfer& msg) { + return { + {"type", TYPE_PREFIX_WASM_MSG_EXECUTE}, + {"value", + { + {"sender", msg.sender_address()}, + {"contract", msg.contract_address()}, + {"execute_msg", Protobuf::wasmTerraExecuteTransferPayload(msg)}, + {"coins", json::array()} // used in case you are sending native tokens along with this message + } + } + }; +} + +static json messageRawJSON(const Proto::Message_RawJSON& message) { + return { + {"type", message.type()}, + {"value", json::parse(message.value())}, + }; +} + +static json messagesJSON(const Proto::SigningInput& input) { + json j = json::array(); + for (auto& msg : input.messages()) { + if (msg.has_send_coins_message()) { + j.push_back(messageSend(msg.send_coins_message())); + } else if (msg.has_stake_message()) { + j.push_back(messageDelegate(msg.stake_message())); + } else if (msg.has_unstake_message()) { + j.push_back(messageUndelegate(msg.unstake_message())); + } else if (msg.has_withdraw_stake_reward_message()) { + j.push_back(messageWithdrawReward(msg.withdraw_stake_reward_message())); + } else if (msg.has_restake_message()) { + j.push_back(messageRedelegate(msg.restake_message())); + } else if (msg.has_raw_json_message()) { + j.push_back(messageRawJSON(msg.raw_json_message())); + } else if ((msg.has_wasm_terra_execute_contract_transfer_message())) { + j.push_back(messageWasmTerraTransfer(msg.wasm_terra_execute_contract_transfer_message())); + } else if (msg.has_transfer_tokens_message() || msg.has_wasm_terra_execute_contract_generic()) { + assert(false); // not supported, use protobuf serialization + return json::array(); + } + } + return j; +} + +json signatureJSON(const Data& signature, const Data& pubkey, TWCoinType coin) { + return { + {"pub_key", { + {"type", coinTypeToPrefixPublicKey(coin)}, + {"value", Base64::encode(pubkey)} + }}, + {"signature", Base64::encode(signature)} + }; +} + +json signaturePreimageJSON(const Proto::SigningInput& input) { + return { + {"account_number", std::to_string(input.account_number())}, + {"chain_id", input.chain_id()}, + {"fee", feeJSON(input.fee())}, + {"memo", input.memo()}, + {"msgs", messagesJSON(input)}, + {"sequence", std::to_string(input.sequence())} + }; +} + +json transactionJSON(const Proto::SigningInput& input, const Data& signature, TWCoinType coin) { + auto privateKey = PrivateKey(input.private_key()); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + json tx = { + {"fee", feeJSON(input.fee())}, + {"memo", input.memo()}, + {"msg", messagesJSON(input)}, + {"signatures", json::array({ + signatureJSON(signature, Data(publicKey.bytes), coin) + })} + }; + return broadcastJSON(tx, input.mode()); +} + +} // namespace TW::Cosmos diff --git a/src/Cosmos/JsonSerialization.h b/src/Cosmos/JsonSerialization.h new file mode 100644 index 00000000000..72212d972f9 --- /dev/null +++ b/src/Cosmos/JsonSerialization.h @@ -0,0 +1,31 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" +#include "../proto/Cosmos.pb.h" +#include +#include + +extern const std::string TYPE_PREFIX_MSG_SEND; +extern const std::string TYPE_PREFIX_MSG_TRANSFER; +extern const std::string TYPE_PREFIX_MSG_DELEGATE; +extern const std::string TYPE_PREFIX_MSG_UNDELEGATE; +extern const std::string TYPE_PREFIX_MSG_REDELEGATE; +extern const std::string TYPE_PREFIX_MSG_WITHDRAW_REWARD; +extern const std::string TYPE_PREFIX_PUBLIC_KEY; + +namespace TW::Cosmos::Json { + +using string = std::string; +using json = nlohmann::json; + +json signaturePreimageJSON(const Proto::SigningInput& input); +json transactionJSON(const Proto::SigningInput& input, const Data& signature, TWCoinType coin); +json signatureJSON(const Data& signature, const Data& pubkey, TWCoinType coin); + +} // namespace TW::Cosmos::json diff --git a/src/Cosmos/Protobuf/.clang-tidy b/src/Cosmos/Protobuf/.clang-tidy new file mode 100644 index 00000000000..2c22f7387dd --- /dev/null +++ b/src/Cosmos/Protobuf/.clang-tidy @@ -0,0 +1,6 @@ +--- +InheritParentConfig: false +Checks: '-*,misc-definitions-in-headers' +CheckOptions: + - { key: HeaderFileExtensions, value: "x" } +... diff --git a/src/Cosmos/Protobuf/.gitignore b/src/Cosmos/Protobuf/.gitignore new file mode 100644 index 00000000000..c96d61208c0 --- /dev/null +++ b/src/Cosmos/Protobuf/.gitignore @@ -0,0 +1,3 @@ +*.cc +*.h + diff --git a/src/Cosmos/Protobuf/authz_tx.proto b/src/Cosmos/Protobuf/authz_tx.proto new file mode 100644 index 00000000000..b92c03bd3f8 --- /dev/null +++ b/src/Cosmos/Protobuf/authz_tx.proto @@ -0,0 +1,32 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.authz.v1beta1; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; + +// Grant gives permissions to execute +// the provide method with expiration time. +message Grant { + google.protobuf.Any authorization = 1; + // time when the grant will expire and will be pruned. If null, then the grant + // doesn't have a time expiration (other conditions in `authorization` + // may apply to invalidate the grant) + google.protobuf.Timestamp expiration = 2; +} + +// MsgGrant is a request type for Grant method. It declares authorization to the grantee +// on behalf of the granter with the provided expiration time. +message MsgGrant { + string granter = 1; + string grantee = 2; + Grant grant = 3; +} + +// MsgRevoke revokes any authorization with the provided sdk.Msg type on the +// granter's account with that has been granted to the grantee. +message MsgRevoke { + string granter = 1; + string grantee = 2; + string msg_type_url = 3; +} diff --git a/src/Cosmos/Protobuf/bank_tx.proto b/src/Cosmos/Protobuf/bank_tx.proto new file mode 100644 index 00000000000..08d4efa19af --- /dev/null +++ b/src/Cosmos/Protobuf/bank_tx.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; +package cosmos.bank.v1beta1; + +// Src: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/bank/v1beta1/tx.proto + +import "coin.proto"; + +// MsgSend represents a message to send coins from one account to another. +message MsgSend { + string from_address = 1; + string to_address = 2; + repeated base.v1beta1.Coin amount = 3; +} diff --git a/src/Cosmos/Protobuf/coin.proto b/src/Cosmos/Protobuf/coin.proto new file mode 100644 index 00000000000..93db5482105 --- /dev/null +++ b/src/Cosmos/Protobuf/coin.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; +package cosmos.base.v1beta1; + +// Src: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/base/v1beta1/coin.proto + +// Coin defines a token with a denomination and an amount. +// Note: The amount field is an Int as string. +message Coin { + string denom = 1; + string amount = 2; +} + +// Omitted: +// DecCoin +// IntProto +// DecProto diff --git a/src/Cosmos/Protobuf/cosmwasm_wasm_v1_tx.proto b/src/Cosmos/Protobuf/cosmwasm_wasm_v1_tx.proto new file mode 100644 index 00000000000..68f0995a016 --- /dev/null +++ b/src/Cosmos/Protobuf/cosmwasm_wasm_v1_tx.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; +package cosmwasm.wasm.v1; + +// Src: https://github.com/CosmWasm/wasmd/blob/main/proto/cosmwasm/wasm/v1/tx.proto + +import "coin.proto"; + +// MsgExecuteContract submits the given message data to a smart contract +message MsgExecuteContract { + // Sender is the that actor that signed the messages + string sender = 1; + // Contract is the address of the smart contract + string contract = 2; + // Msg json encoded message to be passed to the contract + bytes msg = 3; + // Funds coins that are transferred to the contract on execution + // Gap in field numbering is intentional! + repeated cosmos.base.v1beta1.Coin funds = 5; +} diff --git a/src/Cosmos/Protobuf/crypto_multisig.proto b/src/Cosmos/Protobuf/crypto_multisig.proto new file mode 100644 index 00000000000..d87e98dfc76 --- /dev/null +++ b/src/Cosmos/Protobuf/crypto_multisig.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package cosmos.multisig.v1beta1; + +// Src: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/crypto/multisig/v1beta1/multisig.proto + +// MultiSignature is omitted + +// CompactBitArray is an implementation of a space efficient bit array. +// This is used to ensure that the encoded data takes up a minimal amount of +// space after proto encoding. +// This is not thread safe, and is not intended for concurrent usage. +message CompactBitArray { + uint32 extra_bits_stored = 1; + bytes elems = 2; +} diff --git a/src/Cosmos/Protobuf/crypto_secp256k1_keys.proto b/src/Cosmos/Protobuf/crypto_secp256k1_keys.proto new file mode 100644 index 00000000000..4e9035ddec0 --- /dev/null +++ b/src/Cosmos/Protobuf/crypto_secp256k1_keys.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package cosmos.crypto.secp256k1; + +// Src: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/crypto/secp256k1/keys.proto + +// PubKey defines a secp256k1 public key +// Key is the compressed form of the pubkey. The first byte depends is a 0x02 byte +// if the y-coordinate is the lexicographically largest of the two associated with +// the x-coordinate. Otherwise the first byte is a 0x03. +// This prefix is followed with the x-coordinate. +message PubKey { + bytes key = 1; +} + +// PrivKey is omitted diff --git a/src/Cosmos/Protobuf/distribution_tx.proto b/src/Cosmos/Protobuf/distribution_tx.proto new file mode 100644 index 00000000000..1182c562140 --- /dev/null +++ b/src/Cosmos/Protobuf/distribution_tx.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; +package cosmos.distribution.v1beta1; + +// Src: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/distribution/v1beta1/tx.proto + +// MsgWithdrawDelegatorReward represents delegation withdrawal to a delegator +// from a single validator. +message MsgWithdrawDelegatorReward { + string delegator_address = 1; + string validator_address = 2; +} diff --git a/src/Cosmos/Protobuf/ethermint_keys.proto b/src/Cosmos/Protobuf/ethermint_keys.proto new file mode 100644 index 00000000000..07675c19f45 --- /dev/null +++ b/src/Cosmos/Protobuf/ethermint_keys.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +package ethermint.crypto.v1.ethsecp256k1; + +// PubKey defines a type alias for an ecdsa.PublicKey that implements +// Tendermint's PubKey interface. It represents the 33-byte compressed public +// key format. +message PubKey { + bytes key = 1; +} diff --git a/src/Cosmos/Protobuf/gov_tx.proto b/src/Cosmos/Protobuf/gov_tx.proto new file mode 100644 index 00000000000..4f018a37e93 --- /dev/null +++ b/src/Cosmos/Protobuf/gov_tx.proto @@ -0,0 +1,24 @@ +// Since: cosmos-sdk 0.43 +syntax = "proto3"; +package cosmos.gov.v1beta1; + +// VoteOption enumerates the valid vote options for a given governance proposal. +enum VoteOption { + // VOTE_OPTION_UNSPECIFIED defines a no-op vote option. + VOTE_OPTION_UNSPECIFIED = 0; + // VOTE_OPTION_YES defines a yes vote option. + VOTE_OPTION_YES = 1; + // VOTE_OPTION_ABSTAIN defines an abstain vote option. + VOTE_OPTION_ABSTAIN = 2; + // VOTE_OPTION_NO defines a no vote option. + VOTE_OPTION_NO = 3; + // VOTE_OPTION_NO_WITH_VETO defines a no with veto vote option. + VOTE_OPTION_NO_WITH_VETO = 4; +} + +// MsgVote defines a message to cast a vote. +message MsgVote { + uint64 proposal_id = 1; + string voter = 2; + VoteOption option = 3; +} diff --git a/src/Cosmos/Protobuf/ibc_applications_transfer_tx.proto b/src/Cosmos/Protobuf/ibc_applications_transfer_tx.proto new file mode 100644 index 00000000000..79631a99000 --- /dev/null +++ b/src/Cosmos/Protobuf/ibc_applications_transfer_tx.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; +package ibc.applications.transfer.v1; + +// Src: https://github.com/cosmos/ibc-go/blob/main/proto/ibc/applications/transfer/v1/tx.proto + +import "coin.proto"; +import "ibc_core_client.proto"; + +// MsgTransfer defines a msg to transfer fungible tokens (i.e Coins) between ICS20 enabled chains. See ICS Spec here: +// https://github.com/cosmos/ibc/tree/master/spec/app/ics-020-fungible-token-transfer#data-structures +message MsgTransfer { + // the port on which the packet will be sent + string source_port = 1; + // the channel by which the packet will be sent + string source_channel = 2; + // the tokens to be transferred + cosmos.base.v1beta1.Coin token = 3; + // the sender address + string sender = 4; + // the recipient address on the destination chain + string receiver = 5; + // Timeout height relative to the current block height. + // The timeout is disabled when set to 0. + ibc.core.client.v1.Height timeout_height = 6; + // Timeout timestamp (in nanoseconds) relative to the current block timestamp. + // The timeout is disabled when set to 0. + uint64 timeout_timestamp = 7; +} diff --git a/src/Cosmos/Protobuf/ibc_core_client.proto b/src/Cosmos/Protobuf/ibc_core_client.proto new file mode 100644 index 00000000000..7dc60606ac1 --- /dev/null +++ b/src/Cosmos/Protobuf/ibc_core_client.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; +package ibc.core.client.v1; + +// Src: https://github.com/cosmos/ibc-go/blob/main/proto/ibc/core/client/v1/client.proto + +// Height is a monotonically increasing data type +// that can be compared against another Height for the purposes of updating and +// freezing clients +message Height { + // the revision that the client is currently on + uint64 revision_number = 1; + // the height within the given revision + uint64 revision_height = 2; +} diff --git a/src/Cosmos/Protobuf/staking_tx.proto b/src/Cosmos/Protobuf/staking_tx.proto new file mode 100644 index 00000000000..42beec4a443 --- /dev/null +++ b/src/Cosmos/Protobuf/staking_tx.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; +package cosmos.staking.v1beta1; + +// Src: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/staking/v1beta1/tx.proto + +import "coin.proto"; + +// MsgDelegate defines a SDK message for performing a delegation of coins +// from a delegator to a validator. +message MsgDelegate { + string delegator_address = 1; + string validator_address = 2; + base.v1beta1.Coin amount = 3; +} + +// MsgUndelegate defines a SDK message for performing an undelegation from a +// delegate and a validator. +message MsgUndelegate { + string delegator_address = 1; + string validator_address = 2; + base.v1beta1.Coin amount = 3; +} + +// MsgBeginRedelegate defines a SDK message for performing a redelegation +// of coins from a delegator and source validator to a destination validator. +message MsgBeginRedelegate { + string delegator_address = 1; + string validator_src_address = 2; + string validator_dst_address = 3; + base.v1beta1.Coin amount = 4; +} diff --git a/src/Cosmos/Protobuf/terra_wasm_v1beta1_tx.proto b/src/Cosmos/Protobuf/terra_wasm_v1beta1_tx.proto new file mode 100644 index 00000000000..a544e58b2ea --- /dev/null +++ b/src/Cosmos/Protobuf/terra_wasm_v1beta1_tx.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; +package terra.wasm.v1beta1; + +// Terra-Classic-specific fork, used only by Terra Classic +// Src: https://github.com/terra-money/core/blob/main/proto/terra/wasm/v1beta1/tx.proto +// original in https://github.com/CosmWasm/wasmd/blob/master/proto/cosmwasm/wasm/v1/tx.proto + +import "coin.proto"; + +// MsgExecuteContract submits the given message data to a smart contract +message MsgExecuteContract { + // Sender is the that actor that signed the messages + string sender = 1; + // Contract is the address of the smart contract + string contract = 2; + // ExecuteMsg json encoded message to be passed to the contract + bytes execute_msg = 3; + // Coins that are transferred to the contract on execution + // Gap in field numbering is intentional + repeated cosmos.base.v1beta1.Coin coins = 5; +} diff --git a/src/Cosmos/Protobuf/thorchain_bank_tx.proto b/src/Cosmos/Protobuf/thorchain_bank_tx.proto new file mode 100644 index 00000000000..0e5870ab162 --- /dev/null +++ b/src/Cosmos/Protobuf/thorchain_bank_tx.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; +package types; + +// Src: https://gitlab.com/thorchain/thornode/-/blob/develop/proto/thorchain/v1/x/thorchain/types/msg_send.proto +// Cosmos original: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/bank/v1beta1/tx.proto + +import "coin.proto"; + +// MsgSend represents a message to send coins from one account to another. +message MsgSend { + bytes from_address = 1; + bytes to_address = 2; + repeated cosmos.base.v1beta1.Coin amount = 3; +} diff --git a/src/Cosmos/Protobuf/tx.proto b/src/Cosmos/Protobuf/tx.proto new file mode 100644 index 00000000000..b7973cda4bf --- /dev/null +++ b/src/Cosmos/Protobuf/tx.proto @@ -0,0 +1,195 @@ +syntax = "proto3"; +package cosmos; + +// Src: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/tx/v1beta1/tx.proto + +import "coin.proto"; +import "crypto_multisig.proto"; +import "tx_signing.proto"; +import "google/protobuf/any.proto"; + +// Tx is the standard type used for broadcasting transactions. +message Tx { + // body is the processable content of the transaction + TxBody body = 1; + + // auth_info is the authorization related content of the transaction, + // specifically signers, signer modes and fee + AuthInfo auth_info = 2; + + // signatures is a list of signatures that matches the length and order of + // AuthInfo's signer_infos to allow connecting signature meta information like + // public key and signing mode by position. + repeated bytes signatures = 3; +} + +// TxRaw is a variant of Tx that pins the signer's exact binary representation +// of body and auth_info. This is used for signing, broadcasting and +// verification. The binary `serialize(tx: TxRaw)` is stored in Tendermint and +// the hash `sha256(serialize(tx: TxRaw))` becomes the "txhash", commonly used +// as the transaction ID. +message TxRaw { + // body_bytes is a protobuf serialization of a TxBody that matches the + // representation in SignDoc. + bytes body_bytes = 1; + + // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the + // representation in SignDoc. + bytes auth_info_bytes = 2; + + // signatures is a list of signatures that matches the length and order of + // AuthInfo's signer_infos to allow connecting signature meta information like + // public key and signing mode by position. + repeated bytes signatures = 3; +} + +// SignDoc is the type used for generating sign bytes for SIGN_MODE_DIRECT. +message SignDoc { + // body_bytes is protobuf serialization of a TxBody that matches the + // representation in TxRaw. + bytes body_bytes = 1; + + // auth_info_bytes is a protobuf serialization of an AuthInfo that matches the + // representation in TxRaw. + bytes auth_info_bytes = 2; + + // chain_id is the unique identifier of the chain this transaction targets. + // It prevents signed transactions from being used on another chain by an + // attacker + string chain_id = 3; + + // account_number is the account number of the account in state + uint64 account_number = 4; +} + +// SignDocDirectAux is omitted + +// TxBody is the body of a transaction that all signers sign over. +message TxBody { + // messages is a list of messages to be executed. The required signers of + // those messages define the number and order of elements in AuthInfo's + // signer_infos and Tx's signatures. Each required signer address is added to + // the list only the first time it occurs. + // By convention, the first required signer (usually from the first message) + // is referred to as the primary signer and pays the fee for the whole + // transaction. + repeated google.protobuf.Any messages = 1; + + // memo is any arbitrary note/comment to be added to the transaction. + // WARNING: in clients, any publicly exposed text should not be called memo, + // but should be called `note` instead (see https://github.com/cosmos/cosmos-sdk/issues/9122). + string memo = 2; + + // timeout is the block height after which this transaction will not + // be processed by the chain + uint64 timeout_height = 3; + + // extension_options are arbitrary options that can be added by chains + // when the default options are not sufficient. If any of these are present + // and can't be handled, the transaction will be rejected + repeated google.protobuf.Any extension_options = 1023; + + // extension_options are arbitrary options that can be added by chains + // when the default options are not sufficient. If any of these are present + // and can't be handled, they will be ignored + repeated google.protobuf.Any non_critical_extension_options = 2047; +} + +// AuthInfo describes the fee and signer modes that are used to sign a +// transaction. +message AuthInfo { + // signer_infos defines the signing modes for the required signers. The number + // and order of elements must match the required signers from TxBody's + // messages. The first element is the primary signer and the one which pays + // the fee. + repeated SignerInfo signer_infos = 1; + + // Fee is the fee and gas limit for the transaction. The first signer is the + // primary signer and the one which pays the fee. The fee can be calculated + // based on the cost of evaluating the body and doing signature verification + // of the signers. This can be estimated via simulation. + Fee fee = 2; + + // Tip is the optional tip used for meta-transactions. + Tip tip = 3; +} + +// SignerInfo describes the public key and signing mode of a single top-level +// signer. +message SignerInfo { + // public_key is the public key of the signer. It is optional for accounts + // that already exist in state. If unset, the verifier can use the required \ + // signer address for this position and lookup the public key. + google.protobuf.Any public_key = 1; + + // mode_info describes the signing mode of the signer and is a nested + // structure to support nested multisig pubkey's + ModeInfo mode_info = 2; + + // sequence is the sequence of the account, which describes the + // number of committed transactions signed by a given address. It is used to + // prevent replay attacks. + uint64 sequence = 3; +} + +// ModeInfo describes the signing mode of a single or nested multisig signer. +message ModeInfo { + // sum is the oneof that specifies whether this represents a single or nested + // multisig signer + oneof sum { + // single represents a single signer + Single single = 1; + + // multi represents a nested multisig signer + Multi multi = 2; + } + + // Single is the mode info for a single signer. It is structured as a message + // to allow for additional fields such as locale for SIGN_MODE_TEXTUAL in the + // future + message Single { + // mode is the signing mode of the single signer + signing.v1beta1.SignMode mode = 1; + } + + // Multi is the mode info for a multisig public key + message Multi { + // bitarray specifies which keys within the multisig are signing + multisig.v1beta1.CompactBitArray bitarray = 1; + + // mode_infos is the corresponding modes of the signers of the multisig + // which could include nested multisig public keys + repeated ModeInfo mode_infos = 2; + } +} + +// Fee includes the amount of coins paid in fees and the maximum +// gas to be used by the transaction. The ratio yields an effective "gasprice", +// which must be above some miminum to be accepted into the mempool. +message Fee { + // amount is the amount of coins to be paid as a fee + repeated base.v1beta1.Coin amount = 1; + + // gas_limit is the maximum gas that can be used in transaction processing + // before an out of gas error occurs + uint64 gas_limit = 2; + + // if unset, the first signer is responsible for paying the fees. If set, the specified account must pay the fees. + // the payer must be a tx signer (and thus have signed this field in AuthInfo). + // setting this field does *not* change the ordering of required signers for the transaction. + string payer = 3; + + // if set, the fee payer (either the first signer or the value of the payer field) requests that a fee grant be used + // to pay fees instead of the fee payer's own balance. If an appropriate fee grant does not exist or the chain does + // not support fee grants, this will fail + string granter = 4; +} + +// Tip is the tip used for meta-transactions. +message Tip { + // amount is the amount of the tip + repeated base.v1beta1.Coin amount = 1; + + // tipper is the address of the account paying for the tip + string tipper = 2; +} diff --git a/src/Cosmos/Protobuf/tx_signing.proto b/src/Cosmos/Protobuf/tx_signing.proto new file mode 100644 index 00000000000..8eb22240548 --- /dev/null +++ b/src/Cosmos/Protobuf/tx_signing.proto @@ -0,0 +1,89 @@ +syntax = "proto3"; +package cosmos.signing.v1beta1; + +// Src: https://github.com/cosmos/cosmos-sdk/blob/master/proto/cosmos/tx/signing/v1beta1/signing.proto + +import "crypto_multisig.proto"; +import "google/protobuf/any.proto"; + +// SignMode represents a signing mode with its own security guarantees. +enum SignMode { + // SIGN_MODE_UNSPECIFIED specifies an unknown signing mode and will be + // rejected. + SIGN_MODE_UNSPECIFIED = 0; + + // SIGN_MODE_DIRECT specifies a signing mode which uses SignDoc and is + // verified with raw bytes from Tx. + SIGN_MODE_DIRECT = 1; + + // SIGN_MODE_TEXTUAL is a future signing mode that will verify some + // human-readable textual representation on top of the binary representation + // from SIGN_MODE_DIRECT. It is currently not supported. + SIGN_MODE_TEXTUAL = 2; + + // SIGN_MODE_DIRECT_AUX specifies a signing mode which uses + // SignDocDirectAux. As opposed to SIGN_MODE_DIRECT, this sign mode does not + // require signers signing over other signers' `signer_info`. It also allows + // for adding Tips in transactions. + SIGN_MODE_DIRECT_AUX = 3; + + // SIGN_MODE_AMINO_AUX specifies a signing mode which uses + // SignDocAminoAux. + SIGN_MODE_AMINO_AUX = 4; + + // SIGN_MODE_LEGACY_AMINO_JSON is a backwards compatibility mode which uses + // Amino JSON and will be removed in the future. + SIGN_MODE_LEGACY_AMINO_JSON = 127; +} + +// SignatureDescriptors wraps multiple SignatureDescriptor's. +message SignatureDescriptors { + // signatures are the signature descriptors + repeated SignatureDescriptor signatures = 1; +} + +// SignatureDescriptor is a convenience type which represents the full data for +// a signature including the public key of the signer, signing modes and the +// signature itself. It is primarily used for coordinating signatures between +// clients. +message SignatureDescriptor { + // public_key is the public key of the signer + google.protobuf.Any public_key = 1; + + Data data = 2; + + // sequence is the sequence of the account, which describes the + // number of committed transactions signed by a given address. It is used to prevent + // replay attacks. + uint64 sequence = 3; + + // Data represents signature data + message Data { + // sum is the oneof that specifies whether this represents single or multi-signature data + oneof sum { + // single represents a single signer + Single single = 1; + + // multi represents a multisig signer + Multi multi = 2; + } + + // Single is the signature data for a single signer + message Single { + // mode is the signing mode of the single signer + SignMode mode = 1; + + // signature is the raw signature bytes + bytes signature = 2; + } + + // Multi is the signature data for a multisig public key + message Multi { + // bitarray specifies which keys within the multisig are signing + multisig.v1beta1.CompactBitArray bitarray = 1; + + // signatures is the signatures of the multi-signature + repeated Data signatures = 2; + } + } +} diff --git a/src/Cosmos/ProtobufSerialization.cpp b/src/Cosmos/ProtobufSerialization.cpp new file mode 100644 index 00000000000..e2f2e070890 --- /dev/null +++ b/src/Cosmos/ProtobufSerialization.cpp @@ -0,0 +1,457 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "ProtobufSerialization.h" +#include "JsonSerialization.h" +#include "../proto/Cosmos.pb.h" +#include "Protobuf/coin.pb.h" +#include "Protobuf/bank_tx.pb.h" +#include "Protobuf/cosmwasm_wasm_v1_tx.pb.h" +#include "Protobuf/distribution_tx.pb.h" +#include "Protobuf/staking_tx.pb.h" +#include "Protobuf/authz_tx.pb.h" +#include "Protobuf/tx.pb.h" +#include "Protobuf/gov_tx.pb.h" +#include "Protobuf/crypto_secp256k1_keys.pb.h" +#include "Protobuf/ibc_applications_transfer_tx.pb.h" +#include "Protobuf/terra_wasm_v1beta1_tx.pb.h" +#include "Protobuf/thorchain_bank_tx.pb.h" +#include "Protobuf/ethermint_keys.pb.h" + +#include "PrivateKey.h" +#include "Data.h" +#include "Hash.h" +#include "Base64.h" +#include "uint256.h" + +#include + +using namespace TW; + +namespace TW::Cosmos::Protobuf { + +using json = nlohmann::json; +using string = std::string; +const auto ProtobufAnyNamespacePrefix = ""; // to override default 'type.googleapis.com' + +cosmos::base::v1beta1::Coin convertCoin(const Proto::Amount& amount) { + cosmos::base::v1beta1::Coin coin; + coin.set_denom(amount.denom()); + coin.set_amount(amount.amount()); + return coin; +} + +// Convert messages from external protobuf to internal protobuf +google::protobuf::Any convertMessage(const Proto::Message& msg) { + google::protobuf::Any any; + switch (msg.message_oneof_case()) { + case Proto::Message::kSendCoinsMessage: + { + assert(msg.has_send_coins_message()); + const auto& send = msg.send_coins_message(); + auto msgSend = cosmos::bank::v1beta1::MsgSend(); + msgSend.set_from_address(send.from_address()); + msgSend.set_to_address(send.to_address()); + for (auto i = 0; i < send.amounts_size(); ++i) { + *msgSend.add_amount() = convertCoin(send.amounts(i)); + } + any.PackFrom(msgSend, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kTransferTokensMessage: + { + assert(msg.has_transfer_tokens_message()); + const auto& transfer = msg.transfer_tokens_message(); + auto msgTransfer = ibc::applications::transfer::v1::MsgTransfer(); + msgTransfer.set_source_port(transfer.source_port()); + msgTransfer.set_source_channel(transfer.source_channel()); + *msgTransfer.mutable_token() = convertCoin(transfer.token()); + msgTransfer.set_sender(transfer.sender()); + msgTransfer.set_receiver(transfer.receiver()); + msgTransfer.mutable_timeout_height()->set_revision_number(transfer.timeout_height().revision_number()); + msgTransfer.mutable_timeout_height()->set_revision_height(transfer.timeout_height().revision_height()); + msgTransfer.set_timeout_timestamp(transfer.timeout_timestamp()); + any.PackFrom(msgTransfer, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kStakeMessage: + { + assert(msg.has_stake_message()); + const auto& stake = msg.stake_message(); + auto msgDelegate = cosmos::staking::v1beta1::MsgDelegate(); + msgDelegate.set_delegator_address(stake.delegator_address()); + msgDelegate.set_validator_address(stake.validator_address()); + *msgDelegate.mutable_amount() = convertCoin(stake.amount()); + any.PackFrom(msgDelegate, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kUnstakeMessage: + { + assert(msg.has_unstake_message()); + const auto& unstake = msg.unstake_message(); + auto msgUndelegate = cosmos::staking::v1beta1::MsgUndelegate(); + msgUndelegate.set_delegator_address(unstake.delegator_address()); + msgUndelegate.set_validator_address(unstake.validator_address()); + *msgUndelegate.mutable_amount() = convertCoin(unstake.amount()); + any.PackFrom(msgUndelegate, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kRestakeMessage: + { + assert(msg.has_restake_message()); + const auto& restake = msg.restake_message(); + auto msgRedelegate = cosmos::staking::v1beta1::MsgBeginRedelegate(); + msgRedelegate.set_delegator_address(restake.delegator_address()); + msgRedelegate.set_validator_src_address(restake.validator_src_address()); + msgRedelegate.set_validator_dst_address(restake.validator_dst_address()); + *msgRedelegate.mutable_amount() = convertCoin(restake.amount()); + any.PackFrom(msgRedelegate, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kWithdrawStakeRewardMessage: + { + assert(msg.has_withdraw_stake_reward_message()); + const auto& withdraw = msg.withdraw_stake_reward_message(); + auto msgWithdraw = cosmos::distribution::v1beta1::MsgWithdrawDelegatorReward(); + msgWithdraw.set_delegator_address(withdraw.delegator_address()); + msgWithdraw.set_validator_address(withdraw.validator_address()); + any.PackFrom(msgWithdraw, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kWasmTerraExecuteContractTransferMessage: + { + assert(msg.has_wasm_terra_execute_contract_transfer_message()); + const auto& wasmExecute = msg.wasm_terra_execute_contract_transfer_message(); + auto msgExecute = terra::wasm::v1beta1::MsgExecuteContract(); + msgExecute.set_sender(wasmExecute.sender_address()); + msgExecute.set_contract(wasmExecute.contract_address()); + const std::string payload = wasmTerraExecuteTransferPayload(wasmExecute).dump(); + msgExecute.set_execute_msg(payload); + any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kWasmTerraExecuteContractSendMessage: + { + assert(msg.has_wasm_terra_execute_contract_send_message()); + const auto& wasmExecute = msg.wasm_terra_execute_contract_send_message(); + auto msgExecute = terra::wasm::v1beta1::MsgExecuteContract(); + msgExecute.set_sender(wasmExecute.sender_address()); + msgExecute.set_contract(wasmExecute.contract_address()); + const std::string payload = wasmTerraExecuteSendPayload(wasmExecute).dump(); + msgExecute.set_execute_msg(payload); + any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kThorchainSendMessage: + { + assert(msg.has_thorchain_send_message()); + const auto& send = msg.thorchain_send_message(); + auto msgSend =types::MsgSend(); + msgSend.set_from_address(send.from_address()); + msgSend.set_to_address(send.to_address()); + for (auto i = 0; i < send.amounts_size(); ++i) { + *msgSend.add_amount() = convertCoin(send.amounts(i)); + } + any.PackFrom(msgSend, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kWasmTerraExecuteContractGeneric: { + assert(msg.has_wasm_terra_execute_contract_generic()); + const auto& wasmExecute = msg.wasm_terra_execute_contract_generic(); + auto msgExecute = terra::wasm::v1beta1::MsgExecuteContract(); + msgExecute.set_sender(wasmExecute.sender_address()); + msgExecute.set_contract(wasmExecute.contract_address()); + msgExecute.set_execute_msg(wasmExecute.execute_msg()); + + for (auto i = 0; i < wasmExecute.coins_size(); ++i) { + *msgExecute.add_coins() = convertCoin(wasmExecute.coins(i)); + } + any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kWasmExecuteContractTransferMessage: + { + assert(msg.has_wasm_execute_contract_transfer_message()); + const auto& wasmExecute = msg.wasm_execute_contract_transfer_message(); + auto msgExecute = cosmwasm::wasm::v1::MsgExecuteContract(); + msgExecute.set_sender(wasmExecute.sender_address()); + msgExecute.set_contract(wasmExecute.contract_address()); + const std::string payload = wasmExecuteTransferPayload(wasmExecute).dump(); + msgExecute.set_msg(payload); + any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kWasmExecuteContractSendMessage: + { + assert(msg.has_wasm_execute_contract_send_message()); + const auto& wasmExecute = msg.wasm_execute_contract_send_message(); + auto msgExecute = cosmwasm::wasm::v1::MsgExecuteContract(); + msgExecute.set_sender(wasmExecute.sender_address()); + msgExecute.set_contract(wasmExecute.contract_address()); + const std::string payload = wasmExecuteSendPayload(wasmExecute).dump(); + msgExecute.set_msg(payload); + any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kWasmExecuteContractGeneric: { + assert(msg.has_wasm_execute_contract_generic()); + const auto& wasmExecute = msg.wasm_execute_contract_generic(); + auto msgExecute = cosmwasm::wasm::v1::MsgExecuteContract(); + msgExecute.set_sender(wasmExecute.sender_address()); + msgExecute.set_contract(wasmExecute.contract_address()); + msgExecute.set_msg(wasmExecute.execute_msg()); + + for (auto i = 0; i < wasmExecute.coins_size(); ++i) { + *msgExecute.add_funds() = convertCoin(wasmExecute.coins(i)); + } + any.PackFrom(msgExecute, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kAuthGrant: { + assert(msg.has_auth_grant()); + const auto& authGrant = msg.auth_grant(); + auto msgAuthGrant = cosmos::authz::v1beta1::MsgGrant(); + msgAuthGrant.set_grantee(authGrant.grantee()); + msgAuthGrant.set_granter(authGrant.granter()); + auto* mtAuth = msgAuthGrant.mutable_grant()->mutable_authorization(); + // There is multiple grant possibilities, but we add support staking/compounding only for now. + switch (authGrant.grant_type_case()) { + case Proto::Message_AuthGrant::kGrantStake: + mtAuth->PackFrom(authGrant.grant_stake(), ProtobufAnyNamespacePrefix); + mtAuth->set_type_url("/cosmos.staking.v1beta1.StakeAuthorization"); + break; + case Proto::Message_AuthGrant::GRANT_TYPE_NOT_SET: + break; + } + auto* mtExp = msgAuthGrant.mutable_grant()->mutable_expiration(); + mtExp->set_seconds(authGrant.expiration()); + any.PackFrom(msgAuthGrant, ProtobufAnyNamespacePrefix); + return any; + } + + case Proto::Message::kAuthRevoke: { + assert(msg.has_auth_revoke()); + const auto& authRevoke = msg.auth_revoke(); + auto msgAuthRevoke = cosmos::authz::v1beta1::MsgRevoke(); + msgAuthRevoke.set_granter(authRevoke.granter()); + msgAuthRevoke.set_grantee(authRevoke.grantee()); + msgAuthRevoke.set_msg_type_url(authRevoke.msg_type_url()); + any.PackFrom(msgAuthRevoke, ProtobufAnyNamespacePrefix); + return any; + } + case Proto::Message::kMsgVote: { + assert(msg.has_msg_vote()); + const auto& vote = msg.msg_vote(); + auto msgVote = cosmos::gov::v1beta1::MsgVote(); + // LCOV_EXCL_START + switch (vote.option()) { + case Proto::Message_VoteOption__UNSPECIFIED: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_UNSPECIFIED); + break; + case Proto::Message_VoteOption_YES: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_YES); + break; + case Proto::Message_VoteOption_ABSTAIN: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_ABSTAIN); + break; + case Proto::Message_VoteOption_NO: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_NO); + break; + case Proto::Message_VoteOption_NO_WITH_VETO: + msgVote.set_option(cosmos::gov::v1beta1::VOTE_OPTION_NO_WITH_VETO); + break; + case Proto::Message_VoteOption_Message_VoteOption_INT_MIN_SENTINEL_DO_NOT_USE_: + msgVote.set_option(cosmos::gov::v1beta1::VoteOption_INT_MIN_SENTINEL_DO_NOT_USE_); + break; + case Proto::Message_VoteOption_Message_VoteOption_INT_MAX_SENTINEL_DO_NOT_USE_: + msgVote.set_option(cosmos::gov::v1beta1::VoteOption_INT_MAX_SENTINEL_DO_NOT_USE_); + break; + } + // LCOV_EXCL_STOP + msgVote.set_proposal_id(vote.proposal_id()); + msgVote.set_voter(vote.voter()); + any.PackFrom(msgVote, ProtobufAnyNamespacePrefix); + return any; + } + + default: + throw std::invalid_argument(std::string("Message not supported ") + std::to_string(msg.message_oneof_case())); + } +} + +std::string buildProtoTxBody(const Proto::SigningInput& input) { + if (input.messages_size() >= 1 && input.messages(0).has_sign_direct_message()) { + return input.messages(0).sign_direct_message().body_bytes(); + } + + if (input.messages_size() < 1) { + throw std::invalid_argument("No message found"); + } + assert(input.messages_size() >= 1); + auto txBody = cosmos::TxBody(); + for (auto i = 0; i < input.messages_size(); ++i) { + const auto msgAny = convertMessage(input.messages(i)); + *txBody.add_messages() = msgAny; + } + txBody.set_memo(input.memo()); + txBody.set_timeout_height(0); + + return txBody.SerializeAsString(); +} + +std::string buildAuthInfo(const Proto::SigningInput& input, TWCoinType coin) { + if (input.messages_size() >= 1 && input.messages(0).has_sign_direct_message()) { + return input.messages(0).sign_direct_message().auth_info_bytes(); + } + + // AuthInfo + const auto privateKey = PrivateKey(input.private_key()); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto authInfo = cosmos::AuthInfo(); + auto* signerInfo = authInfo.add_signer_infos(); + + signerInfo->mutable_mode_info()->mutable_single()->set_mode(cosmos::signing::v1beta1::SIGN_MODE_DIRECT); + signerInfo->set_sequence(input.sequence()); + switch(coin) { + case TWCoinTypeNativeEvmos: { + auto pubKey = ethermint::crypto::v1::ethsecp256k1::PubKey(); + pubKey.set_key(publicKey.bytes.data(), publicKey.bytes.size()); + signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); + break; + } + default: { + auto pubKey = cosmos::crypto::secp256k1::PubKey(); + pubKey.set_key(publicKey.bytes.data(), publicKey.bytes.size()); + signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); + } + } + + auto* fee = authInfo.mutable_fee(); + for (auto i = 0; i < input.fee().amounts_size(); ++i) { + *fee->add_amount() = convertCoin(input.fee().amounts(i)); + } + + fee->set_gas_limit(input.fee().gas()); + fee->set_payer(""); + fee->set_granter(""); + // tip is omitted + return authInfo.SerializeAsString(); +} + +Data buildSignature(const Proto::SigningInput& input, const std::string& serializedTxBody, const std::string& serializedAuthInfo, TWCoinType coin) { + // SignDoc Preimage + auto signDoc = cosmos::SignDoc(); + signDoc.set_body_bytes(serializedTxBody); + signDoc.set_auth_info_bytes(serializedAuthInfo); + signDoc.set_chain_id(input.chain_id()); + signDoc.set_account_number(input.account_number()); + const auto serializedSignDoc = signDoc.SerializeAsString(); + + Data hashToSign; + switch(coin) { + case TWCoinTypeNativeEvmos: { + hashToSign = Hash::keccak256(serializedSignDoc); + break; + } + default: { + hashToSign = Hash::sha256(serializedSignDoc); + } + } + + const auto privateKey = PrivateKey(input.private_key()); + auto signedHash = privateKey.sign(hashToSign, TWCurveSECP256k1); + auto signature = Data(signedHash.begin(), signedHash.end() - 1); + return signature; +} + +std::string buildProtoTxRaw(const std::string& serializedTxBody, const std::string& serializedAuthInfo, const Data& signature) { + auto txRaw = cosmos::TxRaw(); + txRaw.set_body_bytes(serializedTxBody); + txRaw.set_auth_info_bytes(serializedAuthInfo); + *txRaw.add_signatures() = std::string(signature.begin(), signature.end()); + return txRaw.SerializeAsString(); +} + +static string broadcastMode(Proto::BroadcastMode mode) { + switch (mode) { + case Proto::BroadcastMode::BLOCK: + return "BROADCAST_MODE_BLOCK"; + case Proto::BroadcastMode::ASYNC: + return "BROADCAST_MODE_ASYNC"; + case Proto::BroadcastMode::SYNC: + default: return "BROADCAST_MODE_SYNC"; + } +} + +std::string buildProtoTxJson(const Proto::SigningInput& input, const std::string& serializedTx) { + const string serializedBase64 = Base64::encode(TW::data(serializedTx)); + const json jsonSerialized = { + {"tx_bytes", serializedBase64}, + {"mode", broadcastMode(input.mode())} + }; + return jsonSerialized.dump(); +} + +json wasmExecuteTransferPayload(const Proto::Message_WasmExecuteContractTransfer& msg) { + return { + {"transfer", + { + {"amount", toString(load(data(msg.amount())))}, + {"recipient", msg.recipient_address()} + } + } + }; +} + +json wasmExecuteSendPayload(const Proto::Message_WasmExecuteContractSend& msg) { + return { + {"send", + { + {"amount", toString(load(data(msg.amount())))}, + {"contract", msg.recipient_contract_address()}, + {"msg", msg.msg()} + } + } + }; +} + +json wasmTerraExecuteTransferPayload(const Proto::Message_WasmTerraExecuteContractTransfer& msg) { + return { + {"transfer", + { + {"amount", toString(load(data(msg.amount())))}, + {"recipient", msg.recipient_address()} + } + } + }; +} + +json wasmTerraExecuteSendPayload(const Proto::Message_WasmTerraExecuteContractSend& msg) { + return { + {"send", + { + {"amount", toString(load(data(msg.amount())))}, + {"contract", msg.recipient_contract_address()}, + {"msg", msg.msg()} + } + } + }; +} + +} // namespace diff --git a/src/Cosmos/ProtobufSerialization.h b/src/Cosmos/ProtobufSerialization.h new file mode 100644 index 00000000000..035bb7f2c7e --- /dev/null +++ b/src/Cosmos/ProtobufSerialization.h @@ -0,0 +1,37 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" +#include "../proto/Cosmos.pb.h" + +#include +#include + +#include + +namespace TW::Cosmos::Protobuf { + +std::string buildProtoTxBody(const Proto::SigningInput& input); + +std::string buildAuthInfo(const Proto::SigningInput& input, TWCoinType coin); + +Data buildSignature(const Proto::SigningInput& input, const std::string& serializedTxBody, const std::string& serializedAuthInfo, TWCoinType coin); + +std::string buildProtoTxRaw(const std::string& serializedTxBody, const std::string& serializedAuthInfo, const Data& signature); + +std::string buildProtoTxJson(const Proto::SigningInput& input, const std::string& serializedTx); + +nlohmann::json wasmExecuteTransferPayload(const Proto::Message_WasmExecuteContractTransfer& msg); + +nlohmann::json wasmExecuteSendPayload(const Proto::Message_WasmExecuteContractSend& msg); + +nlohmann::json wasmTerraExecuteTransferPayload(const Proto::Message_WasmTerraExecuteContractTransfer& msg); + +nlohmann::json wasmTerraExecuteSendPayload(const Proto::Message_WasmTerraExecuteContractSend& msg); + +} // namespace TW::Cosmos::protobuf diff --git a/src/Cosmos/Serialization.cpp b/src/Cosmos/Serialization.cpp deleted file mode 100644 index 64104df731c..00000000000 --- a/src/Cosmos/Serialization.cpp +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Serialization.h" - -#include "../Cosmos/Address.h" -#include "../proto/Cosmos.pb.h" -#include "Base64.h" -#include "PrivateKey.h" - -using namespace TW; -using namespace TW::Cosmos; - -using json = nlohmann::json; -using string = std::string; - -const string TYPE_PREFIX_MSG_SEND = "cosmos-sdk/MsgSend"; -const string TYPE_PREFIX_MSG_DELEGATE = "cosmos-sdk/MsgDelegate"; -const string TYPE_PREFIX_MSG_UNDELEGATE = "cosmos-sdk/MsgUndelegate"; -const string TYPE_PREFIX_MSG_REDELEGATE = "cosmos-sdk/MsgBeginRedelegate"; -const string TYPE_PREFIX_MSG_WITHDRAW_REWARD = "cosmos-sdk/MsgWithdrawDelegationReward"; -const string TYPE_PREFIX_PUBLIC_KEY = "tendermint/PubKeySecp256k1"; - -static string broadcastMode(Proto::BroadcastMode mode) { - switch (mode) { - case Proto::BroadcastMode::BLOCK: - return "block"; - case Proto::BroadcastMode::ASYNC: - return "async"; - default: return "sync"; - } -} - -static json broadcastJSON(json& j, Proto::BroadcastMode mode) { - return { - {"tx", j}, - {"mode", broadcastMode(mode)} - }; -} - -static json amountJSON(const Proto::Amount& amount) { - return { - {"amount", std::to_string(amount.amount())}, - {"denom", amount.denom()} - }; -} - -static json amountsJSON(const ::google::protobuf::RepeatedPtrField& amounts) { - json j = json::array(); - for (auto& amount : amounts) { - j.push_back(amountJSON(amount)); - } - return j; -} - -static json feeJSON(const Proto::Fee& fee) { - json js = json::array(); - - for (auto& amount : fee.amounts()) { - js.push_back(amountJSON(amount)); - } - - return { - {"amount", js}, - {"gas", std::to_string(fee.gas())} - }; -} - -static json messageSend(const Proto::Message_Send& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_SEND : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"amount", amountsJSON(message.amounts())}, - {"from_address", message.from_address()}, - {"to_address", message.to_address()} - }} - }; -} - -static json messageDelegate(const Proto::Message_Delegate& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_DELEGATE : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"amount", amountJSON(message.amount())}, - {"delegator_address", message.delegator_address()}, - {"validator_address", message.validator_address()} - }} - }; -} - -static json messageUndelegate(const Proto::Message_Undelegate& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_UNDELEGATE : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"amount", amountJSON(message.amount())}, - {"delegator_address", message.delegator_address()}, - {"validator_address", message.validator_address()} - }} - }; -} - -static json messageRedelegate(const Proto::Message_BeginRedelegate& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_REDELEGATE : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"amount", amountJSON(message.amount())}, - {"delegator_address", message.delegator_address()}, - {"validator_src_address", message.validator_src_address()}, - {"validator_dst_address", message.validator_dst_address()}, - }} - }; -} - -static json messageWithdrawReward(const Proto::Message_WithdrawDelegationReward& message) { - auto typePrefix = message.type_prefix().empty() ? TYPE_PREFIX_MSG_WITHDRAW_REWARD : message.type_prefix(); - - return { - {"type", typePrefix}, - {"value", { - {"delegator_address", message.delegator_address()}, - {"validator_address", message.validator_address()} - }} - }; -} - -static json messageRawJSON(const Proto::Message_RawJSON& message) { - return { - {"type", message.type()}, - {"value", json::parse(message.value())}, - }; -} -static json messagesJSON(const Proto::SigningInput& input) { - json j = json::array(); - for (auto& msg : input.messages()) { - if (msg.has_send_coins_message()) { - j.push_back(messageSend(msg.send_coins_message())); - } else if (msg.has_stake_message()) { - j.push_back(messageDelegate(msg.stake_message())); - } else if (msg.has_unstake_message()) { - j.push_back(messageUndelegate(msg.unstake_message())); - } else if (msg.has_withdraw_stake_reward_message()) { - j.push_back(messageWithdrawReward(msg.withdraw_stake_reward_message())); - } else if (msg.has_restake_message()) { - j.push_back(messageRedelegate(msg.restake_message())); - } else if (msg.has_raw_json_message()) { - j.push_back(messageRawJSON(msg.raw_json_message())); - } - } - return j; -} - -static json signatureJSON(const Data& signature, const Data& pubkey) { - return { - {"pub_key", { - {"type", TYPE_PREFIX_PUBLIC_KEY}, - {"value", Base64::encode(pubkey)} - }}, - {"signature", Base64::encode(signature)} - }; -} - -json Cosmos::signaturePreimage(const Proto::SigningInput& input) { - return { - {"account_number", std::to_string(input.account_number())}, - {"chain_id", input.chain_id()}, - {"fee", feeJSON(input.fee())}, - {"memo", input.memo()}, - {"msgs", messagesJSON(input)}, - {"sequence", std::to_string(input.sequence())} - }; -} - -json Cosmos::transactionJSON(const Proto::SigningInput& input, const Data& signature) { - auto privateKey = PrivateKey(input.private_key()); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - json tx = { - {"fee", feeJSON(input.fee())}, - {"memo", input.memo()}, - {"msg", messagesJSON(input)}, - {"signatures", json::array({ - signatureJSON(signature, Data(publicKey.bytes)) - })} - }; - return broadcastJSON(tx, input.mode()); -} diff --git a/src/Cosmos/Serialization.h b/src/Cosmos/Serialization.h deleted file mode 100644 index bd049eb903b..00000000000 --- a/src/Cosmos/Serialization.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "../proto/Cosmos.pb.h" -#include "Data.h" -#include - -using string = std::string; -using json = nlohmann::json; - -extern const string TYPE_PREFIX_MSG_SEND; -extern const string TYPE_PREFIX_MSG_DELEGATE; -extern const string TYPE_PREFIX_MSG_UNDELEGATE; -extern const string TYPE_PREFIX_MSG_REDELEGATE; -extern const string TYPE_PREFIX_MSG_WITHDRAW_REWARD; -extern const string TYPE_PREFIX_PUBLIC_KEY; - -namespace TW::Cosmos { - -json signaturePreimage(const Proto::SigningInput& input); -json transactionJSON(const Proto::SigningInput& input, const Data& signature); - -} // namespace diff --git a/src/Cosmos/Signer.cpp b/src/Cosmos/Signer.cpp index 7bdbe7b4494..b8b538ad8f5 100644 --- a/src/Cosmos/Signer.cpp +++ b/src/Cosmos/Signer.cpp @@ -1,39 +1,79 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "Signer.h" -#include "PrivateKey.h" -#include "Serialization.h" +#include "JsonSerialization.h" +#include "ProtobufSerialization.h" +#include "PrivateKey.h" #include "Data.h" -#include "Hash.h" - #include -using namespace TW; -using namespace TW::Cosmos; +namespace TW::Cosmos { + +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input, TWCoinType coin) noexcept { + switch (input.signing_mode()) { + case Proto::JSON: + return signJsonSerialized(input, coin); + + case Proto::Protobuf: + default: + return signProtobuf(input, coin); + } +} -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { +Proto::SigningOutput Signer::signJsonSerialized(const Proto::SigningInput& input, TWCoinType coin) noexcept { auto key = PrivateKey(input.private_key()); - auto preimage = signaturePreimage(input).dump(); + auto preimage = Json::signaturePreimageJSON(input).dump(); auto hash = Hash::sha256(preimage); auto signedHash = key.sign(hash, TWCurveSECP256k1); auto output = Proto::SigningOutput(); auto signature = Data(signedHash.begin(), signedHash.end() - 1); - auto txJson = transactionJSON(input, signature); + auto txJson = Json::transactionJSON(input, signature, coin); output.set_json(txJson.dump()); output.set_signature(signature.data(), signature.size()); + output.set_serialized(""); + output.set_error(""); + output.set_signature_json(txJson["tx"]["signatures"].dump()); return output; } -std::string Signer::signJSON(const std::string& json, const Data& key) { +Proto::SigningOutput Signer::signProtobuf(const Proto::SigningInput& input, TWCoinType coin) noexcept { + using namespace Protobuf; + using namespace Json; + try { + const auto serializedTxBody = buildProtoTxBody(input); + const auto serializedAuthInfo = buildAuthInfo(input, coin); + const auto signature = buildSignature(input, serializedTxBody, serializedAuthInfo, coin); + auto serializedTxRaw = buildProtoTxRaw(serializedTxBody, serializedAuthInfo, signature); + + auto output = Proto::SigningOutput(); + const std::string jsonSerialized = buildProtoTxJson(input, serializedTxRaw); + auto publicKey = PrivateKey(input.private_key()).getPublicKey(TWPublicKeyTypeSECP256k1); + auto signatures = nlohmann::json::array({signatureJSON(signature, publicKey.bytes, coin)}); + output.set_serialized(jsonSerialized); + output.set_signature(signature.data(), signature.size()); + output.set_json(""); + output.set_error(""); + output.set_signature_json(signatures.dump()); + return output; + } catch (const std::exception& ex) { + auto output = Proto::SigningOutput(); + output.set_error(std::string("Error: ") + ex.what()); + return output; + } +} + +std::string Signer::signJSON(const std::string& json, const Data& key, TWCoinType coin) { auto input = Proto::SigningInput(); google::protobuf::util::JsonStringToMessage(json, &input); input.set_private_key(key.data(), key.size()); - auto output = Signer::sign(input); + auto output = Signer::sign(input, coin); return output.json(); } + +} // namespace TW::Cosmos diff --git a/src/Cosmos/Signer.h b/src/Cosmos/Signer.h index 4af4193d17f..4a3407021e7 100644 --- a/src/Cosmos/Signer.h +++ b/src/Cosmos/Signer.h @@ -6,18 +6,27 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../proto/Cosmos.pb.h" +#include + namespace TW::Cosmos { /// Helper class that performs Cosmos transaction signing. class Signer { public: /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; + static Proto::SigningOutput sign(const Proto::SigningInput& input, TWCoinType coin) noexcept; + + /// Signs a Proto::SigningInput transaction, using Json serialization + static Proto::SigningOutput signJsonSerialized(const Proto::SigningInput& input, TWCoinType coin) noexcept; + + /// Signs a Proto::SigningInput transaction, using binary Protobuf serialization + static Proto::SigningOutput signProtobuf(const Proto::SigningInput& input, TWCoinType coin) noexcept; + /// Signs a json Proto::SigningInput with private key - static std::string signJSON(const std::string& json, const Data& key); + static std::string signJSON(const std::string& json, const Data& key, TWCoinType coin); }; } // namespace TW::Cosmos diff --git a/src/Crc.cpp b/src/Crc.cpp index 7a388a7c03c..1cbbe6d2696 100644 --- a/src/Crc.cpp +++ b/src/Crc.cpp @@ -6,8 +6,7 @@ #include "Crc.h" -#include // for boost::crc_32_type - +#include #include using namespace TW; @@ -17,7 +16,7 @@ uint16_t Crc::crc16(uint8_t* bytes, uint32_t length) { uint16_t crc = 0x0000; const uint16_t polynomial = 0x1021; - for (auto i = 0; i < length; i++) { + for (auto i = 0ul; i < length; i++) { const auto byte = bytes[i]; for (auto bitidx = 0; bitidx < 8; bitidx++) { const auto bit = ((byte >> (7 - bitidx) & 1) == 1); @@ -32,17 +31,12 @@ uint16_t Crc::crc16(uint8_t* bytes, uint32_t length) { return crc & 0xffff; } -uint32_t Crc::crc32(const Data& data) -{ - boost::crc_32_type result; - result.process_bytes((const void*)data.data(), data.size()); - return (uint32_t)result.checksum(); -} - -uint32_t Crc::crc32C(const Data& data) -{ - using crc_32c_type = boost::crc_optimal<32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true>; - crc_32c_type result; - result.process_bytes((const void*)data.data(), data.size()); - return (uint32_t)result.checksum(); +// Algorithm inspired by this old-style C implementation: +// https://web.mit.edu/freebsd/head/sys/libkern/crc32.c (Public Domain code) +uint32_t Crc::crc32(const Data& data) { + uint32_t c = std::numeric_limits::max(); + for (const auto byte : data) { + c = crc32_table[(c ^ byte) & 0xFF] ^ (c >> 8); + } + return ~c; } diff --git a/src/Crc.h b/src/Crc.h index 88bb3b28854..1180035491b 100644 --- a/src/Crc.h +++ b/src/Crc.h @@ -17,6 +17,50 @@ uint16_t crc16(uint8_t* bytes, uint32_t length); uint32_t crc32(const TW::Data& data); -uint32_t crc32C(const TW::Data& data); - +// Table taken from https://web.mit.edu/freebsd/head/sys/libkern/crc32.c (Public Domain code) +// This table is used to speed up the crc calculation. +static constexpr uint32_t crc32_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d}; } // namespace TW::Crc diff --git a/src/Data.cpp b/src/Data.cpp index 15a42af6f07..8eea5816ce5 100644 --- a/src/Data.cpp +++ b/src/Data.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,13 +9,18 @@ namespace TW { Data subData(const Data& data, size_t startIndex, size_t length) { - size_t subLength = length; - if (startIndex + subLength > data.size()) { subLength = data.size() - startIndex; } // guard against over-length + if (startIndex >= data.size()) { + return Data(); + } + const size_t subLength = std::min(length, data.size() - startIndex); // guard against over-length return TW::data(data.data() + startIndex, subLength); } Data subData(const Data& data, size_t startIndex) { - size_t subLength = data.size() - startIndex; + if (startIndex >= data.size()) { + return Data(); + } + const size_t subLength = data.size() - startIndex; return TW::data(data.data() + startIndex, subLength); } diff --git a/src/Data.h b/src/Data.h index e7521a92ee6..1c43fdc68b0 100644 --- a/src/Data.h +++ b/src/Data.h @@ -21,11 +21,11 @@ inline void pad_left(Data& data, const uint32_t size) { } inline Data data(const std::string& data) { - return std::vector(data.begin(), data.end()); + return Data(data.begin(), data.end()); } inline Data data(const byte* data, size_t size) { - return std::vector(data, data + size); + return Data(data, data + size); } inline void append(Data& data, const Data& suffix) { diff --git a/src/Decred/Address.cpp b/src/Decred/Address.cpp index 1a7496c7a13..cd3685f9463 100644 --- a/src/Decred/Address.cpp +++ b/src/Decred/Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,19 +7,17 @@ #include "Address.h" #include "../Base58.h" -#include "../Hash.h" #include "../Coin.h" #include -using namespace TW; -using namespace TW::Decred; +namespace TW::Decred { static const auto keyhashSize = Hash::ripemdSize; static const auto addressDataSize = keyhashSize + 2; bool Address::isValid(const std::string& string) noexcept { - const auto data = Base58::bitcoin.decodeCheck(string, Hash::blake256d); + const auto data = Base58::bitcoin.decodeCheck(string, Hash::HasherBlake256d); if (data.size() != addressDataSize) { return false; } @@ -27,12 +25,12 @@ bool Address::isValid(const std::string& string) noexcept { return false; } - return (data[1] == TW::p2pkhPrefix(TWCoinTypeDecred) || + return (data[1] == TW::p2pkhPrefix(TWCoinTypeDecred) || data[1] == TW::p2shPrefix(TWCoinTypeDecred)); } Address::Address(const std::string& string) { - const auto data = Base58::bitcoin.decodeCheck(string, Hash::blake256d); + const auto data = Base58::bitcoin.decodeCheck(string, Hash::HasherBlake256d); if (data.size() != addressDataSize) { throw std::invalid_argument("Invalid address string"); } @@ -51,5 +49,7 @@ Address::Address(const PublicKey& publicKey) { } std::string Address::string() const { - return Base58::bitcoin.encodeCheck(bytes, Hash::blake256d); + return Base58::bitcoin.encodeCheck(bytes, Hash::HasherBlake256d); } + +} // namespace TW::Decred diff --git a/src/Decred/Address.h b/src/Decred/Address.h index d86bfcba2bf..fb534ccffc0 100644 --- a/src/Decred/Address.h +++ b/src/Decred/Address.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/Decred/Entry.cpp b/src/Decred/Entry.cpp index e6ecaf91aae..2bfc7149e39 100644 --- a/src/Decred/Entry.cpp +++ b/src/Decred/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,21 +9,27 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Decred; -using namespace std; +namespace TW::Decred { -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] TW::byte p2sh, [[maybe_unused]] const char* hrp) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] const char* hrp) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Address(address); + return {addr.bytes.begin() + 2, addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -void Entry::plan(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::plan([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { planTemplate(dataIn, dataOut); } + +} // namespace TW::Decred diff --git a/src/Decred/Entry.h b/src/Decred/Entry.h index c840d0d9648..0d21789afa8 100644 --- a/src/Decred/Entry.h +++ b/src/Decred/Entry.h @@ -12,13 +12,13 @@ namespace TW::Decred { /// Decred entry dispatcher. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeDecred}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Decred diff --git a/src/Decred/OutPoint.cpp b/src/Decred/OutPoint.cpp index 27cb836c50a..e33f39badd4 100644 --- a/src/Decred/OutPoint.cpp +++ b/src/Decred/OutPoint.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,10 +8,12 @@ #include "../BinaryCoding.h" -using namespace TW::Decred; +namespace TW::Decred { void OutPoint::encode(Data& data) const { std::copy(std::begin(hash), std::end(hash), std::back_inserter(data)); encode32LE(index, data); data.push_back(static_cast(tree)); } + +} // namespace TW::Decred diff --git a/src/Decred/OutPoint.h b/src/Decred/OutPoint.h index 3ce3b5b169a..315d78e693d 100644 --- a/src/Decred/OutPoint.h +++ b/src/Decred/OutPoint.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../Bitcoin/OutPoint.h" #include "../proto/Bitcoin.pb.h" diff --git a/src/Decred/Signer.cpp b/src/Decred/Signer.cpp index ed02b0ebf48..5cb610c137e 100644 --- a/src/Decred/Signer.cpp +++ b/src/Decred/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,23 +10,18 @@ #include "TransactionOutput.h" #include "../Bitcoin/SigHashType.h" #include "../Bitcoin/SignatureBuilder.h" - #include "../BinaryCoding.h" -#include "../Hash.h" #include "../HexCoding.h" -#include "Bitcoin/OpCodes.h" - -using namespace TW; -using namespace TW::Decred; +namespace TW::Decred { Bitcoin::Proto::TransactionPlan Signer::plan(const Bitcoin::Proto::SigningInput& input) noexcept { - auto signer = Signer(std::move(input)); + auto signer = Signer(input); return signer.txPlan.proto(); } Proto::SigningOutput Signer::sign(const Bitcoin::Proto::SigningInput& input) noexcept { - auto signer = Signer(std::move(input)); + auto signer = Signer(input); auto result = signer.sign(); auto output = Proto::SigningOutput(); if (!result) { @@ -47,18 +42,18 @@ Proto::SigningOutput Signer::sign(const Bitcoin::Proto::SigningInput& input) noe } Result Signer::sign() { - if (txPlan.utxos.size() == 0 || transaction.inputs.size() == 0) { + if (txPlan.utxos.empty() || _transaction.inputs.empty()) { return Result::failure(Common::Proto::Error_missing_input_utxos); } - signedInputs = transaction.inputs; + signedInputs = _transaction.inputs; const auto hashSingle = Bitcoin::hashTypeIsSingle(static_cast(input.hash_type())); - for (auto i = 0; i < txPlan.utxos.size(); i += 1) { + for (auto i = 0ul; i < txPlan.utxos.size(); i += 1) { auto& utxo = txPlan.utxos[i]; // Only sign TWBitcoinSigHashTypeSingle if there's a corresponding output - if (hashSingle && i >= transaction.outputs.size()) { + if (hashSingle && i >= _transaction.outputs.size()) { continue; } auto result = sign(utxo.script, i); @@ -68,14 +63,14 @@ Result Signer::sign() { signedInputs[i].script = result.payload(); } - Transaction tx(transaction); - tx.inputs = move(signedInputs); - tx.outputs = transaction.outputs; + Transaction tx(_transaction); + tx.inputs = std::move(signedInputs); + tx.outputs = _transaction.outputs; return Result::success(std::move(tx)); } Result Signer::sign(Bitcoin::Script script, size_t index) { - assert(index < transaction.inputs.size()); + assert(index < _transaction.inputs.size()); Bitcoin::Script redeemScript; std::vector results; @@ -86,15 +81,15 @@ Result Signer::sign(Bitcoin::Scrip } else { return Result::failure(result.error()); } - auto txin = transaction.inputs[index]; + auto txin = _transaction.inputs[index]; if (script.isPayToScriptHash()) { script = Bitcoin::Script(results.front().begin(), results.front().end()); - auto result = signStep(script, index); - if (!result) { - return Result::failure(result.error()); + auto result_ = signStep(script, index); + if (!result_) { + return Result::failure(result_.error()); } - results = result.payload(); + results = result_.payload(); results.push_back(script.bytes); redeemScript = script; results.push_back(redeemScript.bytes); @@ -104,9 +99,9 @@ Result Signer::sign(Bitcoin::Scrip } Result, Common::Proto::SigningError> Signer::signStep(Bitcoin::Script script, size_t index) { - Transaction transactionToSign(transaction); + Transaction transactionToSign(_transaction); transactionToSign.inputs = signedInputs; - transactionToSign.outputs = transaction.outputs; + transactionToSign.outputs = _transaction.outputs; Data data; std::vector keys; @@ -149,7 +144,7 @@ Result, Common::Proto::SigningError> Signer::signStep(Bitcoin: } else if (script.matchMultisig(keys, required)) { auto results = std::vector{{}}; for (auto& pubKey : keys) { - if (results.size() >= required + 1) { + if (results.size() >= required + 1ul) { break; } auto keyHash = TW::Hash::ripemd(TW::Hash::blake256(pubKey)); @@ -177,7 +172,7 @@ Data Signer::createSignature(const Transaction& transaction, const Bitcoin::Scri const Data& key, size_t index) { auto sighash = transaction.computeSignatureHash(script, index, static_cast(input.hash_type())); auto pk = PrivateKey(key); - auto signature = pk.signAsDER(Data(begin(sighash), end(sighash)), TWCurveSECP256k1); + auto signature = pk.signAsDER(Data(begin(sighash), end(sighash))); if (script.empty()) { return {}; } @@ -206,3 +201,5 @@ Data Signer::scriptForScriptHash(const Data& hash) const { } return Data(it->second.begin(), it->second.end()); } + +} // namespace TW::Decred diff --git a/src/Decred/Signer.h b/src/Decred/Signer.h index 6e299fa65d6..170c5e8b818 100644 --- a/src/Decred/Signer.h +++ b/src/Decred/Signer.h @@ -41,7 +41,7 @@ class Signer { Bitcoin::TransactionPlan txPlan; /// Transaction being signed. - Transaction transaction; + Transaction _transaction; private: /// List of signed inputs. @@ -59,7 +59,7 @@ class Signer { } else { txPlan = TransactionBuilder::plan(input); } - transaction = TransactionBuilder::build(txPlan, input.to_address(), input.change_address()); + _transaction = TransactionBuilder::build(txPlan, input.to_address(), input.change_address()); } /// Signs the transaction. diff --git a/src/Decred/Transaction.cpp b/src/Decred/Transaction.cpp index 7f924d59204..958a843cd49 100644 --- a/src/Decred/Transaction.cpp +++ b/src/Decred/Transaction.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,14 +8,10 @@ #include "../Bitcoin/SigHashType.h" #include "../BinaryCoding.h" -#include "../Hash.h" - -#include "Bitcoin/SignatureVersion.h" #include -using namespace TW; -using namespace TW::Decred; +namespace TW::Decred { namespace { // Indicates the serialization does not include any witness data. @@ -52,7 +48,7 @@ Data Transaction::computeSignatureHash(const Bitcoin::Script& prevOutScript, siz break; case TWBitcoinSigHashTypeSingle: outputsToSign.clear(); - std::copy(outputs.begin(), outputs.begin() + index + 1, outputsToSign.end()); + std::copy(outputs.begin(), outputs.begin() + index + 1, std::back_inserter(outputsToSign)); break; default: // Keep all outputs @@ -86,7 +82,7 @@ Data Transaction::computePrefixHash(const std::vector& inputsT // Commit to the relevant transaction inputs. encodeVarInt(inputsToSign.size(), preimage); - for (auto i = 0; i < inputsToSign.size(); i += 1) { + for (auto i = 0ul; i < inputsToSign.size(); i += 1) { auto& input = inputsToSign[i]; input.previousOutput.encode(preimage); @@ -100,7 +96,7 @@ Data Transaction::computePrefixHash(const std::vector& inputsT // Commit to the relevant transaction outputs. encodeVarInt(outputsToSign.size(), preimage); - for (auto i = 0; i < outputsToSign.size(); i += 1) { + for (auto i = 0ul; i < outputsToSign.size(); i += 1) { auto& output = outputsToSign[i]; auto value = output.value; auto pkScript = output.script; @@ -133,7 +129,7 @@ Data Transaction::computeWitnessHash(const std::vector& inputs // Commit to the relevant transaction inputs. encodeVarInt(inputsToSign.size(), witnessBuf); - for (auto i = 0; i < inputsToSign.size(); i += 1) { + for (auto i = 0ul; i < inputsToSign.size(); i += 1) { if (i == signIndex) { signScript.encode(witnessBuf); } else { @@ -239,3 +235,5 @@ std::size_t sigHashWitnessSize(const std::vector& inputs, signScript.bytes.size(); } } // namespace + +} // namespace TW::Decred diff --git a/src/Decred/Transaction.h b/src/Decred/Transaction.h index ba53dcba98f..ee2a36fc7d2 100644 --- a/src/Decred/Transaction.h +++ b/src/Decred/Transaction.h @@ -11,7 +11,7 @@ #include "TransactionOutput.h" #include "Bitcoin/Transaction.h" #include "Bitcoin/Script.h" -#include "../Data.h" +#include "Data.h" #include "../proto/Decred.pb.h" #include "Bitcoin/SignatureVersion.h" diff --git a/src/Decred/TransactionInput.cpp b/src/Decred/TransactionInput.cpp index 0cf57aba6d8..47308501e2e 100644 --- a/src/Decred/TransactionInput.cpp +++ b/src/Decred/TransactionInput.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,7 +8,7 @@ #include "../BinaryCoding.h" -using namespace TW::Decred; +namespace TW::Decred { void TransactionInput::encode(Data& data) const { previousOutput.encode(data); @@ -21,3 +21,5 @@ void TransactionInput::encodeWitness(Data& data) const { encode32LE(blockIndex, data); script.encode(data); } + +} // namespace TW::Decred diff --git a/src/Decred/TransactionInput.h b/src/Decred/TransactionInput.h index 98e1a2933ac..dde3c075055 100644 --- a/src/Decred/TransactionInput.h +++ b/src/Decred/TransactionInput.h @@ -8,7 +8,7 @@ #include "OutPoint.h" #include "../Bitcoin/Script.h" -#include "../Data.h" +#include "Data.h" #include #include diff --git a/src/Decred/TransactionOutput.cpp b/src/Decred/TransactionOutput.cpp index 9bc46efde7d..1aadfd41d37 100644 --- a/src/Decred/TransactionOutput.cpp +++ b/src/Decred/TransactionOutput.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,10 +8,12 @@ #include "../BinaryCoding.h" -using namespace TW::Decred; +namespace TW::Decred { void TransactionOutput::encode(Data& data) const { encode64LE(value, data); encode16LE(version, data); script.encode(data); } + +} // namespace TW::Decred \ No newline at end of file diff --git a/src/Decred/TransactionOutput.h b/src/Decred/TransactionOutput.h index 7d5cf89b30f..2d9575d4a08 100644 --- a/src/Decred/TransactionOutput.h +++ b/src/Decred/TransactionOutput.h @@ -8,7 +8,7 @@ #include "../Bitcoin/Amount.h" #include "../Bitcoin/Script.h" -#include "../Data.h" +#include "Data.h" namespace TW::Decred { diff --git a/src/Defer.h b/src/Defer.h new file mode 100644 index 00000000000..744eba85655 --- /dev/null +++ b/src/Defer.h @@ -0,0 +1,19 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#ifndef defer // https://stackoverflow.com/a/42060129/411431 + +struct defer_dummy {}; +template struct deferrer { F f; ~deferrer() { f(); } }; +template deferrer operator*(defer_dummy, F f) { return {f}; } + +#define DEFER_(LINE) zz_defer##LINE +#define DEFER(LINE) DEFER_(LINE) +#define defer auto DEFER(__LINE__) = defer_dummy{} *[&]() + +#endif // defer diff --git a/src/DerivationPath.h b/src/DerivationPath.h index be6b6602d07..6ab0bca83ef 100644 --- a/src/DerivationPath.h +++ b/src/DerivationPath.h @@ -21,7 +21,8 @@ struct DerivationPathIndex { bool hardened = true; DerivationPathIndex() = default; - DerivationPathIndex(uint32_t value, bool hardened = true) : value(value), hardened(hardened) {} + DerivationPathIndex(uint32_t value, bool hardened = true) + : value(value), hardened(hardened) {} /// The derivation index. uint32_t derivationIndex() const { @@ -46,63 +47,85 @@ struct DerivationPath { std::vector indices; TWPurpose purpose() const { - if (indices.size() == 0) { return TWPurposeBIP44; } + if (indices.size() == 0) { + return TWPurposeBIP44; + } return static_cast(indices[0].value); } void setPurpose(TWPurpose v) { - if (indices.size() == 0) { return; } + if (indices.size() == 0) { + return; + } indices[0] = DerivationPathIndex(v, /* hardened: */ true); } uint32_t coin() const { - if (indices.size() <= 1) { return TWCoinTypeBitcoin; } + if (indices.size() <= 1) { + return TWCoinTypeBitcoin; + } return indices[1].value; } void setCoin(uint32_t v) { - if (indices.size() <= 1) { return; } + if (indices.size() <= 1) { + return; + } indices[1] = DerivationPathIndex(v, /* hardened: */ true); } uint32_t account() const { - if (indices.size() <= 2) { return 0; } + if (indices.size() <= 2) { + return 0; + } return indices[2].value; } void setAccount(uint32_t v) { - if (indices.size() <= 2) { return; } + if (indices.size() <= 2) { + return; + } indices[2] = DerivationPathIndex(v, /* hardened: */ true); } uint32_t change() const { - if (indices.size() <= 3) { return 0; } + if (indices.size() <= 3) { + return 0; + } return indices[3].value; } void setChange(uint32_t v) { - if (indices.size() <= 3) { return; } + if (indices.size() <= 3) { + return; + } indices[3] = DerivationPathIndex(v, /* hardened: */ false); } uint32_t address() const { - if (indices.size() <= 4) { return 0; } + if (indices.size() <= 4) { + return 0; + } return indices[4].value; } void setAddress(uint32_t v) { - if (indices.size() <= 4) { return; } + if (indices.size() <= 4) { + return; + } indices[4] = DerivationPathIndex(v, /* hardened: */ false); } DerivationPath() = default; - explicit DerivationPath(std::initializer_list l) : indices(l) {} - explicit DerivationPath(std::vector indices) : indices(std::move(indices)) {} + explicit DerivationPath(std::initializer_list l) + : indices(l) {} + explicit DerivationPath(std::vector indices) + : indices(std::move(indices)) {} /// Creates a `DerivationPath` by BIP44 components. DerivationPath(TWPurpose purpose, uint32_t coin, uint32_t account, uint32_t change, - uint32_t address) - : indices(std::vector(5)) { + uint32_t address) + : indices(std::vector(5)) { setPurpose(purpose); setCoin(coin); setAccount(account); @@ -112,7 +135,7 @@ struct DerivationPath { /// Creates a derivation path with a string description like `m/10/0/2'/3` /// - /// @throws std::invalid_argument if the string is not a valid derivation + /// \throws std::invalid_argument if the string is not a valid derivation /// path. explicit DerivationPath(const std::string& string); @@ -130,3 +153,12 @@ inline bool operator==(const DerivationPath& lhs, const DerivationPath& rhs) { } } // namespace TW + +/// Wrapper for C interface. +struct TWDerivationPath { + TW::DerivationPath impl; +}; + +struct TWDerivationPathIndex { + TW::DerivationPathIndex impl; +}; diff --git a/src/EOS/Action.cpp b/src/EOS/Action.cpp index dae38bbabf7..e47c40f2fa2 100644 --- a/src/EOS/Action.cpp +++ b/src/EOS/Action.cpp @@ -6,11 +6,8 @@ #include "Action.h" #include "../HexCoding.h" -#include "../EOS/Serialization.h" -using namespace TW; -using namespace TW::EOS; -using json = nlohmann::json; +namespace TW::EOS { void PermissionLevel::serialize(Data& o) const { actor.serialize(o); @@ -41,11 +38,11 @@ json Action::serialize() const noexcept { return obj; } -TransferAction::TransferAction( const std::string& currency, - const std::string& from, - const std::string& to, - const Asset& asset, - const std::string& memo) { +TransferAction::TransferAction(const std::string& currency, + const std::string& from, + const std::string& to, + const Asset& asset, + const std::string& memo) { account = Name(currency); name = Name("transfer"); authorization.emplace_back(PermissionLevel(Name(from), Name("active"))); @@ -63,3 +60,5 @@ void TransferAction::setData(const std::string& from, const std::string& to, con asset.serialize(data); encodeString(memo, data); } + +} // namespace TW::EOS diff --git a/src/EOS/Action.h b/src/EOS/Action.h index cd55cb47fb5..c2ebcb6fa8f 100644 --- a/src/EOS/Action.h +++ b/src/EOS/Action.h @@ -12,8 +12,6 @@ #include #include -using Data = TW::Data; - namespace TW::EOS { class PermissionLevel { diff --git a/src/EOS/Address.cpp b/src/EOS/Address.cpp index 7d8d1a6d8fa..db97a6d7853 100644 --- a/src/EOS/Address.cpp +++ b/src/EOS/Address.cpp @@ -4,16 +4,15 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#include "Address.h" #include "../Base58.h" #include "../BinaryCoding.h" -#include "Address.h" #include #include -using namespace TW; -using namespace TW::EOS; +namespace TW::EOS { bool Address::isValid(const std::string& string) { return extractKeyData(string); @@ -22,11 +21,15 @@ bool Address::isValid(const std::string& string) { /// Determines whether the given byte vector is a valid keyBuffer /// Verifies the buffer's size and it's checksum bytes bool Address::isValid(const Data& bytes, EOS::Type type) { - if (bytes.size() != KeyDataSize) return false; + if (bytes.size() != KeyDataSize) { + return false; + } // last Address::ChecksumSize bytes are a checksum uint32_t checksum = decode32LE(bytes.data() + PublicKeyDataSize); - if (createChecksum(bytes, type) != checksum) return false; + if (createChecksum(bytes, type) != checksum) { + return false; + } return true; } @@ -48,15 +51,15 @@ uint32_t Address::createChecksum(const Data& bytes, Type type) { break; case Type::ModernK1: - ripemd160_Update(&ctx, - (const uint8_t *) Modern::K1::prefix.c_str(), - static_cast(Modern::K1::prefix.size())); + ripemd160_Update(&ctx, + (const uint8_t*)Modern::K1::prefix.c_str(), + static_cast(Modern::K1::prefix.size())); break; case Type::ModernR1: - ripemd160_Update(&ctx, - (const uint8_t *) Modern::R1::prefix.c_str(), - static_cast(Modern::R1::prefix.size())); + ripemd160_Update(&ctx, + (const uint8_t*)Modern::R1::prefix.c_str(), + static_cast(Modern::R1::prefix.size())); break; } @@ -67,9 +70,9 @@ uint32_t Address::createChecksum(const Data& bytes, Type type) { } /// Extracts and verifies the key data from a base58 string. -/// If the second arg is provided, the keyData and isTestNet +/// If the second arg is provided, the keyData and isTestNet /// properties of that object are set from the extracted data. -bool Address::extractKeyData(const std::string& string, Address *address) { +bool Address::extractKeyData(const std::string& string, Address* address) { // verify if the string has one of the valid prefixes Type type; size_t prefixSize; @@ -111,14 +114,16 @@ Address::Address(const std::string& string) { } /// Initializes a EOS address from raw bytes -Address::Address(const Data& data, Type type) : keyData(data), type(type) { +Address::Address(const Data& data, Type type) + : keyData(data), type(type) { if (!isValid(data, type)) { throw std::invalid_argument("Invalid byte size!"); } } /// Initializes a EOS address from a public key. -Address::Address(const PublicKey& publicKey, Type type) : type(type) { +Address::Address(const PublicKey& publicKey, Type type) + : type(type) { assert(PublicKeyDataSize == TW::PublicKey::secp256k1Size); // copy the raw, compressed key data @@ -136,3 +141,5 @@ Address::Address(const PublicKey& publicKey, Type type) : type(type) { std::string Address::string() const { return prefix() + Base58::bitcoin.encode(keyData); } + +} // namespace TW::EOS diff --git a/src/EOS/Address.h b/src/EOS/Address.h index f0484c046c1..0749b8244b7 100644 --- a/src/EOS/Address.h +++ b/src/EOS/Address.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include "Prefixes.h" diff --git a/src/EOS/Asset.cpp b/src/EOS/Asset.cpp index 17d03be7976..e2f246315e1 100644 --- a/src/EOS/Asset.cpp +++ b/src/EOS/Asset.cpp @@ -10,7 +10,7 @@ #include #include -using namespace TW::EOS; +namespace TW::EOS { static const int64_t Precision = 1000; static const uint8_t MaxDecimals = 18; @@ -21,12 +21,12 @@ Asset::Asset(int64_t amount, uint8_t decimals, const std::string& symbol) { } this->symbol |= decimals; - if (symbol.size() < 1 || symbol.size() > 7) { + if (symbol.empty() || symbol.size() > 7) { throw std::invalid_argument("Symbol size invalid!"); } - for (int i = 0; i < symbol.size(); i++) { - uint64_t c = symbol[i]; + for (std::size_t i = 0; i < symbol.size(); i++) { + uint64_t c = (unsigned char) symbol[i]; if (c < 'A' || c > 'Z') { throw std::invalid_argument("Invalid symbol " + symbol + ".\n Symbol can only have upper case alphabets!"); } @@ -61,8 +61,8 @@ Asset Asset::fromString(std::string assetString) { if (dotPosition != string::npos) { decimals = static_cast(amountString.size() - dotPosition - 1); } - - int64_t precision = static_cast(pow(10, static_cast(decimals))); + + auto precision = static_cast(pow(10, static_cast(decimals))); // Parse amount int64_t intPart, fractPart = 0; @@ -110,10 +110,10 @@ std::string Asset::string() const { auto decimals = getDecimals(); - int charsWritten = snprintf(buffer, maxBufferSize, "%.*f %s", - decimals, - static_cast(amount) / Precision, - getSymbol().c_str()); + int charsWritten = snprintf(buffer, maxBufferSize, "%.*f %s", + decimals, + static_cast(amount) / Precision, + getSymbol().c_str()); if (charsWritten < 0 || charsWritten > maxBufferSize) { throw std::runtime_error("Failed to create string representation of asset!"); @@ -133,3 +133,5 @@ std::string Asset::getSymbol() const noexcept { return str; } + +} // namespace TW::EOS diff --git a/src/EOS/Entry.cpp b/src/EOS/Entry.cpp index 37de7a06c4c..fa3af508bb4 100644 --- a/src/EOS/Entry.cpp +++ b/src/EOS/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,19 +9,20 @@ #include "Address.h" #include "Signer.h" -using namespace TW::EOS; -using namespace std; +namespace TW::EOS { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} diff --git a/src/EOS/Entry.h b/src/EOS/Entry.h index f6e9f4fcd56..63823c8c020 100644 --- a/src/EOS/Entry.h +++ b/src/EOS/Entry.h @@ -12,12 +12,11 @@ namespace TW::EOS { /// Entry point for implementation of EOS coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeEOS}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::EOS diff --git a/src/EOS/Name.cpp b/src/EOS/Name.cpp index 07ec3a131e9..9cb6805bf87 100644 --- a/src/EOS/Name.cpp +++ b/src/EOS/Name.cpp @@ -10,15 +10,14 @@ #include #include -using namespace TW; -using namespace TW::EOS; +namespace TW::EOS { Name::Name(const std::string& str) { if (str.size() > 13) { throw std::invalid_argument(str + ": size too long!"); } - int i = 0; + std::size_t i = 0; while (i < std::min(size_t(12), str.size())) { value |= (toSymbol(str[i]) & 0x1f) << (64 - (5 * (i + 1))); i++; @@ -28,7 +27,7 @@ Name::Name(const std::string& str) { value |= (toSymbol(str[i]) & 0x0f); } -uint64_t Name::toSymbol(char c) const noexcept { +uint64_t Name::toSymbol(char c) noexcept { if (c >= 'a' && c <= 'z') return c - 'a' + 6; @@ -41,22 +40,24 @@ uint64_t Name::toSymbol(char c) const noexcept { std::string Name::string() const noexcept { static const char* charMap = ".12345abcdefghijklmnopqrstuvwxyz"; - std::string str(13,'.'); + std::string str(13, '.'); uint64_t tmp = value; str[12] = charMap[tmp & 0x0f]; tmp >>= 4; - for( uint32_t i = 1; i <= 12; ++i ) { + for (uint32_t i = 1; i <= 12; ++i) { char c = charMap[tmp & 0x1f]; - str[12-i] = c; + str[12 - i] = c; tmp >>= 5; } - boost::algorithm::trim_right_if( str, []( char c ){ return c == '.'; } ); + boost::algorithm::trim_right_if(str, [](char c) { return c == '.'; }); return str; } -void Name::serialize(Data& o) const noexcept { +void Name::serialize(Data& o) const noexcept { encode64LE(value, o); -} \ No newline at end of file +} + +} // namespace TW::EOS diff --git a/src/EOS/Name.h b/src/EOS/Name.h index 3d3d3721a47..6fb75b9e7e4 100644 --- a/src/EOS/Name.h +++ b/src/EOS/Name.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" namespace TW::EOS { @@ -14,9 +14,9 @@ class Name { public: uint64_t value = 0; - Name() { } + Name() = default; Name(const std::string& str); - uint64_t toSymbol(char c) const noexcept; + static uint64_t toSymbol(char c) noexcept; std::string string() const noexcept; void serialize(TW::Data& o) const noexcept; diff --git a/src/EOS/PackedTransaction.cpp b/src/EOS/PackedTransaction.cpp index 56708474227..a1d3e74ea3d 100644 --- a/src/EOS/PackedTransaction.cpp +++ b/src/EOS/PackedTransaction.cpp @@ -8,15 +8,14 @@ #include "../HexCoding.h" -using namespace TW; -using namespace TW::EOS; -using json = nlohmann::json; +namespace TW::EOS { -PackedTransaction::PackedTransaction(const Transaction& transaction, CompressionType type) noexcept : compression(type) { +PackedTransaction::PackedTransaction(const Transaction& transaction, CompressionType type) noexcept + : compression(type) { transaction.serialize(packedTrx); const Data& cfd = transaction.contextFreeData; - if (cfd.size()) { + if (!cfd.empty()) { packedCFD.push_back(1); encodeVarInt64(cfd.size(), packedCFD); append(packedCFD, cfd); @@ -49,4 +48,6 @@ json PackedTransaction::serialize() const noexcept { obj["packed_trx"] = hex(packedTrx); return obj; -} \ No newline at end of file +} + +} // namespace TW::EOS diff --git a/src/EOS/Serialization.h b/src/EOS/Serialization.h index af6f3820fa4..f6aa5c83680 100644 --- a/src/EOS/Serialization.h +++ b/src/EOS/Serialization.h @@ -2,7 +2,7 @@ #include -#include "../Data.h" +#include "Data.h" #include "../BinaryCoding.h" #include diff --git a/src/EOS/Signer.cpp b/src/EOS/Signer.cpp index c268761018b..b01ba0051e9 100644 --- a/src/EOS/Signer.cpp +++ b/src/EOS/Signer.cpp @@ -7,21 +7,16 @@ #include "Signer.h" #include "Asset.h" #include "PackedTransaction.h" -#include "../proto/Common.pb.h" -#include "../HexCoding.h" #include -#include -#include -using namespace TW; -using namespace TW::EOS; +namespace TW::EOS { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { Proto::SigningOutput output; try { // create an asset object - auto assetData = input.asset(); + const auto& assetData = input.asset(); auto asset = Asset(assetData.amount(), static_cast(assetData.decimals()), assetData.symbol()); @@ -31,7 +26,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { // create a Transaction and add the transfer action auto tx = Transaction(Data(input.reference_block_id().begin(), input.reference_block_id().end()), - input.reference_block_time()); + input.reference_block_time()); tx.actions.push_back(action); // get key type @@ -93,7 +88,7 @@ TW::Data Signer::hash(const Transaction& transaction) const noexcept { transaction.serialize(hashInput); Data cfdHash(Hash::sha256Size); // default value for empty cfd - if (transaction.contextFreeData.size()) { + if (!transaction.contextFreeData.empty()) { cfdHash = Hash::sha256(transaction.contextFreeData); } @@ -102,9 +97,13 @@ TW::Data Signer::hash(const Transaction& transaction) const noexcept { } // canonical check for EOS -int Signer::isCanonical(uint8_t by, uint8_t sig[64]) { +int Signer::isCanonical([[maybe_unused]] uint8_t by, uint8_t sig[64]) { + // clang-format off return !(sig[0] & 0x80) && !(sig[0] == 0 && !(sig[1] & 0x80)) && !(sig[32] & 0x80) && !(sig[32] == 0 && !(sig[33] & 0x80)); + // clang-format on } + +} // namespace TW::EOS diff --git a/src/EOS/Signer.h b/src/EOS/Signer.h index 1aee6d97364..95832639489 100644 --- a/src/EOS/Signer.h +++ b/src/EOS/Signer.h @@ -8,7 +8,7 @@ #include "Prefixes.h" #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/EOS.pb.h" diff --git a/src/EOS/Transaction.cpp b/src/EOS/Transaction.cpp index e7ae58700aa..72c2791b460 100644 --- a/src/EOS/Transaction.cpp +++ b/src/EOS/Transaction.cpp @@ -5,7 +5,6 @@ // file LICENSE at the root of the source code distribution tree. #include "../Base58.h" -#include "../Hash.h" #include "../HexCoding.h" #include "Transaction.h" @@ -13,12 +12,12 @@ #include #include +#include -using namespace TW; -using namespace TW::EOS; -using json = nlohmann::json; +namespace TW::EOS { -Signature::Signature(const Data& sig, Type type) : data(sig), type(type) { +Signature::Signature(const Data& sig, Type type) + : data(sig), type(type) { if (sig.size() != DataSize) { throw std::invalid_argument("Invalid signature size!"); } @@ -54,7 +53,7 @@ std::string Signature::string() const noexcept { // drop the subPrefix and append the checksum to the bufer buffer.resize(DataSize); - for(size_t i = 0; i < ChecksumSize; i++) { + for (size_t i = 0; i < ChecksumSize; i++) { buffer.push_back(hash[i]); } @@ -85,7 +84,7 @@ void Transaction::setReferenceBlock(const Data& refBlockId) { refBlockPrefix = decode32LE(refBlockId.data() + 8); } -void Transaction::serialize(Data& os) const noexcept{ +void Transaction::serialize(Data& os) const noexcept { encode32LE(expiration, os); encode16LE(refBlockNumber, os); @@ -99,14 +98,19 @@ void Transaction::serialize(Data& os) const noexcept{ encodeCollection(transactionExtensions, os); } -json Transaction::serialize() const { +std::string Transaction::formatDate(int32_t date) { + // format is "2022-06-02T08:53:20", always 19 characters long + constexpr size_t DateSize = 19; + constexpr size_t BufferSize = DateSize + 1; + char formattedDate[BufferSize]; + auto time = static_cast(date); + const size_t len = strftime(formattedDate, BufferSize, "%FT%T", std::gmtime(&time)); + assert(len == DateSize); + return {formattedDate, len}; +} - // get a formatted date - char formattedDate[20]; - time_t time = expiration; - if (strftime(formattedDate, 19, "%FT%T", std::gmtime(&time)) != 19) { - std::runtime_error("Error creating a formatted string!"); - } +json Transaction::serialize() const { + const auto expirationDateFormatted = formatDate(expiration); // create a json array of signatures json sigs = json::array(); @@ -118,7 +122,7 @@ json Transaction::serialize() const { json obj; obj["ref_block_num"] = refBlockNumber; obj["ref_block_prefix"] = refBlockPrefix; - obj["expiration"] = std::string(formattedDate, 19); + obj["expiration"] = expirationDateFormatted; obj["max_net_usage_words"] = maxNetUsageWords; obj["max_cpu_usage_ms"] = maxCPUUsageInMS; obj["delay_sec"] = delaySeconds; @@ -130,3 +134,5 @@ json Transaction::serialize() const { return obj; } + +} // namespace TW::EOS diff --git a/src/EOS/Transaction.h b/src/EOS/Transaction.h index 5d6660c462a..63a93edad09 100644 --- a/src/EOS/Transaction.h +++ b/src/EOS/Transaction.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "Action.h" #include "Prefixes.h" @@ -68,5 +68,7 @@ class Transaction { void setReferenceBlock(const Data& referenceBlockId); static const int32_t ExpirySeconds = 30; + /// Get formatted date + static std::string formatDate(int32_t date); }; } // namespace TW::EOS \ No newline at end of file diff --git a/src/Elrond/Address.cpp b/src/Elrond/Address.cpp index 1af84ace200..581f95b1e9e 100644 --- a/src/Elrond/Address.cpp +++ b/src/Elrond/Address.cpp @@ -8,10 +8,12 @@ #include "Address.h" -using namespace TW::Elrond; +namespace TW::Elrond { const std::string Address::hrp = HRP_ELROND; bool Address::isValid(const std::string& string) { return Bech32Address::isValid(string, hrp); } + +} // namespace TW::Elrond diff --git a/src/Elrond/Address.h b/src/Elrond/Address.h index 1f96edc6700..a638b4af8bd 100644 --- a/src/Elrond/Address.h +++ b/src/Elrond/Address.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include "../Bech32Address.h" @@ -16,7 +16,7 @@ namespace TW::Elrond { class Address : public Bech32Address { public: - // The human-readable part of the address, as defined in "coins.json" + /// The human-readable part of the address, as defined in "registry.json" static const std::string hrp; // HRP_ELROND /// Determines whether a string makes a valid address. diff --git a/src/Elrond/Codec.cpp b/src/Elrond/Codec.cpp new file mode 100644 index 00000000000..dbdf0859291 --- /dev/null +++ b/src/Elrond/Codec.cpp @@ -0,0 +1,45 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Codec.h" + +#include "HexCoding.h" +#include "uint256.h" + +namespace TW::Elrond { + +std::string Codec::encodeString(const std::string& value) { + std::string encoded = hex(TW::data(value)); + return encoded; +} + +std::string Codec::encodeUint64(uint64_t value) { + std::string encoded = hex(store(uint256_t(value))); + return encoded; +} + +std::string Codec::encodeBigInt(const std::string& value) { + return encodeBigInt(uint256_t(value)); +} + +// For reference, see https://docs.elrond.com/developers/developer-reference/elrond-serialization-format/#arbitrary-width-big-numbers. +std::string Codec::encodeBigInt(uint256_t value) { + std::string encoded = hex(store(value)); + return encoded; +} + +std::string Codec::encodeAddress(const std::string& bech32Address) { + Address address; + Address::decode(bech32Address, address); + return encodeAddress(address); +} + +std::string Codec::encodeAddress(const Address& address) { + std::string encoded = hex(address.getKeyHash()); + return encoded; +} + +} // namespace TW::Elrond diff --git a/src/Elrond/Codec.h b/src/Elrond/Codec.h new file mode 100644 index 00000000000..b2ffd73405b --- /dev/null +++ b/src/Elrond/Codec.h @@ -0,0 +1,29 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Address.h" +#include "uint256.h" + +namespace TW::Elrond { + +/// A stripped-down variant of the Elrond codec. +/// For reference, see: +/// - https://docs.elrond.com/developers/developer-reference/elrond-serialization-format +/// - https://github.com/ElrondNetwork/elrond-sdk-erdjs/tree/main/src/smartcontracts/codec +/// - https://github.com/ElrondNetwork/elrond-wasm-rs/tree/master/elrond-codec +class Codec { +public: + static std::string encodeString(const std::string& value); + static std::string encodeUint64(uint64_t value); + static std::string encodeBigInt(const std::string& value); + static std::string encodeBigInt(TW::uint256_t value); + static std::string encodeAddress(const std::string& bech32Address); + static std::string encodeAddress(const Address& address); +}; + +} // namespace diff --git a/src/Elrond/Entry.cpp b/src/Elrond/Entry.cpp index 76b806ff128..2535f0bec96 100644 --- a/src/Elrond/Entry.cpp +++ b/src/Elrond/Entry.cpp @@ -9,23 +9,34 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Elrond; +using namespace TW; using namespace std; +namespace TW::Elrond { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + Address addr; + if (!Elrond::Address::decode(address, addr)) { + return Data(); + } + return addr.getKeyHash(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +} // namespace TW::Elrond diff --git a/src/Elrond/Entry.h b/src/Elrond/Entry.h index d0cc67d631e..e0c4ed7b0a1 100644 --- a/src/Elrond/Entry.h +++ b/src/Elrond/Entry.h @@ -12,14 +12,14 @@ namespace TW::Elrond { /// Entry point for implementation of Elrond coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final: public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeElrond}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Elrond diff --git a/src/Elrond/GasEstimator.cpp b/src/Elrond/GasEstimator.cpp new file mode 100644 index 00000000000..6db7cbc9580 --- /dev/null +++ b/src/Elrond/GasEstimator.cpp @@ -0,0 +1,50 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "GasEstimator.h" + +namespace TW::Elrond { + +// Additional gas to account for eventual increases in gas requirements (thus avoid breaking changes in clients of TW-core). +const uint64_t ADDITIONAL_GAS_FOR_ESDT_TRANSFER = 100000; + +// Additional gas to account for extra blockchain operations (e.g. data movement (between accounts) for NFTs), +// and for eventual increases in gas requirements (thus avoid breaking changes in clients of TW-core). +const uint64_t ADDITIONAL_GAS_FOR_ESDT_NFT_TRANSFER = 500000; + +GasEstimator::GasEstimator(const NetworkConfig& networkConfig) { + this->networkConfig = networkConfig; +} + +uint64_t GasEstimator::forEGLDTransfer(size_t dataLength) const { + uint64_t gasLimit = + this->networkConfig.getMinGasLimit() + + this->networkConfig.getGasPerDataByte() * dataLength; + + return gasLimit; +} + +uint64_t GasEstimator::forESDTTransfer(size_t dataLength) const { + uint64_t gasLimit = + this->networkConfig.getMinGasLimit() + + this->networkConfig.getGasCostESDTTransfer() + + this->networkConfig.getGasPerDataByte() * dataLength + + ADDITIONAL_GAS_FOR_ESDT_TRANSFER; + + return gasLimit; +} + +uint64_t GasEstimator::forESDTNFTTransfer(size_t dataLength) const { + uint64_t gasLimit = + this->networkConfig.getMinGasLimit() + + this->networkConfig.getGasCostESDTNFTTransfer() + + this->networkConfig.getGasPerDataByte() * dataLength + + ADDITIONAL_GAS_FOR_ESDT_NFT_TRANSFER; + + return gasLimit; +} + +} // namespace TW::Elrond diff --git a/src/Elrond/GasEstimator.h b/src/Elrond/GasEstimator.h new file mode 100644 index 00000000000..4c14ed434d6 --- /dev/null +++ b/src/Elrond/GasEstimator.h @@ -0,0 +1,24 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include "NetworkConfig.h" + +namespace TW::Elrond { + +class GasEstimator { + NetworkConfig networkConfig; +public: + GasEstimator(const NetworkConfig& networkConfig); + + uint64_t forEGLDTransfer(size_t dataLength) const; + uint64_t forESDTTransfer(size_t dataLength) const; + uint64_t forESDTNFTTransfer(size_t dataLength) const; +}; + +} // namespace diff --git a/src/Elrond/NetworkConfig.cpp b/src/Elrond/NetworkConfig.cpp new file mode 100644 index 00000000000..ba514e9183f --- /dev/null +++ b/src/Elrond/NetworkConfig.cpp @@ -0,0 +1,85 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "NetworkConfig.h" + +#include + +namespace TW::Elrond { + +NetworkConfig::NetworkConfig() + : chainId("1") /* Mainnet */ { +} + +const std::string& NetworkConfig::getChainId() const { + return this->chainId; +} + +void NetworkConfig::setChainId(const std::string& value) { + this->chainId = value; +} + +uint32_t NetworkConfig::getGasPerDataByte() const { + return this->gasPerDataByte; +} + +void NetworkConfig::setGasPerDataByte(uint32_t value) { + this->gasPerDataByte = value; +} + +uint32_t NetworkConfig::getMinGasLimit() const { + return this->minGasLimit; +} + +void NetworkConfig::setMinGasLimit(uint32_t value) { + this->minGasLimit = value; +} + +uint64_t NetworkConfig::getMinGasPrice() const { + return this->minGasPrice; +} + +void NetworkConfig::setMinGasPrice(uint64_t value) { + this->minGasPrice = value; +} + +uint32_t NetworkConfig::getGasCostESDTTransfer() const { + return this->gasCostESDTTransfer; +} + +void NetworkConfig::setGasCostESDTTransfer(uint32_t value) { + this->gasCostESDTTransfer = value; +} + +uint32_t NetworkConfig::getGasCostESDTNFTTransfer() const { + return this->gasCostESDTNFTTransfer; +} + +void NetworkConfig::setGasCostESDTNFTTransfer(uint32_t value) { + this->gasCostESDTNFTTransfer = value; +} + +NetworkConfig NetworkConfig::GetDefault() { + const uint64_t timestamp = duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + return GetByTimestamp(timestamp); +} + +NetworkConfig NetworkConfig::GetByTimestamp(uint64_t timestamp) { + NetworkConfig networkConfig; + + // Mainnet values at the time of defining the "NetworkConfig" component (December 2021). + if (timestamp > 0) { + networkConfig.setGasPerDataByte(1500); + networkConfig.setMinGasLimit(50000); + networkConfig.setMinGasPrice(1000000000); + networkConfig.setGasCostESDTTransfer(200000); + networkConfig.setGasCostESDTNFTTransfer(200000); + } + + return networkConfig; +} + +} // namespace TW::Elrond diff --git a/src/Elrond/NetworkConfig.h b/src/Elrond/NetworkConfig.h new file mode 100644 index 00000000000..79b5ebf61d0 --- /dev/null +++ b/src/Elrond/NetworkConfig.h @@ -0,0 +1,54 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include + +namespace TW::Elrond { + +/// A "NetworkConfig" object holds the network parameters relevant to creating transactions (e.g. minimum gas limit, minimum gas price). +class NetworkConfig { + /// The following fields can (should) be fetched from https://api.elrond.com/network/config. + /// However, a "NetworkConfig" object is initialized with proper default values for Elrond Mainnet (as of December 2021). + std::string chainId; + uint32_t gasPerDataByte; + uint32_t minGasLimit; + uint64_t minGasPrice; + + /// GasSchedule entries of interest (only one at this moment), according to: https://github.com/ElrondNetwork/elrond-config-mainnet/blob/master/gasSchedules. + /// Here, for the sake of simplicity, we define the gas costs of interest directly in the class "NetworkConfig" + /// (that is, without defining extra nested structures such as "GasSchedule" and "BuiltInCosts"). + uint32_t gasCostESDTTransfer; + uint32_t gasCostESDTNFTTransfer; +public: + NetworkConfig(); + + const std::string& getChainId() const; + void setChainId(const std::string& value); + + uint32_t getGasPerDataByte() const; + void setGasPerDataByte(uint32_t value); + + uint32_t getMinGasLimit() const; + void setMinGasLimit(uint32_t value); + + uint64_t getMinGasPrice() const; + void setMinGasPrice(uint64_t value); + + uint32_t getGasCostESDTTransfer() const; + void setGasCostESDTTransfer(uint32_t value); + + uint32_t getGasCostESDTNFTTransfer() const; + void setGasCostESDTNFTTransfer(uint32_t value); + + static NetworkConfig GetDefault(); + + /// Useful to implement upwards-compatible changes of the network configuration (a TWCore client can receive planned configuration updates, in advance). + static NetworkConfig GetByTimestamp(uint64_t timestamp); +}; + +} // namespace diff --git a/src/Elrond/Serialization.cpp b/src/Elrond/Serialization.cpp index eaccaabf417..ffe2241bd08 100644 --- a/src/Elrond/Serialization.cpp +++ b/src/Elrond/Serialization.cpp @@ -6,63 +6,76 @@ #include "Serialization.h" -#include "../Elrond/Address.h" -#include "../proto/Elrond.pb.h" +#include "Address.h" #include "Base64.h" -#include "PrivateKey.h" using namespace TW; -std::map fields_order { +std::map fields_order{ {"nonce", 1}, {"value", 2}, {"receiver", 3}, {"sender", 4}, - {"gasPrice", 5}, - {"gasLimit", 6}, - {"data", 7}, - {"chainID", 8}, - {"version", 9}, - {"signature", 10} -}; + {"senderUsername", 5}, + {"receiverUsername", 6}, + {"gasPrice", 7}, + {"gasLimit", 8}, + {"data", 9}, + {"chainID", 10}, + {"version", 11}, + {"options", 12}, + {"signature", 13}}; struct FieldsSorter { - bool operator() (const string& lhs, const string& rhs) const { - return fields_order[lhs] < fields_order[rhs]; + bool operator()(const std::string& lhs, const std::string& rhs) const { + return fields_order[lhs] < fields_order[rhs]; } }; -template +template using sorted_map = std::map; using sorted_json = nlohmann::basic_json; -sorted_json preparePayload(const Elrond::Proto::TransactionMessage& message) { - sorted_json payload { - {"nonce", json(message.nonce())}, - {"value", json(message.value())}, - {"receiver", json(message.receiver())}, - {"sender", json(message.sender())}, - {"gasPrice", json(message.gas_price())}, - {"gasLimit", json(message.gas_limit())}, +sorted_json preparePayload(const Elrond::Transaction& transaction) { + using namespace nlohmann; + sorted_json payload{ + {"nonce", json(transaction.nonce)}, + {"value", json(transaction.value)}, + {"receiver", json(transaction.receiver)}, + {"sender", json(transaction.sender)}, + {"gasPrice", json(transaction.gasPrice)}, + {"gasLimit", json(transaction.gasLimit)}, }; - if (!message.data().empty()) { - payload["data"] = json(TW::Base64::encode(TW::data(message.data()))); + if (!transaction.senderUsername.empty()) { + payload["senderUsername"] = json(Base64::encode(data(transaction.senderUsername))); + } + + if (!transaction.receiverUsername.empty()) { + payload["receiverUsername"] = json(Base64::encode(data(transaction.receiverUsername))); } - payload["chainID"] = json(message.chain_id()); - payload["version"] = json(message.version()); + if (!transaction.data.empty()) { + payload["data"] = json(Base64::encode(data(transaction.data))); + } + + payload["chainID"] = json(transaction.chainID); + payload["version"] = json(transaction.version); + + if (transaction.options != 0) { + payload["options"] = json(transaction.options); + } return payload; } -string Elrond::serializeTransaction(const Proto::TransactionMessage& message) { - sorted_json payload = preparePayload(message); +std::string Elrond::serializeTransaction(const Elrond::Transaction& transaction) { + sorted_json payload = preparePayload(transaction); return payload.dump(); } -string Elrond::serializeSignedTransaction(const Proto::TransactionMessage& message, string signature) { - sorted_json payload = preparePayload(message); - payload["signature"] = json(signature); +std::string Elrond::serializeSignedTransaction(const Elrond::Transaction& transaction, std::string signature) { + sorted_json payload = preparePayload(transaction); + payload["signature"] = nlohmann::json(signature); return payload.dump(); } diff --git a/src/Elrond/Serialization.h b/src/Elrond/Serialization.h index e56d8a5bf87..64023d47213 100644 --- a/src/Elrond/Serialization.h +++ b/src/Elrond/Serialization.h @@ -6,16 +6,16 @@ #pragma once -#include "../proto/Elrond.pb.h" #include "Data.h" +#include "Transaction.h" #include +namespace TW::Elrond { + using string = std::string; using json = nlohmann::json; -namespace TW::Elrond { - -string serializeTransaction(const Proto::TransactionMessage& message); -string serializeSignedTransaction(const Proto::TransactionMessage& message, string encodedSignature); +string serializeTransaction(const Transaction& transaction); +string serializeSignedTransaction(const Transaction& transaction, string encodedSignature); -} // namespace +} // namespace TW::Elrond diff --git a/src/Elrond/Signer.cpp b/src/Elrond/Signer.cpp index 20519c8b80a..f26e6c0460c 100644 --- a/src/Elrond/Signer.cpp +++ b/src/Elrond/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,21 +7,23 @@ #include "Signer.h" #include "Address.h" #include "Serialization.h" -#include "../PublicKey.h" +#include "TransactionFactory.h" #include "HexCoding.h" #include -using namespace TW; -using namespace TW::Elrond; +namespace TW::Elrond { -Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { + TransactionFactory factory; + + auto transaction = factory.create(input); auto privateKey = PrivateKey(input.private_key()); - auto signableAsString = serializeTransaction(input.transaction()); + auto signableAsString = serializeTransaction(transaction); auto signableAsData = TW::data(signableAsString); auto signature = privateKey.sign(signableAsData, TWCurveED25519); auto encodedSignature = hex(signature); - auto encoded = serializeSignedTransaction(input.transaction(), encodedSignature); + auto encoded = serializeSignedTransaction(transaction, encodedSignature); auto protoOutput = Proto::SigningOutput(); protoOutput.set_signature(encodedSignature); @@ -36,3 +38,5 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { auto output = sign(input); return output.encoded(); } + +} // namespace TW::Elrond diff --git a/src/Elrond/Signer.h b/src/Elrond/Signer.h index 046f5e52d67..df43bf1295d 100644 --- a/src/Elrond/Signer.h +++ b/src/Elrond/Signer.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/Elrond.pb.h" diff --git a/src/Elrond/Transaction.cpp b/src/Elrond/Transaction.cpp new file mode 100644 index 00000000000..24c8373e9c5 --- /dev/null +++ b/src/Elrond/Transaction.cpp @@ -0,0 +1,16 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Transaction.h" + +namespace TW::Elrond { + +Transaction::Transaction() + : nonce(0), sender(""), senderUsername(""), receiver(""), receiverUsername(""), value("0"), data(""), gasPrice(0), gasLimit(0), chainID(""), version(0), options(0) { +} + +} // namespace TW::Elrond + diff --git a/src/Elrond/Transaction.h b/src/Elrond/Transaction.h new file mode 100644 index 00000000000..a98e7435360 --- /dev/null +++ b/src/Elrond/Transaction.h @@ -0,0 +1,31 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include + +namespace TW::Elrond { + +class Transaction { +public: + uint64_t nonce; + std::string sender; + std::string senderUsername; + std::string receiver; + std::string receiverUsername; + std::string value; + std::string data; + uint64_t gasPrice; + uint64_t gasLimit; + std::string chainID; + uint32_t version; + uint32_t options; + + Transaction(); +}; + +} // namespace diff --git a/src/Elrond/TransactionFactory.cpp b/src/Elrond/TransactionFactory.cpp new file mode 100644 index 00000000000..4e3a271165e --- /dev/null +++ b/src/Elrond/TransactionFactory.cpp @@ -0,0 +1,151 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TransactionFactory.h" + +#include "Codec.h" + +namespace TW::Elrond { + +const int TX_VERSION = 1; + +TransactionFactory::TransactionFactory() + : TransactionFactory(NetworkConfig::GetDefault()) { +} + +TransactionFactory::TransactionFactory(const NetworkConfig& networkConfig) + : networkConfig(networkConfig), gasEstimator(networkConfig) { +} + +Transaction TransactionFactory::create(const Proto::SigningInput& input) { + if (input.has_egld_transfer()) { + return fromEGLDTransfer(input); + } else if (input.has_esdt_transfer()) { + return fromESDTTransfer(input); + } else if (input.has_esdtnft_transfer()) { + return fromESDTNFTTransfer(input); + } else { + return fromGenericAction(input); + } +} + +/// Copies the input fields into a transaction object, without any other logic. +Transaction TransactionFactory::fromGenericAction(const Proto::SigningInput& input) { + auto action = input.generic_action(); + + Transaction transaction; + transaction.nonce = action.accounts().sender_nonce(); + transaction.sender = action.accounts().sender(); + transaction.senderUsername = action.accounts().sender_username(); + transaction.receiver = action.accounts().receiver(); + transaction.receiverUsername = action.accounts().receiver_username(); + transaction.value = action.value(); + transaction.data = action.data(); + transaction.gasLimit = input.gas_limit(); + transaction.gasPrice = input.gas_price(); + transaction.chainID = input.chain_id(); + transaction.version = action.version(); + transaction.options = action.options(); + + return transaction; +} + +Transaction TransactionFactory::fromEGLDTransfer(const Proto::SigningInput& input) { + auto transfer = input.egld_transfer(); + + uint64_t estimatedGasLimit = this->gasEstimator.forEGLDTransfer(0); + + Transaction transaction; + transaction.nonce = transfer.accounts().sender_nonce(); + transaction.sender = transfer.accounts().sender(); + transaction.senderUsername = transfer.accounts().sender_username(); + transaction.receiver = transfer.accounts().receiver(); + transaction.receiverUsername = transfer.accounts().receiver_username(); + transaction.value = transfer.amount(); + transaction.gasLimit = coalesceGasLimit(input.gas_limit(), estimatedGasLimit); + transaction.gasPrice = coalesceGasPrice(input.gas_price()); + transaction.chainID = coalesceChainId(input.chain_id()); + transaction.version = TX_VERSION; + + return transaction; +} + +Transaction TransactionFactory::fromESDTTransfer(const Proto::SigningInput& input) { + auto transfer = input.esdt_transfer(); + + std::string encodedTokenIdentifier = Codec::encodeString(transfer.token_identifier()); + std::string encodedAmount = Codec::encodeBigInt(transfer.amount()); + std::string data = prepareFunctionCall("ESDTTransfer", {encodedTokenIdentifier, encodedAmount}); + uint64_t estimatedGasLimit = this->gasEstimator.forESDTTransfer(data.size()); + + Transaction transaction; + transaction.nonce = transfer.accounts().sender_nonce(); + transaction.sender = transfer.accounts().sender(); + transaction.senderUsername = transfer.accounts().sender_username(); + transaction.receiver = transfer.accounts().receiver(); + transaction.receiverUsername = transfer.accounts().receiver_username(); + transaction.value = "0"; + transaction.data = data; + transaction.gasLimit = coalesceGasLimit(input.gas_limit(), estimatedGasLimit); + transaction.gasPrice = coalesceGasPrice(input.gas_price()); + transaction.chainID = coalesceChainId(input.chain_id()); + transaction.version = TX_VERSION; + + return transaction; +} + +Transaction TransactionFactory::fromESDTNFTTransfer(const Proto::SigningInput& input) { + auto transfer = input.esdtnft_transfer(); + + std::string encodedCollection = Codec::encodeString(transfer.token_collection()); + std::string encodedNonce = Codec::encodeUint64(transfer.token_nonce()); + std::string encodedQuantity = Codec::encodeBigInt(transfer.amount()); + std::string encodedReceiver = Codec::encodeAddress(transfer.accounts().receiver()); + std::string data = prepareFunctionCall("ESDTNFTTransfer", {encodedCollection, encodedNonce, encodedQuantity, encodedReceiver}); + uint64_t estimatedGasLimit = this->gasEstimator.forESDTNFTTransfer(data.size()); + + Transaction transaction; + transaction.nonce = transfer.accounts().sender_nonce(); + // For NFT, SFT and MetaESDT, transaction.sender == transaction.receiver. + transaction.sender = transfer.accounts().sender(); + transaction.receiver = transfer.accounts().sender(); + transaction.value = "0"; + transaction.data = data; + transaction.gasLimit = coalesceGasLimit(input.gas_limit(), estimatedGasLimit); + transaction.gasPrice = coalesceGasPrice(input.gas_price()); + transaction.chainID = coalesceChainId(input.chain_id()); + transaction.version = TX_VERSION; + + return transaction; +} + +uint64_t TransactionFactory::coalesceGasLimit(uint64_t providedGasLimit, uint64_t estimatedGasLimit) { + return providedGasLimit > 0 ? providedGasLimit : estimatedGasLimit; +} + +uint64_t TransactionFactory::coalesceGasPrice(uint64_t gasPrice) { + return gasPrice > 0 ? gasPrice : this->networkConfig.getMinGasPrice(); +} + +std::string TransactionFactory::coalesceChainId(std::string chainID) { + return chainID.empty() ? this->networkConfig.getChainId() : chainID; +} + +std::string TransactionFactory::prepareFunctionCall(const std::string& function, std::initializer_list arguments) { + const auto ARGUMENTS_SEPARATOR = "@"; + std::string result; + + result.append(function); + + for (auto argument : arguments) { + result.append(ARGUMENTS_SEPARATOR); + result.append(argument); + } + + return result; +} + +} // namespace TW::Elrond diff --git a/src/Elrond/TransactionFactory.h b/src/Elrond/TransactionFactory.h new file mode 100644 index 00000000000..db3b3058b9d --- /dev/null +++ b/src/Elrond/TransactionFactory.h @@ -0,0 +1,57 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../proto/Elrond.pb.h" +#include "Address.h" +#include "NetworkConfig.h" +#include "GasEstimator.h" +#include "Transaction.h" +#include "uint256.h" + +namespace TW::Elrond { + +/// Creates specific transaction objects, wrt. the provided "NetworkConfig". +class TransactionFactory { +private: + NetworkConfig networkConfig; + GasEstimator gasEstimator; +public: + TransactionFactory(); + TransactionFactory(const NetworkConfig& networkConfig); + + /// Creates the appropriate transaction object, with respect to the "oneof" field (substructure) of Proto::SigningInput. + Transaction create(const Proto::SigningInput &input); + + Transaction fromGenericAction(const Proto::SigningInput& input); + + /// This should be used to transfer EGLD. + /// For reference, see: https://docs.elrond.com/developers/signing-transactions/signing-transactions/#general-structure. + Transaction fromEGLDTransfer(const Proto::SigningInput& input); + + /// This should be used to transfer regular ESDTs (fungible tokens). + /// For reference, see: https://docs.elrond.com/developers/esdt-tokens/#transfers + /// + /// The "regular" ESDT tokens held by an account can be fetched from https://api.elrond.com/accounts/{address}/tokens. + Transaction fromESDTTransfer(const Proto::SigningInput& input); + + /// This should be used to transfer NFTs, SFTs and Meta ESDTs. + /// For reference, see: https://docs.elrond.com/developers/nft-tokens/#transfers + /// + /// The semi-fungible and non-fungible tokens held by an account can be fetched from https://api.elrond.com/accounts/{address}/nfts?type=SemiFungibleESDT,NonFungibleESDT. + /// The Meta ESDTs (a special kind of SFTs) held by an account can be fetched from https://api.elrond.com/accounts/{address}/nfts?type=MetaESDT. + /// + /// The fields "token_collection" and "token_nonce" are found as well in the HTTP response of the API call (as "collection" and "nonce", respectively). + Transaction fromESDTNFTTransfer(const Proto::SigningInput& input); +private: + uint64_t coalesceGasLimit(uint64_t providedGasLimit, uint64_t estimatedGasLimit); + uint64_t coalesceGasPrice(uint64_t gasPrice); + std::string coalesceChainId(std::string chainID); + std::string prepareFunctionCall(const std::string& function, std::initializer_list arguments); +}; + +} // namespace diff --git a/src/Encrypt.h b/src/Encrypt.h index 5b514b3b8c5..e024186f653 100644 --- a/src/Encrypt.h +++ b/src/Encrypt.h @@ -11,7 +11,7 @@ namespace TW::Encrypt { -/// Determind needed padding size (used internally) +/// Determined needed padding size (used internally) size_t paddingSize(size_t origSize, size_t blockSize, TWAESPaddingMode paddingMode); /// Encrypts a block of data using AES in Cipher Block Chaining (CBC) mode. diff --git a/src/Ethereum/ABI/Array.cpp b/src/Ethereum/ABI/Array.cpp index cc5f9255bbd..90071786336 100644 --- a/src/Ethereum/ABI/Array.cpp +++ b/src/Ethereum/ABI/Array.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -13,28 +13,41 @@ #include -using namespace TW::Ethereum::ABI; +namespace TW::Ethereum::ABI { + using namespace TW; using json = nlohmann::json; int ParamArray::addParam(const std::shared_ptr& param) { assert(param != nullptr); - if (param == nullptr) { return -1; } - if (_params.getCount() >= 1 && param->getType() != getFirstType()) { return -2; } // do not add different types + if (param == nullptr) { + return -1; + } + if (_params.getCount() >= 1 && param->getType() != getProtoType()) { + return -2; + } // do not add different types return _params.addParam(param); } void ParamArray::addParams(const std::vector>& params) { - for (auto p: params) { addParam(p); } + for (auto p : params) { + addParam(p); + } +} + +std::shared_ptr ParamArray::getProtoElem() const { + if (_params.getCount() >= 1) { + return _params.getParamUnsafe(0); + } + return _proto; } -std::string ParamArray::getFirstType() const { - if (_params.getCount() == 0) { return "empty"; } - return _params.getParamUnsafe(0)->getType(); +std::string ParamArray::getProtoType() const { + const auto proto = getProtoElem(); + return (proto != nullptr) ? proto->getType() : "__empty__"; } -size_t ParamArray::getSize() const -{ +size_t ParamArray::getSize() const { return 32 + _params.getSize(); } @@ -92,10 +105,10 @@ bool ParamArray::setValueJson(const std::string& value) { } // make sure enough elements are in the array while (_params.getCount() < valuesJson.size()) { - addParam(ParamFactory::make(getFirstType())); + addParam(ParamFactory::make(getProtoType())); } int cnt = 0; - for (const auto& e: valuesJson) { + for (const auto& e : valuesJson) { std::string eString = e.is_string() ? e.get() : e.dump(); _params.getParamUnsafe(cnt)->setValueJson(eString); ++cnt; @@ -104,6 +117,9 @@ bool ParamArray::setValueJson(const std::string& value) { } Data ParamArray::hashStruct() const { + if (_params.getCount() == 0) { + return Hash::keccak256(Data()); + } Data hash(32); Data hashes = _params.encodeHashes(); if (hashes.size() > 0) { @@ -113,9 +129,44 @@ Data ParamArray::hashStruct() const { } std::string ParamArray::getExtraTypes(std::vector& ignoreList) const { - std::shared_ptr p; - if (!_params.getParam(0, p)) { - return ""; + const auto& proto = getProtoElem(); + return (proto != nullptr) ? proto->getExtraTypes(ignoreList) : ""; +} + +void ParamArrayFix::encode(Data& data) const { + this->_params.encode(data); +} + +bool ParamArrayFix::decode(const Data& encoded, size_t& offset_inout) { + return this->_params.decode(encoded, offset_inout); +} + +bool ParamArrayFix::setValueJson(const std::string& value) { + auto valuesJson = json::parse(value, nullptr, false); + if (valuesJson.is_discarded() || !valuesJson.is_array() || _params.getCount() != valuesJson.size()) { + return false; + } + + std::size_t idx{0}; + for (auto&& e : valuesJson) { + std::string eString = e.is_string() ? e.get() : e.dump(); + _params.getParamUnsafe(idx)->setValueJson(eString); + ++idx; } - return p->getExtraTypes(ignoreList); + return true; } + +void ParamArrayFix::addParams(const Params& params) { + auto addParamFunctor = [this](auto&& param) { + if (param == nullptr) { + throw std::runtime_error("param can't be nullptr"); + } + if (_params.getCount() >= 1 && param->getType() != _params.getParamUnsafe(0)->getType()) { + throw std::runtime_error("params need to be the same type"); + } // do not add different types + _params.addParam(param); + }; + std::for_each(begin(params), end(params), addParamFunctor); +} + +} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Array.h b/src/Ethereum/ABI/Array.h index c7b29f04c7b..08138aad6a2 100644 --- a/src/Ethereum/ABI/Array.h +++ b/src/Ethereum/ABI/Array.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -13,22 +13,30 @@ namespace TW::Ethereum::ABI { /// Dynamic array of the same types, "[]" -class ParamArray: public ParamCollection -{ +/// Normally has at least one element. Empty array can have prototype set so its type is known. +/// Empty with no prototype is possible, but should be avoided. +class ParamArray : public ParamCollection { private: ParamSet _params; + std::shared_ptr _proto; // an optional prototype element, determines the array type, useful in empty array case + +private: + std::shared_ptr getProtoElem() const; // the first element if exists, otherwise the proto element + std::string getProtoType() const; public: ParamArray() = default; - ParamArray(const std::shared_ptr& param1) : ParamCollection() { addParam(param1); } - ParamArray(const std::vector>& params) : ParamCollection() { setVal(params); } + ParamArray(const std::shared_ptr& param1) + : ParamCollection() { addParam(param1); } + ParamArray(const std::vector>& params) + : ParamCollection() { setVal(params); } void setVal(const std::vector>& params) { addParams(params); } std::vector> const& getVal() const { return _params.getParams(); } int addParam(const std::shared_ptr& param); void addParams(const std::vector>& params); - std::string getFirstType() const; + void setProto(const std::shared_ptr& proto) { _proto = proto; } std::shared_ptr getParam(int paramIndex) { return _params.getParamUnsafe(paramIndex); } - virtual std::string getType() const { return getFirstType() + "[]"; } + virtual std::string getType() const { return getProtoType() + "[]"; } virtual size_t getSize() const; virtual bool isDynamic() const { return true; } virtual size_t getCount() const { return _params.getCount(); } @@ -39,4 +47,33 @@ class ParamArray: public ParamCollection virtual std::string getExtraTypes(std::vector& ignoreList) const; }; +/// Fixed-size array of the same type e.g, "type[4]" +class ParamArrayFix final : public ParamCollection { +public: + //! Public Definitions + using Params = std::vector>; + + //! Public constructor + explicit ParamArrayFix(const Params& params) noexcept(false) + : ParamCollection() { + this->addParams(params); + } + + //! Public member methods + [[nodiscard]] std::size_t getCount() const final { return _params.getCount(); } + [[nodiscard]] size_t getSize() const final { return _params.getSize(); } + [[nodiscard]] bool isDynamic() const final { return false; } + [[nodiscard]] std::string getType() const final { return _params.getParamUnsafe(0)->getType() + "[" + std::to_string(_params.getCount()) + "]"; } + void encode(Data& data) const final; + bool decode(const Data& encoded, size_t& offset_inout) final; + bool setValueJson(const std::string& value) final; + +private: + //! Private member functions + void addParams(const Params& params) noexcept(false); + + //! Private member fields + ParamSet _params; +}; + } // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Bytes.cpp b/src/Ethereum/ABI/Bytes.cpp index 164991a2dba..0b40d521bb1 100644 --- a/src/Ethereum/ABI/Bytes.cpp +++ b/src/Ethereum/ABI/Bytes.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,8 +12,7 @@ #include -using namespace TW::Ethereum::ABI; -using namespace TW; +namespace TW::Ethereum::ABI { void ParamByteArray::encodeBytes(const Data& bytes, Data& data) { ValueEncoder::encodeUInt256(uint256_t(bytes.size()), data); @@ -123,8 +122,8 @@ bool ParamString::decodeString(const Data& encoded, std::string& decoded, size_t Data ParamString::hashStruct() const { Data hash(32); Data encoded = data(_str); - if (encoded.size() > 0) { - hash = Hash::keccak256(encoded); - } + hash = Hash::keccak256(encoded); return hash; } + +} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Function.cpp b/src/Ethereum/ABI/Function.cpp index b2c067fcb26..9057d539d74 100644 --- a/src/Ethereum/ABI/Function.cpp +++ b/src/Ethereum/ABI/Function.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,12 +6,9 @@ #include "Function.h" -#include "../../Data.h" - #include -using namespace TW; -using namespace TW::Ethereum::ABI; +namespace TW::Ethereum::ABI { Data Function::getSignature() const { auto typ = getType(); @@ -28,14 +25,18 @@ void Function::encode(Data& data) const { bool Function::decodeOutput(const Data& encoded, size_t& offset_inout) { // read parameter values - if (!_outParams.decode(encoded, offset_inout)) { return false; } + if (!_outParams.decode(encoded, offset_inout)) { + return false; + } return true; } bool Function::decodeInput(const Data& encoded, size_t& offset_inout) { // read 4-byte hash auto p = ParamByteArrayFix(4); - if (!p.decode(encoded, offset_inout)) { return false; } + if (!p.decode(encoded, offset_inout)) { + return false; + } std::vector hash = p.getVal(); // adjust offset; hash is NOT padded to 32 bytes offset_inout = offset_inout - 32 + 4; @@ -46,6 +47,10 @@ bool Function::decodeInput(const Data& encoded, size_t& offset_inout) { return false; } // read parameters - if (!_inParams.decode(encoded, offset_inout)) { return false; } + if (!_inParams.decode(encoded, offset_inout)) { + return false; + } return true; } + +} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamAddress.cpp b/src/Ethereum/ABI/ParamAddress.cpp index de7663b4603..2d06aca161e 100644 --- a/src/Ethereum/ABI/ParamAddress.cpp +++ b/src/Ethereum/ABI/ParamAddress.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,19 +8,16 @@ #include #include -using namespace TW::Ethereum::ABI; -using namespace TW; +namespace TW::Ethereum::ABI { Data ParamAddress::getData() const { - Data data = store(getVal()); - if (data.size() >= bytes) { return data; } - // need to pad - Data padded(bytes - data.size()); - append(padded, data); - return padded; + Data data = store(getVal(), bytes); + return data; } bool ParamAddress::setValueJson(const std::string& value) { setVal(load(parse_hex(value))); return true; } + +} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamBase.h b/src/Ethereum/ABI/ParamBase.h index 464fa92c109..161abdc7c0c 100644 --- a/src/Ethereum/ABI/ParamBase.h +++ b/src/Ethereum/ABI/ParamBase.h @@ -26,7 +26,7 @@ class ParamBase // EIP712-style hash of the value (used for signing); default implementation virtual Data hashStruct() const; // Helper for EIP712 encoding; provide full type of all used types (recursively). Default is empty implementation. - virtual std::string getExtraTypes(std::vector& ignoreList) const { return ""; } + virtual std::string getExtraTypes([[maybe_unused]] std::vector& ignoreList) const { return ""; } }; /// Collection of parameters base class diff --git a/src/Ethereum/ABI/ParamFactory.cpp b/src/Ethereum/ABI/ParamFactory.cpp index 85660ad06e1..e82ce201246 100644 --- a/src/Ethereum/ABI/ParamFactory.cpp +++ b/src/Ethereum/ABI/ParamFactory.cpp @@ -117,6 +117,23 @@ std::shared_ptr ParamFactory::makeNamed(const std::string& name, con return std::make_shared(name, param); } +bool ParamFactory::isPrimitive(const std::string& type) { + if (starts_with(type, "address")) { + return true; + } else if (starts_with(type, "uint")) { + return true; + } else if (starts_with(type, "int")) { + return true; + } else if (starts_with(type, "bool")) { + return true; + } else if (starts_with(type, "bytes")) { + return true; + } else if (starts_with(type, "string")) { + return true; + } + return false; +} + std::string ParamFactory::getValue(const std::shared_ptr& param, const std::string& type) { std::string result = ""; if (isArrayType(type)) { @@ -126,7 +143,9 @@ std::string ParamFactory::getValue(const std::shared_ptr& param, cons auto value = dynamic_pointer_cast(param); result = hexEncoded(value->getData()); } else if (type == "uint8") { - result = boost::lexical_cast((unsigned int)dynamic_pointer_cast(param)->getVal()); + //result = boost::lexical_cast((uint)dynamic_pointer_cast(param)->getVal()); + result = boost::lexical_cast((unsigned int)dynamic_pointer_cast(param)->getVal());//win + } else if (type == "uint16") { result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); } else if (type == "uint32") { @@ -178,7 +197,7 @@ std::vector ParamFactory::getArrayValue(const std::shared_ptrgetVal(); std::vector values(elems.size()); - for (auto i = 0; i < elems.size(); ++i) { + for (auto i = 0ul; i < elems.size(); ++i) { values[i] = getValue(elems[i], elemType); } return values; diff --git a/src/Ethereum/ABI/ParamFactory.h b/src/Ethereum/ABI/ParamFactory.h index bd7e875fb30..7ccdb948684 100644 --- a/src/Ethereum/ABI/ParamFactory.h +++ b/src/Ethereum/ABI/ParamFactory.h @@ -14,19 +14,23 @@ #include #include +#include "Wasm.h" + namespace TW::Ethereum::ABI { /// Factory creates concrete ParamBase class from string type. -class ParamFactory -{ +class ParamFactory { public: /// Create a param of given type static std::shared_ptr make(const std::string& type); /// Create a named param, with given name and type static std::shared_ptr makeNamed(const std::string& name, const std::string& type); + /// Check if given type is a primitive type + static bool isPrimitive(const std::string& type); static std::string getValue(const std::shared_ptr& param, const std::string& type); - static std::vector getArrayValue(const std::shared_ptr& param, const std::string& type); + static std::vector getArrayValue(const std::shared_ptr& param, + const std::string& type); }; } // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamNumber.cpp b/src/Ethereum/ABI/ParamNumber.cpp index ba4308cc985..07c05516883 100644 --- a/src/Ethereum/ABI/ParamNumber.cpp +++ b/src/Ethereum/ABI/ParamNumber.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -15,9 +15,7 @@ #include #include -using namespace TW; -using namespace TW::Ethereum::ABI; - +namespace TW::Ethereum::ABI { bool ParamUInt256::setUInt256FromValueJson(uint256_t& dest, const std::string& value) { // try hex string or number @@ -38,21 +36,31 @@ bool ParamInt256::setInt256FromValueJson(int256_t& dest, const std::string& valu } bool ParamBool::setValueJson(const std::string& value) { - if (value == "true" || value == "1") { setVal(true); return true; } - if (value == "false" || value == "0") { setVal(false); return true; } + if (value == "true" || value == "1") { + setVal(true); + return true; + } + if (value == "false" || value == "0") { + setVal(false); + return true; + } return false; } bool ParamUInt8::setValueJson(const std::string& value) { uint16_t val; - if (!boost::conversion::detail::try_lexical_convert(value, val)) { return false; } + if (!boost::conversion::detail::try_lexical_convert(value, val)) { + return false; + } setVal(static_cast(val)); return true; } bool ParamInt8::setValueJson(const std::string& value) { int16_t val; - if (!boost::conversion::detail::try_lexical_convert(value, val)) { return false; } + if (!boost::conversion::detail::try_lexical_convert(value, val)) { + return false; + } setVal(static_cast(val)); return true; } @@ -103,7 +111,8 @@ bool ParamIntN::decode(const Data& encoded, size_t& offset_inout) { return res; } -void ParamIntN::init() -{ +void ParamIntN::init() { _mask = ParamUIntN::maskForBits(bits); } + +} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamStruct.cpp b/src/Ethereum/ABI/ParamStruct.cpp index a23d2fcbf1d..7910c0db7f5 100644 --- a/src/Ethereum/ABI/ParamStruct.cpp +++ b/src/Ethereum/ABI/ParamStruct.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,23 +7,22 @@ #include "ParamStruct.h" #include "ValueEncoder.h" #include "ParamFactory.h" -#include "ParamAddress.h" #include #include +#include #include #include #include -using namespace TW::Ethereum::ABI; -using namespace TW; +namespace TW::Ethereum::ABI { + using json = nlohmann::json; static const Data EipStructPrefix = parse_hex("1901"); static const auto Eip712Domain = "EIP712Domain"; - std::string ParamNamed::getType() const { return _param->getType() + " " + _name; } @@ -63,7 +62,7 @@ std::string ParamSetNamed::getType() const { Data ParamSetNamed::encodeHashes() const { Data hashes; - for (auto p: _params) { + for (auto p : _params) { append(hashes, p->hashStruct()); } return hashes; @@ -71,7 +70,22 @@ Data ParamSetNamed::encodeHashes() const { std::string ParamSetNamed::getExtraTypes(std::vector& ignoreList) const { std::string types; - for (auto& p: _params) { + + auto params = _params; + /// referenced struct type should be sorted by name see: https://eips.ethereum.org/EIPS/eip-712#definition-of-encodetype + std::stable_sort(params.begin(), params.end(), [](auto& a, auto& b) { + auto lhs = a->getType(); + auto rhs = b->getType(); + if (ParamFactory::isPrimitive(lhs)) { + return true; + } + if (ParamFactory::isPrimitive(rhs)) { + return true; + } + return lhs.compare(rhs) < 0; + }); + + for (auto& p : params) { auto pType = p->_param->getType(); if (std::find(ignoreList.begin(), ignoreList.end(), pType) == ignoreList.end()) { types += p->getExtraTypes(ignoreList); @@ -82,7 +96,7 @@ std::string ParamSetNamed::getExtraTypes(std::vector& ignoreList) c } std::shared_ptr ParamSetNamed::findParamByName(const std::string& name) const { - for (auto& p: _params) { + for (auto& p : _params) { if (p->_name == name) { return p; } @@ -160,15 +174,16 @@ Data ParamStruct::hashStructJson(const std::string& messageJson) { message["message"].dump(), message["types"].dump()); if (messageStruct) { - TW::append(hashes, messageStruct->hashStruct()); + const auto messageHash = messageStruct->hashStruct(); + TW::append(hashes, messageHash); return Hash::keccak256(hashes); } } - return {}; // fallback + return {}; // fallback } std::shared_ptr findType(const std::string& typeName, const std::vector>& types) { - for (auto& t: types) { + for (auto& t : types) { if (t->getType() == typeName) { return t; } @@ -195,9 +210,9 @@ std::shared_ptr ParamStruct::makeStruct(const std::string& structTy std::vector> params; const auto& typeParams = typeInfo->getParams(); // iterate through the type; order is important and field order in the value json is not defined - for (int i = 0; i < typeParams.getCount(); ++i) { - auto name = typeParams.getParam(i)->getName(); - auto type = typeParams.getParam(i)->getParam()->getType(); + for (auto i = 0ul; i < typeParams.getCount(); ++i) { + auto name = typeParams.getParam(static_cast(i))->getName(); + auto type = typeParams.getParam(static_cast(i))->getParam()->getType(); // look for it in value (may throw) auto value = values[name]; // first try simple params @@ -221,15 +236,27 @@ std::shared_ptr ParamStruct::makeStruct(const std::string& structTy throw std::invalid_argument("Value must be array for type " + type); } std::vector> paramsArray; - for (const auto& e: value) { - auto subStruct = makeStruct(arrayType, e.dump(), typesJson); + if (value.size() == 0) { + // empty array + auto subStruct = makeStruct(arrayType, "{}", typesJson); if (!subStruct) { - throw std::invalid_argument("Could not process array sub-struct " + arrayType + " " + e.dump()); + throw std::invalid_argument("Could not process array sub-struct " + arrayType + " " + "{}"); } assert(subStruct); - paramsArray.push_back(subStruct); + auto tmp = std::make_shared(paramsArray); + tmp->setProto(subStruct); + params.push_back(std::make_shared(name, tmp)); + } else { + for (const auto& e : value) { + auto subStruct = makeStruct(arrayType, e.dump(), typesJson); + if (!subStruct) { + throw std::invalid_argument("Could not process array sub-struct " + arrayType + " " + e.dump()); + } + assert(subStruct); + paramsArray.push_back(subStruct); + } + params.push_back(std::make_shared(name, std::make_shared(paramsArray))); } - params.push_back(std::make_shared(name, std::make_shared(paramsArray))); } else { // try if sub struct auto subTypeInfo = findType(type, types); @@ -271,7 +298,7 @@ std::shared_ptr ParamStruct::makeType(const std::string& structName throw std::invalid_argument("Expecting array"); } std::vector> params; - for(auto& p2: jsonValue) { + for (auto& p2 : jsonValue) { auto name = p2["name"].get(); auto type = p2["type"].get(); if (name.empty() || type.empty()) { @@ -354,3 +381,5 @@ std::vector> ParamStruct::makeTypes(const std::stri throw std::invalid_argument("Could not process Json"); } } + +} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/ParamStruct.h b/src/Ethereum/ABI/ParamStruct.h index 53eb8d42711..816720905dd 100644 --- a/src/Ethereum/ABI/ParamStruct.h +++ b/src/Ethereum/ABI/ParamStruct.h @@ -85,9 +85,9 @@ class ParamStruct: public ParamCollection virtual size_t getSize() const { return _params.getCount(); } virtual bool isDynamic() const { return true; } virtual size_t getCount() const { return _params.getCount(); } - virtual void encode(Data& data) const {} - virtual bool decode(const Data& encoded, size_t& offset_inout) { return true; } - virtual bool setValueJson(const std::string& value) { return false; } // see makeStruct + virtual void encode([[maybe_unused]] Data& data) const {} + virtual bool decode([[maybe_unused]] const Data& encoded, [[maybe_unused]] size_t& offset_inout) { return true; } + virtual bool setValueJson([[maybe_unused]] const std::string& value) { return false; } // see makeStruct Data encodeHashes() const; virtual std::string getExtraTypes(std::vector& ignoreList) const; std::shared_ptr findParamByName(const std::string& name) const { return _params.findParamByName(name); } diff --git a/src/Ethereum/ABI/Parameters.cpp b/src/Ethereum/ABI/Parameters.cpp index ed734d78257..3cc66daefd9 100644 --- a/src/Ethereum/ABI/Parameters.cpp +++ b/src/Ethereum/ABI/Parameters.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,8 +11,7 @@ #include #include -using namespace TW::Ethereum::ABI; -using namespace TW; +namespace TW::Ethereum::ABI { ParamSet::~ParamSet() { _params.clear(); @@ -34,7 +33,7 @@ void ParamSet::addParams(const std::vector>& params) } } -bool ParamSet::getParam(int paramIndex, std::shared_ptr& param_out) const { +bool ParamSet::getParam(size_t paramIndex, std::shared_ptr& param_out) const { if (paramIndex >= _params.size() || paramIndex < 0) { return false; } @@ -42,7 +41,7 @@ bool ParamSet::getParam(int paramIndex, std::shared_ptr& param_out) c return true; } -std::shared_ptr ParamSet::getParamUnsafe(int paramIndex) const { +std::shared_ptr ParamSet::getParamUnsafe(size_t paramIndex) const { if (_params.size() == 0) { // zero parameter, nothing to return. This may cause trouble (segfault) return nullptr; @@ -69,7 +68,7 @@ std::string ParamSet::getType() const { } bool ParamSet::isDynamic() const { - for (const auto& p: _params) { + for (const auto& p : _params) { if (p->isDynamic()) { return true; } @@ -80,7 +79,7 @@ bool ParamSet::isDynamic() const { size_t ParamSet::getSize() const { // 2-pass encoding size_t s = 0; - for (auto p: _params) { + for (auto p : _params) { if (p->isDynamic() || p->getSize() > ValueEncoder::encodedIntSize) { // offset used s += 32; @@ -156,7 +155,7 @@ bool ParamSet::decode(const Data& encoded, size_t& offset_inout) { Data ParamSet::encodeHashes() const { Data hashes; - for (auto p: _params) { + for (auto p : _params) { append(hashes, p->hashStruct()); } return hashes; @@ -170,3 +169,5 @@ Data Parameters::hashStruct() const { } return hash; } + +} // namespace TW::Ethereum::ABI diff --git a/src/Ethereum/ABI/Parameters.h b/src/Ethereum/ABI/Parameters.h index 4f62ff8f326..ac315235fbe 100644 --- a/src/Ethereum/ABI/Parameters.h +++ b/src/Ethereum/ABI/Parameters.h @@ -29,8 +29,8 @@ class ParamSet { /// Returns the index of the parameter int addParam(const std::shared_ptr& param); void addParams(const std::vector>& params); - bool getParam(int paramIndex, std::shared_ptr& param_out) const; - std::shared_ptr getParamUnsafe(int paramIndex) const; + bool getParam(size_t paramIndex, std::shared_ptr& param_out) const; + std::shared_ptr getParamUnsafe(size_t paramIndex) const; size_t getCount() const { return _params.size(); } std::vector> const& getParams() const { return _params; } /// Return the function type signature, of the form "baz(int32,uint256)" @@ -56,14 +56,14 @@ class Parameters: public ParamCollection Parameters(const std::vector>& params) : ParamCollection(), _params(ParamSet(params)) {} void addParam(const std::shared_ptr& param) { _params.addParam(param); } void addParams(const std::vector>& params) { _params.addParams(params); } - std::shared_ptr getParam(int paramIndex) const { return _params.getParamUnsafe(paramIndex); } + std::shared_ptr getParam(size_t paramIndex) const { return _params.getParamUnsafe(paramIndex); } virtual std::string getType() const { return _params.getType(); } virtual size_t getSize() const { return _params.getSize(); } virtual bool isDynamic() const { return true; } virtual size_t getCount() const { return _params.getCount(); } virtual void encode(Data& data) const { _params.encode(data); } virtual bool decode(const Data& encoded, size_t& offset_inout) { return _params.decode(encoded, offset_inout); } - virtual bool setValueJson(const std::string& value) { return false; } + virtual bool setValueJson([[maybe_unused]] const std::string& value) { return false; } virtual Data hashStruct() const; }; diff --git a/src/Ethereum/ABI/Tuple.cpp b/src/Ethereum/ABI/Tuple.cpp index 72c6a6a66d2..c674592ff2b 100644 --- a/src/Ethereum/ABI/Tuple.cpp +++ b/src/Ethereum/ABI/Tuple.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,14 +6,12 @@ #include "Tuple.h" -#include "Data.h" - #include -using namespace TW; -using namespace TW::Ethereum::ABI; - +namespace TW::Ethereum::ABI { int ParamTuple::addParam(std::shared_ptr param) { return _params.addParam(param); } + +} // namespace TW::Ethereum::ABI \ No newline at end of file diff --git a/src/Ethereum/ABI/Tuple.h b/src/Ethereum/ABI/Tuple.h index b9aca129e28..6579f9784f6 100644 --- a/src/Ethereum/ABI/Tuple.h +++ b/src/Ethereum/ABI/Tuple.h @@ -34,7 +34,7 @@ class ParamTuple: public ParamCollection virtual bool isDynamic() const { return _params.isDynamic(); } virtual void encode(Data& data) const { return _params.encode(data); } virtual bool decode(const Data& encoded, size_t& offset_inout) { return _params.decode(encoded, offset_inout); } - virtual bool setValueJson(const std::string& value) { return false; } + virtual bool setValueJson([[maybe_unused]] const std::string& value) { return false; } virtual size_t getCount() const { return _params.getCount(); } }; diff --git a/src/Ethereum/ABI/ValueEncoder.cpp b/src/Ethereum/ABI/ValueEncoder.cpp index 44cb7aa7f76..d244a5acbf0 100644 --- a/src/Ethereum/ABI/ValueEncoder.cpp +++ b/src/Ethereum/ABI/ValueEncoder.cpp @@ -46,7 +46,7 @@ inline Data paddedOnLeft(const Data& inout) { } void ValueEncoder::encodeUInt256(const uint256_t& value, Data& inout) { - append(inout, paddedOnLeft(store(value))); + append(inout, store(value, 32)); } /// Encoding primitive: encode a number of bytes by taking hash diff --git a/src/Ethereum/Address.cpp b/src/Ethereum/Address.cpp index 09a7c893f54..48d013bde0c 100644 --- a/src/Ethereum/Address.cpp +++ b/src/Ethereum/Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,10 +6,9 @@ #include "Address.h" #include "AddressChecksum.h" -#include "../Hash.h" #include "../HexCoding.h" -using namespace TW::Ethereum; +namespace TW::Ethereum { bool Address::isValid(const std::string& string) { if (string.size() != 42 || string[0] != '0' || string[1] != 'x') { @@ -38,10 +37,12 @@ Address::Address(const PublicKey& publicKey) { if (publicKey.type != TWPublicKeyTypeSECP256k1Extended) { throw std::invalid_argument("Ethereum::Address needs an extended SECP256k1 public key."); } - const auto data = publicKey.hash({}, static_cast(Hash::keccak256), true); + const auto data = publicKey.hash({}, Hash::HasherKeccak256, true); std::copy(data.end() - Address::size, data.end(), bytes.begin()); } std::string Address::string() const { - return checksumed(*this, ChecksumType::eip55); + return checksumed(*this); } + +} // namespace TW::Ethereum diff --git a/src/Ethereum/Address.h b/src/Ethereum/Address.h index 1f9e25d3490..c14a56146f1 100644 --- a/src/Ethereum/Address.h +++ b/src/Ethereum/Address.h @@ -40,6 +40,8 @@ class Address { /// Returns a string representation of the address. std::string string() const; + protected: + Address() = default; }; inline bool operator==(const Address& lhs, const Address& rhs) { @@ -47,8 +49,3 @@ inline bool operator==(const Address& lhs, const Address& rhs) { } } // namespace TW::Ethereum - -/// Wrapper for C interface. -struct TWEthereumAddress { - TW::Ethereum::Address impl; -}; diff --git a/src/Ethereum/AddressChecksum.cpp b/src/Ethereum/AddressChecksum.cpp index 65fa5ba458f..da69f905a7e 100644 --- a/src/Ethereum/AddressChecksum.cpp +++ b/src/Ethereum/AddressChecksum.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,19 +6,17 @@ #include "AddressChecksum.h" -#include "../Hash.h" #include "../HexCoding.h" #include -using namespace TW; -using namespace TW::Ethereum; +namespace TW::Ethereum { -std::string Ethereum::checksumed(const Address& address, enum ChecksumType type) { +std::string checksumed(const Address& address) { const auto addressString = hex(address.bytes); const auto hash = hex(Hash::keccak256(addressString)); std::string string = "0x"; - for (auto i = 0; i < std::min(addressString.size(), hash.size()); i += 1) { + for (auto i = 0ul; i < std::min(addressString.size(), hash.size()); i += 1) { const auto a = addressString[i]; const auto h = hash[i]; if (a >= '0' && a <= '9') { @@ -32,3 +30,5 @@ std::string Ethereum::checksumed(const Address& address, enum ChecksumType type) return string; } + +} // namespace TW::Ethereum diff --git a/src/Ethereum/AddressChecksum.h b/src/Ethereum/AddressChecksum.h index 2aa11e989d4..922110c6869 100644 --- a/src/Ethereum/AddressChecksum.h +++ b/src/Ethereum/AddressChecksum.h @@ -11,12 +11,6 @@ namespace TW::Ethereum { -/// Checksum types for Ethereum-based blockchains. -enum ChecksumType { - eip55 = 0, - wanchain = 1, -}; - -std::string checksumed(const Address& address, enum ChecksumType type); +std::string checksumed(const Address& address); } // namespace TW::Ethereum diff --git a/src/Ethereum/ContractCall.cpp b/src/Ethereum/ContractCall.cpp index 4409577e914..0ae3a65db23 100644 --- a/src/Ethereum/ContractCall.cpp +++ b/src/Ethereum/ContractCall.cpp @@ -16,10 +16,9 @@ using json = nlohmann::json; namespace TW::Ethereum::ABI { static void fillArray(ParamSet& paramSet, const string& type) { - auto param = make_shared(); auto baseType = string(type.begin(), type.end() - 2); auto value = ParamFactory::make(baseType); - param->addParam(value); + auto param = make_shared(value); paramSet.addParam(param); } @@ -40,7 +39,7 @@ static vector getArrayValue(ParamSet& paramSet, const string& type, int static json buildInputs(ParamSet& paramSet, const json& registry); // forward -static json getTupleValue(ParamSet& paramSet, const string& type, int idx, const json& typeInfo) { +static json getTupleValue(ParamSet& paramSet, [[maybe_unused]] const string& type, int idx, const json& typeInfo) { shared_ptr param; paramSet.getParam(idx, param); auto paramTuple = dynamic_pointer_cast(param); @@ -58,7 +57,7 @@ static string getValue(ParamSet& paramSet, const string& type, int idx) { static json buildInputs(ParamSet& paramSet, const json& registry) { auto inputs = json::array(); - for (int i = 0; i < registry.size(); i++) { + for (auto i = 0ul; i < registry.size(); i++) { auto info = registry[i]; auto type = info["type"]; auto input = json{ @@ -66,13 +65,13 @@ static json buildInputs(ParamSet& paramSet, const json& registry) { {"type", type} }; if (boost::algorithm::ends_with(type.get(), "[]")) { - input["value"] = json(getArrayValue(paramSet, type, i)); + input["value"] = json(getArrayValue(paramSet, type, static_cast(i))); } else if (type == "tuple") { - input["components"] = getTupleValue(paramSet, type, i, info); + input["components"] = getTupleValue(paramSet, type, static_cast(i), info); } else if (type == "bool") { - input["value"] = getValue(paramSet, type, i) == "true" ? json(true) : json(false); + input["value"] = getValue(paramSet, type, static_cast(i)) == "true" ? json(true) : json(false); } else { - input["value"] = getValue(paramSet, type, i); + input["value"] = getValue(paramSet, type, static_cast(i)); } inputs.push_back(input); } diff --git a/src/Ethereum/Entry.cpp b/src/Ethereum/Entry.cpp index 35297782d23..f5a7c7ab87e 100644 --- a/src/Ethereum/Entry.cpp +++ b/src/Ethereum/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,26 +9,86 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Ethereum; +#include "proto/TransactionCompiler.pb.h" + +namespace TW::Ethereum { + using namespace std; -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::normalizeAddress(TWCoinType coin, const string& address) const { +string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& address) const { // normalized with EIP55 checksum return Address(address).string(); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Address(address); + return {addr.bytes.begin(), addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +Data Entry::preImageHashes([[maybe_unused]] TWCoinType coin, const Data& txInputData) const { + return txCompilerTemplate( + txInputData, [](const auto& input, auto& output) { + const auto transaction = Signer::build(input); + const auto chainId = load(data(input.chain_id())); // retrieve chainId from input + auto preHash = transaction->preHash(chainId); + auto preImage = transaction->serialize(chainId); + output.set_data_hash(preHash.data(), preHash.size()); + output.set_data(preImage.data(), preImage.size()); + }); +} + +void Entry::compile([[maybe_unused]] TWCoinType coin, const Data& txInputData, const std::vector& signatures, [[maybe_unused]] const std::vector& publicKeys, Data& dataOut) const { + dataOut = txCompilerTemplate( + txInputData, [&](const auto& input, auto& output) { + if (signatures.size() != 1) { + output.set_error(Common::Proto::Error_signatures_count); + output.set_error_message(Common::Proto::SigningError_Name(Common::Proto::Error_signatures_count)); + return; + } + output = Signer::compile(input, signatures[0]); + }); +} + +Data Entry::buildTransactionInput([[maybe_unused]] TWCoinType coinType, [[maybe_unused]] const std::string& from, const std::string& to, const uint256_t& amount, [[maybe_unused]] const std::string& asset, [[maybe_unused]] const std::string& memo, const std::string& chainId) const { + Proto::SigningInput input; + + auto chainIdData = store(uint256_t(1)); + if (chainId.length() > 0) { + // parse amount + uint256_t chainIdUint256{chainId}; + chainIdData = store(chainIdUint256); + } + input.set_chain_id(chainIdData.data(), chainIdData.size()); + + if (!Address::isValid(to)) { + throw std::invalid_argument("Invalid to address"); + } + input.set_to_address(to); + + auto& transfer = *input.mutable_transaction()->mutable_transfer(); + const auto amountData = store(amount); + transfer.set_amount(amountData.data(), amountData.size()); + + // not set: nonce, gasPrice, gasLimit, tx_mode (need to be set afterwards) + + const auto txInputData = data(input.SerializeAsString()); + return txInputData; +} + +} // namespace TW::Ethereum diff --git a/src/Ethereum/Entry.h b/src/Ethereum/Entry.h index 9fe4bcc6c90..23a31b2c8d1 100644 --- a/src/Ethereum/Entry.h +++ b/src/Ethereum/Entry.h @@ -12,37 +12,19 @@ namespace TW::Ethereum { /// Entry point for Ethereum and Ethereum-fork coins. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry : public CoinEntry { public: - virtual const std::vector coinTypes() const { - return { - TWCoinTypeCallisto, - TWCoinTypeEthereum, - TWCoinTypeEthereumClassic, - TWCoinTypeGoChain, - TWCoinTypePOANetwork, - TWCoinTypeThunderToken, - TWCoinTypeTomoChain, - TWCoinTypeSmartChainLegacy, - TWCoinTypeSmartChain, - TWCoinTypePolygon, - TWCoinTypeWanchain, - TWCoinTypeOptimism, - TWCoinTypeArbitrum, - TWCoinTypeECOChain, - TWCoinTypeAvalancheCChain, - TWCoinTypeXDai, - TWCoinTypeFantom, - TWCoinTypeCelo, - TWCoinTypeRonin, - }; - } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const final; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const final; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const final; + Data addressToData(TWCoinType coin, const std::string& address) const final; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const override; + bool supportsJSONSigning() const final { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const final; + + Data preImageHashes(TWCoinType coin, const Data& txInputData) const final; + void compile(TWCoinType coin, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys, Data& dataOut) const final; + Data buildTransactionInput(TWCoinType coinType, const std::string& from, const std::string& to, const uint256_t& amount, const std::string& asset, const std::string& memo, const std::string& chainId) const final; }; } // namespace TW::Ethereum diff --git a/src/Ethereum/Fee.cpp b/src/Ethereum/Fee.cpp deleted file mode 100644 index 0416faddb0e..00000000000 --- a/src/Ethereum/Fee.cpp +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#ifdef _MSC_VER -#define _USE_MATH_DEFINES -#endif - -#include "Fee.h" - -#include "Data.h" -#include "HexCoding.h" -#include "uint256.h" -// always includes nvp.hpp before cpp_bin_float.hpp -#include -#include - -#include -#include -#include - -using json = nlohmann::json; -using float128_t = boost::multiprecision::cpp_bin_float_quad; -using namespace std; - -namespace TW::Ethereum::Fee { - -static const uint256_t defaultTip = 2000000000; - -auto samplingCurve(double sumWeight) -> double { - static const double sampleMin = 0.1; - static const double sampleMax = 0.3; - - if (sumWeight <= sampleMin) { - return 0.0; - } - - if (sumWeight >= sampleMax) { - return 1.0; - } - - return (1 - cos((sumWeight - sampleMin) * 2 * M_PI / (sampleMax - sampleMin))) / 2; -} - -auto suggestTip(const json& feeHistory) -> uint256_t { - - if (!feeHistory.contains("reward")) { - return defaultTip; - } - - vector rewards; - - for (auto& item : feeHistory["reward"]) { - const auto hex = parse_hex(item[0], true); - const auto reward = load(hex); - if (reward > 0) { - rewards.push_back(reward); - } - } - - if (rewards.empty()) { - return defaultTip; - } - - // return the median of the rewards - sort(rewards.begin(), rewards.end()); - return rewards[rewards.size() / 2]; -} - -auto suggestBaseFee(vector baseFees, vector order, double timeFactor) - -> uint256_t { - if (timeFactor < 1e-6) { - return baseFees.back(); - } - - double pendingWeight = (1 - exp(-1 / timeFactor)) / (1 - exp(-1 * double(baseFees.size()) / timeFactor)); - double sumWeight = 0.0; - double samplingCurveLast = 0.0; - float128_t result = 0; - for (size_t i = 0; i < order.size(); i++) { - sumWeight += pendingWeight * exp((double(order[i]) - baseFees.size() + 1) / timeFactor); - double samplingCurveValue = samplingCurve(sumWeight); - result += (samplingCurveValue - samplingCurveLast) * float128_t(baseFees[order[i]]); - if (samplingCurveValue >= 1) { - return uint256_t(boost::multiprecision::ceil(result)); - } - samplingCurveLast = samplingCurveValue; - } - return uint256_t(boost::multiprecision::ceil(result)); -} - -auto suggestFee(const json& feeHistory) -> json { - // tailored from: - // https://github.com/zsfelfoldi/ethereum-docs/blob/master/eip1559/feeHistory_example.js - vector baseFees; - vector order; - - const auto& array = feeHistory["baseFeePerGas"]; - for (size_t i = 0; i < array.size(); i++) { - const auto hex = parse_hex(array[i], true); - baseFees.push_back(load(hex)); - order.push_back(i); - } - - // The last (pending) block is also assumed to end up being full - baseFees.back() = baseFees.back() * 9 / 8; - - const auto& gasRatio = feeHistory["gasUsedRatio"]; - for (size_t i = gasRatio.size() - 1; i > 0; i--) { - if (i < gasRatio.size() && gasRatio[i] > 0.9) { - baseFees[i] = baseFees[i + 1]; - } - } - - std::sort(order.begin(), order.end(), [&baseFees](const size_t lhs, const size_t rhs) { - return baseFees[lhs] > baseFees[rhs]; - }); - - const auto baseFee = suggestBaseFee(baseFees, order, 15); - const auto tip = suggestTip(feeHistory); - - return json{ - {"baseFee", toString(baseFee)}, - {"maxPriorityFee", toString(tip)}, - }; -} - -} // namespace TW::Ethereum::Fee diff --git a/src/Ethereum/Fee.h b/src/Ethereum/Fee.h deleted file mode 100644 index 6fe94672b87..00000000000 --- a/src/Ethereum/Fee.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "uint256.h" -#include - -namespace TW::Ethereum::Fee { - -auto suggestFee(const nlohmann::json& feeHistory) -> nlohmann::json; - -} // namespace TW::Ethereum::Fee diff --git a/src/Ethereum/RLP.cpp b/src/Ethereum/RLP.cpp index 94a2a39420e..50817bbd467 100644 --- a/src/Ethereum/RLP.cpp +++ b/src/Ethereum/RLP.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,14 +6,11 @@ #include "RLP.h" -#include "../Data.h" -#include "../uint256.h" #include "../BinaryCoding.h" #include -using namespace TW; -using namespace TW::Ethereum; +namespace TW::Ethereum { Data RLP::encode(const uint256_t& value) noexcept { using boost::multiprecision::cpp_int; @@ -63,7 +60,7 @@ Data RLP::encodeHeader(uint64_t size, uint8_t smallTag, uint8_t largeTag) noexce Data RLP::putVarInt(uint64_t i) noexcept { Data bytes; // accumulate bytes here, in reverse order do { - // take LSB byte, append + // take LSB byte, append bytes.push_back(i & 0xff); i = i >> 8; } while (i); @@ -83,7 +80,7 @@ uint64_t RLP::parseVarInt(size_t size, const Data& data, size_t index) { throw std::invalid_argument("multi-byte length must have no leading zero"); } uint64_t val = 0; - for (auto i = 0; i < size; ++i) { + for (auto i = 0ul; i < size; ++i) { val = val << 8; val += data[index + i]; } @@ -93,7 +90,7 @@ uint64_t RLP::parseVarInt(size_t size, const Data& data, size_t index) { RLP::DecodedItem RLP::decodeList(const Data& input) { RLP::DecodedItem item; auto remainder = input; - while(true) { + while (true) { auto listItem = RLP::decode(remainder); item.decoded.push_back(listItem.decoded[0]); if (listItem.remainder.size() == 0) { @@ -114,7 +111,7 @@ RLP::DecodedItem RLP::decode(const Data& input) { auto prefix = input[0]; if (prefix <= 0x7f) { // 00--7f: a single byte whose value is in the [0x00, 0x7f] range, that byte is its own RLP encoding. - item.decoded.push_back(Data{input[0]}); + item.decoded.emplace_back(Data{input[0]}); item.remainder = subData(input, 1); return item; } @@ -135,18 +132,18 @@ RLP::DecodedItem RLP::decode(const Data& input) { throw std::invalid_argument("single byte below 128 must be encoded as itself"); } - if (inputLen < (1 + strLen)) { + if (inputLen < (1ul + strLen)) { throw std::invalid_argument(std::string("invalid short string, length ") + std::to_string(strLen)); } item.decoded.push_back(subData(input, 1, strLen)); item.remainder = subData(input, 1 + strLen); return item; - } + } if (prefix <= 0xbf) { // b8--bf: long string - auto lenOfStrLen = prefix - 0xb7; - auto strLen = parseVarInt(lenOfStrLen, input, 1); + auto lenOfStrLen = size_t(prefix - 0xb7); + auto strLen = static_cast(parseVarInt(lenOfStrLen, input, 1)); if (inputLen < lenOfStrLen || inputLen < (1 + lenOfStrLen + strLen)) { throw std::invalid_argument(std::string("Invalid rlp encoding length, length ") + std::to_string(strLen)); } @@ -154,10 +151,10 @@ RLP::DecodedItem RLP::decode(const Data& input) { item.decoded.push_back(data); item.remainder = subData(input, 1 + lenOfStrLen + strLen); return item; - } + } if (prefix <= 0xf7) { // c0--f7: a list between 0-55 bytes long - auto listLen = prefix - 0xc0; + auto listLen = size_t(prefix - 0xc0); if (inputLen < (1 + listLen)) { throw std::invalid_argument(std::string("Invalid rlp string length, length ") + std::to_string(listLen)); } @@ -174,10 +171,10 @@ RLP::DecodedItem RLP::decode(const Data& input) { } item.remainder = subData(input, 1 + listLen); return item; - } - // f8--ff - auto lenOfListLen = prefix - 0xf7; - auto listLen = parseVarInt(lenOfListLen, input, 1); + } + // f8--ff + auto lenOfListLen = size_t(prefix - 0xf7); + auto listLen = static_cast(parseVarInt(lenOfListLen, input, 1)); if (listLen < 56) { throw std::invalid_argument("length below 56 must be encoded in one byte"); } @@ -193,3 +190,5 @@ RLP::DecodedItem RLP::decode(const Data& input) { item.remainder = subData(input, 1 + lenOfListLen + listLen); return item; } + +} // namespace TW::Ethereum diff --git a/src/Ethereum/RLP.h b/src/Ethereum/RLP.h index 7bdc17c60ed..c65ecbbc6cb 100644 --- a/src/Ethereum/RLP.h +++ b/src/Ethereum/RLP.h @@ -7,7 +7,7 @@ #pragma once #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../uint256.h" #include diff --git a/src/Ethereum/Signer.cpp b/src/Ethereum/Signer.cpp index 6d8ce136a53..edfca7a0620 100644 --- a/src/Ethereum/Signer.cpp +++ b/src/Ethereum/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,10 +6,11 @@ #include "Signer.h" #include "HexCoding.h" +#include "Coin.h" +#include #include -using namespace TW; -using namespace TW::Ethereum; +namespace TW::Ethereum { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { try { @@ -24,11 +25,11 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto encoded = transaction->encoded(signature, chainID); output.set_encoded(encoded.data(), encoded.size()); - auto v = store(signature.v); + auto v = store(signature.v, 1); output.set_v(v.data(), v.size()); - auto r = store(signature.r); + auto r = store(signature.r, 32); output.set_r(r.data(), r.size()); - auto s = store(signature.s); + auto s = store(signature.s, 32); output.set_s(s.data(), s.size()); output.set_data(transaction->payload.data(), transaction->payload.size()); @@ -47,7 +48,41 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { return hex(output.encoded()); } -Signature Signer::signatureDataToStruct(const Data& signature) noexcept { +Proto::SigningOutput Signer::compile(const Proto::SigningInput& input, const Data& signature) noexcept { + try { + uint256_t chainID = load(input.chain_id()); + auto transaction = Signer::build(input); + + // prepare Signature + const Signature sigStruct = signatureDataToStruct(signature, transaction->usesReplayProtection(), chainID); + + auto output = Proto::SigningOutput(); + + auto encoded = transaction->encoded(sigStruct, chainID); + output.set_encoded(encoded.data(), encoded.size()); + + auto v = store(sigStruct.v, 1); + output.set_v(v.data(), v.size()); + auto r = store(sigStruct.r, 32); + output.set_r(r.data(), r.size()); + auto s = store(sigStruct.s, 32); + output.set_s(s.data(), s.size()); + + output.set_data(transaction->payload.data(), transaction->payload.size()); + return output; + } catch (std::exception&) { + return Proto::SigningOutput(); + } +} + +Signature Signer::signatureDataToStruct(const Data& signature, bool includeEip155, const uint256_t& chainID) noexcept { + if (!includeEip155) { + return signatureDataToStructSimple(signature); + } + return signatureDataToStructWithEip155(chainID, signature); +} + +Signature Signer::signatureDataToStructSimple(const Data& signature) noexcept { boost::multiprecision::uint256_t r, s, v; import_bits(r, signature.begin(), signature.begin() + 32); import_bits(s, signature.begin() + 32, signature.begin() + 64); @@ -56,7 +91,7 @@ Signature Signer::signatureDataToStruct(const Data& signature) noexcept { } Signature Signer::signatureDataToStructWithEip155(const uint256_t& chainID, const Data& signature) noexcept { - Signature rsv = signatureDataToStruct(signature); + Signature rsv = signatureDataToStructSimple(signature); // Embed chainID in V param, for replay protection, legacy (EIP155) if (chainID != 0) { rsv.v += 35 + chainID + chainID; @@ -68,10 +103,7 @@ Signature Signer::signatureDataToStructWithEip155(const uint256_t& chainID, cons Signature Signer::sign(const PrivateKey& privateKey, const Data& hash, bool includeEip155, const uint256_t& chainID) noexcept { auto signature = privateKey.sign(hash, TWCurveSECP256k1); - if (!includeEip155) { - return signatureDataToStruct(signature); - } - return signatureDataToStructWithEip155(chainID, signature); + return signatureDataToStruct(signature, includeEip155, chainID); } // May throw @@ -79,11 +111,11 @@ Data addressStringToData(const std::string& asString) { if (asString.empty()) { return {}; } - auto address = Address(asString); - Data asData; - asData.resize(20); - std::copy(address.bytes.begin(), address.bytes.end(), asData.data()); - return asData; + // only ronin address prefix is not 0x + if (asString.compare(0, 2, "0x") != 0) { + return TW::addressToData(TWCoinTypeRonin, asString); + } + return TW::addressToData(TWCoinTypeEthereum, asString); } std::shared_ptr Signer::build(const Proto::SigningInput& input) { @@ -94,140 +126,134 @@ std::shared_ptr Signer::build(const Proto::SigningInput& input) uint256_t maxInclusionFeePerGas = load(input.max_inclusion_fee_per_gas()); uint256_t maxFeePerGas = load(input.max_fee_per_gas()); switch (input.transaction().transaction_oneof_case()) { - case Proto::Transaction::kTransfer: - { - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildNativeTransfer( - nonce, gasPrice, gasLimit, - /* to: */ toAddress, - /* amount: */ load(input.transaction().transfer().amount()), - /* optional data: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildNativeTransfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* to: */ toAddress, - /* amount: */ load(input.transaction().transfer().amount()), - /* optional data: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end())); - } - } - - case Proto::Transaction::kErc20Transfer: - { - Data tokenToAddress = addressStringToData(input.transaction().erc20_transfer().to()); - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildERC20Transfer( - nonce, gasPrice, gasLimit, - /* tokenContract: */ toAddress, - /* toAddress */ tokenToAddress, - /* amount: */ load(input.transaction().erc20_transfer().amount())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildERC20Transfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* tokenContract: */ toAddress, - /* toAddress */ tokenToAddress, - /* amount: */ load(input.transaction().erc20_transfer().amount())); - } - } - - case Proto::Transaction::kErc20Approve: - { - Data spenderAddress = addressStringToData(input.transaction().erc20_approve().spender()); - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildERC20Approve( - nonce, gasPrice, gasLimit, - /* tokenContract: */ toAddress, - /* toAddress */ spenderAddress, - /* amount: */ load(input.transaction().erc20_approve().amount())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildERC20Approve( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* tokenContract: */ toAddress, - /* toAddress */ spenderAddress, - /* amount: */ load(input.transaction().erc20_approve().amount())); - } - } - - case Proto::Transaction::kErc721Transfer: - { - Data tokenToAddress = addressStringToData(input.transaction().erc721_transfer().to()); - Data tokenFromAddress = addressStringToData(input.transaction().erc721_transfer().from()); - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildERC721Transfer( - nonce, gasPrice, gasLimit, - /* tokenContract: */ toAddress, - /* fromAddress: */ tokenFromAddress, - /* toAddress */ tokenToAddress, - /* tokenId: */ load(input.transaction().erc721_transfer().token_id())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildERC721Transfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* tokenContract: */ toAddress, - /* fromAddress: */ tokenFromAddress, - /* toAddress */ tokenToAddress, - /* tokenId: */ load(input.transaction().erc721_transfer().token_id())); - } - } - - case Proto::Transaction::kErc1155Transfer: - { - Data tokenToAddress = addressStringToData(input.transaction().erc1155_transfer().to()); - Data tokenFromAddress = addressStringToData(input.transaction().erc1155_transfer().from()); - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildERC1155Transfer( - nonce, gasPrice, gasLimit, - /* tokenContract: */ toAddress, - /* fromAddress: */ tokenFromAddress, - /* toAddress */ tokenToAddress, - /* tokenId: */ load(input.transaction().erc1155_transfer().token_id()), - /* value */ load(input.transaction().erc1155_transfer().value()), - /* data */ Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildERC1155Transfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* tokenContract: */ toAddress, - /* fromAddress: */ tokenFromAddress, - /* toAddress */ tokenToAddress, - /* tokenId: */ load(input.transaction().erc1155_transfer().token_id()), - /* value */ load(input.transaction().erc1155_transfer().value()), - /* data */ Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end())); - } - } - - case Proto::Transaction::kContractGeneric: + case Proto::Transaction::kTransfer: { + switch (input.tx_mode()) { + case Proto::TransactionMode::Legacy: + default: + return TransactionNonTyped::buildNativeTransfer( + nonce, gasPrice, gasLimit, + /* to: */ toAddress, + /* amount: */ load(input.transaction().transfer().amount()), + /* optional data: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end())); + + case Proto::TransactionMode::Enveloped: // Eip1559 + return TransactionEip1559::buildNativeTransfer( + nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, + /* to: */ toAddress, + /* amount: */ load(input.transaction().transfer().amount()), + /* optional data: */ Data(input.transaction().transfer().data().begin(), input.transaction().transfer().data().end())); + } + } + + case Proto::Transaction::kErc20Transfer: { + Data tokenToAddress = addressStringToData(input.transaction().erc20_transfer().to()); + switch (input.tx_mode()) { + case Proto::TransactionMode::Legacy: + default: + return TransactionNonTyped::buildERC20Transfer( + nonce, gasPrice, gasLimit, + /* tokenContract: */ toAddress, + /* toAddress */ tokenToAddress, + /* amount: */ load(input.transaction().erc20_transfer().amount())); + + case Proto::TransactionMode::Enveloped: // Eip1559 + return TransactionEip1559::buildERC20Transfer( + nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, + /* tokenContract: */ toAddress, + /* toAddress */ tokenToAddress, + /* amount: */ load(input.transaction().erc20_transfer().amount())); + } + } + + case Proto::Transaction::kErc20Approve: { + Data spenderAddress = addressStringToData(input.transaction().erc20_approve().spender()); + switch (input.tx_mode()) { + case Proto::TransactionMode::Legacy: + default: + return TransactionNonTyped::buildERC20Approve( + nonce, gasPrice, gasLimit, + /* tokenContract: */ toAddress, + /* toAddress */ spenderAddress, + /* amount: */ load(input.transaction().erc20_approve().amount())); + + case Proto::TransactionMode::Enveloped: // Eip1559 + return TransactionEip1559::buildERC20Approve( + nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, + /* tokenContract: */ toAddress, + /* toAddress */ spenderAddress, + /* amount: */ load(input.transaction().erc20_approve().amount())); + } + } + + case Proto::Transaction::kErc721Transfer: { + Data tokenToAddress = addressStringToData(input.transaction().erc721_transfer().to()); + Data tokenFromAddress = addressStringToData(input.transaction().erc721_transfer().from()); + switch (input.tx_mode()) { + case Proto::TransactionMode::Legacy: default: - { - switch (input.tx_mode()) { - case Proto::TransactionMode::Legacy: - default: - return TransactionNonTyped::buildNativeTransfer( - nonce, gasPrice, gasLimit, - /* to: */ toAddress, - /* amount: */ load(input.transaction().contract_generic().amount()), - /* transaction: */ Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); - - case Proto::TransactionMode::Enveloped: // Eip1559 - return TransactionEip1559::buildNativeTransfer( - nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, - /* to: */ toAddress, - /* amount: */ load(input.transaction().contract_generic().amount()), - /* transaction: */ Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); - } - } + return TransactionNonTyped::buildERC721Transfer( + nonce, gasPrice, gasLimit, + /* tokenContract: */ toAddress, + /* fromAddress: */ tokenFromAddress, + /* toAddress */ tokenToAddress, + /* tokenId: */ load(input.transaction().erc721_transfer().token_id())); + + case Proto::TransactionMode::Enveloped: // Eip1559 + return TransactionEip1559::buildERC721Transfer( + nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, + /* tokenContract: */ toAddress, + /* fromAddress: */ tokenFromAddress, + /* toAddress */ tokenToAddress, + /* tokenId: */ load(input.transaction().erc721_transfer().token_id())); + } + } + + case Proto::Transaction::kErc1155Transfer: { + Data tokenToAddress = addressStringToData(input.transaction().erc1155_transfer().to()); + Data tokenFromAddress = addressStringToData(input.transaction().erc1155_transfer().from()); + switch (input.tx_mode()) { + case Proto::TransactionMode::Legacy: + default: + return TransactionNonTyped::buildERC1155Transfer( + nonce, gasPrice, gasLimit, + /* tokenContract: */ toAddress, + /* fromAddress: */ tokenFromAddress, + /* toAddress */ tokenToAddress, + /* tokenId: */ load(input.transaction().erc1155_transfer().token_id()), + /* value */ load(input.transaction().erc1155_transfer().value()), + /* data */ Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end())); + + case Proto::TransactionMode::Enveloped: // Eip1559 + return TransactionEip1559::buildERC1155Transfer( + nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, + /* tokenContract: */ toAddress, + /* fromAddress: */ tokenFromAddress, + /* toAddress */ tokenToAddress, + /* tokenId: */ load(input.transaction().erc1155_transfer().token_id()), + /* value */ load(input.transaction().erc1155_transfer().value()), + /* data */ Data(input.transaction().erc1155_transfer().data().begin(), input.transaction().erc1155_transfer().data().end())); + } + } + + case Proto::Transaction::kContractGeneric: + default: { + switch (input.tx_mode()) { + case Proto::TransactionMode::Legacy: + default: + return TransactionNonTyped::buildNativeTransfer( + nonce, gasPrice, gasLimit, + /* to: */ toAddress, + /* amount: */ load(input.transaction().contract_generic().amount()), + /* transaction: */ Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); + + case Proto::TransactionMode::Enveloped: // Eip1559 + return TransactionEip1559::buildNativeTransfer( + nonce, maxInclusionFeePerGas, maxFeePerGas, gasLimit, + /* to: */ toAddress, + /* amount: */ load(input.transaction().contract_generic().amount()), + /* transaction: */ Data(input.transaction().contract_generic().data().begin(), input.transaction().contract_generic().data().end())); + } + } } } @@ -235,3 +261,5 @@ Signature Signer::sign(const PrivateKey& privateKey, const uint256_t& chainID, s auto preHash = transaction->preHash(chainID); return Signer::sign(privateKey, preHash, transaction->usesReplayProtection(), chainID); } + +} // namespace TW::Ethereum diff --git a/src/Ethereum/Signer.h b/src/Ethereum/Signer.h index 2111b5428b3..3fb42172c1d 100644 --- a/src/Ethereum/Signer.h +++ b/src/Ethereum/Signer.h @@ -8,7 +8,7 @@ #include "RLP.h" #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/Ethereum.pb.h" @@ -34,6 +34,8 @@ class Signer { /// Signs the given transaction. static Signature sign(const PrivateKey& privateKey, const uint256_t& chainID, std::shared_ptr transaction) noexcept; + /// Compiles a Proto::SigningInput transaction, with external signature + static Proto::SigningOutput compile(const Proto::SigningInput& input, const Data& signature) noexcept; public: /// build Transaction from signing input @@ -41,21 +43,20 @@ class Signer { /// Signs a hash with the given private key for the given chain identifier. /// - /// @returns the r, s, and v values of the transaction signature + /// \returns the r, s, and v values of the transaction signature static Signature sign(const PrivateKey& privateKey, const Data& hash, bool includeEip155, const uint256_t& chainID) noexcept; /// Break up the signature into the R, S, and V values. - /// @returns the r, s, and v values of the transaction signature - static Signature signatureDataToStruct(const Data& signature) noexcept; + /// \returns the r, s, and v values of the transaction signature + static Signature signatureDataToStruct(const Data& signature, bool includeEip155, const uint256_t& chainID) noexcept; + + /// Break up the signature into the R, S, and V values, with no replay protection. + /// \returns the r, s, and v values of the transaction signature + static Signature signatureDataToStructSimple(const Data& signature) noexcept; /// Break up the signature into the R, S, and V values, and include chainID in V for replay protection (Eip155) - /// @returns the r, s, and v values of the transaction signature + /// \returns the r, s, and v values of the transaction signature static Signature signatureDataToStructWithEip155(const uint256_t& chainID, const Data& signature) noexcept; }; } // namespace TW::Ethereum - -/// Wrapper for C interface. -struct TWEthereumSigner { - TW::Ethereum::Signer impl; -}; diff --git a/src/Ethereum/Transaction.cpp b/src/Ethereum/Transaction.cpp index aed18318f36..d033dbb6f80 100644 --- a/src/Ethereum/Transaction.cpp +++ b/src/Ethereum/Transaction.cpp @@ -6,49 +6,55 @@ #include "Transaction.h" #include "ABI/Function.h" -#include "ABI/ParamBase.h" #include "ABI/ParamAddress.h" -#include "RLP.h" +#include "ABI/ParamBase.h" #include "HexCoding.h" +#include "RLP.h" -using namespace TW::Ethereum::ABI; -using namespace TW::Ethereum; -using namespace TW; - +namespace TW::Ethereum { static const Data EmptyListEncoded = parse_hex("c0"); -std::shared_ptr TransactionNonTyped::buildNativeTransfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& toAddress, const uint256_t& amount, const Data& data) { +std::shared_ptr +TransactionNonTyped::buildNativeTransfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& toAddress, const uint256_t& amount, const Data& data) { return std::make_shared(nonce, gasPrice, gasLimit, toAddress, amount, data); } -std::shared_ptr TransactionNonTyped::buildERC20Transfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& toAddress, const uint256_t& amount) { +std::shared_ptr +TransactionNonTyped::buildERC20Transfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& toAddress, const uint256_t& amount) { return std::make_shared(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC20TransferCall(toAddress, amount)); } -std::shared_ptr TransactionNonTyped::buildERC20Approve(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount) { +std::shared_ptr +TransactionNonTyped::buildERC20Approve(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount) { return std::make_shared(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC20ApproveCall(spenderAddress, amount)); } -std::shared_ptr TransactionNonTyped::buildERC721Transfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId) { +std::shared_ptr +TransactionNonTyped::buildERC721Transfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId) { return std::make_shared(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC721TransferFromCall(from, to, tokenId)); } -std::shared_ptr TransactionNonTyped::buildERC1155Transfer(const uint256_t& nonce, - const uint256_t& gasPrice, const uint256_t& gasLimit, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { +std::shared_ptr +TransactionNonTyped::buildERC1155Transfer(const uint256_t& nonce, + const uint256_t& gasPrice, const uint256_t& gasLimit, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { return std::make_shared(nonce, gasPrice, gasLimit, tokenContract, 0, buildERC1155TransferFromCall(from, to, tokenId, value, data)); } Data TransactionNonTyped::preHash(const uint256_t chainID) const { + return Hash::keccak256(serialize(chainID)); +} + +Data TransactionNonTyped::serialize(const uint256_t chainID) const { Data encoded; append(encoded, RLP::encode(nonce)); append(encoded, RLP::encode(gasPrice)); @@ -59,10 +65,10 @@ Data TransactionNonTyped::preHash(const uint256_t chainID) const { append(encoded, RLP::encode(chainID)); append(encoded, RLP::encode(0)); append(encoded, RLP::encode(0)); - return Hash::keccak256(RLP::encodeList(encoded)); + return RLP::encodeList(encoded); } -Data TransactionNonTyped::encoded(const Signature& signature, const uint256_t chainID) const { +Data TransactionNonTyped::encoded(const Signature& signature, [[maybe_unused]] const uint256_t chainID) const { Data encoded; append(encoded, RLP::encode(nonce)); append(encoded, RLP::encode(gasPrice)); @@ -77,50 +83,62 @@ Data TransactionNonTyped::encoded(const Signature& signature, const uint256_t ch } Data TransactionNonTyped::buildERC20TransferCall(const Data& to, const uint256_t& amount) { - auto func = Function("transfer", std::vector>{ - std::make_shared(to), - std::make_shared(amount) + // clang-format off + auto func = ABI::Function("transfer", std::vector>{ + std::make_shared(to), + std::make_shared(amount) }); + // clang-format on Data payload; func.encode(payload); return payload; } Data TransactionNonTyped::buildERC20ApproveCall(const Data& spender, const uint256_t& amount) { - auto func = Function("approve", std::vector>{ - std::make_shared(spender), - std::make_shared(amount) + // clang-format off + auto func = ABI::Function("approve", std::vector>{ + std::make_shared(spender), + std::make_shared(amount) }); + // clang-format on Data payload; func.encode(payload); return payload; } Data TransactionNonTyped::buildERC721TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId) { - auto func = Function("transferFrom", std::vector>{ - std::make_shared(from), - std::make_shared(to), - std::make_shared(tokenId) + // clang-format off + auto func = ABI::Function("transferFrom", std::vector>{ + std::make_shared(from), + std::make_shared(to), + std::make_shared(tokenId) }); + // clang-format on Data payload; func.encode(payload); return payload; } Data TransactionNonTyped::buildERC1155TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { - auto func = Function("safeTransferFrom", std::vector>{ - std::make_shared(from), - std::make_shared(to), - std::make_shared(tokenId), - std::make_shared(value), - std::make_shared(data) + // clang-format off + auto func = ABI::Function("safeTransferFrom", std::vector>{ + std::make_shared(from), + std::make_shared(to), + std::make_shared(tokenId), + std::make_shared(value), + std::make_shared(data) }); + // clang-format on Data payload; func.encode(payload); return payload; } Data TransactionEip1559::preHash(const uint256_t chainID) const { + return Hash::keccak256(serialize(chainID)); +} + +Data TransactionEip1559::serialize(const uint256_t chainID) const { Data encoded; append(encoded, RLP::encode(chainID)); append(encoded, RLP::encode(nonce)); @@ -135,7 +153,7 @@ Data TransactionEip1559::preHash(const uint256_t chainID) const { Data envelope; append(envelope, static_cast(type)); append(envelope, RLP::encodeList(encoded)); - return Hash::keccak256(envelope); + return envelope; } Data TransactionEip1559::encoded(const Signature& signature, const uint256_t chainID) const { @@ -159,32 +177,39 @@ Data TransactionEip1559::encoded(const Signature& signature, const uint256_t cha return envelope; } -std::shared_ptr TransactionEip1559::buildNativeTransfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& toAddress, const uint256_t& amount, const Data& data) { +std::shared_ptr +TransactionEip1559::buildNativeTransfer(const uint256_t& nonce, + const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, + const Data& toAddress, const uint256_t& amount, const Data& data) { return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, toAddress, amount, data); } -std::shared_ptr TransactionEip1559::buildERC20Transfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& toAddress, const uint256_t& amount) { +std::shared_ptr +TransactionEip1559::buildERC20Transfer(const uint256_t& nonce, + const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, + const Data& tokenContract, const Data& toAddress, const uint256_t& amount) { return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, tokenContract, 0, TransactionNonTyped::buildERC20TransferCall(toAddress, amount)); } -std::shared_ptr TransactionEip1559::buildERC20Approve(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount) { +std::shared_ptr +TransactionEip1559::buildERC20Approve(const uint256_t& nonce, + const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, + const Data& tokenContract, const Data& spenderAddress, const uint256_t& amount) { return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, tokenContract, 0, TransactionNonTyped::buildERC20ApproveCall(spenderAddress, amount)); } -std::shared_ptr TransactionEip1559::buildERC721Transfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId) { +std::shared_ptr +TransactionEip1559::buildERC721Transfer(const uint256_t& nonce, + const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId) { return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, tokenContract, 0, TransactionNonTyped::buildERC721TransferFromCall(from, to, tokenId)); } -std::shared_ptr TransactionEip1559::buildERC1155Transfer(const uint256_t& nonce, - const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, - const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { +std::shared_ptr +TransactionEip1559::buildERC1155Transfer(const uint256_t& nonce, + const uint256_t& maxInclusionFeePerGas, const uint256_t& maxFeePerGas, const uint256_t& gasPrice, + const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data) { return std::make_shared(nonce, maxInclusionFeePerGas, maxFeePerGas, gasPrice, tokenContract, 0, TransactionNonTyped::buildERC1155TransferFromCall(from, to, tokenId, value, data)); } + +} // namespace TW::Ethereum diff --git a/src/Ethereum/Transaction.h b/src/Ethereum/Transaction.h index 64bbe8fe729..d43fd77aa10 100644 --- a/src/Ethereum/Transaction.h +++ b/src/Ethereum/Transaction.h @@ -37,6 +37,8 @@ class TransactionBase { virtual ~TransactionBase() {} // pre-sign hash of the tx, for signing virtual Data preHash(const uint256_t chainID) const = 0; + // pre-sign image of tx + virtual Data serialize(const uint256_t chainID) const = 0; // encoded tx (signed) virtual Data encoded(const Signature& signature, const uint256_t chainID) const = 0; // Signals wether this tx type uses Eip155-style replay protection in the signature @@ -88,6 +90,7 @@ class TransactionNonTyped: public TransactionBase { static Data buildERC1155TransferFromCall(const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data); virtual Data preHash(const uint256_t chainID) const; + virtual Data serialize(const uint256_t chainID) const; virtual Data encoded(const Signature& signature, const uint256_t chainID) const; virtual bool usesReplayProtection() const { return true; } @@ -151,6 +154,7 @@ class TransactionEip1559: public TransactionTyped { const Data& tokenContract, const Data& from, const Data& to, const uint256_t& tokenId, const uint256_t& value, const Data& data); virtual Data preHash(const uint256_t chainID) const; + virtual Data serialize(const uint256_t chainID) const; virtual Data encoded(const Signature& signature, const uint256_t chainID) const; public: diff --git a/src/Everscale/Address.cpp b/src/Everscale/Address.cpp new file mode 100644 index 00000000000..177313473cd --- /dev/null +++ b/src/Everscale/Address.cpp @@ -0,0 +1,77 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include + +#include "Address.h" +#include "HexCoding.h" +#include "Wallet.h" +#include "WorkchainType.h" + +using namespace TW; + +namespace TW::Everscale { + +using MaybeWorkchain = std::optional>; + +MaybeWorkchain parseWorkchainId(const std::string& string) { + if (auto pos = string.find(':'); pos != std::string::npos) { + try { + auto workchainId = static_cast(std::stoi(string.substr(0, pos))); + return std::make_pair(workchainId, pos + 1); + } catch (...) { + // Do nothing and return empty value later + } + } + + return {}; +} + +bool Address::isValid(const std::string& string) noexcept { + auto parsed = parseWorkchainId(string); + if (!parsed.has_value()) { + return false; + } + + auto [workchainId, pos] = *parsed; + + if (workchainId != WorkchainType::Basechain && workchainId != WorkchainType::Masterchain) { + return false; + } + + if (string.size() != pos + hexAddrLen) { + return false; + } + + std::string addr = string.substr(pos); + return parse_hex(addr).size() == size; +} + +Address::Address(const std::string& string) { + if (!Address::isValid(string)) { + throw std::invalid_argument("Invalid address string!"); + } + + auto parsed = parseWorkchainId(string); + auto [parsedWorkchainId, pos] = *parsed; + + workchainId = parsedWorkchainId; + + const auto parsedHash = parse_hex(string.substr(pos)); + std::copy(begin(parsedHash), end(parsedHash), begin(hash)); +} + +Address::Address(const PublicKey& publicKey, int8_t workchainId) + : Address(InitData(publicKey).computeAddr(workchainId)) { +} + +std::string Address::string() const { + std::string address = std::to_string(workchainId) + ":" + hex(hash); + return address; +} + +} // namespace TW::Everscale diff --git a/src/Everscale/Address.h b/src/Everscale/Address.h new file mode 100644 index 00000000000..d97ec61f44c --- /dev/null +++ b/src/Everscale/Address.h @@ -0,0 +1,51 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" +#include "../PublicKey.h" + +#include +#include + +namespace TW::Everscale { + +class Address { +public: + /// Number of bytes in an address + static const size_t size = Hash::sha256Size; + + /// Hex address length + static const size_t hexAddrLen = size * 2; + + /// Workchain ID (-1 for masterchain, 0 for base workchain) + std::int8_t workchainId; + /// StateInit hash + std::array hash{}; + + /// Determines whether a string makes a valid address. + [[nodiscard]] static bool isValid(const std::string& string) noexcept; + + /// Initializes an Everscale address with a string representation. + explicit Address(const std::string& string); + + /// Initializes an Everscale address with a public key and a workchain id. + explicit Address(const PublicKey& publicKey, int8_t workchainId); + + /// Initializes an Everscale address with its parts + explicit Address(int8_t workchainId, std::array hash) + : workchainId(workchainId), hash(hash) {} + + /// Returns a string representation of the address. + [[nodiscard]] std::string string() const; +}; + +inline bool operator==(const Address& lhs, const Address& rhs) { + return lhs.workchainId == rhs.workchainId && lhs.hash == rhs.hash; +} + +} // namespace TW::Everscale diff --git a/src/Everscale/Cell.cpp b/src/Everscale/Cell.cpp new file mode 100644 index 00000000000..8d1349937c9 --- /dev/null +++ b/src/Everscale/Cell.cpp @@ -0,0 +1,405 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cell.h" + +#include +#include +#include +#include + +#include "../BinaryCoding.h" + +#include +#include + +#ifdef _MSC_VER +#include +typedef SSIZE_T ssize_t; +#endif + +using namespace TW; + +namespace TW::Everscale { + +constexpr static uint32_t BOC_MAGIC = 0xb5ee9c72; + +uint16_t computeBitLen(const Data& data, bool aligned) { + auto bitLen = static_cast(data.size() * 8); + if (aligned) { + return bitLen; + } + + for (auto i = static_cast(data.size() - 1); i >= 0; --i) { + if (data[i] == 0) { + bitLen -= 8; + } else { + auto skip = 1; + uint8_t mask = 1; + while ((data[i] & mask) == 0) { + skip += 1; + mask <<= 1; + } + bitLen -= skip; + break; + } + } + return bitLen; +} + +struct Reader { + const uint8_t* _Nonnull buffer; + size_t bufferLen; + size_t offset = 0; + + explicit Reader(const uint8_t* _Nonnull buffer, size_t len) noexcept + : buffer(buffer), bufferLen(len) { + } + + void require(size_t bytes) const { + if (offset + bytes > bufferLen) { + throw std::runtime_error("unexpected eof"); + } + } + + void advance(size_t bytes) { + offset += bytes; + } + + const uint8_t* _Nonnull data() const { + return buffer + offset; + } + + size_t readNextUint(uint8_t len) { + const auto* _Nonnull p = data(); + advance(len); + + switch (len) { + case 1: + return static_cast(*p); + case 2: + return static_cast(decode16BE(p)); + case 3: + return static_cast(p[2]) | (static_cast(p[1]) << 8) | (static_cast(p[0]) << 16); + case 4: + return static_cast(decode32BE(p)); + default: + // Unreachable in valid cells + return 0; + } + } +}; + +std::shared_ptr Cell::fromBase64(const std::string& encoded) { + // `Hash::base64` trims \0 bytes from the end of the _decoded_ data, so + // raw transform is used here + using namespace boost::archive::iterators; + using It = transform_width, 8, 6>; + Data boc(It(begin(encoded)), It(end(encoded))); + return Cell::deserialize(boc.data(), boc.size()); +} + +std::shared_ptr Cell::deserialize(const uint8_t* _Nonnull data, size_t len) { + Reader reader(data, len); + + // Parse header + reader.require(6); + // 1. Magic + if (reader.readNextUint(sizeof(BOC_MAGIC)) != BOC_MAGIC) { + throw std::runtime_error("unknown magic"); + } + // 2. Flags + struct Flags { + uint8_t refSize : 3; + uint8_t : 2; // unused + bool hasCacheBits : 1; + bool hasCrc : 1; + bool indexIncluded : 1; + }; + + static_assert(sizeof(Flags) == 1, "flags must be represented as 1 byte"); + const auto flags = reinterpret_cast(reader.data())[0]; + const auto refSize = flags.refSize; + const auto offsetSize = reader.data()[1]; + reader.advance(2); + + // 3. Counters and root index + reader.require(refSize * 3 + offsetSize + refSize); + const auto cellCount = reader.readNextUint(refSize); + const auto rootCount = reader.readNextUint(refSize); + if (rootCount != 1) { + throw std::runtime_error("unsupported root count"); + } + if (rootCount > cellCount) { + throw std::runtime_error("root count is greater than cell count"); + } + const auto absent_count = reader.readNextUint(refSize); + if (absent_count > 0) { + throw std::runtime_error("absent cells are not supported"); + } + + reader.readNextUint(offsetSize); // total cell size + + const auto rootIndex = reader.readNextUint(refSize); + + // 4. Cell offsets (skip if specified) + if (flags.indexIncluded) { + reader.advance(cellCount * offsetSize); + } + + // 5. Deserialize cells + struct IntermediateCell { + uint16_t bitLen; + Data data; + std::vector references; + }; + + std::vector intermediate{}; + intermediate.reserve(cellCount); + + for (size_t i = 0; i < cellCount; ++i) { + struct Descriptor { + uint8_t refCount : 3; + bool exotic : 1; + bool storeHashes : 1; + uint8_t level : 3; + }; + + static_assert(sizeof(Descriptor) == 1, "cell descriptor must be represented as 1 byte"); + + reader.require(2); + const auto d1 = reinterpret_cast(reader.data())[0]; + if (d1.level != 0) { + throw std::runtime_error("non-zero level is not supported"); + } + if (d1.exotic) { + throw std::runtime_error("exotic cells are not supported"); + } + if (d1.refCount == 7 && d1.storeHashes) { + throw std::runtime_error("absent cells are not supported"); + } + if (d1.refCount > 4) { + throw std::runtime_error("invalid ref count"); + } + const auto d2 = reader.data()[1]; + const auto byteLen = (d2 >> 1) + (d2 & 0b1); + reader.advance(2); + + // Skip stored hashes + if (d1.storeHashes) { + reader.advance(sizeof(uint16_t) + Hash::sha256Size); + } + + reader.require(byteLen); + Data cellData(byteLen); + std::memcpy(cellData.data(), reader.data(), byteLen); + reader.advance(byteLen); + + std::vector references{}; + references.reserve(refSize * d1.refCount); + reader.require(refSize * d1.refCount); + for (size_t r = 0; r < d1.refCount; ++r) { + const auto index = reader.readNextUint(refSize); + if (index > cellCount || index <= i) { + throw std::runtime_error("invalid child index"); + } + + references.push_back(index); + } + + const auto bitLen = computeBitLen(cellData, (d2 & 0b1) == 0); + intermediate.emplace_back( + IntermediateCell{ + .bitLen = bitLen, + .data = std::move(cellData), + .references = std::move(references), + }); + } + + std::unordered_map doneCells{}; + + size_t index = cellCount; + for (auto it = intermediate.rbegin(); it != intermediate.rend(); ++it, --index) { + auto& raw = *it; + + Cell::Refs references{}; + for (size_t r = 0; r < raw.references.size(); ++r) { + const auto child = doneCells.find(raw.references[r]); + if (child == doneCells.end()) { + throw std::runtime_error("child cell not found"); + } + references[r] = child->second; + } + + auto cell = std::make_shared(raw.bitLen, std::move(raw.data), raw.references.size(), std::move(references)); + cell->finalize(); + doneCells.emplace(index - 1, cell); + } + + const auto root = doneCells.find(rootIndex); + if (root == doneCells.end()) { + throw std::runtime_error("root cell not found"); + } + return std::move(root->second); +} + +class SerializationContext { +public: + static SerializationContext build(const Cell& cell) { + SerializationContext ctx{}; + fillContext(cell, ctx); + return ctx; + } + + void encode(Data& os) const { + os.reserve(os.size() + HEADER_SIZE + cellsSize); + + const auto cellCount = static_cast(reversedCells.size()); + + // Write header + encode32BE(BOC_MAGIC, os); + os.push_back(REF_SIZE); + os.push_back(OFFSET_SIZE); + encode16BE(static_cast(cellCount), os); + encode16BE(1, os); // root count + encode16BE(0, os); // absent cell count + encode16BE(static_cast(cellsSize), os); + encode16BE(0, os); // root cell index + + // Write cells + size_t i = cellCount - 1; + while (true) { + const auto& cell = *reversedCells[i]; + + // Write cell data + const auto [d1, d2] = cell.getDescriptorBytes(); + os.push_back(d1); + os.push_back(d2); + os.insert(os.end(), cell.data.begin(), cell.data.end()); + + // Write cell references + for (const auto& child : cell.references) { + if (child == nullptr) { + break; + } + + // Map cell hash to index (which must be presented) + const auto it = indices.find(child->hash); + assert(it != indices.end()); + + encode16BE(cellCount - it->second - 1, os); + } + + if (i == 0) { + break; + } else { + --i; + } + } + } + +private: + // uint16_t will be enough for wallet transactions (e.g. 64k is the size of the whole elector) + using ref_t = uint16_t; + using offset_t = uint16_t; + + constexpr static uint8_t REF_SIZE = sizeof(ref_t); + constexpr static uint8_t OFFSET_SIZE = sizeof(offset_t); + constexpr static size_t HEADER_SIZE = + /*magic*/ sizeof(BOC_MAGIC) + + /*ref_size*/ 1 + + /*offset_size*/ 1 + + /*cell_count*/ REF_SIZE + + /*root_count*/ REF_SIZE + + /*absent_count*/ REF_SIZE + + /*data_size*/ OFFSET_SIZE + + /*root_cell_index*/ REF_SIZE; + + size_t cellsSize = 0; + ref_t index = 0; + std::map indices{}; + std::vector reversedCells{}; + + static void fillContext(const Cell& cell, SerializationContext& ctx) { + if (ctx.indices.find(cell.hash) != ctx.indices.end()) { + return; + } + + for (const auto& ref : cell.references) { + if (ref == nullptr) { + break; + } + fillContext(*ref, ctx); + } + + ctx.indices.insert(std::make_pair(cell.hash, ctx.index++)); + ctx.reversedCells.emplace_back(&cell); + ctx.cellsSize += cell.serializedSize(REF_SIZE); + } +}; + +void Cell::serialize(Data& os) const { + assert(finalized); + const auto ctx = SerializationContext::build(*this); + ctx.encode(os); +} + +void Cell::finalize() { + if (finalized) { + return; + } + + if (bitLen > Cell::MAX_BITS || refCount > Cell::MAX_REFS) { + throw std::invalid_argument("invalid cell"); + } + + // Finalize child cells and update current cell depth + // NOTE: Use before context creation to reduce stack size + for (const auto& ref : references) { + if (ref == nullptr) { + break; + } + ref->finalize(); + depth = std::max(depth, static_cast(ref->depth + 1)); + } + + // Compute cell hash + const auto dataSize = std::min(data.size(), static_cast((bitLen + 7) / 8)); + + Data normalized{}; + normalized.reserve(/* descriptor bytes */ 2 + dataSize + refCount * (sizeof(uint16_t) + Hash::sha256Size)); + + // Write descriptor bytes + const auto [d1, d2] = getDescriptorBytes(); + normalized.push_back(d1); + normalized.push_back(d2); + std::copy(data.begin(), std::next(data.begin(), static_cast(dataSize)), std::back_inserter(normalized)); + + // Write all children depths + for (const auto& ref : references) { + if (ref == nullptr) { + break; + } + encode16BE(ref->depth, normalized); + } + + // Write all children hashes + for (const auto& ref : references) { + if (ref == nullptr) { + break; + } + std::copy(ref->hash.begin(), ref->hash.end(), std::back_inserter(normalized)); + } + + // Done + const auto computedHash = Hash::sha256(normalized); + assert(computedHash.size() == Hash::sha256Size); + + std::copy(computedHash.begin(), computedHash.end(), hash.begin()); + finalized = true; +} + +} // namespace TW::Everscale diff --git a/src/Everscale/Cell.h b/src/Everscale/Cell.h new file mode 100644 index 00000000000..8ff98f09c28 --- /dev/null +++ b/src/Everscale/Cell.h @@ -0,0 +1,70 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include +#include + +#include + +#include "Data.h" +#include "../Hash.h" + +//win +#ifdef _MSC_VER +#define _Nonnull +#endif +namespace TW::Everscale { + +class Cell { +public: + constexpr static uint16_t MAX_BITS = 1023; + constexpr static uint8_t MAX_REFS = 4; + + using Ref = std::shared_ptr; + using Refs = std::array; + using CellHash = std::array; + + bool finalized = false; + uint16_t bitLen = 0; + std::vector data{}; + uint8_t refCount = 0; + Refs references{}; + + uint16_t depth = 0; + CellHash hash{}; + + Cell() = default; + + Cell(uint16_t bitLen, std::vector data, uint8_t refCount, Refs references) + : bitLen(bitLen), data(std::move(data)), refCount(refCount), references(std::move(references)) {} + + // Deserialize from Base64 + static std::shared_ptr fromBase64(const std::string& encoded); + + // Deserialize from BOC representation + static std::shared_ptr deserialize(const uint8_t* _Nonnull data, size_t len); + + // Serialize to binary stream + void serialize(Data& os) const; + + // Compute cell depth and hash + void finalize(); + + [[nodiscard]] inline std::pair getDescriptorBytes() const noexcept { + const uint8_t d1 = refCount; + const uint8_t d2 = (static_cast(bitLen >> 2) & 0b11111110) | (bitLen % 8 != 0); + return std::pair{d1, d2}; + } + + [[nodiscard]] inline size_t serializedSize(uint8_t refSize) const noexcept { + return 2 + (bitLen + 7) / 8 + refCount * refSize; + } +}; + +} // namespace TW::Everscale diff --git a/src/Everscale/CellBuilder.cpp b/src/Everscale/CellBuilder.cpp new file mode 100644 index 00000000000..8ddc3ccbc94 --- /dev/null +++ b/src/Everscale/CellBuilder.cpp @@ -0,0 +1,260 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "CellBuilder.h" +#include "Cell.h" + +#include +#include +#include + +#include "../BinaryCoding.h" + +using namespace TW; + +namespace TW::Everscale { + +CellBuilder::CellBuilder(Data& appendedData, uint16_t bits) { + assert(bits <= appendedData.size() * 8); + assert(bits < Cell::MAX_BITS); + assert(bits % 8 == 0); + + appendedData.resize(bits / 8); + + data = appendedData; + bitLen = bits; + references = {}; +} + +void CellBuilder::appendBitZero() { + Data appendedData{0x00}; + appendRaw(appendedData, 1); +} + +void CellBuilder::appendBitOne() { + Data appendedData{0xFF}; + appendRaw(appendedData, 1); +} + +void CellBuilder::appendBitBool(bool bit) { + auto getAppendedData = [](bool bit) { + if (bit) { + return Data{0xFF}; + } else { + return Data{0x00}; + } + }; + auto appendedData = getAppendedData(bit); + appendRaw(appendedData, 1); +} + +void CellBuilder::appendU8(uint8_t value) { + Data appendedData{value}; + appendRaw(appendedData, 8); +} + +void CellBuilder::appendU32(uint32_t value) { + Data appendedData; + encode32BE(value, appendedData); + appendRaw(appendedData, 32); +} + +void CellBuilder::appendU64(uint64_t value) { + Data appendedData; + encode64BE(value, appendedData); + appendRaw(appendedData, 64); +} + +void CellBuilder::appendU128(const uint128_t& value) { + uint8_t bits = 4; + uint16_t bytes = 16 - clzU128(value) / 8; + + appendBits(bytes, bits); + + Data encodedValue; + encode128BE(value, encodedValue); + + auto offset = static_cast(encodedValue.size() - bytes); + + Data appendedData(encodedValue.begin() + offset, encodedValue.end()); + appendRaw(appendedData, bytes * 8); +} + +void CellBuilder::appendI8(int8_t value) { + Data appendedData{static_cast(value)}; + appendRaw(appendedData, 8); +} + +void CellBuilder::appendBits(uint64_t value, uint8_t bits) { + assert(bits >= 1 && bits <= 7); + + Data appendedData; + + auto val = static_cast(value << (8 - bits)); + appendedData.push_back(val); + + appendRaw(appendedData, bits); +} + +void CellBuilder::appendRaw(const Data& appendedData, uint16_t bits) { + if (appendedData.size() * 8 < bits) { + throw std::invalid_argument("invalid builder data"); + } else if (bitLen + bits > Cell::MAX_BITS) { + throw std::runtime_error("cell data overflow"); + } else if (bits != 0) { + if ((bitLen % 8) == 0) { + if ((bits % 8) == 0) { + appendWithoutShifting(appendedData, bits); + } else { + appendWithSliceShifting(appendedData, bits); + } + } else { + appendWithDoubleShifting(appendedData, bits); + } + } + assert(bitLen <= Cell::MAX_BITS); + assert(data.size() * 8 <= Cell::MAX_BITS + 1); +} + +void CellBuilder::prependRaw(Data& appendedData, uint16_t bits) { + if (bits != 0) { + auto buffer = CellBuilder(appendedData, bits); + buffer.appendRaw(data, bitLen); + + data = buffer.data; + bitLen = buffer.bitLen; + } +} + +void CellBuilder::appendReferenceCell(std::shared_ptr child) { + if (child) { + if (references.size() + 1 > Cell::MAX_REFS) { + throw std::runtime_error("cell refs overflow"); + } + references.emplace_back(std::move(child)); + } +} + +void CellBuilder::appendBuilder(const CellBuilder& builder) { + appendRaw(builder.data, builder.bitLen); + for (const auto& reference : builder.references) { + appendReferenceCell(reference); + } +} + +void CellBuilder::appendCellSlice(const CellSlice& other) { + Data appendedData(other.cell->data); + appendRaw(appendedData, other.cell->bitLen); + + for (const auto& cell : other.cell->references) { + appendReferenceCell(cell); + } +} + +Cell::Ref CellBuilder::intoCell() { + // Append tag + if (bitLen & 7) { + const auto mask = static_cast(0x80 >> (bitLen & 7)); + const auto l = bitLen / 8; + data[l] = static_cast((data[l] & ~mask) | mask); + } + + auto refCount = references.size(); + + Cell::Refs refs; + std::move(references.begin(), references.end(), refs.begin()); + + auto cell = std::make_shared(bitLen, std::move(data), refCount, std::move(refs)); + cell->finalize(); + + bitLen = 0; + refCount = 0; + + return cell; +} + +void CellBuilder::appendWithoutShifting(const Data& appendedData, uint16_t bits) { + assert(bits % 8 == 0); + assert(bitLen % 8 == 0); + + data.resize(bitLen / 8); + data.insert(data.end(), appendedData.begin(), appendedData.end()); + bitLen += bits; + data.resize(bitLen / 8); +} + +void CellBuilder::appendWithSliceShifting(const Data& appendedData, uint16_t bits) { + assert(bits % 8 != 0); + assert(bitLen % 8 == 0); + + data.resize(bitLen / 8); + data.insert(data.end(), appendedData.begin(), appendedData.end()); + bitLen += bits; + data.resize(1 + bitLen / 8); + + data.back() &= ~static_cast(0xff >> (bits % 8)); +} + +void CellBuilder::appendWithDoubleShifting(const Data& appendedData, uint16_t bits) { + auto selfShift = bitLen % 8; + data.resize(1 + bitLen / 8); + bitLen += bits; + + // yyyyy000 -> 00000000 000yyyyy + auto y = static_cast(data.back() >> (8 - selfShift)); + data.pop_back(); + + for (const auto x : appendedData) { + // 00000000 000yyyyy -> 000yyyyy xxxxxxxx + y = static_cast(y << 8) | x; + // 000yyyyy xxxxxxxx -> 00000000 yyyyyxxx + data.push_back(static_cast(y >> selfShift)); + } + // 00000000 yyyyyxxx + data.push_back(static_cast(y << (8 - selfShift))); + + auto shift = bitLen % 8; + if (shift == 0) { + data.resize(bitLen / 8); + } else { + data.resize(bitLen / 8 + 1); + data.back() &= ~static_cast(0xff >> (bitLen % 8)); + } +} + +uint8_t CellBuilder::clzU128(const uint128_t& u) { + auto hi = static_cast(u >> 64); + auto lo = static_cast(u); + + if (lo == 0 && hi == 0) { + return 128; + } else if (hi == 0) { + return static_cast(std::countl_zero(lo) + 64); + } else { + return static_cast(std::countl_zero(hi)); + } +} + +void CellBuilder::encode128BE(const uint128_t& val, Data& data) { + data.emplace_back(static_cast((val >> 120))); + data.emplace_back(static_cast((val >> 112))); + data.emplace_back(static_cast((val >> 104))); + data.emplace_back(static_cast((val >> 96))); + data.emplace_back(static_cast((val >> 88))); + data.emplace_back(static_cast((val >> 80))); + data.emplace_back(static_cast((val >> 72))); + data.emplace_back(static_cast((val >> 64))); + data.emplace_back(static_cast((val >> 56))); + data.emplace_back(static_cast((val >> 48))); + data.emplace_back(static_cast((val >> 40))); + data.emplace_back(static_cast((val >> 32))); + data.emplace_back(static_cast((val >> 24))); + data.emplace_back(static_cast((val >> 16))); + data.emplace_back(static_cast((val >> 8))); + data.emplace_back(static_cast(val)); +} + +} // namespace TW::Everscale diff --git a/src/Everscale/CellBuilder.h b/src/Everscale/CellBuilder.h new file mode 100644 index 00000000000..8b192ec42b3 --- /dev/null +++ b/src/Everscale/CellBuilder.h @@ -0,0 +1,55 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include +#include + +#include "Cell.h" +#include "CellSlice.h" + +namespace TW::Everscale { + +class CellBuilder { + uint16_t bitLen = 0; + std::vector data{}; + std::vector references{}; + +public: + using uint128_t = boost::multiprecision::uint128_t; + + CellBuilder() = default; + CellBuilder(Data& appendedData, uint16_t bits); + + void appendBitZero(); + void appendBitOne(); + void appendU8(uint8_t value); + void appendBitBool(bool bit); + void appendU32(uint32_t value); + void appendU64(uint64_t value); + void appendU128(const uint128_t& value); + void appendI8(int8_t value); + void appendBits(uint64_t value, uint8_t bits); + void appendRaw(const Data& appendedData, uint16_t bits); + void prependRaw(Data& appendedData, uint16_t bits); + void appendReferenceCell(Cell::Ref child); + void appendBuilder(const CellBuilder& builder); + void appendCellSlice(const CellSlice& other); + + Cell::Ref intoCell(); + +private: + void appendWithoutShifting(const Data& data, uint16_t bits); + void appendWithSliceShifting(const Data& data, uint16_t bits); + void appendWithDoubleShifting(const Data& data, uint16_t bits); + + static uint8_t clzU128(const uint128_t& u); + static void encode128BE(const uint128_t& value, Data& data); +}; + +} // namespace TW::Everscale diff --git a/src/Everscale/CellSlice.cpp b/src/Everscale/CellSlice.cpp new file mode 100644 index 00000000000..346f4a65365 --- /dev/null +++ b/src/Everscale/CellSlice.cpp @@ -0,0 +1,59 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "CellSlice.h" + +#include + +#include "../BinaryCoding.h" + +using namespace TW; + +namespace TW::Everscale { + +uint32_t CellSlice::getNextU32() { + const auto bytes = getNextBytes(sizeof(uint32_t)); + return decode32BE(bytes.data()); +} + +Data CellSlice::getNextBytes(uint8_t bytes) { + if (bytes == 0) { + return Data{}; + } + require(bytes * 8); + Data result{}; + result.reserve(bytes); + + const size_t q = dataOffset / 8; + const auto r = dataOffset % 8; + const auto invR = 8 - r; + + dataOffset += bytes * 8; + + if (r == 0) { + const auto begin = cell->data.begin() + q; + const auto end = begin + bytes; + std::copy(begin, end, std::back_inserter(result)); + return result; + } + + for (size_t byte = q; byte < q + bytes; ++byte) { + auto bits = static_cast(static_cast(cell->data[byte]) << 8); + if (byte + 1 < cell->data.size()) { + bits |= static_cast(cell->data[byte + 1]); + } + result.push_back(static_cast(bits >> invR)); + } + return result; +} + +void CellSlice::require(uint16_t bits) const { + if (dataOffset + bits > cell->bitLen) { + throw std::runtime_error("cell data underflow"); + } +} + +} // namespace TW::Everscale diff --git a/src/Everscale/CellSlice.h b/src/Everscale/CellSlice.h new file mode 100644 index 00000000000..f7446f1482f --- /dev/null +++ b/src/Everscale/CellSlice.h @@ -0,0 +1,36 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include +#include + +#include "Cell.h" +//win +#ifdef _MSC_VER +#define _Nonnull +#endif + +namespace TW::Everscale { + +class CellSlice { +public: + const Cell* _Nonnull cell; + uint16_t dataOffset = 0; + + explicit CellSlice(const Cell* _Nonnull cell) noexcept + : cell(cell) {} + + uint32_t getNextU32(); + Data getNextBytes(uint8_t bytes); + +private: + void require(uint16_t bits) const; +}; + +} // namespace TW::Everscale diff --git a/src/Everscale/Entry.cpp b/src/Everscale/Entry.cpp new file mode 100644 index 00000000000..e17aa9d5997 --- /dev/null +++ b/src/Everscale/Entry.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Entry.h" + +#include "Address.h" +#include "Signer.h" +#include "WorkchainType.h" + +using namespace TW; +using namespace std; + +namespace TW::Everscale { + +// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { + return Address::isValid(address); +} + +string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& address) const { + return Address(address).string(); +} + +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { + return Address(publicKey, WorkchainType::Basechain).string(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +} // namespace TW::Everscale diff --git a/src/Everscale/Entry.h b/src/Everscale/Entry.h new file mode 100644 index 00000000000..c83a0d133ad --- /dev/null +++ b/src/Everscale/Entry.h @@ -0,0 +1,23 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../CoinEntry.h" + +namespace TW::Everscale { + +/// Entry point for implementation of Everscale coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry final : public CoinEntry { +public: + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* d) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; +}; + +} // namespace TW::Everscale diff --git a/src/Everscale/Messages.cpp b/src/Everscale/Messages.cpp new file mode 100644 index 00000000000..a20ee263f08 --- /dev/null +++ b/src/Everscale/Messages.cpp @@ -0,0 +1,146 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Messages.h" +#include "WorkchainType.h" + +using namespace TW; + +namespace TW::Everscale { + +void ExternalInboundMessageHeader::writeTo(CellBuilder& builder) const { + builder.appendBitOne(); + builder.appendBitZero(); + + // addr src (none) + builder.appendRaw(Data{0x00}, 2); + + // addr dst + Data dstAddr(_dst.hash.begin(), _dst.hash.end()); + + Data prefix{0x80}; + builder.appendRaw(prefix, 2); + + builder.appendBitZero(); + builder.appendI8(_dst.workchainId); + builder.appendRaw(dstAddr, 256); + + // fee + builder.appendU128(_importFee); +} + +void InternalMessageHeader::writeTo(CellBuilder& builder) const { + // tag + builder.appendBitZero(); + + builder.appendBitBool(_ihrDisabled); + builder.appendBitBool(_bounce); + builder.appendBitBool(_bounced); + + // addr src (none) + builder.appendRaw(Data{0x00}, 2); + + // addr dst + Data dstAddr(_dst.hash.begin(), _dst.hash.end()); + + Data prefix{0x80}; + builder.appendRaw(prefix, 2); + + builder.appendBitZero(); + builder.appendI8(_dst.workchainId); + builder.appendRaw(dstAddr, 256); + + // value + builder.appendU128(_value); + builder.appendBitZero(); + + // fee + builder.appendU128(_ihrFee); + builder.appendU128(_fwdFee); + + // created + builder.appendU64(_createdLt); + builder.appendU32(_createdAt); +} + +Cell::Ref Message::intoCell() const { + CellBuilder builder; + + // write Header + _header->writeTo(builder); + + // write StateInit + if (_init.has_value()) { + auto initBuilder = _init.value().writeTo(); + + builder.appendBitOne(); + builder.appendBitZero(); + builder.appendBuilder(initBuilder); + } else { + builder.appendBitZero(); + } + + // write Body + if (_body.has_value()) { + builder.appendBitZero(); + builder.appendCellSlice(_body.value()); + } else { + builder.appendBitZero(); + } + + return builder.intoCell(); +} + +Data createSignedMessage(PublicKey& publicKey, PrivateKey& key, bool bounce, uint32_t flags, uint64_t amount, uint32_t expiredAt, + Address to, const Cell::Ref& contractData) { + auto getInitData = [](const PublicKey& publicKey, const Cell::Ref& contractData) { + if (contractData != nullptr) { + auto cellSlice = CellSlice(contractData.get()); + return std::make_pair(InitData(cellSlice), /* withInitState */ false); + } else { + return std::make_pair(InitData(publicKey), /* withInitState */ true); + } + }; + + auto [initData, withInitState] = getInitData(publicKey, contractData); + + auto gift = Wallet::Gift{ + .bounce = bounce, + .amount = amount, + .to = to, + .flags = static_cast(flags), + }; + + auto payload = initData.makeTransferPayload(expiredAt, gift); + + auto payloadCopy = payload; + auto payloadCell = payloadCopy.intoCell(); + + Data data(payloadCell->hash.begin(), payloadCell->hash.end()); + auto signature = key.sign(data, TWCurveED25519); + payload.prependRaw(signature, static_cast(signature.size()) * 8); + + auto header = std::make_shared(InitData(publicKey).computeAddr(WorkchainType::Basechain)); + auto message = Message(header); + + if (withInitState) { + message.setStateInit(initData.makeStateInit()); + } + + auto cell = payload.intoCell(); + auto body = CellSlice(cell.get()); + + message.setBody(body); + + const auto messageCell = message.intoCell(); + + Data result{}; + messageCell->serialize(result); + + return result; +} + +} // namespace TW::Everscale diff --git a/src/Everscale/Messages.h b/src/Everscale/Messages.h new file mode 100644 index 00000000000..345f75667ec --- /dev/null +++ b/src/Everscale/Messages.h @@ -0,0 +1,81 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include + +#include "Address.h" +#include "CellBuilder.h" +#include "CellSlice.h" +#include "Wallet.h" + +#include "../PrivateKey.h" + +namespace TW::Everscale { + +using uint128_t = CellBuilder::uint128_t; + +class CommonMsgInfo { +public: + virtual void writeTo(CellBuilder& builder) const = 0; + + virtual ~CommonMsgInfo() = default; +}; + +class ExternalInboundMessageHeader : public CommonMsgInfo { + Address _dst; + uint128_t _importFee{}; + +public: + explicit ExternalInboundMessageHeader(Address dst) + : _dst(dst) {} + + void writeTo(CellBuilder& builder) const override; +}; + +class InternalMessageHeader : public CommonMsgInfo { + bool _ihrDisabled; + bool _bounce; + Address _dst; + uint128_t _value; + + bool _bounced{}; + uint128_t _ihrFee{}; + uint128_t _fwdFee{}; + uint64_t _createdLt{}; + uint32_t _createdAt{}; + +public: + InternalMessageHeader(bool ihrDisabled, bool bounce, Address dst, uint64_t value) + : _ihrDisabled(ihrDisabled), _bounce(bounce), _dst(dst), _value(static_cast(value)) {} + + void writeTo(CellBuilder& builder) const override; +}; + +class Message { +private: + std::shared_ptr _header; + + std::optional _init{}; + std::optional _body{}; + +public: + using HeaderRef = std::shared_ptr; + + explicit Message(HeaderRef header) + : _header(std::move(header)) {} + + [[nodiscard]] Cell::Ref intoCell() const; + inline void setBody(CellSlice body) { _body = body; } + inline void setStateInit(const StateInit& stateInit) { _init = stateInit; } +}; + +Data createSignedMessage(PublicKey& publicKey, PrivateKey& key, bool bounce, uint32_t flags, uint64_t amount, + uint32_t expiredAt, Address destination, const Cell::Ref& contractData); + +} // namespace TW::Everscale diff --git a/src/Everscale/Signer.cpp b/src/Everscale/Signer.cpp new file mode 100644 index 00000000000..1f2a0fb9d10 --- /dev/null +++ b/src/Everscale/Signer.cpp @@ -0,0 +1,69 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Signer.h" +#include "Address.h" +#include "Messages.h" + +#include "../Base64.h" + +using namespace TW; +using namespace std::chrono; + +namespace TW::Everscale { + +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { + auto key = PrivateKey(input.private_key()); + auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); + + auto protoOutput = Proto::SigningOutput(); + + switch (input.action_oneof_case()) { + case Proto::SigningInput::ActionOneofCase::kTransfer: { + const auto& transfer = input.transfer(); + + uint8_t flags; + switch (transfer.behavior()) { + case Proto::MessageBehavior::SendAllBalance: { + flags = Wallet::sendAllBalanceFlags; + break; + } + default: { + flags = Wallet::simpleTransferFlags; + break; + } + } + + Cell::Ref contractData{}; + switch (transfer.account_state_oneof_case()) { + case Proto::Transfer::AccountStateOneofCase::kEncodedContractData: { + contractData = Cell::fromBase64(transfer.encoded_contract_data()); + break; + } + default: + break; + } + + auto signedMessage = createSignedMessage( + publicKey, + key, + transfer.bounce(), + flags, + transfer.amount(), + transfer.expired_at(), + Address(transfer.to()), + contractData); + protoOutput.set_encoded(TW::Base64::encode(signedMessage)); + break; + } + default: + break; + } + + return protoOutput; +} + +} // namespace TW::Everscale diff --git a/src/Everscale/Signer.h b/src/Everscale/Signer.h new file mode 100644 index 00000000000..35cfcd953a0 --- /dev/null +++ b/src/Everscale/Signer.h @@ -0,0 +1,25 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" +#include "../PrivateKey.h" +#include "../proto/Everscale.pb.h" + +namespace TW::Everscale { + +/// Helper class that performs Everscale transaction signing. +class Signer { +public: + /// Hide default constructor + Signer() = delete; + + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; +}; + +} // namespace TW::Everscale diff --git a/src/Everscale/Wallet.cpp b/src/Everscale/Wallet.cpp new file mode 100644 index 00000000000..09bf1e442e5 --- /dev/null +++ b/src/Everscale/Wallet.cpp @@ -0,0 +1,82 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Wallet.h" +#include "CellBuilder.h" +#include "Messages.h" + +#include "HexCoding.h" + +using namespace TW; + +namespace TW::Everscale { + +// WalletV3 contract https://github.com/tonlabs/ton-1/blob/master/crypto/smartcont/wallet3-code.fc +const Data Wallet::code = parse_hex("b5ee9c720101010100710000deff0020dd2082014c97ba218201339cbab19f71b0ed44d0d31fd31f31d70bffe304e0a4f2608308d71820d31fd31fd31ff82313bbf263ed44d0d31fd31fd3ffd15132baf2a15144baf2a204f901541055f910f2a3f8009320d74a96d307d402fb00e8d101a4c8cb1fcb1fcbffc9ed54"); + +CellBuilder InitData::writeTo() const { + CellBuilder builder; + + builder.appendU32(_seqno); + builder.appendU32(_walletId); + builder.appendRaw(_publicKey.bytes, 256); + + return builder; +} + +Address InitData::computeAddr(int8_t workchainId) const { + auto builder = this->writeTo(); + + StateInit stateInit{ + .code = Cell::deserialize(Wallet::code.data(), Wallet::code.size()), + .data = builder.intoCell(), + }; + return Address(workchainId, stateInit.writeTo().intoCell()->hash); +} + +StateInit InitData::makeStateInit() const { + auto builder = this->writeTo(); + + return StateInit{ + .code = Cell::deserialize(Wallet::code.data(), Wallet::code.size()), + .data = builder.intoCell(), + }; +} + +CellBuilder InitData::makeTransferPayload(uint32_t expireAt, const Wallet::Gift& gift) const { + CellBuilder payload; + + // insert prefix + payload.appendU32(_walletId); + payload.appendU32(expireAt); + payload.appendU32(_seqno); + + // create internal message + Message::HeaderRef header = std::make_shared(true, gift.bounce, gift.to, gift.amount); + auto message = Message(header); + + // append it to the body + payload.appendU8(gift.flags); + payload.appendReferenceCell(message.intoCell()); + + return payload; +} + +CellBuilder StateInit::writeTo() const { + CellBuilder builder; + + builder.appendBitZero(); // split_depth + builder.appendBitZero(); // special + builder.appendBitOne(); // code + builder.appendReferenceCell(code); + builder.appendBitOne(); // data + builder.appendReferenceCell(data); + builder.appendBitZero(); // library + + return builder; +} + +} // namespace TW::Everscale diff --git a/src/Everscale/Wallet.h b/src/Everscale/Wallet.h new file mode 100644 index 00000000000..a0216c465db --- /dev/null +++ b/src/Everscale/Wallet.h @@ -0,0 +1,77 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include + +#include "../PublicKey.h" + +#include "Address.h" +#include "Cell.h" +#include "CellBuilder.h" +#include "CellSlice.h" + +const uint32_t WALLET_ID = 0x4BA92D8A; + +namespace TW::Everscale { + +class Wallet { +public: + enum MessageFlags : uint8_t { + // Sender wants to pay transfer fees separately + // (from account balance instead of message balance) + FeesFromAccountBalance = (1 << 0), + + // If there are some errors during the action phase it should be ignored + IgnoreActionPhaseErrors = (1 << 1), + + // Message will carry all the remaining balance + AttachAllBalance = (1 << 7), + }; + + struct Gift { + bool bounce; + uint64_t amount; + Address to; + uint8_t flags; + }; + + static constexpr uint8_t simpleTransferFlags = + MessageFlags::FeesFromAccountBalance | MessageFlags::IgnoreActionPhaseErrors; + static constexpr uint8_t sendAllBalanceFlags = + MessageFlags::AttachAllBalance | MessageFlags::IgnoreActionPhaseErrors; + + static const Data code; +}; + +class StateInit { +public: + Cell::Ref code; + Cell::Ref data; + + [[nodiscard]] CellBuilder writeTo() const; +}; + +class InitData { + uint32_t _seqno; + uint32_t _walletId; + PublicKey _publicKey; + +public: + explicit InitData(PublicKey publicKey) + : _seqno(0), _walletId(WALLET_ID), _publicKey(std::move(publicKey)) {} + explicit InitData(CellSlice cs) + : _seqno(cs.getNextU32()), _walletId(cs.getNextU32()), _publicKey(PublicKey(cs.getNextBytes(32), TWPublicKeyTypeED25519)) {} + + [[nodiscard]] CellBuilder writeTo() const; + [[nodiscard]] StateInit makeStateInit() const; + [[nodiscard]] Address computeAddr(int8_t workchainId) const; + [[nodiscard]] CellBuilder makeTransferPayload(uint32_t expireAt, const Wallet::Gift& gift) const; +}; + +} // namespace TW::Everscale diff --git a/src/Everscale/WorkchainType.h b/src/Everscale/WorkchainType.h new file mode 100644 index 00000000000..786343e1065 --- /dev/null +++ b/src/Everscale/WorkchainType.h @@ -0,0 +1,16 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +namespace TW::Everscale { + +enum WorkchainType { + Masterchain = -1, + Basechain = 0, +}; + +} // namespace TW::Everscale diff --git a/src/FIO/Action.cpp b/src/FIO/Action.cpp index 9a64137e7bb..db6a0c9d536 100644 --- a/src/FIO/Action.cpp +++ b/src/FIO/Action.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. #include "Action.h" -#include "../Data.h" +#include "Data.h" namespace TW::FIO { diff --git a/src/FIO/Action.h b/src/FIO/Action.h index c79ef65a500..70566cfd7ba 100644 --- a/src/FIO/Action.h +++ b/src/FIO/Action.h @@ -7,7 +7,7 @@ #pragma once #include "EOS/Name.h" // Name is reused -#include "../Data.h" +#include "Data.h" #include "../BinaryCoding.h" #include @@ -16,7 +16,7 @@ namespace TW::FIO { /// Encodes a value as a variable-length integer. -/// @returns the number of bytes written. +/// \returns the number of bytes written. uint8_t encodeVarInt(uint64_t num, Data& data); /// Encodes an ASCII string prefixed by the length (varInt) diff --git a/src/FIO/Actor.cpp b/src/FIO/Actor.cpp index 550b52489bd..f30c351e34f 100644 --- a/src/FIO/Actor.cpp +++ b/src/FIO/Actor.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,49 +8,50 @@ #include -using namespace TW::FIO; -using namespace std; +namespace TW::FIO { -static const auto pattern = regex(R"(\b([a-z1-5]{3,})[.@]?\b)"); -string Actor::actor(const Address& addr) -{ +static const auto pattern = std::regex(R"(\b([a-z1-5]{3,})[.@]?\b)"); +std::string Actor::actor(const Address& addr) { uint64_t shortenedKey = shortenKey(addr.bytes); - string name13 = name(shortenedKey); + std::string name13 = name(shortenedKey); // trim to 12 chracters return name13.substr(0, 12); } bool Actor::validate(const std::string& addr) { - smatch match; + std::smatch match; return regex_search(addr, match, pattern); } -uint64_t Actor::shortenKey(const array& addrKey) -{ +uint64_t Actor::shortenKey(const std::array& addrKey) { uint64_t res = 0; int i = 1; // Ignore key head int len = 0; while (len <= 12) { assert(i < 33); // Means the key has > 20 bytes with trailing zeroes... - + auto trimmed_char = uint64_t(addrKey[i] & (len == 12 ? 0x0f : 0x1f)); - if (trimmed_char == 0) { i++; continue; } // Skip a zero and move to next + if (trimmed_char == 0) { + i++; + continue; + } // Skip a zero and move to next auto shuffle = len == 12 ? 0 : 5 * (12 - len) - 1; res |= trimmed_char << shuffle; - len++; i++; + len++; + i++; } return res; } -string Actor::name(uint64_t shortKey) { +std::string Actor::name(uint64_t shortKey) { static const char* charmap = ".12345abcdefghijklmnopqrstuvwxyz"; - string str(13,'.'); //We are forcing the string to be 13 characters + std::string str(13, '.'); // We are forcing the string to be 13 characters uint64_t tmp = shortKey; - for(uint32_t i = 0; i <= 12; i++ ) { + for (uint32_t i = 0; i <= 12; i++) { char c = charmap[tmp & (i == 0 ? 0x0f : 0x1f)]; str[12 - i] = c; tmp >>= (i == 0 ? 4 : 5); @@ -58,3 +59,5 @@ string Actor::name(uint64_t shortKey) { return str; } + +} // namespace TW::FIO diff --git a/src/FIO/Address.cpp b/src/FIO/Address.cpp index 362cd7c6a9d..52d1086e48f 100644 --- a/src/FIO/Address.cpp +++ b/src/FIO/Address.cpp @@ -4,16 +4,17 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#include "Address.h" #include "../Base58.h" #include "../BinaryCoding.h" -#include "Address.h" #include #include using namespace TW; -using namespace TW::FIO; + +namespace TW::FIO { bool Address::isValid(const std::string& string) { return decodeKeyData(string).has_value(); @@ -101,3 +102,5 @@ PublicKey Address::publicKey() const { const Data keyData = TW::data(bytes.data(), PublicKey::secp256k1Size); return PublicKey(keyData, TWPublicKeyTypeSECP256k1); } + +} // namespace TW::FIO diff --git a/src/FIO/Address.h b/src/FIO/Address.h index 25dba8e7bf5..2d00fa70dfa 100644 --- a/src/FIO/Address.h +++ b/src/FIO/Address.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/FIO/Encryption.h b/src/FIO/Encryption.h index ecc7106f8b8..e293f3358fd 100644 --- a/src/FIO/Encryption.h +++ b/src/FIO/Encryption.h @@ -6,35 +6,35 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../PublicKey.h" namespace TW::FIO { /// Payload message encryption/decryption. -/// See also https://github.com/fioprotocol/fiojs/blob/master/src/encryption-check.ts +/// \see https://github.com/fioprotocol/fiojs/blob/master/src/encryption-check.ts class Encryption { public: /** * Provides AES-256-CBC encryption and message authentication. * The CBC cipher is used for good platform native compatability. - * @see https://security.stackexchange.com/a/63134 - * @see https://security.stackexchange.com/a/20493 - * @arg secret - Shared secret (64-bytes). - * @arg message - Plaintext message (arbitrary length). - * @arg iv - An unpredictable strong random value (16 bytes) is required + * \see https://security.stackexchange.com/a/63134 + * \see https://security.stackexchange.com/a/20493 + * \param secret - Shared secret (64-bytes). + * \param message - Plaintext message (arbitrary length). + * \param iv - An unpredictable strong random value (16 bytes) is required * and supplied by default. Unit tests may provide a static value to achieve predictable results. - * @throws {Error} invalid IV size + * \throws {Error} invalid IV size */ static Data checkEncrypt(const Data& secret, const Data& message, Data& iv); /** * Provides AES-256-CBC message authentication then decryption. - * @arg secret - Shared secret (64-bytes). - * @arg message - Ciphertext (from checkEncrypt()) - * @throws {Error} Message too short - * @throws {Error} Decrypt failed, HMAC mismatch + * \param secret - Shared secret (64-bytes). + * \param message - Ciphertext (from checkEncrypt()) + * \throws {Error} Message too short + * \throws {Error} Decrypt failed, HMAC mismatch */ static Data checkDecrypt(const Data& secret, const Data& message); diff --git a/src/FIO/Entry.cpp b/src/FIO/Entry.cpp index 1caec6f5a68..a9194cb53ae 100644 --- a/src/FIO/Entry.cpp +++ b/src/FIO/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,19 +9,20 @@ #include "Address.h" #include "Signer.h" -using namespace TW::FIO; -using namespace std; +namespace TW::FIO { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::FIO diff --git a/src/FIO/Entry.h b/src/FIO/Entry.h index 3978da39256..cb9ab8c38a2 100644 --- a/src/FIO/Entry.h +++ b/src/FIO/Entry.h @@ -12,12 +12,11 @@ namespace TW::FIO { /// Entry point for implementation of FIO coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeFIO}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::FIO diff --git a/src/FIO/NewFundsRequest.h b/src/FIO/NewFundsRequest.h index e3602fd54b1..3b58d2c3a40 100644 --- a/src/FIO/NewFundsRequest.h +++ b/src/FIO/NewFundsRequest.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include diff --git a/src/FIO/Signer.cpp b/src/FIO/Signer.cpp index f3a96ad0954..e4390dd3b44 100644 --- a/src/FIO/Signer.cpp +++ b/src/FIO/Signer.cpp @@ -40,7 +40,7 @@ Data Signer::signData(const PrivateKey& privKey, const Data& data) { return signature; } -std::string Signer::signatureToBsase58(const Data& sig) { +std::string Signer::signatureToBase58(const Data& sig) { Data sigWithSuffix(sig); append(sigWithSuffix, TW::data(SignatureSuffix)); // take hash, ripemd, first 4 bytes @@ -56,7 +56,7 @@ bool Signer::verify(const PublicKey& pubKey, const Data& data, const Data& signa } // canonical check for FIO, both R and S lenght is 32 -int Signer::isCanonical(uint8_t by, uint8_t sig[64]) { +int Signer::isCanonical([[maybe_unused]] uint8_t by, uint8_t sig[64]) { return !(sig[0] & 0x80) && !(sig[0] == 0 && !(sig[1] & 0x80)) && !(sig[32] & 0x80) diff --git a/src/FIO/Signer.h b/src/FIO/Signer.h index 7604ca4f953..7e0800d71d9 100644 --- a/src/FIO/Signer.h +++ b/src/FIO/Signer.h @@ -7,7 +7,7 @@ #pragma once #include "Address.h" -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../PublicKey.h" #include "../proto/FIO.pb.h" @@ -30,7 +30,7 @@ class Signer { static Data signData(const PrivateKey& privKey, const Data& data); /// Used internally, encode signature to base58 with prefix. Ex.: "SIG_K1_K54CA1jmhgWrSdvrNrkokPyvqh7dwsSoQHNU9xgD3Ezf6cJySzhKeUubVRqmpYdnjoP1DM6SorroVAgrCu3qqvJ9coAQ6u" - static std::string signatureToBsase58(const Data& sig); + static std::string signatureToBase58(const Data& sig); /// Verify a signature, used in testing static bool verify(const PublicKey& pubKey, const Data& data, const Data& signature); diff --git a/src/FIO/Transaction.h b/src/FIO/Transaction.h index 67f70939ff7..1bfa26369b2 100644 --- a/src/FIO/Transaction.h +++ b/src/FIO/Transaction.h @@ -7,7 +7,7 @@ #pragma once #include "Action.h" -#include "../Data.h" +#include "Data.h" #include diff --git a/src/FIO/TransactionBuilder.cpp b/src/FIO/TransactionBuilder.cpp index c0203ec9911..1c5fa2b4d8b 100644 --- a/src/FIO/TransactionBuilder.cpp +++ b/src/FIO/TransactionBuilder.cpp @@ -240,7 +240,7 @@ string TransactionBuilder::signAdnBuildTx(const Data& chainId, const Data& packe Data sigBuf(chainId); append(sigBuf, packedTx); append(sigBuf, TW::Data(32)); // context_free - string signature = Signer::signatureToBsase58(Signer::signData(privateKey, sigBuf)); + string signature = Signer::signatureToBase58(Signer::signData(privateKey, sigBuf)); // Build json json tx = { diff --git a/src/FIO/TransactionBuilder.h b/src/FIO/TransactionBuilder.h index eede5d91ca2..607e0d48963 100644 --- a/src/FIO/TransactionBuilder.h +++ b/src/FIO/TransactionBuilder.h @@ -10,7 +10,7 @@ #include "Address.h" #include "../proto/FIO.pb.h" -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include diff --git a/src/Filecoin/Address.cpp b/src/Filecoin/Address.cpp index 28ca9daf774..56fc2b9963b 100644 --- a/src/Filecoin/Address.cpp +++ b/src/Filecoin/Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust. +// Copyright © 2017-2022 Trust. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,12 +7,11 @@ #include "Address.h" #include +#include #include "../Base32.h" -#include "../Data.h" -using namespace TW; -using namespace TW::Filecoin; +namespace TW::Filecoin { static const char BASE32_ALPHABET_FILECOIN[] = "abcdefghijklmnopqrstuvwxyz234567"; static constexpr size_t checksumSize = 4; @@ -32,7 +31,7 @@ bool Address::isValid(const Data& data) { if (data.size() == 11 && data[10] > 0x01) { return false; } - int i; + std::size_t i; for (i = 1; i < data.size(); i++) { if ((data[i] & 0x80) == 0) { break; @@ -47,7 +46,7 @@ bool Address::isValid(const Data& data) { static bool isValidID(const std::string& string) { if (string.length() > 22) return false; - for (int i = 2; i < string.length(); i++) { + for (auto i = 2ul; i < string.length(); i++) { if (string[i] < '0' || string[i] > '9') { return false; } @@ -150,12 +149,12 @@ std::string Address::string() const { if (type() == Type::ID) { uint64_t id = 0; unsigned shift = 0; - for (int i = 1; i < bytes.size(); i++) { - if (bytes[i] < 0x80) { - id |= bytes[i] << shift; + for (auto i = 1ul; i < bytes.size(); i++) { + if (bytes[i] <= SCHAR_MAX) { + id |= static_cast(bytes[i]) << shift; break; } else { - id |= ((uint64_t)(bytes[i] & 0x7F)) << shift; + id |= static_cast(bytes[i] & SCHAR_MAX) << shift; shift += 7; } } @@ -178,3 +177,5 @@ std::string Address::string() const { return s; } + +} // namespace TW::Filecoin diff --git a/src/Filecoin/Entry.cpp b/src/Filecoin/Entry.cpp index 39db5385ded..5ab6065a3ca 100644 --- a/src/Filecoin/Entry.cpp +++ b/src/Filecoin/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,25 +9,26 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Filecoin; -using namespace std; +namespace TW::Filecoin { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +std::string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +} // namespace TW::Filecoin diff --git a/src/Filecoin/Entry.h b/src/Filecoin/Entry.h index f972089aa03..a989db0676e 100644 --- a/src/Filecoin/Entry.h +++ b/src/Filecoin/Entry.h @@ -13,16 +13,15 @@ namespace TW::Filecoin { /// Entry point for implementation of Filecoin coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific /// includes in this file -class Entry : public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeFilecoin}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Filecoin diff --git a/src/Filecoin/Signer.cpp b/src/Filecoin/Signer.cpp index 88f48a7e740..67676a9eeb7 100644 --- a/src/Filecoin/Signer.cpp +++ b/src/Filecoin/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust. +// Copyright © 2017-2022 Trust. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,8 +8,7 @@ #include "HexCoding.h" #include -using namespace TW; -using namespace TW::Filecoin; +namespace TW::Filecoin { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { // Load private key and transaction from Protobuf input. @@ -24,8 +23,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { /* value */ load(input.value()), /* gasLimit */ input.gas_limit(), /* gasFeeCap */ load(input.gas_fee_cap()), - /* gasPremium */ load(input.gas_premium()) - ); + /* gasPremium */ load(input.gas_premium())); // Sign transaction. auto signature = sign(key, transaction); @@ -50,3 +48,5 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { auto output = Signer::sign(input); return output.json(); } + +} // namespace TW::Filecoin diff --git a/src/Filecoin/Signer.h b/src/Filecoin/Signer.h index 3d20617bcb4..b266e95e120 100644 --- a/src/Filecoin/Signer.h +++ b/src/Filecoin/Signer.h @@ -8,7 +8,7 @@ #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/Filecoin.pb.h" @@ -31,8 +31,3 @@ class Signer { }; } // namespace TW::Filecoin - -/// Wrapper for C interface. -struct TWFilecoinSigner { - TW::Filecoin::Signer impl; -}; diff --git a/src/Filecoin/Transaction.cpp b/src/Filecoin/Transaction.cpp index ff9a2960fcc..311f71ecf97 100644 --- a/src/Filecoin/Transaction.cpp +++ b/src/Filecoin/Transaction.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust. +// Copyright © 2017-2022 Trust. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,12 +8,12 @@ #include #include "Base64.h" +namespace TW::Filecoin { + using json = nlohmann::json; -using namespace TW; -using namespace TW::Filecoin; // encodeBigInt encodes a Filecoin BigInt to CBOR. -Data TW::Filecoin::encodeBigInt(const uint256_t& value) { +Data encodeBigInt(const uint256_t& value) { if (value.is_zero()) { return {}; } @@ -61,6 +61,7 @@ Data Transaction::cid() const { return cid; } std::string Transaction::serialize(Data& signature) const { + // clang-format off json tx = { {"Message", json{ {"To", to.string()}, @@ -78,5 +79,8 @@ std::string Transaction::serialize(Data& signature) const { } }, }; + // clang-format on return tx.dump(); } + +} // namespace TW::Filecoin diff --git a/src/Generated/.clang-tidy b/src/Generated/.clang-tidy new file mode 100644 index 00000000000..2c22f7387dd --- /dev/null +++ b/src/Generated/.clang-tidy @@ -0,0 +1,6 @@ +--- +InheritParentConfig: false +Checks: '-*,misc-definitions-in-headers' +CheckOptions: + - { key: HeaderFileExtensions, value: "x" } +... diff --git a/src/Groestlcoin/Address.cpp b/src/Groestlcoin/Address.cpp index bf77a0f7ddb..290b097ad69 100644 --- a/src/Groestlcoin/Address.cpp +++ b/src/Groestlcoin/Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,12 +9,10 @@ #include "../Base58.h" #include -#include - -using namespace TW::Groestlcoin; +namespace TW::Groestlcoin { bool Address::isValid(const std::string& string) { - const auto decoded = Base58::bitcoin.decodeCheck(string, Hash::groestl512d); + const auto decoded = Base58::bitcoin.decodeCheck(string, Hash::HasherGroestl512d); if (decoded.size() != Address::size) { return false; } @@ -23,7 +21,7 @@ bool Address::isValid(const std::string& string) { } bool Address::isValid(const std::string& string, const std::vector& validPrefixes) { - const auto decoded = Base58::bitcoin.decodeCheck(string, Hash::groestl512d); + const auto decoded = Base58::bitcoin.decodeCheck(string, Hash::HasherGroestl512d); if (decoded.size() != Address::size) { return false; } @@ -34,7 +32,7 @@ bool Address::isValid(const std::string& string, const std::vector& validP } Address::Address(const std::string& string) { - const auto decoded = Base58::bitcoin.decodeCheck(string, Hash::groestl512d); + const auto decoded = Base58::bitcoin.decodeCheck(string, Hash::HasherGroestl512d); if (decoded.size() != Address::size) { throw std::invalid_argument("Invalid address string"); } @@ -58,5 +56,7 @@ Address::Address(const PublicKey& publicKey, uint8_t prefix) { } std::string Address::string() const { - return Base58::bitcoin.encodeCheck(bytes, Hash::groestl512d); + return Base58::bitcoin.encodeCheck(bytes, Hash::HasherGroestl512d); } + +} // namespace TW::Groestlcoin diff --git a/src/Groestlcoin/Address.h b/src/Groestlcoin/Address.h index 2084cea1b9f..d29c37d4e68 100644 --- a/src/Groestlcoin/Address.h +++ b/src/Groestlcoin/Address.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/Groestlcoin/Entry.cpp b/src/Groestlcoin/Entry.cpp index 20878ff6eef..ae12284620a 100644 --- a/src/Groestlcoin/Entry.cpp +++ b/src/Groestlcoin/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,22 +10,22 @@ #include "../Bitcoin/SegwitAddress.h" #include "Signer.h" -using namespace TW::Groestlcoin; -using namespace std; +namespace TW::Groestlcoin { -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const { - return TW::Bitcoin::SegwitAddress::isValid(address, hrp) - || Address::isValid(address, {p2pkh, p2sh}); +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const { + return TW::Bitcoin::SegwitAddress::isValid(address, hrp) || Address::isValid(address, {p2pkh, p2sh}); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const { - return TW::Bitcoin::SegwitAddress(publicKey, 0, hrp).string(); +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TW::byte p2pkh, const char* hrp) const { + return TW::Bitcoin::SegwitAddress(publicKey, hrp).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -void Entry::plan(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::plan([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { planTemplate(dataIn, dataOut); } + +} // namespace TW::Groestlcoin diff --git a/src/Groestlcoin/Entry.h b/src/Groestlcoin/Entry.h index 33470bbc05d..069cc75074e 100644 --- a/src/Groestlcoin/Entry.h +++ b/src/Groestlcoin/Entry.h @@ -12,13 +12,12 @@ namespace TW::Groestlcoin { /// Groestlcoin entry dispatcher. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeGroestlcoin}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Groestlcoin diff --git a/src/Groestlcoin/Signer.cpp b/src/Groestlcoin/Signer.cpp index d395fadc901..7b87e290ff6 100644 --- a/src/Groestlcoin/Signer.cpp +++ b/src/Groestlcoin/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,8 +12,7 @@ #include "HexCoding.h" #include "Transaction.h" -using namespace TW; -using namespace TW::Groestlcoin; +namespace TW::Groestlcoin { using TransactionBuilder = Bitcoin::TransactionBuilder; @@ -46,3 +45,5 @@ SigningOutput Signer::sign(const SigningInput& input) noexcept { output.set_transaction_id(hex(txHash)); return output; } + +} // namespace TW::Groestlcoin diff --git a/src/Groestlcoin/Transaction.h b/src/Groestlcoin/Transaction.h index 6b3929a5956..10e678c7de3 100644 --- a/src/Groestlcoin/Transaction.h +++ b/src/Groestlcoin/Transaction.h @@ -11,9 +11,9 @@ namespace TW::Groestlcoin { struct Transaction : public Bitcoin::Transaction { - Transaction() : Bitcoin::Transaction(1, 0, static_cast(Hash::sha256)) {} + Transaction() : Bitcoin::Transaction(1, 0, Hash::HasherSha256) {} Transaction(int32_t version, uint32_t lockTime = 0) : - Bitcoin::Transaction(version, lockTime, static_cast(Hash::sha256)) {} + Bitcoin::Transaction(version, lockTime, Hash::HasherSha256) {} }; } // namespace TW::Groestlcoin diff --git a/src/HDWallet.cpp b/src/HDWallet.cpp index b4229ad62ef..068de7f20e5 100644 --- a/src/HDWallet.cpp +++ b/src/HDWallet.cpp @@ -8,18 +8,22 @@ #include "Base58.h" #include "BinaryCoding.h" -#include "Bitcoin/SegwitAddress.h" #include "Bitcoin/CashAddress.h" +#include "Bitcoin/SegwitAddress.h" #include "Coin.h" #include "Mnemonic.h" +#include "memory/memzero_wrapper.h" #include #include -#include +#include + #include #include +#include #include +#include #include #include @@ -29,9 +33,9 @@ using namespace TW; namespace { -uint32_t fingerprint(HDNode *node, Hash::Hasher hasher); -std::string serialize(const HDNode *node, uint32_t fingerprint, uint32_t version, bool use_public, Hash::Hasher hasher); -bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher, HDNode *node); +uint32_t fingerprint(HDNode* node, Hash::Hasher hasher); +std::string serialize(const HDNode* node, uint32_t fingerprint, uint32_t version, bool use_public, Hash::Hasher hasher); +bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher, HDNode* node); HDNode getNode(const HDWallet& wallet, TWCurve curve, const DerivationPath& derivationPath); HDNode getMasterNode(const HDWallet& wallet, TWCurve curve); @@ -43,16 +47,18 @@ const int MnemonicBufLength = Mnemonic::MaxWords * (BIP39_MAX_WORD_LENGTH + 3) + HDWallet::HDWallet(int strength, const std::string& passphrase) : passphrase(passphrase) { char buf[MnemonicBufLength]; + //win if (!random_init()) { throw std::runtime_error("Failed to initialize random number generator"); } const char* mnemonic_chars = mnemonic_generate(strength, buf, MnemonicBufLength); if (mnemonic_chars == nullptr) { + //win random_release(); throw std::invalid_argument("Invalid strength"); } mnemonic = mnemonic_chars; - memzero(buf, MnemonicBufLength); + TW::memzero(buf, MnemonicBufLength); updateSeedAndEntropy(); } @@ -62,6 +68,7 @@ HDWallet::HDWallet(const std::string& mnemonic, const std::string& passphrase, c (check && !Mnemonic::isValid(mnemonic))) { throw std::invalid_argument("Invalid mnemonic"); } + //win if (!random_init()) { throw std::runtime_error("Failed to initialize random number generator"); } @@ -76,7 +83,8 @@ HDWallet::HDWallet(const Data& entropy, const std::string& passphrase) throw std::invalid_argument("Invalid mnemonic data"); } mnemonic = mnemonic_chars; - memzero(buf, MnemonicBufLength); + TW::memzero(buf, MnemonicBufLength); + //win if (!random_init()) { throw std::runtime_error("Failed to initialize random number generator"); } @@ -84,7 +92,7 @@ HDWallet::HDWallet(const Data& entropy, const std::string& passphrase) } HDWallet::~HDWallet() { - random_release(); + random_release();//win std::fill(seed.begin(), seed.end(), 0); std::fill(mnemonic.begin(), mnemonic.end(), 0); std::fill(passphrase.begin(), passphrase.end(), 0); @@ -107,34 +115,64 @@ void HDWallet::updateSeedAndEntropy(bool check) { PrivateKey HDWallet::getMasterKey(TWCurve curve) const { auto node = getMasterNode(*this, curve); - auto data = Data(node.private_key, node.private_key + PrivateKey::size); + auto data = Data(node.private_key, node.private_key + PrivateKey::_size); return PrivateKey(data); } PrivateKey HDWallet::getMasterKeyExtension(TWCurve curve) const { auto node = getMasterNode(*this, curve); - auto data = Data(node.private_key_extension, node.private_key_extension + PrivateKey::size); + auto data = Data(node.private_key_extension, node.private_key_extension + PrivateKey::_size); return PrivateKey(data); } +PrivateKey HDWallet::getKey(TWCoinType coin, TWDerivation derivation) const { + const auto path = TW::derivationPath(coin, derivation); + return getKey(coin, path); +} + +DerivationPath HDWallet::cardanoStakingDerivationPath(const DerivationPath& path) { + DerivationPath stakingPath = path; + stakingPath.indices[3].value = 2; + stakingPath.indices[4].value = 0; + return stakingPath; +} + PrivateKey HDWallet::getKey(TWCoinType coin, const DerivationPath& derivationPath) const { const auto curve = TWCoinTypeCurve(coin); - const auto privateKeyType = getPrivateKeyType(curve); + return getKeyByCurve(curve, derivationPath); +} + +PrivateKey HDWallet::getKeyByCurve(TWCurve curve, const DerivationPath& derivationPath) const { + const auto privateKeyType = PrivateKey::getType(curve); auto node = getNode(*this, curve, derivationPath); switch (privateKeyType) { - case PrivateKeyTypeExtended96: - { - auto pkData = Data(node.private_key, node.private_key + PrivateKey::size); - auto extData = Data(node.private_key_extension, node.private_key_extension + PrivateKey::size); - auto chainCode = Data(node.chain_code, node.chain_code + PrivateKey::size); - return PrivateKey(pkData, extData, chainCode); - } - - case PrivateKeyTypeDefault32: - default: - // default path - auto data = Data(node.private_key, node.private_key + PrivateKey::size); - return PrivateKey(data); + case TWPrivateKeyTypeCardano: { + if (derivationPath.indices.size() < 4 || derivationPath.indices[3].value > 1) { + // invalid derivation path + return PrivateKey(Data(PrivateKey::cardanoKeySize)); + } + const DerivationPath stakingPath = cardanoStakingDerivationPath(derivationPath); + + auto pkData = Data(node.private_key, node.private_key + PrivateKey::_size); + auto extData = Data(node.private_key_extension, node.private_key_extension + PrivateKey::_size); + auto chainCode = Data(node.chain_code, node.chain_code + PrivateKey::_size); + + // repeat with staking path + const auto node2 = getNode(*this, curve, stakingPath); + auto pkData2 = Data(node2.private_key, node2.private_key + PrivateKey::_size); + auto extData2 = Data(node2.private_key_extension, node2.private_key_extension + PrivateKey::_size); + auto chainCode2 = Data(node2.chain_code, node2.chain_code + PrivateKey::_size); + + TW::memzero(&node); + return PrivateKey(pkData, extData, chainCode, pkData2, extData2, chainCode2); + } + + case TWPrivateKeyTypeDefault: + default: + // default path + auto data = Data(node.private_key, node.private_key + PrivateKey::_size); + TW::memzero(&node); + return PrivateKey(data); } } @@ -145,33 +183,39 @@ std::string HDWallet::getRootKey(TWCoinType coin, TWHDVersion version) const { } std::string HDWallet::deriveAddress(TWCoinType coin) const { - const auto derivationPath = TW::derivationPath(coin); - return TW::deriveAddress(coin, getKey(coin, derivationPath)); + return deriveAddress(coin, TWDerivationDefault); +} + +std::string HDWallet::deriveAddress(TWCoinType coin, TWDerivation derivation) const { + const auto derivationPath = TW::derivationPath(coin, derivation); + return TW::deriveAddress(coin, getKey(coin, derivationPath), derivation); } -std::string HDWallet::getExtendedPrivateKey(TWPurpose purpose, TWCoinType coin, TWHDVersion version) const { +std::string HDWallet::getExtendedPrivateKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const { if (version == TWHDVersionNone) { return ""; } - + const auto curve = TWCoinTypeCurve(coin); - auto derivationPath = TW::DerivationPath({DerivationPathIndex(purpose, true), DerivationPathIndex(coin, true)}); + const auto path = TW::derivationPath(coin, derivation); + auto derivationPath = DerivationPath({DerivationPathIndex(purpose, true), DerivationPathIndex(path.coin(), true)}); auto node = getNode(*this, curve, derivationPath); auto fingerprintValue = fingerprint(&node, publicKeyHasher(coin)); - hdnode_private_ckd(&node, 0x80000000); + hdnode_private_ckd(&node, account + 0x80000000); return serialize(&node, fingerprintValue, version, false, base58Hasher(coin)); } -std::string HDWallet::getExtendedPublicKey(TWPurpose purpose, TWCoinType coin, TWHDVersion version) const { +std::string HDWallet::getExtendedPublicKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const { if (version == TWHDVersionNone) { return ""; } - + const auto curve = TWCoinTypeCurve(coin); - auto derivationPath = TW::DerivationPath({DerivationPathIndex(purpose, true), DerivationPathIndex(coin, true)}); + const auto path = TW::derivationPath(coin, derivation); + auto derivationPath = DerivationPath({DerivationPathIndex(purpose, true), DerivationPathIndex(path.coin(), true)}); auto node = getNode(*this, curve, derivationPath); auto fingerprintValue = fingerprint(&node, publicKeyHasher(coin)); - hdnode_private_ckd(&node, 0x80000000); + hdnode_private_ckd(&node, account + 0x80000000); hdnode_fill_public_key(&node); return serialize(&node, fingerprintValue, version, true, base58Hasher(coin)); } @@ -192,7 +236,7 @@ std::optional HDWallet::getPublicKeyFromExtended(const std::string& e hdnode_fill_public_key(&node); // These public key type are not applicable. Handled above, as node.curve->params is null - assert(curve != TWCurveED25519 && curve != TWCurveED25519Blake2bNano && curve != TWCurveED25519Extended && curve != TWCurveCurve25519); + assert(curve != TWCurveED25519 && curve != TWCurveED25519Blake2bNano && curve != TWCurveED25519ExtendedCardano && curve != TWCurveCurve25519); TWPublicKeyType keyType = TW::publicKeyType(coin); if (curve == TWCurveSECP256k1) { auto pubkey = PublicKey(Data(node.public_key, node.public_key + 33), TWPublicKeyTypeSECP256k1); @@ -226,26 +270,15 @@ std::optional HDWallet::getPrivateKeyFromExtended(const std::string& return PrivateKey(Data(node.private_key, node.private_key + 32)); } -HDWallet::PrivateKeyType HDWallet::getPrivateKeyType(TWCurve curve) { - switch (curve) { - case TWCurve::TWCurveED25519Extended: - // used by Cardano - return PrivateKeyTypeExtended96; - default: - // default - return PrivateKeyTypeDefault32; - } -} - namespace { -uint32_t fingerprint(HDNode *node, Hash::Hasher hasher) { +uint32_t fingerprint(HDNode* node, Hash::Hasher hasher) { hdnode_fill_public_key(node); - auto digest = hasher(node->public_key, 33); - return ((uint32_t) digest[0] << 24) + (digest[1] << 16) + (digest[2] << 8) + digest[3]; + auto digest = Hash::hash(hasher, node->public_key, 33); + return ((uint32_t)digest[0] << 24) + (digest[1] << 16) + (digest[2] << 8) + digest[3]; } -std::string serialize(const HDNode *node, uint32_t fingerprint, uint32_t version, bool use_public, Hash::Hasher hasher) { +std::string serialize(const HDNode* node, uint32_t fingerprint, uint32_t version, bool use_public, Hash::Hasher hasher) { Data node_data; node_data.reserve(78); @@ -265,7 +298,7 @@ std::string serialize(const HDNode *node, uint32_t fingerprint, uint32_t version } bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher, HDNode* node) { - memset(node, 0, sizeof(HDNode)); + TW::memzero(node); const char* curveNameStr = curveName(curve); if (curveNameStr == nullptr || ::strlen(curveNameStr) == 0) { return false; @@ -296,35 +329,40 @@ bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher } HDNode getNode(const HDWallet& wallet, TWCurve curve, const DerivationPath& derivationPath) { - const auto privateKeyType = HDWallet::getPrivateKeyType(curve); + const auto privateKeyType = PrivateKey::getType(curve); auto node = getMasterNode(wallet, curve); for (auto& index : derivationPath.indices) { switch (privateKeyType) { - case HDWallet::PrivateKeyTypeExtended96: - // special handling for extended - hdnode_private_ckd_cardano(&node, index.derivationIndex()); - break; - case HDWallet::PrivateKeyTypeDefault32: - default: - hdnode_private_ckd(&node, index.derivationIndex()); - break; + case TWPrivateKeyTypeCardano: + hdnode_private_ckd_cardano(&node, index.derivationIndex()); + break; + case TWPrivateKeyTypeDefault: + default: + hdnode_private_ckd(&node, index.derivationIndex()); + break; } } return node; } HDNode getMasterNode(const HDWallet& wallet, TWCurve curve) { - const auto privateKeyType = HDWallet::getPrivateKeyType(curve); - auto node = HDNode(); + const auto privateKeyType = PrivateKey::getType(curve); + HDNode node; switch (privateKeyType) { - case HDWallet::PrivateKeyTypeExtended96: - // special handling for extended, use entropy (not seed) - hdnode_from_entropy_cardano_icarus((const uint8_t*)"", 0, wallet.getEntropy().data(), (int)wallet.getEntropy().size(), &node); - break; - case HDWallet::PrivateKeyTypeDefault32: - default: - hdnode_from_seed(wallet.getSeed().data(), HDWallet::seedSize, curveName(curve), &node); - break; + case TWPrivateKeyTypeCardano: { + // Derives the root Cardano HDNode from a passphrase and the entropy encoded in + // a BIP-0039 mnemonic using the Icarus derivation (V2) scheme + const auto entropy = wallet.getEntropy(); + uint8_t secret[CARDANO_SECRET_LENGTH]; + secret_from_entropy_cardano_icarus((const uint8_t*)"", 0, entropy.data(), int(entropy.size()), secret, nullptr); + hdnode_from_secret_cardano(secret, &node); + TW::memzero(secret, CARDANO_SECRET_LENGTH); + break; + } + case TWPrivateKeyTypeDefault: + default: + hdnode_from_seed(wallet.getSeed().data(), HDWallet::seedSize, curveName(curve), &node); + break; } return node; } @@ -337,7 +375,7 @@ const char* curveName(TWCurve curve) { return ED25519_NAME; case TWCurveED25519Blake2bNano: return ED25519_BLAKE2B_NANO_NAME; - case TWCurveED25519Extended: + case TWCurveED25519ExtendedCardano: return ED25519_CARDANO_NAME; case TWCurveNIST256p1: return NIST256P1_NAME; diff --git a/src/HDWallet.h b/src/HDWallet.h index 9d683d0ffd0..0579591c82e 100644 --- a/src/HDWallet.h +++ b/src/HDWallet.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -74,17 +75,46 @@ class HDWallet { /// Returns the master private key extension (32 byte). PrivateKey getMasterKeyExtension(TWCurve curve) const; + /// Returns the private key with the given derivation. + PrivateKey getKey(const TWCoinType coin, TWDerivation derivation) const; + /// Returns the private key at the given derivation path. PrivateKey getKey(const TWCoinType coin, const DerivationPath& derivationPath) const; - /// Derives the address for a coin. + /// Returns the private key at the given derivation path and curve. + PrivateKey getKeyByCurve(TWCurve curve, const DerivationPath& derivationPath) const; + + /// Derives the address for a coin (default derivation). std::string deriveAddress(TWCoinType coin) const; - /// Returns the extended private key. - std::string getExtendedPrivateKey(TWPurpose purpose, TWCoinType coin, TWHDVersion version) const; + /// Derives the address for a coin with given derivation. + std::string deriveAddress(TWCoinType coin, TWDerivation derivation) const; + + /// Returns the extended private key for default 0 account with the given derivation. + std::string getExtendedPrivateKeyDerivation(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version) const { + return getExtendedPrivateKeyAccount(purpose, coin, derivation, version, 0); + } + + /// Returns the extended public key for default 0 account with the given derivation. + std::string getExtendedPublicKeyDerivation(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version) const { + return getExtendedPublicKeyAccount(purpose, coin, derivation, version, 0); + } + + /// Returns the extended private key for default 0 account; derivation path used is "m/purpose'/coin'/0'". + std::string getExtendedPrivateKey(TWPurpose purpose, TWCoinType coin, TWHDVersion version) const { + return getExtendedPrivateKeyAccount(purpose, coin, TWDerivationDefault, version, 0); + } - /// Returns the extended public key. - std::string getExtendedPublicKey(TWPurpose purpose, TWCoinType coin, TWHDVersion version) const; + /// Returns the extended public key for default 0 account; derivation path used is "m/purpose'/coin'/0'". + std::string getExtendedPublicKey(TWPurpose purpose, TWCoinType coin, TWHDVersion version) const { + return getExtendedPublicKeyAccount(purpose, coin, TWDerivationDefault, version, 0); + } + + /// Returns the extended private key for a custom account; derivation path used is "m/purpose'/coin'/account'". + std::string getExtendedPrivateKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const; + + /// Returns the extended public key for a custom account; derivation path used is "m/purpose'/coin'/account'". + std::string getExtendedPublicKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const; /// Returns the BIP32 Root Key (private) std::string getRootKey(TWCoinType coin, TWHDVersion version) const; @@ -95,18 +125,11 @@ class HDWallet { /// Computes the private key from an extended private key representation. static std::optional getPrivateKeyFromExtended(const std::string& extended, TWCoinType coin, const DerivationPath& path); - public: - // Private key type (later could be moved out of HDWallet) - enum PrivateKeyType { - PrivateKeyTypeDefault32 = 0, // 32-byte private key - PrivateKeyTypeExtended96 = 1, // 3*32-byte extended private key - }; - - // obtain privateKeyType used by the coin/curve - static PrivateKeyType getPrivateKeyType(TWCurve curve); - private: void updateSeedAndEntropy(bool check = true); + + // For Cardano, derive 2nd staking derivation path from the primary one + static DerivationPath cardanoStakingDerivationPath(const DerivationPath& path); }; } // namespace TW diff --git a/src/Harmony/Address.cpp b/src/Harmony/Address.cpp index 69a68fab06a..e4be72aa81f 100644 --- a/src/Harmony/Address.cpp +++ b/src/Harmony/Address.cpp @@ -10,7 +10,8 @@ #include -using namespace TW::Harmony; +namespace TW::Harmony { const std::string Address::hrp = HRP_HARMONY; +} diff --git a/src/Harmony/Address.h b/src/Harmony/Address.h index a36d8507ec1..da4748f7a15 100644 --- a/src/Harmony/Address.h +++ b/src/Harmony/Address.h @@ -30,7 +30,7 @@ class Address: public Bech32Address { } /// Initializes an address with a public key. - Address(const PublicKey& publicKey) : Bech32Address(hrp, HASHER_SHA3K, publicKey) { + Address(const PublicKey& publicKey) : Bech32Address(hrp, Hash::HasherKeccak256, publicKey) { if (publicKey.type != TWPublicKeyTypeSECP256k1Extended) { throw std::invalid_argument("address may only be an extended SECP256k1 public key"); } diff --git a/src/Harmony/Entry.cpp b/src/Harmony/Entry.cpp index 3f926546bbb..ad652105739 100644 --- a/src/Harmony/Entry.cpp +++ b/src/Harmony/Entry.cpp @@ -9,23 +9,35 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Harmony; +using namespace TW; using namespace std; +namespace TW::Harmony { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + Address addr; + if (!Address::decode(address, addr)) { + return Data(); + } + return addr.getKeyHash(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +} // namespace TW::Harmony diff --git a/src/Harmony/Entry.h b/src/Harmony/Entry.h index ba288c5371c..1feefb3ca70 100644 --- a/src/Harmony/Entry.h +++ b/src/Harmony/Entry.h @@ -12,14 +12,14 @@ namespace TW::Harmony { /// Entry point for implementation of Harmony coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeHarmony}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Harmony diff --git a/src/Harmony/Signer.cpp b/src/Harmony/Signer.cpp index 0cfd1abe36c..1c7d2c4a803 100644 --- a/src/Harmony/Signer.cpp +++ b/src/Harmony/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,11 +9,9 @@ #include "../HexCoding.h" #include +namespace TW::Harmony { -using namespace TW; -using namespace TW::Harmony; - -std::tuple Signer::values(const uint256_t &chainID, +std::tuple Signer::values(const uint256_t& chainID, const Data& signature) noexcept { auto r = load(Data(signature.begin(), signature.begin() + 32)); auto s = load(Data(signature.begin() + 32, signature.begin() + 64)); @@ -23,18 +21,18 @@ std::tuple Signer::values(const uint256_t &chai } std::tuple -Signer::sign(const uint256_t &chainID, const PrivateKey &privateKey, const Data& hash) noexcept { +Signer::sign(const uint256_t& chainID, const PrivateKey& privateKey, const Data& hash) noexcept { auto signature = privateKey.sign(hash, TWCurveSECP256k1); return values(chainID, signature); } template -Proto::SigningOutput Signer::prepareOutput(const Data& encoded, const T &transaction) noexcept { +Proto::SigningOutput Signer::prepareOutput(const Data& encoded, const T& transaction) noexcept { auto protoOutput = Proto::SigningOutput(); - auto v = store(transaction.v); - auto r = store(transaction.r); - auto s = store(transaction.s); + auto v = store(transaction.v, 1); + auto r = store(transaction.r, 32); + auto s = store(transaction.s, 32); protoOutput.set_encoded(encoded.data(), encoded.size()); protoOutput.set_v(v.data(), v.size()); @@ -44,7 +42,7 @@ Proto::SigningOutput Signer::prepareOutput(const Data& encoded, const T &transac return protoOutput; } -Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { if (input.has_transaction_message()) { return signTransaction(input); } @@ -76,7 +74,7 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { return hex(Signer::sign(input).encoded()); } -Proto::SigningOutput Signer::signTransaction(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::signTransaction(const Proto::SigningInput& input) noexcept { auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); Address toAddr; if (!Address::decode(input.transaction_message().to_address(), toAddr)) { @@ -104,7 +102,7 @@ Proto::SigningOutput Signer::signTransaction(const Proto::SigningInput &input) n return prepareOutput(encoded, transaction); } -Proto::SigningOutput Signer::signCreateValidator(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::signCreateValidator(const Proto::SigningInput& input) noexcept { auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); auto description = Description( /* name */ input.staking_message().create_validator_message().description().name(), @@ -186,7 +184,7 @@ Proto::SigningOutput Signer::signCreateValidator(const Proto::SigningInput &inpu return prepareOutput>(encoded, stakingTx); } -Proto::SigningOutput Signer::signEditValidator(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::signEditValidator(const Proto::SigningInput& input) noexcept { auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); auto description = Description( @@ -245,7 +243,7 @@ Proto::SigningOutput Signer::signEditValidator(const Proto::SigningInput &input) return prepareOutput>(encoded, stakingTx); } -Proto::SigningOutput Signer::signDelegate(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::signDelegate(const Proto::SigningInput& input) noexcept { auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); Address delegatorAddr; @@ -275,7 +273,7 @@ Proto::SigningOutput Signer::signDelegate(const Proto::SigningInput &input) noex return prepareOutput>(encoded, stakingTx); } -Proto::SigningOutput Signer::signUndelegate(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::signUndelegate(const Proto::SigningInput& input) noexcept { auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); Address delegatorAddr; @@ -305,7 +303,7 @@ Proto::SigningOutput Signer::signUndelegate(const Proto::SigningInput &input) no return prepareOutput>(encoded, stakingTx); } -Proto::SigningOutput Signer::signCollectRewards(const Proto::SigningInput &input) noexcept { +Proto::SigningOutput Signer::signCollectRewards(const Proto::SigningInput& input) noexcept { auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); Address delegatorAddr; @@ -329,16 +327,16 @@ Proto::SigningOutput Signer::signCollectRewards(const Proto::SigningInput &input } template -void Signer::sign(const PrivateKey &privateKey, const Data& hash, T &transaction) const noexcept { +void Signer::sign(const PrivateKey& privateKey, const Data& hash, T& transaction) const noexcept { auto tuple = sign(chainID, privateKey, hash); transaction.r = std::get<0>(tuple); transaction.s = std::get<1>(tuple); transaction.v = std::get<2>(tuple); } -Data Signer::rlpNoHash(const Transaction &transaction, const bool include_vrs) const noexcept { +Data Signer::rlpNoHash(const Transaction& transaction, const bool include_vrs) const noexcept { auto encoded = Data(); - using namespace TW::Ethereum; + using RLP = TW::Ethereum::RLP; append(encoded, RLP::encode(transaction.nonce)); append(encoded, RLP::encode(transaction.gasPrice)); append(encoded, RLP::encode(transaction.gasLimit)); @@ -360,10 +358,11 @@ Data Signer::rlpNoHash(const Transaction &transaction, const bool include_vrs) c } template -Data Signer::rlpNoHash(const Staking &transaction, const bool include_vrs) const +Data Signer::rlpNoHash(const Staking& transaction, const bool include_vrs) const noexcept { auto encoded = Data(); - using namespace TW::Ethereum; + using RLP = TW::Ethereum::RLP; + append(encoded, RLP::encode(transaction.directive)); append(encoded, rlpNoHashDirective(transaction)); @@ -382,9 +381,9 @@ Data Signer::rlpNoHash(const Staking &transaction, const bool include return RLP::encodeList(encoded); } -Data Signer::rlpNoHashDirective(const Staking &transaction) const noexcept { +Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { auto encoded = Data(); - using namespace TW::Ethereum; + using RLP = TW::Ethereum::RLP; append(encoded, RLP::encode(transaction.stakeMsg.validatorAddress.getKeyHash())); @@ -433,9 +432,9 @@ Data Signer::rlpNoHashDirective(const Staking &transaction) con return RLP::encodeList(encoded); } -Data Signer::rlpNoHashDirective(const Staking &transaction) const noexcept { +Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { auto encoded = Data(); - using namespace TW::Ethereum; + using RLP = TW::Ethereum::RLP; append(encoded, RLP::encode(transaction.stakeMsg.validatorAddress.getKeyHash())); @@ -466,45 +465,47 @@ Data Signer::rlpNoHashDirective(const Staking &transaction) const return RLP::encodeList(encoded); } -Data Signer::rlpNoHashDirective(const Staking &transaction) const noexcept { +Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { auto encoded = Data(); - using namespace TW::Ethereum; + using RLP = TW::Ethereum::RLP; append(encoded, RLP::encode(transaction.stakeMsg.delegatorAddress.getKeyHash())); append(encoded, RLP::encode(transaction.stakeMsg.validatorAddress.getKeyHash())); append(encoded, RLP::encode(transaction.stakeMsg.amount)); return RLP::encodeList(encoded); } -Data Signer::rlpNoHashDirective(const Staking &transaction) const noexcept { +Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { auto encoded = Data(); - using namespace TW::Ethereum; + using RLP = TW::Ethereum::RLP; append(encoded, RLP::encode(transaction.stakeMsg.delegatorAddress.getKeyHash())); append(encoded, RLP::encode(transaction.stakeMsg.validatorAddress.getKeyHash())); append(encoded, RLP::encode(transaction.stakeMsg.amount)); return RLP::encodeList(encoded); } -Data Signer::rlpNoHashDirective(const Staking &transaction) const noexcept { +Data Signer::rlpNoHashDirective(const Staking& transaction) const noexcept { auto encoded = Data(); - using namespace TW::Ethereum; + using RLP = TW::Ethereum::RLP; append(encoded, RLP::encode(transaction.stakeMsg.delegatorAddress.getKeyHash())); return RLP::encodeList(encoded); } -std::string Signer::txnAsRLPHex(Transaction &transaction) const noexcept { +std::string Signer::txnAsRLPHex(Transaction& transaction) const noexcept { return TW::hex(rlpNoHash(transaction, false)); } template -std::string Signer::txnAsRLPHex(Staking &transaction) const noexcept { +std::string Signer::txnAsRLPHex(Staking& transaction) const noexcept { return TW::hex(rlpNoHash(transaction, false)); } -Data Signer::hash(const Transaction &transaction) const noexcept { +Data Signer::hash(const Transaction& transaction) const noexcept { return Hash::keccak256(rlpNoHash(transaction, false)); } template -Data Signer::hash(const Staking &transaction) const noexcept { +Data Signer::hash(const Staking& transaction) const noexcept { return Hash::keccak256(rlpNoHash(transaction, false)); } + +} // namespace TW::Harmony diff --git a/src/Harmony/Signer.h b/src/Harmony/Signer.h index 722cf2a7558..dc0cac9ada9 100644 --- a/src/Harmony/Signer.h +++ b/src/Harmony/Signer.h @@ -8,7 +8,7 @@ #include "Staking.h" #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/Harmony.pb.h" @@ -62,13 +62,13 @@ class Signer { /// Signs a hash with the given private key for the given chain identifier. /// - /// @returns the r, s, and v values of the transaction signature + /// \returns the r, s, and v values of the transaction signature static std::tuple sign(const uint256_t &chainID, const PrivateKey &privateKey, const Data& hash) noexcept; /// R, S, and V values for the given chain identifier and signature. /// - /// @returns the r, s, and v values of the transaction signature + /// \returns the r, s, and v values of the transaction signature static std::tuple values(const uint256_t &chainID, const Data& signature) noexcept; @@ -97,8 +97,3 @@ class Signer { }; } // namespace TW::Harmony - -/// Wrapper for C interface. -struct TWHarmonySigner { - TW::Harmony::Signer impl; -}; diff --git a/src/Harmony/Staking.cpp b/src/Harmony/Staking.cpp index a3f6291bbd8..7e99697541b 100644 --- a/src/Harmony/Staking.cpp +++ b/src/Harmony/Staking.cpp @@ -3,7 +3,3 @@ // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. - -#include "Staking.h" - -using namespace TW::Harmony; diff --git a/src/Harmony/Staking.h b/src/Harmony/Staking.h index 56c43735389..bf08cb0d199 100644 --- a/src/Harmony/Staking.h +++ b/src/Harmony/Staking.h @@ -37,14 +37,14 @@ class Staking { Staking(uint8_t directive, Directive stakeMsg, uint256_t nonce, uint256_t gasPrice, uint256_t gasLimit, uint256_t v, uint256_t r, uint256_t s) - : directive(move(directive)) - , stakeMsg(move(stakeMsg)) - , nonce(move(nonce)) - , gasPrice(move(gasPrice)) - , gasLimit(move(gasLimit)) - , v(move(v)) - , r(move(r)) - , s(move(s)) {} + : directive(std::move(directive)) + , stakeMsg(std::move(stakeMsg)) + , nonce(std::move(nonce)) + , gasPrice(std::move(gasPrice)) + , gasLimit(std::move(gasLimit)) + , v(std::move(v)) + , r(std::move(r)) + , s(std::move(s)) {} }; enum Directive : uint8_t { @@ -65,11 +65,11 @@ class Description { Description(string name, string identity, string website, string securityContact, string details) - : name(move(name)) - , identity(move(identity)) - , website(move(website)) - , securityContact(move(securityContact)) - , details(move(details)) {} + : name(std::move(name)) + , identity(std::move(identity)) + , website(std::move(website)) + , securityContact(std::move(securityContact)) + , details(std::move(details)) {} }; const uint256_t MAX_PRECISION = 18; @@ -95,7 +95,7 @@ class CommissionRate { Decimal maxChangeRate; CommissionRate(Decimal rate, Decimal maxRate, Decimal maxChangeRate) - : rate(move(rate)), maxRate(move(maxRate)), maxChangeRate(move(maxChangeRate)) {} + : rate(std::move(rate)), maxRate(std::move(maxRate)), maxChangeRate(std::move(maxChangeRate)) {} }; class CreateValidator { @@ -113,14 +113,14 @@ class CreateValidator { CommissionRate commissionRates, uint256_t minSelfDelegation, uint256_t maxTotalDelegation, vector> slotPubKeys, vector> slotKeySigs,uint256_t amount) - : validatorAddress(move(validatorAddress)) - , description(move(description)) - , commissionRates(move(commissionRates)) - , minSelfDelegation(move(minSelfDelegation)) - , maxTotalDelegation(move(maxTotalDelegation)) - , amount(move(amount)) - , slotPubKeys(move(slotPubKeys)) - , slotKeySigs(move(slotKeySigs)) {} + : validatorAddress(std::move(validatorAddress)) + , description(std::move(description)) + , commissionRates(std::move(commissionRates)) + , minSelfDelegation(std::move(minSelfDelegation)) + , maxTotalDelegation(std::move(maxTotalDelegation)) + , amount(std::move(amount)) + , slotPubKeys(std::move(slotPubKeys)) + , slotKeySigs(std::move(slotKeySigs)) {} }; class EditValidator { @@ -139,15 +139,15 @@ class EditValidator { uint256_t minSelfDelegation, uint256_t maxTotalDelegation, vector slotKeyToRemove, vector slotKeyToAdd, vector slotKeyToAddSig, uint256_t active) - : validatorAddress(move(validatorAddress)) - , description(move(description)) - , commissionRate(move(commissionRate)) - , minSelfDelegation(move(minSelfDelegation)) - , maxTotalDelegation(move(maxTotalDelegation)) - , slotKeyToRemove(move(slotKeyToRemove)) - , slotKeyToAdd(move(slotKeyToAdd)) - , slotKeyToAddSig(move(slotKeyToAddSig)) - , active(move(active)){} + : validatorAddress(std::move(validatorAddress)) + , description(std::move(description)) + , commissionRate(std::move(commissionRate)) + , minSelfDelegation(std::move(minSelfDelegation)) + , maxTotalDelegation(std::move(maxTotalDelegation)) + , slotKeyToRemove(std::move(slotKeyToRemove)) + , slotKeyToAdd(std::move(slotKeyToAdd)) + , slotKeyToAddSig(std::move(slotKeyToAddSig)) + , active(std::move(active)){} }; class Delegate { @@ -157,9 +157,9 @@ class Delegate { uint256_t amount; Delegate(Address delegatorAddress, Address validatorAddress, uint256_t amount) - : delegatorAddress(move(delegatorAddress)) - , validatorAddress(move(validatorAddress)) - , amount(move(amount)) {} + : delegatorAddress(std::move(delegatorAddress)) + , validatorAddress(std::move(validatorAddress)) + , amount(std::move(amount)) {} }; class Undelegate { @@ -169,16 +169,16 @@ class Undelegate { uint256_t amount; Undelegate(Address delegatorAddress, Address validatorAddress, uint256_t amount) - : delegatorAddress(move(delegatorAddress)) - , validatorAddress(move(validatorAddress)) - , amount(move(amount)) {} + : delegatorAddress(std::move(delegatorAddress)) + , validatorAddress(std::move(validatorAddress)) + , amount(std::move(amount)) {} }; class CollectRewards { public: Address delegatorAddress; - CollectRewards(Address delegatorAddress) : delegatorAddress(move(delegatorAddress)) {} + CollectRewards(Address delegatorAddress) : delegatorAddress(std::move(delegatorAddress)) {} }; } // namespace TW::Harmony diff --git a/src/Harmony/Transaction.cpp b/src/Harmony/Transaction.cpp deleted file mode 100644 index b63874ff7fb..00000000000 --- a/src/Harmony/Transaction.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Transaction.h" - -using namespace TW::Harmony; diff --git a/src/Hash.cpp b/src/Hash.cpp index 925d3c28fe5..aaf4c5c1339 100644 --- a/src/Hash.cpp +++ b/src/Hash.cpp @@ -1,11 +1,10 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "Hash.h" -#include "XXHash64.h" #include "BinaryCoding.h" #include @@ -20,6 +19,28 @@ using namespace TW; +TW::Hash::HasherSimpleType Hash::functionPointerFromEnum(TW::Hash::Hasher hasher) { + switch (hasher) { + case Hash::HasherSha1: return Hash::sha1; + default: case Hash::HasherSha256: return Hash::sha256; + case Hash::HasherSha512: return Hash::sha512; + case Hash::HasherSha512_256: return Hash::sha512_256; + case Hash::HasherKeccak256: return Hash::keccak256; + case Hash::HasherKeccak512: return Hash::keccak512; + case Hash::HasherSha3_256: return Hash::sha3_256; + case Hash::HasherSha3_512: return Hash::sha3_512; + case Hash::HasherRipemd: return Hash::ripemd; + case Hash::HasherBlake256: return Hash::blake256; + case Hash::HasherGroestl512: return Hash::groestl512; + case Hash::HasherSha256d: return Hash::sha256d; + case Hash::HasherSha256ripemd: return Hash::sha256ripemd; + case Hash::HasherSha3_256ripemd: return Hash::sha3_256ripemd; + case Hash::HasherBlake256d: return Hash::blake256d; + case Hash::HasherBlake256ripemd: return Hash::blake256ripemd; + case Hash::HasherGroestl512d: return Hash::groestl512d; + } +} + Data Hash::sha1(const byte* data, size_t size) { Data result(sha1Size); sha1_Raw(data, size, result.data()); @@ -101,27 +122,6 @@ Data Hash::groestl512(const byte* data, size_t size) { return result; } -uint64_t Hash::xxhash(const byte* data, size_t size, uint64_t seed) -{ - return XXHash64::hash(data, size, seed); -} - -Data Hash::xxhash64(const byte* data, size_t size, uint64_t seed) -{ - const auto hash = XXHash64::hash(data, size, seed); - Data result; - encode64LE(hash, result); - return result; -} - -Data Hash::xxhash64concat(const byte* data, size_t size) -{ - auto key1 = xxhash64(data, size, 0); - const auto key2 = xxhash64(data, size, 1); - TW::append(key1, key2); - return key1; -} - Data Hash::hmac256(const Data& key, const Data& message) { Data hmac(SHA256_DIGEST_LENGTH); hmac_sha256(key.data(), static_cast(key.size()), message.data(), static_cast(message.size()), hmac.data()); diff --git a/src/Hash.h b/src/Hash.h index 47171dedcb6..7d16544976e 100644 --- a/src/Hash.h +++ b/src/Hash.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,9 +12,32 @@ namespace TW::Hash { +/// Enum selector for the supported hash functions +enum Hasher { + HasherSha1 = 0, // SHA1 + HasherSha256, // SHA256 + HasherSha512, // SHA512 + HasherSha512_256, // SHA512/256 + HasherKeccak256, // Keccak SHA256 + HasherKeccak512, // Keccak SHA512 + HasherSha3_256, // version 3 SHA256 + HasherSha3_512, // version 3 SHA512 + HasherRipemd, // RIPEMD160 + HasherBlake256, // Blake256 + HasherGroestl512, // Groestl 512 + HasherSha256d, // SHA256 hash of the SHA256 hash + HasherSha256ripemd, // ripemd hash of the SHA256 hash + HasherSha3_256ripemd, // ripemd hash of the SHA256 hash + HasherBlake256d, // Blake256 hash of the Blake256 hash + HasherBlake256ripemd, // ripemd hash of the Blake256 hash + HasherGroestl512d, // Groestl512 hash of the Groestl512 hash +}; + /// Hashing function. typedef TW::Data (*HasherSimpleType)(const TW::byte*, size_t); -using Hasher = std::function; + +/// Hash function (pointer type) from enum +TW::Hash::HasherSimpleType functionPointerFromEnum(TW::Hash::Hasher hasher); // Digest size constants, duplicating constants from underlying lib /// Number of bytes in a SHA1 hash. @@ -67,32 +90,21 @@ Data blake2b(const byte* data, size_t dataSize, size_t hsshSize, const Data& per /// Computes the Groestl 512 hash. Data groestl512(const byte* data, size_t size); -/// Computes the XXHash hash. -uint64_t xxhash(const byte* data, size_t size, uint64_t seed); - -/// Computes the XXHash hash with 64 encoding. -Data xxhash64(const byte* data, size_t size, uint64_t seed); - -/// Computes the XXHash hash concatenated, xxhash64 with seed 0 and 1, -Data xxhash64concat(const byte* data, size_t size); - -/// Computes the XXHash hash. -uint64_t xxhash(const byte* data, const byte* end, uint64_t seed); - -/// Computes the XXHash hash with 64 encoding. -Data xxhash64(const byte* data, const byte* end, uint64_t seed); - -/// Computes the XXHash hash concatenated, xxhash64 with seed 0 and 1, -Data xxhash64concat(const byte* data, const byte* end); - -// Templated versions for any type with data() and size() +/// Computes requested hash for data (hasher enum, bytes) +inline Data hash(Hasher hasher, const byte* data, size_t dataSize) { + const auto func = functionPointerFromEnum(hasher); + return func(data, dataSize); +} -/// Computes requested hash for data. +/// Computes requested hash for data (hasher enum) template Data hash(Hasher hasher, const T& data) { - return hasher(reinterpret_cast(data.data()), data.size()); + const auto func = functionPointerFromEnum(hasher); + return func(reinterpret_cast(data.data()), data.size()); } +// Templated versions for any type with data() and size() + /// Computes the SHA1 hash. template Data sha1(const T& data) { diff --git a/src/Icon/Address.cpp b/src/Icon/Address.cpp index 4c86ae521d9..6f219a3b537 100644 --- a/src/Icon/Address.cpp +++ b/src/Icon/Address.cpp @@ -6,14 +6,11 @@ #include "Address.h" -#include "../Hash.h" #include "../HexCoding.h" -#include "../PrivateKey.h" #include -using namespace TW; -using namespace TW::Icon; +namespace TW::Icon { static const std::string addressPrefix = "hx"; static const std::string contractPrefix = "cx"; @@ -46,7 +43,8 @@ Address::Address(const std::string& string) { std::copy(data.begin(), data.end(), bytes.begin()); } -Address::Address(const PublicKey& publicKey, enum AddressType type) : type(type) { +Address::Address(const PublicKey& publicKey, enum AddressType type) + : type(type) { auto hash = std::array(); sha3_256(publicKey.bytes.data() + 1, publicKey.bytes.size() - 1, hash.data()); std::copy(hash.end() - Address::size, hash.end(), bytes.begin()); @@ -62,3 +60,5 @@ std::string Address::string() const { return ""; } } + +} // namespace TW::Icon diff --git a/src/Icon/Address.h b/src/Icon/Address.h index 1c2a8645f1c..8c699e91f71 100644 --- a/src/Icon/Address.h +++ b/src/Icon/Address.h @@ -23,7 +23,7 @@ class Address { /// Address data consisting of a prefix byte followed by the public key /// hash. - std::array bytes; + std::array bytes{}; /// Address type. enum AddressType type; diff --git a/src/Icon/Entry.cpp b/src/Icon/Entry.cpp index 2786ebd14b1..66c6ff5d2ad 100644 --- a/src/Icon/Entry.cpp +++ b/src/Icon/Entry.cpp @@ -9,19 +9,20 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Icon; -using namespace std; +namespace TW::Icon { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey, Icon::TypeAddress).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Icon diff --git a/src/Icon/Entry.h b/src/Icon/Entry.h index 699cb8be0d7..9547997f419 100644 --- a/src/Icon/Entry.h +++ b/src/Icon/Entry.h @@ -12,12 +12,11 @@ namespace TW::Icon { /// Entry point for implementation of ICON coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeICON}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Icon diff --git a/src/Icon/Signer.cpp b/src/Icon/Signer.cpp index 9028f327d3c..b9c290c867e 100644 --- a/src/Icon/Signer.cpp +++ b/src/Icon/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,17 +10,14 @@ #include "../Hash.h" #include "../HexCoding.h" #include "../PrivateKey.h" -#include "../uint256.h" #include #include #include -#include #include -using namespace TW; -using namespace TW::Icon; +namespace TW::Icon { std::string to_hex(int64_t i) { std::stringstream ss; @@ -92,3 +89,5 @@ Proto::SigningOutput Signer::sign() const noexcept { return output; } + +} // namespace TW::Icon diff --git a/src/Icon/Signer.h b/src/Icon/Signer.h index 987ae90e546..209757dc00f 100644 --- a/src/Icon/Signer.h +++ b/src/Icon/Signer.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../proto/Icon.pb.h" #include diff --git a/src/IoTeX/Address.cpp b/src/IoTeX/Address.cpp index 606af156391..7aca2f45b63 100644 --- a/src/IoTeX/Address.cpp +++ b/src/IoTeX/Address.cpp @@ -8,6 +8,8 @@ #include -using namespace TW::IoTeX; +namespace TW::IoTeX { const std::string Address::hrp = HRP_IOTEX; + +} diff --git a/src/IoTeX/Address.h b/src/IoTeX/Address.h index a76743a4250..5dbf03b4a39 100644 --- a/src/IoTeX/Address.h +++ b/src/IoTeX/Address.h @@ -30,7 +30,7 @@ class Address: public Bech32Address { } /// Initializes an address with a public key. - Address(const PublicKey& publicKey) : Bech32Address(hrp, HASHER_SHA3K, publicKey) { + Address(const PublicKey& publicKey) : Bech32Address(hrp, Hash::HasherKeccak256, publicKey) { if (publicKey.type != TWPublicKeyTypeSECP256k1Extended) { throw std::invalid_argument("address may only be an extended SECP256k1 public key"); } diff --git a/src/IoTeX/Entry.cpp b/src/IoTeX/Entry.cpp index dc4c73dcc9d..5e427d85a67 100644 --- a/src/IoTeX/Entry.cpp +++ b/src/IoTeX/Entry.cpp @@ -9,19 +9,22 @@ #include "Address.h" #include "Signer.h" -using namespace TW::IoTeX; using namespace std; +namespace TW::IoTeX { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::IoTeX diff --git a/src/IoTeX/Entry.h b/src/IoTeX/Entry.h index 8695c74e08e..ecf04236fc7 100644 --- a/src/IoTeX/Entry.h +++ b/src/IoTeX/Entry.h @@ -12,12 +12,11 @@ namespace TW::IoTeX { /// Entry point for implementation of IoTeX coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeIoTeX}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::IoTeX diff --git a/src/IoTeX/Signer.cpp b/src/IoTeX/Signer.cpp index 6b0e6d6ec1a..11f4f9d9067 100644 --- a/src/IoTeX/Signer.cpp +++ b/src/IoTeX/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,12 +6,11 @@ #include "Signer.h" #include "Hash.h" -#include "HexCoding.h" -#include "IoTeX/Staking.h" #include "PrivateKey.h" using namespace TW; -using namespace TW::IoTeX; + +namespace TW::IoTeX { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto signer = Signer(input); @@ -48,3 +47,5 @@ void Signer::toActionCore() { action.ParseFromString(input.SerializeAsString()); action.DiscardUnknownFields(); } + +} // namespace TW::IoTeX diff --git a/src/Keystore/AESParameters.cpp b/src/Keystore/AESParameters.cpp index 146e1a12727..6aa7bfdc5b0 100644 --- a/src/Keystore/AESParameters.cpp +++ b/src/Keystore/AESParameters.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,7 +11,8 @@ #include using namespace TW; -using namespace TW::Keystore; + +namespace TW::Keystore { AESParameters::AESParameters() { iv = Data(blockSize, 0); @@ -33,3 +34,5 @@ nlohmann::json AESParameters::json() const { j[CodingKeys::iv] = hex(iv); return j; } + +} // namespace TW::Keystore diff --git a/src/Keystore/AESParameters.h b/src/Keystore/AESParameters.h index 5f6b994157c..edae9ab9b4f 100644 --- a/src/Keystore/AESParameters.h +++ b/src/Keystore/AESParameters.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include diff --git a/src/Keystore/Account.cpp b/src/Keystore/Account.cpp index c27b7dbfca7..6c061ce30e7 100644 --- a/src/Keystore/Account.cpp +++ b/src/Keystore/Account.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,22 +8,28 @@ #include "../Base64.h" #include "../Coin.h" -#include "../HexCoding.h" using namespace TW; -using namespace TW::Keystore; + +namespace TW::Keystore { namespace CodingKeys { - static const auto address = "address"; - static const auto derivationPath = "derivationPath"; - static const auto extendedPublicKey = "extendedPublicKey"; - static const auto indices = "indices"; - static const auto value = "value"; - static const auto hardened = "hardened"; - static const auto coin = "coin"; +static const auto address = "address"; +static const auto derivation = "derivation"; +static const auto derivationPath = "derivationPath"; +static const auto extendedPublicKey = "extendedPublicKey"; +static const auto indices = "indices"; +static const auto value = "value"; +static const auto hardened = "hardened"; +static const auto coin = "coin"; +static const auto publicKey = "publicKey"; } // namespace CodingKeys Account::Account(const nlohmann::json& json) { + if (json.find(CodingKeys::derivation) != json.end()) { + derivation = TWDerivation(json[CodingKeys::derivation].get()); + } + if (json[CodingKeys::derivationPath].is_object()) { const auto indices = json[CodingKeys::derivationPath][CodingKeys::indices]; for (auto& indexJSON : indices) { @@ -51,15 +57,28 @@ Account::Account(const nlohmann::json& json) { json[CodingKeys::extendedPublicKey].is_string()) { extendedPublicKey = json[CodingKeys::extendedPublicKey].get(); } + + if (json.count(CodingKeys::publicKey) > 0 && + json[CodingKeys::publicKey].is_string()) { + publicKey = json[CodingKeys::publicKey].get(); + } } nlohmann::json Account::json() const { nlohmann::json j; j[CodingKeys::address] = address; + if (derivation != TWDerivationDefault) { + j[CodingKeys::derivation] = static_cast(derivation); + } j[CodingKeys::derivationPath] = derivationPath.string(); j[CodingKeys::coin] = coin; if (!extendedPublicKey.empty()) { j[CodingKeys::extendedPublicKey] = extendedPublicKey; } + if (!publicKey.empty()) { + j[CodingKeys::publicKey] = publicKey; + } return j; } + +} // namespace TW::Keystore diff --git a/src/Keystore/Account.h b/src/Keystore/Account.h index fe5c137fe9a..6c48ec8723f 100644 --- a/src/Keystore/Account.h +++ b/src/Keystore/Account.h @@ -7,6 +7,7 @@ #pragma once #include "../DerivationPath.h" +#include #include #include @@ -16,24 +17,32 @@ namespace TW::Keystore { /// Account for a particular coin within a wallet. class Account { public: + /// Coin this account is for + TWCoinType coin; + /// Account public address std::string address; - /// Account derivation path, only relevant for HD wallets. + /// Account derivation. May be missing or unreliable in Json stored format. + TWDerivation derivation = TWDerivationDefault; + + /// Account derivation path, only relevant for HD wallets; info only. DerivationPath derivationPath; - /// Extended public key. - std::string extendedPublicKey; + /// Account public key in hex format. + std::string publicKey; - /// Coin this account is for. - TWCoinType coin; + /// Extended public key, info only. + std::string extendedPublicKey; Account() = default; - Account(std::string address, TWCoinType coin, DerivationPath derivationPath, std::string extendedPublicKey = "") - : address(std::move(address)) + Account(std::string address, TWCoinType coin, TWDerivation derivation, DerivationPath derivationPath, std::string publicKey, std::string extendedPublicKey) + : coin(coin) + , address(std::move(address)) + , derivation(derivation) , derivationPath(std::move(derivationPath)) - , extendedPublicKey(std::move(extendedPublicKey)) - , coin(coin) {} + , publicKey(std::move(publicKey)) + , extendedPublicKey(std::move(extendedPublicKey)) {} /// Initializes `Account` with a JSON object. Account(const nlohmann::json& json); diff --git a/src/Keystore/EncryptionParameters.cpp b/src/Keystore/EncryptionParameters.cpp index b166d724919..d3e08dd6ebe 100644 --- a/src/Keystore/EncryptionParameters.cpp +++ b/src/Keystore/EncryptionParameters.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,17 +7,15 @@ #include "EncryptionParameters.h" #include "../Hash.h" -#include "../HexCoding.h" #include #include #include - -#include #include using namespace TW; -using namespace TW::Keystore; + +namespace TW::Keystore { template static Data computeMAC(Iter begin, Iter end, const Data& key) { @@ -28,8 +26,50 @@ static Data computeMAC(Iter begin, Iter end, const Data& key) { return Hash::keccak256(data); } -EncryptionParameters::EncryptionParameters(const Data& password, const Data& data) : mac() { - auto scryptParams = boost::get(kdfParams); +// ----------------- +// Encoding/Decoding +// ----------------- + +namespace CodingKeys { +static const auto encrypted = "ciphertext"; +static const auto cipher = "cipher"; +static const auto cipherParams = "cipherparams"; +static const auto kdf = "kdf"; +static const auto kdfParams = "kdfparams"; +static const auto mac = "mac"; +} // namespace CodingKeys + +EncryptionParameters::EncryptionParameters(const nlohmann::json& json) { + cipher = json[CodingKeys::cipher].get(); + cipherParams = AESParameters(json[CodingKeys::cipherParams]); + + auto kdf = json[CodingKeys::kdf].get(); + if (kdf == "scrypt") { + kdfParams = ScryptParameters(json[CodingKeys::kdfParams]); + } else if (kdf == "pbkdf2") { + kdfParams = PBKDF2Parameters(json[CodingKeys::kdfParams]); + } +} + +nlohmann::json EncryptionParameters::json() const { + nlohmann::json j; + j[CodingKeys::cipher] = cipher; + j[CodingKeys::cipherParams] = cipherParams.json(); + + if (auto* scryptParams = std::get_if(&kdfParams); scryptParams) { + j[CodingKeys::kdf] = "scrypt"; + j[CodingKeys::kdfParams] = scryptParams->json(); + } else if (auto* pbkdf2Params = std::get_if(&kdfParams); pbkdf2Params) { + j[CodingKeys::kdf] = "pbkdf2"; + j[CodingKeys::kdfParams] = pbkdf2Params->json(); + } + + return j; +} + +EncryptedPayload::EncryptedPayload(const Data& password, const Data& data, const EncryptionParameters& params) + : params(std::move(params)), _mac() { + auto scryptParams = std::get(this->params.kdfParams); auto derivedKey = Data(scryptParams.desiredKeyLength); scrypt(reinterpret_cast(password.data()), password.size(), scryptParams.salt.data(), scryptParams.salt.size(), scryptParams.n, scryptParams.r, scryptParams.p, derivedKey.data(), @@ -39,59 +79,62 @@ EncryptionParameters::EncryptionParameters(const Data& password, const Data& dat auto result = aes_encrypt_key128(derivedKey.data(), &ctx); assert(result == EXIT_SUCCESS); if (result == EXIT_SUCCESS) { - Data iv = cipherParams.iv; + Data iv = this->params.cipherParams.iv; encrypted = Data(data.size()); aes_ctr_encrypt(data.data(), encrypted.data(), static_cast(data.size()), iv.data(), aes_ctr_cbuf_inc, &ctx); - mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); + _mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); } } -EncryptionParameters::~EncryptionParameters() { +EncryptedPayload::~EncryptedPayload() { std::fill(encrypted.begin(), encrypted.end(), 0); + std::fill(_mac.begin(), _mac.end(), 0); } -Data EncryptionParameters::decrypt(const Data& password) const { +Data EncryptedPayload::decrypt(const Data& password) const { auto derivedKey = Data(); auto mac = Data(); - if (kdfParams.which() == 0) { - auto scryptParams = boost::get(kdfParams); - derivedKey.resize(scryptParams.defaultDesiredKeyLength); - scrypt(password.data(), password.size(), scryptParams.salt.data(), - scryptParams.salt.size(), scryptParams.n, scryptParams.r, scryptParams.p, derivedKey.data(), - scryptParams.defaultDesiredKeyLength); + if (auto* scryptParams = std::get_if(¶ms.kdfParams); scryptParams) { + derivedKey.resize(scryptParams->defaultDesiredKeyLength); + scrypt(password.data(), password.size(), scryptParams->salt.data(), + scryptParams->salt.size(), scryptParams->n, scryptParams->r, scryptParams->p, derivedKey.data(), + scryptParams->defaultDesiredKeyLength); mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); - } else if (kdfParams.which() == 1) { - auto pbkdf2Params = boost::get(kdfParams); - derivedKey.resize(pbkdf2Params.defaultDesiredKeyLength); - pbkdf2_hmac_sha256(password.data(), static_cast(password.size()), pbkdf2Params.salt.data(), - static_cast(pbkdf2Params.salt.size()), pbkdf2Params.iterations, derivedKey.data(), - pbkdf2Params.defaultDesiredKeyLength); + } else if (auto* pbkdf2Params = std::get_if(¶ms.kdfParams); pbkdf2Params) { + derivedKey.resize(pbkdf2Params->defaultDesiredKeyLength); + pbkdf2_hmac_sha256(password.data(), static_cast(password.size()), pbkdf2Params->salt.data(), + static_cast(pbkdf2Params->salt.size()), pbkdf2Params->iterations, derivedKey.data(), + pbkdf2Params->defaultDesiredKeyLength); mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); } else { throw DecryptionError::unsupportedKDF; } - if (mac != this->mac) { + if (mac != _mac) { throw DecryptionError::invalidPassword; } Data decrypted(encrypted.size()); - Data iv = cipherParams.iv; - if (cipher == "aes-128-ctr") { + Data iv = params.cipherParams.iv; + if (params.cipher == "aes-128-ctr") { aes_encrypt_ctx ctx; - [[maybe_unused]] auto result = aes_encrypt_key(derivedKey.data(), 16, &ctx); + //win + //auto __attribute__((unused)) result = aes_encrypt_key(derivedKey.data(), 16, &ctx); + [[maybe_unused]] auto result = aes_encrypt_key(derivedKey.data(), 16, &ctx); assert(result != EXIT_FAILURE); aes_ctr_decrypt(encrypted.data(), decrypted.data(), static_cast(encrypted.size()), iv.data(), aes_ctr_cbuf_inc, &ctx); - } else if (cipher == "aes-128-cbc") { + } else if (params.cipher == "aes-128-cbc") { aes_decrypt_ctx ctx; + //win + //auto __attribute__((unused)) result = aes_decrypt_key(derivedKey.data(), 16, &ctx); [[maybe_unused]] auto result = aes_decrypt_key(derivedKey.data(), 16, &ctx); assert(result != EXIT_FAILURE); - for (auto i = 0; i < encrypted.size(); i += 16) { + for (auto i = 0ul; i < encrypted.size(); i += 16) { aes_cbc_decrypt(encrypted.data() + i, decrypted.data() + i, 16, iv.data(), &ctx); } } else { @@ -101,50 +144,17 @@ Data EncryptionParameters::decrypt(const Data& password) const { return decrypted; } -// ----------------- -// Encoding/Decoding -// ----------------- - -namespace CodingKeys { -static const auto encrypted = "ciphertext"; -static const auto cipher = "cipher"; -static const auto cipherParams = "cipherparams"; -static const auto kdf = "kdf"; -static const auto kdfParams = "kdfparams"; -static const auto mac = "mac"; -} // namespace CodingKeys - -EncryptionParameters::EncryptionParameters(const nlohmann::json& json) { +EncryptedPayload::EncryptedPayload(const nlohmann::json& json) { + params = EncryptionParameters(json); encrypted = parse_hex(json[CodingKeys::encrypted].get()); - cipher = json[CodingKeys::cipher].get(); - cipherParams = AESParameters(json[CodingKeys::cipherParams]); - mac = parse_hex(json[CodingKeys::mac].get()); - - auto kdf = json[CodingKeys::kdf].get(); - if (kdf == "scrypt") { - kdfParams = ScryptParameters(json[CodingKeys::kdfParams]); - } else if (kdf == "pbkdf2") { - kdfParams = PBKDF2Parameters(json[CodingKeys::kdfParams]); - } + _mac = parse_hex(json[CodingKeys::mac].get()); } -nlohmann::json EncryptionParameters::json() const { - nlohmann::json j; +nlohmann::json EncryptedPayload::json() const { + nlohmann::json j = params.json(); j[CodingKeys::encrypted] = hex(encrypted); - j[CodingKeys::cipher] = cipher; - j[CodingKeys::cipherParams] = cipherParams.json(); - j[CodingKeys::mac] = hex(mac); - - if (kdfParams.which() == 0) { - auto scryptParams = boost::get(kdfParams); - j[CodingKeys::kdf] = "scrypt"; - j[CodingKeys::kdfParams] = scryptParams.json(); - } else if (kdfParams.which() == 1) { - auto pbkdf2Params = boost::get(kdfParams); - j[CodingKeys::kdf] = "pbkdf2"; - j[CodingKeys::kdfParams] = pbkdf2Params.json(); - - } - + j[CodingKeys::mac] = hex(_mac); return j; } + +} // namespace TW::Keystore diff --git a/src/Keystore/EncryptionParameters.h b/src/Keystore/EncryptionParameters.h index e1e68933977..6592b9756cd 100644 --- a/src/Keystore/EncryptionParameters.h +++ b/src/Keystore/EncryptionParameters.h @@ -9,14 +9,60 @@ #include "AESParameters.h" #include "PBKDF2Parameters.h" #include "ScryptParameters.h" -#include "../Data.h" +#include "Data.h" +#include -#include +#include #include #include namespace TW::Keystore { +/// Set of parameters used when encoding +struct EncryptionParameters { + static EncryptionParameters getPreset(enum TWStoredKeyEncryptionLevel preset) { + switch (preset) { + case TWStoredKeyEncryptionLevelMinimal: + return EncryptionParameters(AESParameters(), ScryptParameters::Minimal); + case TWStoredKeyEncryptionLevelWeak: + case TWStoredKeyEncryptionLevelDefault: + default: + return EncryptionParameters(AESParameters(), ScryptParameters::Weak); + case TWStoredKeyEncryptionLevelStandard: + return EncryptionParameters(AESParameters(), ScryptParameters::Standard); + } + } + + /// Cipher algorithm. + std::string cipher = "aes-128-ctr"; + + /// Cipher parameters. + AESParameters cipherParams = AESParameters(); + + /// Key derivation function parameters. + std::variant kdfParams = ScryptParameters(); + + EncryptionParameters() = default; + + /// Initializes with standard values. + EncryptionParameters(AESParameters cipherParams, std::variant kdfParams) + : cipherParams(std::move(cipherParams)) + , kdfParams(std::move(kdfParams)) {} + + /// Initializes with a JSON object. + EncryptionParameters(const nlohmann::json& json); + + /// Saves `this` as a JSON object. + nlohmann::json json() const; + + EncryptionParameters(const EncryptionParameters& other) = default; + EncryptionParameters(EncryptionParameters&& other) = default; + EncryptionParameters& operator=(const EncryptionParameters& other) = default; + EncryptionParameters& operator=(EncryptionParameters&& other) = default; + + virtual ~EncryptionParameters() = default; +}; + /// Errors thrown when decrypting a key. enum class DecryptionError { unsupportedKDF, @@ -27,37 +73,31 @@ enum class DecryptionError { invalidPassword, }; -struct EncryptionParameters { +/// An encrypted payload data +struct EncryptedPayload { +public: + EncryptionParameters params; + /// Encrypted data. Data encrypted; - /// Cipher algorithm. - std::string cipher = "aes-128-ctr"; - - /// Cipher parameters. - AESParameters cipherParams = AESParameters(); - - /// Key derivation function parameters. - boost::variant kdfParams = ScryptParameters(); - /// Message authentication code. - Data mac; + Data _mac; - EncryptionParameters() = default; + EncryptedPayload() = default; - /// Initializes `EncryptionParameters` with standard values. - EncryptionParameters(const Data& encrypted, AESParameters cipherParams, boost::variant kdfParams, const Data& mac) - : encrypted(std::move(encrypted)) - , cipherParams(std::move(cipherParams)) - , kdfParams(std::move(kdfParams)) - , mac(std::move(mac)) {} + /// Initializes with standard values. + EncryptedPayload(const EncryptionParameters& params, const Data& encrypted, const Data& mac) + : params(std::move(params)) + , encrypted(std::move(encrypted)) + , _mac(std::move(mac)) {} - /// Initializes `EncryptionParameters` by encrypting data with a password + /// Initializes by encrypting data with a password /// using standard values. - EncryptionParameters(const Data& password, const Data& data); + EncryptedPayload(const Data& password, const Data& data, const EncryptionParameters& params); - /// Initializes `EncryptionParameters` with a JSON object. - EncryptionParameters(const nlohmann::json& json); + /// Initializes with a JSON object. + EncryptedPayload(const nlohmann::json& json); /// Decrypts the payload with the given password. Data decrypt(const Data& password) const; @@ -65,12 +105,12 @@ struct EncryptionParameters { /// Saves `this` as a JSON object. nlohmann::json json() const; - EncryptionParameters(const EncryptionParameters& other) = default; - EncryptionParameters(EncryptionParameters&& other) = default; - EncryptionParameters& operator=(const EncryptionParameters& other) = default; - EncryptionParameters& operator=(EncryptionParameters&& other) = default; + EncryptedPayload(const EncryptedPayload& other) = default; + EncryptedPayload(EncryptedPayload&& other) = default; + EncryptedPayload& operator=(const EncryptedPayload& other) = default; + EncryptedPayload& operator=(EncryptedPayload&& other) = default; - virtual ~EncryptionParameters(); + virtual ~EncryptedPayload(); }; } // namespace TW::Keystore diff --git a/src/Keystore/PBKDF2Parameters.cpp b/src/Keystore/PBKDF2Parameters.cpp index fef2ebe5197..d364184db07 100644 --- a/src/Keystore/PBKDF2Parameters.cpp +++ b/src/Keystore/PBKDF2Parameters.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,12 +7,13 @@ #include "PBKDF2Parameters.h" #include -#include using namespace TW; -using namespace TW::Keystore; -PBKDF2Parameters::PBKDF2Parameters() : salt(32) { +namespace TW::Keystore { + +PBKDF2Parameters::PBKDF2Parameters() + : salt(32) { random_buffer(salt.data(), salt.size()); } @@ -41,3 +42,5 @@ nlohmann::json PBKDF2Parameters::json() const { j[CodingKeys::iterations] = iterations; return j; } + +} // namespace TW::Keystore diff --git a/src/Keystore/PBKDF2Parameters.h b/src/Keystore/PBKDF2Parameters.h index 0d473b5615b..175a5106157 100644 --- a/src/Keystore/PBKDF2Parameters.h +++ b/src/Keystore/PBKDF2Parameters.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../HexCoding.h" #include diff --git a/src/Keystore/ScryptParameters.cpp b/src/Keystore/ScryptParameters.cpp index 16ccd489c4c..cc32e0357a0 100644 --- a/src/Keystore/ScryptParameters.cpp +++ b/src/Keystore/ScryptParameters.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,16 +10,22 @@ #include using namespace TW; -using namespace TW::Keystore; -ScryptParameters::ScryptParameters() : salt(32) { +namespace TW::Keystore { + +ScryptParameters ScryptParameters::Minimal = ScryptParameters(Data(), minimalN, defaultR, minimalP, defaultDesiredKeyLength); +ScryptParameters ScryptParameters::Weak = ScryptParameters(Data(), weakN, defaultR, weakP, defaultDesiredKeyLength); +ScryptParameters ScryptParameters::Standard = ScryptParameters(Data(), standardN, defaultR, standardP, defaultDesiredKeyLength); + +ScryptParameters::ScryptParameters() + : salt(32) { random_buffer(salt.data(), salt.size()); } #pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" std::optional ScryptParameters::validate() const { - if (desiredKeyLength > ((1ULL << 32) - 1) * 32) { // depending on size_t size on platform, may be always false + if (desiredKeyLength > ((1ULL << 32) - 1) * 32) { // depending on size_t size on platform, may be always false return ScryptValidationError::desiredKeyLengthTooLarge; } if (static_cast(r) * static_cast(p) >= (1 << 30)) { @@ -39,32 +45,36 @@ std::optional ScryptParameters::validate() const { // Encoding/Decoding // ----------------- -namespace CodingKeys { +namespace CodingKeys::SP { + static const auto salt = "salt"; static const auto desiredKeyLength = "dklen"; static const auto n = "n"; static const auto p = "p"; static const auto r = "r"; -} // namespace CodingKeys + +} // namespace CodingKeys::SP ScryptParameters::ScryptParameters(const nlohmann::json& json) { - salt = parse_hex(json[CodingKeys::salt].get()); - desiredKeyLength = json[CodingKeys::desiredKeyLength]; - if (json.count(CodingKeys::n) != 0) - n = json[CodingKeys::n]; - if (json.count(CodingKeys::n) != 0) - p = json[CodingKeys::p]; - if (json.count(CodingKeys::n) != 0) - r = json[CodingKeys::r]; + salt = parse_hex(json[CodingKeys::SP::salt].get()); + desiredKeyLength = json[CodingKeys::SP::desiredKeyLength]; + if (json.count(CodingKeys::SP::n) != 0) + n = json[CodingKeys::SP::n]; + if (json.count(CodingKeys::SP::n) != 0) + p = json[CodingKeys::SP::p]; + if (json.count(CodingKeys::SP::n) != 0) + r = json[CodingKeys::SP::r]; } /// Saves `this` as a JSON object. nlohmann::json ScryptParameters::json() const { nlohmann::json j; - j[CodingKeys::salt] = hex(salt); - j[CodingKeys::desiredKeyLength] = desiredKeyLength; - j[CodingKeys::n] = n; - j[CodingKeys::p] = p; - j[CodingKeys::r] = r; + j[CodingKeys::SP::salt] = hex(salt); + j[CodingKeys::SP::desiredKeyLength] = desiredKeyLength; + j[CodingKeys::SP::n] = n; + j[CodingKeys::SP::p] = p; + j[CodingKeys::SP::r] = r; return j; } + +} // namespace TW::Keystore diff --git a/src/Keystore/ScryptParameters.h b/src/Keystore/ScryptParameters.h index 10e7c019bd6..b29faa0e344 100644 --- a/src/Keystore/ScryptParameters.h +++ b/src/Keystore/ScryptParameters.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../HexCoding.h" #include @@ -23,21 +23,22 @@ enum class ScryptValidationError { /// Scrypt function parameters. struct ScryptParameters { - /// The N parameter of Scrypt encryption algorithm, using 256MB memory and - /// taking approximately 1s CPU time on a modern processor. - static const uint32_t standardN = 1 << 18; + static ScryptParameters Minimal; + static ScryptParameters Weak; + static ScryptParameters Standard; - /// The P parameter of Scrypt encryption algorithm, using 256MB memory and + /// The N and P parameters of Scrypt encryption algorithm, using 256MB memory and /// taking approximately 1s CPU time on a modern processor. + static const uint32_t standardN = 1 << 18; static const uint32_t standardP = 1; - /// The N parameter of Scrypt encryption algorithm, using 4MB memory and - /// taking approximately 100ms CPU time on a modern processor. - static const uint32_t lightN = 1 << 12; + static const uint32_t weakN = 1 << 14; + static const uint32_t weakP = 4; - /// The P parameter of Scrypt encryption algorithm, using 4MB memory and + /// The N and P parameters of Scrypt encryption algorithm, using 4MB memory and /// taking approximately 100ms CPU time on a modern processor. - static const uint32_t lightP = 6; + static const uint32_t minimalN = 1 << 12; + static const uint32_t minimalP = 6; /// Default `R` parameter of Scrypt encryption algorithm. static const uint32_t defaultR = 8; @@ -52,10 +53,10 @@ struct ScryptParameters { std::size_t desiredKeyLength = defaultDesiredKeyLength; /// CPU/Memory cost factor. - uint32_t n = lightN; + uint32_t n = minimalN; /// Parallelization factor (1..232-1 * hLen/MFlen). - uint32_t p = lightP; + uint32_t p = minimalP; /// Block size factor. uint32_t r = defaultR; diff --git a/src/Keystore/StoredKey.cpp b/src/Keystore/StoredKey.cpp index ee7b5d9dcc5..add26c2ca26 100644 --- a/src/Keystore/StoredKey.cpp +++ b/src/Keystore/StoredKey.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,60 +7,51 @@ #include "StoredKey.h" #include "Coin.h" +#include "HexCoding.h" #include "Mnemonic.h" #include "PrivateKey.h" #define BOOST_UUID_RANDOM_PROVIDER_FORCE_POSIX 1 #include -#include #include #include #include +#include #include -#include -#include #include -#include using namespace TW; -using namespace TW::Keystore; -StoredKey StoredKey::createWithMnemonic(const std::string& name, const Data& password, const std::string& mnemonic) { +namespace TW::Keystore { + +StoredKey StoredKey::createWithMnemonic(const std::string& name, const Data& password, const std::string& mnemonic, TWStoredKeyEncryptionLevel encryptionLevel) { if (!Mnemonic::isValid(mnemonic)) { throw std::invalid_argument("Invalid mnemonic"); } - + Data mnemonicData = TW::Data(mnemonic.begin(), mnemonic.end()); - StoredKey key = StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData); - return key; + return StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel); } -StoredKey StoredKey::createWithMnemonicRandom(const std::string& name, const Data& password) { +StoredKey StoredKey::createWithMnemonicRandom(const std::string& name, const Data& password, TWStoredKeyEncryptionLevel encryptionLevel) { const auto wallet = TW::HDWallet(128, ""); const auto& mnemonic = wallet.getMnemonic(); assert(Mnemonic::isValid(mnemonic)); Data mnemonicData = TW::Data(mnemonic.begin(), mnemonic.end()); - StoredKey key = StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData); - return key; + return StoredKey(StoredKeyType::mnemonicPhrase, name, password, mnemonicData, encryptionLevel); } StoredKey StoredKey::createWithMnemonicAddDefaultAddress(const std::string& name, const Data& password, const std::string& mnemonic, TWCoinType coin) { - StoredKey key = createWithMnemonic(name, password, mnemonic); - - const auto wallet = HDWallet(mnemonic, ""); - const auto derivationPath = TW::derivationPath(coin); - const auto address = TW::deriveAddress(coin, wallet.getKey(coin, derivationPath)); - const auto extendedKey = wallet.getExtendedPublicKey(TW::purpose(coin), coin, TW::xpubVersion(coin)); - key.accounts.emplace_back(address, coin, derivationPath, extendedKey); - + StoredKey key = createWithMnemonic(name, password, mnemonic, TWStoredKeyEncryptionLevelDefault); + const auto wallet = key.wallet(password); + key.account(coin, &wallet); return key; } StoredKey StoredKey::createWithPrivateKey(const std::string& name, const Data& password, const Data& privateKeyData) { - StoredKey key = StoredKey(StoredKeyType::privateKey, name, password, privateKeyData); - return key; + return StoredKey(StoredKeyType::privateKey, name, password, privateKeyData, TWStoredKeyEncryptionLevelDefault); } StoredKey StoredKey::createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData) { @@ -70,16 +61,18 @@ StoredKey StoredKey::createWithPrivateKeyAddDefaultAddress(const std::string& na } StoredKey key = createWithPrivateKey(name, password, privateKeyData); - const auto derivationPath = TW::derivationPath(coin); + const auto pubKeyType = TW::publicKeyType(coin); + const auto pubKey = PrivateKey(privateKeyData).getPublicKey(pubKeyType); const auto address = TW::deriveAddress(coin, PrivateKey(privateKeyData)); - key.accounts.emplace_back(address, coin, derivationPath); - + key.accounts.emplace_back(address, coin, TWDerivationDefault, derivationPath, hex(pubKey.bytes), ""); return key; } -StoredKey::StoredKey(StoredKeyType type, std::string name, const Data& password, const Data& data) - : type(type), id(), name(std::move(name)), payload(password, data), accounts() { +StoredKey::StoredKey(StoredKeyType type, std::string name, const Data& password, const Data& data, TWStoredKeyEncryptionLevel encryptionLevel) + : type(type), id(), name(std::move(name)), accounts() { + const auto encryptionParams = EncryptionParameters::getPreset(encryptionLevel); + payload = EncryptedPayload(password, data, encryptionParams); boost::uuids::random_generator gen; id = boost::lexical_cast(gen()); } @@ -93,91 +86,210 @@ const HDWallet StoredKey::wallet(const Data& password) const { return HDWallet(mnemonic, ""); } -std::optional StoredKey::account(TWCoinType coin) const { +std::vector StoredKey::getAccounts(TWCoinType coin) const { + std::vector result; for (auto& account : accounts) { if (account.coin == coin) { + result.push_back(account); + } + } + return result; +} + +std::optional StoredKey::getDefaultAccount(TWCoinType coin, const HDWallet* wallet) const { + // there are multiple, try to look for default + if (wallet != nullptr) { + const auto address = wallet->deriveAddress(coin); + const auto defaultAccount = getAccount(coin, address); + if (defaultAccount.has_value()) { + return defaultAccount; + } + } + // no wallet or not found, rely on derivation=0 condition + const auto coinAccounts = getAccounts(coin); + for (auto& account : coinAccounts) { + if (account.derivation == TWDerivationDefault) { return account; } } return std::nullopt; } -std::optional StoredKey::account(TWCoinType coin, const HDWallet* wallet) { - if (wallet == nullptr) { - return account(coin); +std::optional StoredKey::getDefaultAccountOrAny(TWCoinType coin, const HDWallet* wallet) const { + const auto defaultAccount = getDefaultAccount(coin, wallet); + if (defaultAccount.has_value()) { + return defaultAccount; } - assert(wallet != nullptr); + // return any + const auto coinAccounts = getAccounts(coin); + if (coinAccounts.size() > 0) { + return coinAccounts[0]; + } + return std::nullopt; +} +std::optional StoredKey::getAccount(TWCoinType coin, const std::string& address) const { for (auto& account : accounts) { - if (account.coin == coin) { - if (account.address.empty()) { - account.address = wallet->deriveAddress(coin); - } + if (account.coin == coin && account.address == address) { return account; } } + return std::nullopt; +} + +std::optional StoredKey::getAccount(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) const { + // obtain address + const auto address = wallet.deriveAddress(coin, derivation); + return getAccount(coin, address); +} + +Account StoredKey::fillAddressIfMissing(Account& account, const HDWallet* wallet) const { + if (account.address.empty() && wallet != nullptr) { + account.address = wallet->deriveAddress(account.coin, account.derivation); + } + if (account.publicKey.empty() && wallet != nullptr) { + const auto pubKeyType = TW::publicKeyType(account.coin); + const auto pubKey = wallet->getKey(account.coin, account.derivationPath).getPublicKey(pubKeyType); + account.publicKey = hex(pubKey.bytes); + } + return account; +} +std::optional StoredKey::account(TWCoinType coin, const HDWallet* wallet) { + const auto account = getDefaultAccountOrAny(coin, wallet); + if (account.has_value()) { + Account accountLval = account.value(); + return fillAddressIfMissing(accountLval, wallet); + } + // not found, add + if (wallet == nullptr) { + return std::nullopt; + } + assert(wallet != nullptr); const auto derivationPath = TW::derivationPath(coin); const auto address = wallet->deriveAddress(coin); - const auto version = TW::xpubVersion(coin); const auto extendedPublicKey = wallet->getExtendedPublicKey(derivationPath.purpose(), coin, version); + const auto pubKeyType = TW::publicKeyType(coin); + const auto pubKey = wallet->getKey(coin, derivationPath).getPublicKey(pubKeyType); - accounts.emplace_back(address, coin, derivationPath, extendedPublicKey); + addAccount(address, coin, TWDerivationDefault, derivationPath, hex(pubKey.bytes), extendedPublicKey); + return accounts.back(); +} + +Account StoredKey::account(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) { + const auto coinAccount = getAccount(coin, derivation, wallet); + if (coinAccount.has_value()) { + Account accountLval = coinAccount.value(); + return fillAddressIfMissing(accountLval, &wallet); + } + // not found, add + const auto derivationPath = TW::derivationPath(coin, derivation); + const auto address = wallet.deriveAddress(coin, derivation); + const auto version = TW::xpubVersionDerivation(coin, derivation); + const auto extendedPublicKey = wallet.getExtendedPublicKey(derivationPath.purpose(), coin, version); + const auto pubKeyType = TW::publicKeyType(coin); + const auto pubKey = wallet.getKey(coin, derivationPath).getPublicKey(pubKeyType); + + addAccount(address, coin, derivation, derivationPath, hex(pubKey.bytes), extendedPublicKey); return accounts.back(); } -void StoredKey::addAccount(const std::string& address, TWCoinType coin, const DerivationPath& derivationPath, const std::string& extetndedPublicKey) { - accounts.emplace_back(address, coin, derivationPath, extetndedPublicKey); +std::optional StoredKey::account(TWCoinType coin) const { + return getDefaultAccountOrAny(coin, nullptr); +} + +std::optional StoredKey::account(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) const { + const auto account = getAccount(coin, derivation, wallet); + if (account.has_value()) { + Account accountLval = account.value(); + return fillAddressIfMissing(accountLval, &wallet); + } + return std::nullopt; +} + +void StoredKey::addAccount( + const std::string& address, + TWCoinType coin, + TWDerivation derivation, + const DerivationPath& derivationPath, + const std::string& publicKey, + const std::string& extendedPublicKey) { + if (getAccount(coin, address).has_value()) { + // address already present + return; + } + accounts.emplace_back(address, coin, derivation, derivationPath, publicKey, extendedPublicKey); } void StoredKey::removeAccount(TWCoinType coin) { - accounts.erase(std::remove_if(accounts.begin(), accounts.end(), [coin](Account& account) -> bool { - return account.coin == coin; - }), accounts.end()); + accounts.erase( + std::remove_if(accounts.begin(), accounts.end(), [coin](Account& account) -> bool { return account.coin == coin; }), + accounts.end()); +} + +void StoredKey::removeAccount(TWCoinType coin, TWDerivation derivation) { + accounts.erase( + std::remove_if(accounts.begin(), accounts.end(), [coin, derivation](Account& account) -> bool { + return account.coin == coin && account.derivation == derivation; + }), + accounts.end()); +} + +void StoredKey::removeAccount(TWCoinType coin, DerivationPath derivationPath) { + accounts.erase( + std::remove_if(accounts.begin(), accounts.end(), [coin, derivationPath](Account& account) -> bool { + return account.coin == coin && account.derivationPath == derivationPath; + }), + accounts.end()); } const PrivateKey StoredKey::privateKey(TWCoinType coin, const Data& password) { - switch (type) { - case StoredKeyType::mnemonicPhrase: { + return privateKey(coin, TWDerivationDefault, password); +} + +const PrivateKey StoredKey::privateKey(TWCoinType coin, [[maybe_unused]] TWDerivation derivation, const Data& password) { + if (type == StoredKeyType::mnemonicPhrase) { const auto wallet = this->wallet(password); - const auto account = this->account(coin, &wallet); - return wallet.getKey(coin, account->derivationPath); - } - case StoredKeyType::privateKey: - return PrivateKey(payload.decrypt(password)); + const Account& account = this->account(coin, derivation, wallet); + return wallet.getKey(coin, account.derivationPath); } + // type == StoredKeyType::privateKey + return PrivateKey(payload.decrypt(password)); } void StoredKey::fixAddresses(const Data& password) { switch (type) { - case StoredKeyType::mnemonicPhrase: { - const auto wallet = this->wallet(password); - for (auto& account : accounts) { - if (!account.address.empty() && TW::validateAddress(account.coin, account.address)) { - continue; - } - const auto& derivationPath = account.derivationPath; - const auto key = wallet.getKey(account.coin, derivationPath); - account.address = TW::deriveAddress(account.coin, key); - } + case StoredKeyType::mnemonicPhrase: { + const auto wallet = this->wallet(password); + for (auto& account : accounts) { + if (!account.address.empty() && !account.publicKey.empty() && + TW::validateAddress(account.coin, account.address)) { + continue; } - break; - - case StoredKeyType::privateKey: { - auto key = PrivateKey(payload.decrypt(password)); - for (auto& account : accounts) { - if (!account.address.empty() && TW::validateAddress(account.coin, account.address)) { - continue; - } - account.address = TW::deriveAddress(account.coin, key); - } + const auto& derivationPath = account.derivationPath; + const auto key = wallet.getKey(account.coin, derivationPath); + const auto pubKey = key.getPublicKey(TW::publicKeyType(account.coin)); + account.address = TW::deriveAddress(account.coin, pubKey, account.derivation); + account.publicKey = hex(pubKey.bytes); + } + } break; + + case StoredKeyType::privateKey: { + auto key = PrivateKey(payload.decrypt(password)); + for (auto& account : accounts) { + if (!account.address.empty() && !account.publicKey.empty() && + TW::validateAddress(account.coin, account.address)) { + continue; } - break; + const auto pubKey = key.getPublicKey(TW::publicKeyType(account.coin)); + account.address = TW::deriveAddress(account.coin, pubKey, account.derivation); + account.publicKey = hex(pubKey.bytes); + } + } break; } } - // ----------------- // Encoding/Decoding // ----------------- @@ -188,93 +300,96 @@ StoredKey StoredKey::createWithJson(const nlohmann::json& json) { return storedKey; } -namespace CodingKeys { - static const auto address = "address"; - static const auto type = "type"; - static const auto name = "name"; - static const auto id = "id"; - static const auto crypto = "crypto"; - static const auto activeAccounts = "activeAccounts"; - static const auto version = "version"; - static const auto coin = "coin"; -} // namespace CodingKeys +namespace CodingKeys::SK { + +static const auto address = "address"; +static const auto type = "type"; +static const auto name = "name"; +static const auto id = "id"; +static const auto crypto = "crypto"; +static const auto activeAccounts = "activeAccounts"; +static const auto version = "version"; +static const auto coin = "coin"; + +} // namespace CodingKeys::SK namespace UppercaseCodingKeys { - static const auto crypto = "Crypto"; +static const auto crypto = "Crypto"; } // namespace UppercaseCodingKeys namespace TypeString { - static const auto privateKey = "private-key"; - static const auto mnemonic = "mnemonic"; +static const auto privateKey = "private-key"; +static const auto mnemonic = "mnemonic"; } // namespace TypeString void StoredKey::loadJson(const nlohmann::json& json) { - if (json.count(CodingKeys::type) != 0 && - json[CodingKeys::type].get() == TypeString::mnemonic) { + if (json.count(CodingKeys::SK::type) != 0 && + json[CodingKeys::SK::type].get() == TypeString::mnemonic) { type = StoredKeyType::mnemonicPhrase; } else { type = StoredKeyType::privateKey; } - if (json.count(CodingKeys::name) != 0) { - name = json[CodingKeys::name].get(); + if (json.count(CodingKeys::SK::name) != 0) { + name = json[CodingKeys::SK::name].get(); } - if (json.count(CodingKeys::id) != 0) { - id = json[CodingKeys::id].get(); + if (json.count(CodingKeys::SK::id) != 0) { + id = json[CodingKeys::SK::id].get(); } - if (json.count(CodingKeys::crypto) != 0) { - payload = EncryptionParameters(json[CodingKeys::crypto]); + if (json.count(CodingKeys::SK::crypto) != 0) { + payload = EncryptedPayload(json[CodingKeys::SK::crypto]); } else if (json.count(UppercaseCodingKeys::crypto) != 0) { // Workaround for myEtherWallet files - payload = EncryptionParameters(json[UppercaseCodingKeys::crypto]); + payload = EncryptedPayload(json[UppercaseCodingKeys::crypto]); } else { throw DecryptionError::invalidKeyFile; } - if (json.count(CodingKeys::activeAccounts) != 0 && - json[CodingKeys::activeAccounts].is_array()) { - for (auto& accountJSON : json[CodingKeys::activeAccounts]) { + if (json.count(CodingKeys::SK::activeAccounts) != 0 && + json[CodingKeys::SK::activeAccounts].is_array()) { + for (auto& accountJSON : json[CodingKeys::SK::activeAccounts]) { accounts.emplace_back(accountJSON); } } - if (accounts.empty() && json.count(CodingKeys::address) != 0 && json[CodingKeys::address].is_string()) { + if (accounts.empty() && json.count(CodingKeys::SK::address) != 0 && + json[CodingKeys::SK::address].is_string()) { TWCoinType coin = TWCoinTypeEthereum; - if (json.count(CodingKeys::coin) != 0) { - coin = json[CodingKeys::coin].get(); + if (json.count(CodingKeys::SK::coin) != 0) { + coin = json[CodingKeys::SK::coin].get(); } - auto address = json[CodingKeys::address].get(); - accounts.emplace_back(address, coin, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(coin), 0, 0, 0)); + auto address = json[CodingKeys::SK::address].get(); + accounts.emplace_back(address, coin, TWDerivationDefault, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(coin), 0, 0, 0), "", ""); } } nlohmann::json StoredKey::json() const { nlohmann::json j; - j[CodingKeys::version] = 3; + j[CodingKeys::SK::version] = 3; switch (type) { case StoredKeyType::privateKey: - j[CodingKeys::type] = TypeString::privateKey; + j[CodingKeys::SK::type] = TypeString::privateKey; break; case StoredKeyType::mnemonicPhrase: - j[CodingKeys::type] = TypeString::mnemonic; + j[CodingKeys::SK::type] = TypeString::mnemonic; break; } if (id) { - j[CodingKeys::id] = *id; + j[CodingKeys::SK::id] = *id; } - j[CodingKeys::name] = name; - j[CodingKeys::crypto] = payload.json(); + j[CodingKeys::SK::name] = name; + j[CodingKeys::SK::crypto] = payload.json(); nlohmann::json accountsJSON = nlohmann::json::array(); for (const auto& account : accounts) { accountsJSON.push_back(account.json()); } - j[CodingKeys::activeAccounts] = accountsJSON; + j[CodingKeys::SK::activeAccounts] = accountsJSON; return j; } @@ -296,3 +411,5 @@ StoredKey StoredKey::load(const std::string& path) { return createWithJson(j); } + +} // namespace TW::Keystore diff --git a/src/Keystore/StoredKey.h b/src/Keystore/StoredKey.h index 4a019c5b5b3..96d3010064b 100644 --- a/src/Keystore/StoredKey.h +++ b/src/Keystore/StoredKey.h @@ -8,14 +8,16 @@ #include "Account.h" #include "EncryptionParameters.h" -#include "../Data.h" +#include "Data.h" #include "../HDWallet.h" #include +#include #include #include #include +#include namespace TW::Keystore { @@ -36,29 +38,29 @@ class StoredKey { std::string name; /// Encrypted payload. - EncryptionParameters payload; + EncryptedPayload payload; - /// Active accounts. + /// Active accounts. Address should be unique. std::vector accounts; /// Create a new StoredKey, with the given name, mnemonic and password. /// @throws std::invalid_argument if mnemonic is invalid - static StoredKey createWithMnemonic(const std::string& name, const Data& password, const std::string& mnemonic); + static StoredKey createWithMnemonic(const std::string& name, const Data& password, const std::string& mnemonic, TWStoredKeyEncryptionLevel encryptionLevel); /// Create a new StoredKey, with the given name, mnemonic and password. /// @throws std::invalid_argument if mnemonic is invalid - static StoredKey createWithMnemonicRandom(const std::string& name, const Data& password); + static StoredKey createWithMnemonicRandom(const std::string& name, const Data& password, TWStoredKeyEncryptionLevel encryptionLevel); /// Create a new StoredKey, with the given name, mnemonic and password, and also add the default address for the given coin.. /// @throws std::invalid_argument if mnemonic is invalid static StoredKey createWithMnemonicAddDefaultAddress(const std::string& name, const Data& password, const std::string& mnemonic, TWCoinType coin); /// Create a new StoredKey, with the given name and private key. - /// @throws std::invalid_argument if privateKeyData is not a vald private key + /// @throws std::invalid_argument if privateKeyData is not a valid private key static StoredKey createWithPrivateKey(const std::string& name, const Data& password, const Data& privateKeyData); /// Create a new StoredKey, with the given name and private key, and also add the default address for the given coin.. - /// @throws std::invalid_argument if privateKeyData is not a vald private key + /// @throws std::invalid_argument if privateKeyData is not a valid private key static StoredKey createWithPrivateKeyAddDefaultAddress(const std::string& name, const Data& password, TWCoinType coin, const Data& privateKeyData); /// Create a StoredKey from a JSON object. @@ -69,35 +71,66 @@ class StoredKey { /// @throws std::invalid_argument if this key is of a type other than `mnemonicPhrase`. const HDWallet wallet(const Data& password) const; - /// Returns the account for a specific coin, creating it if necessary and - /// the provided wallet is not `nullptr`. + /// Returns all the accounts for a specific coin: 0, 1, or more. + std::vector getAccounts(TWCoinType coin) const; + + /// If found, returns the account for a specific coin. In case of muliple accounts, the default derivation is returned, or the first one is returned. + /// If none exists, and wallet is not null, an account is created (with default derivation). std::optional account(TWCoinType coin, const HDWallet* wallet); + /// If found, returns the account for a specific coin and derivation. In case of muliple accounts, the first one is returned. + /// If none exists, an account is created. + Account account(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet); + /// Returns the account for a specific coin if it exists. + /// In case of muliple accounts, the default derivation is returned, or the first one is returned. std::optional account(TWCoinType coin) const; - /// Add an account - void addAccount(const std::string& address, TWCoinType coin, const DerivationPath& derivationPath, const std::string& extetndedPublicKey); - - /// Remove the account for a specific coin + /// Returns the account for a specific coin and derivation, if it exists. + std::optional account(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) const; + + /// Add an account with aribitrary address/derivation path. Discouraged, use account() versions. + /// Address must be unique (for a coin). + void addAccount( + const std::string& address, + TWCoinType coin, + TWDerivation derivation, + const DerivationPath& derivationPath, + const std::string& publicKey, + const std::string& extendedPublicKey + ); + + /// Remove the account(s) for a specific coin void removeAccount(TWCoinType coin); - - /// Returns the private key for a specific coin, creating an account if necessary. + + /// Remove the account for a specific coin with the given derivation. + void removeAccount(TWCoinType coin, TWDerivation derivation); + + /// Remove the account for a specific coin with the given derivation path. + void removeAccount(TWCoinType coin, DerivationPath derivationPath); + + /// Returns the private key for a specific coin, using default derivation, creating an account if necessary. /// - /// @throws std::invalid_argument if this key is of a type other than + /// \throws std::invalid_argument if this key is of a type other than /// `mnemonicPhrase` and a coin other than the default is requested. const PrivateKey privateKey(TWCoinType coin, const Data& password); + /// Returns the private key for a specific coin, creating an account if necessary. + /// + /// \throws std::invalid_argument if this key is of a type other than + /// `mnemonicPhrase` and a coin other than the default is requested. + const PrivateKey privateKey(TWCoinType coin, TWDerivation derivation, const Data& password); + /// Loads and decrypts a stored key from a file. /// - /// @param path file path to load from. - /// @returns descrypted key. - /// @throws DecryptionError + /// \param path file path to load from. + /// \returns decrypted key. + /// \throws DecryptionError static StoredKey load(const std::string& path); /// Stores the key into an encrypted file. /// - /// @param path file path to store in. + /// \param path file path to store in. void store(const std::string& path); /// Initializes `StoredKey` with a JSON object. @@ -106,7 +139,7 @@ class StoredKey { /// Saves `this` as a JSON object. nlohmann::json json() const; - /// Fills in all empty and invalid addresses. + /// Fills in all empty or invalid addresses and public keys. /// /// Use to fix legacy wallets with invalid address data. This method needs /// the encryption password to re-derive addresses from private keys. @@ -117,9 +150,26 @@ class StoredKey { StoredKey() : type(StoredKeyType::mnemonicPhrase) {} /// Initializes a `StoredKey` with a type, an encryption password, and unencrypted data. - /// This contstructor will encrypt the provided data with default encryption + /// This constructor will encrypt the provided data with default encryption /// parameters. - StoredKey(StoredKeyType type, std::string name, const Data& password, const Data& data); + StoredKey(StoredKeyType type, std::string name, const Data& password, const Data& data, TWStoredKeyEncryptionLevel encryptionLevel); + + /// Find default account for coin, if exists. If multiple exist, default is returned. + /// Optional wallet is needed to derive default address + std::optional getDefaultAccount(TWCoinType coin, const HDWallet* wallet) const; + + /// Find account for coin, if exists. If multiple exist, default is returned, or any. + /// Optional wallet is needed to derive default address + std::optional getDefaultAccountOrAny(TWCoinType coin, const HDWallet* wallet) const; + + /// Find account by coin+address (should be one, if multiple, first is returned) + std::optional getAccount(TWCoinType coin, const std::string& address) const; + + /// Find account by coin+derivation (should be one, if multiple, first is returned) + std::optional getAccount(TWCoinType coin, TWDerivation derivation, const HDWallet& wallet) const; + + /// Re-derive account address if missing + Account fillAddressIfMissing(Account& account, const HDWallet* wallet) const; }; } // namespace TW::Keystore diff --git a/src/Kusama/Address.h b/src/Kusama/Address.h index 40163a1cb3f..9635b6972ca 100644 --- a/src/Kusama/Address.h +++ b/src/Kusama/Address.h @@ -6,9 +6,9 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" -#include "../SS58Address.h" +#include "../Polkadot/SS58Address.h" #include #include diff --git a/src/Kusama/Entry.cpp b/src/Kusama/Entry.cpp index fa1bb8671dc..28e56d9bfb0 100644 --- a/src/Kusama/Entry.cpp +++ b/src/Kusama/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,19 +9,25 @@ #include "Address.h" #include "Polkadot/Signer.h" -using namespace TW::Kusama; -using namespace std; +namespace TW::Kusama { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Address(address); + return {addr.bytes.begin() + 1, addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Kusama diff --git a/src/Kusama/Entry.h b/src/Kusama/Entry.h index d766dd678a0..f9d566239a1 100644 --- a/src/Kusama/Entry.h +++ b/src/Kusama/Entry.h @@ -12,12 +12,12 @@ namespace TW::Kusama { /// Entry point for implementation of Kusama coin. See also Polkadot. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeKusama}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Kusama diff --git a/src/Mnemonic.cpp b/src/Mnemonic.cpp index 08a3112ec2a..1bff4d76ea7 100644 --- a/src/Mnemonic.cpp +++ b/src/Mnemonic.cpp @@ -28,16 +28,15 @@ inline const char* const* mnemonicWordlist() { return wordlist; } bool Mnemonic::isValidWord(const std::string& word) { const char* wordC = word.c_str(); const auto len = word.length(); + // Although this operation is not security-critical, we aim for constant-time operation here as well + // (i.e., no early exit on match) + auto found = false; for (const char* const* w = mnemonicWordlist(); *w != nullptr; ++w) { - if (strlen(*w) != len) { - continue; - } - if (strncmp(*w, wordC, len) == 0) { - return true; + if (strlen(*w) == len && strncmp(*w, wordC, len) == 0) { + found = true; } } - // not found - return false; + return found; } std::string Mnemonic::suggest(const std::string& prefix) { diff --git a/src/NEAR/Account.cpp b/src/NEAR/Account.cpp index bf9762fbb3c..0bb0bbf42fd 100644 --- a/src/NEAR/Account.cpp +++ b/src/NEAR/Account.cpp @@ -8,10 +8,10 @@ #include -using namespace TW; -using namespace TW::NEAR; +namespace TW::NEAR { static auto pattern = std::regex(R"(^(([a-z\d]+[\-_])*[a-z\d]+\.)*([a-z\d]+[\-_])*[a-z\d]+$)"); + bool Account::isValid(const std::string& string) { // https://docs.near.org/docs/concepts/account#account-id-rules if (string.size() < 2 || string.size() > 64) { @@ -20,3 +20,5 @@ bool Account::isValid(const std::string& string) { std::smatch match; return regex_search(string, match, pattern); } + +} // namespace TW::NEAR diff --git a/src/NEAR/Address.cpp b/src/NEAR/Address.cpp index 3d8eeff1fad..1d669e3d449 100644 --- a/src/NEAR/Address.cpp +++ b/src/NEAR/Address.cpp @@ -4,20 +4,21 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#include "Address.h" #include "Base58.h" #include "HexCoding.h" -#include "Address.h" #include using namespace TW; -using namespace TW::NEAR; + +namespace TW::NEAR { bool Address::isValid(const std::string& string) { const auto data = Address::decodeLegacyAddress(string); if (data.has_value()) { return true; - } + } const auto parsed = parse_hex(string); return parsed.size() == PublicKey::ed25519Size; } @@ -40,7 +41,7 @@ Address::Address(const std::string& string) { std::copy(std::begin(*data), std::end(*data), std::begin(bytes)); } else { if (!Address::isValid(string)) { - throw std::invalid_argument("Invalid address string!"); + throw std::invalid_argument("Invalid address string!"); } const auto parsed = parse_hex(string); std::copy(std::begin(parsed), std::end(parsed), std::begin(bytes)); @@ -58,3 +59,5 @@ Address::Address(const PublicKey& publicKey) { std::string Address::string() const { return hex(bytes); } + +} // namespace TW::NEAR diff --git a/src/NEAR/Address.h b/src/NEAR/Address.h index 5a81fd24c03..791aa107b9f 100644 --- a/src/NEAR/Address.h +++ b/src/NEAR/Address.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/NEAR/Entry.cpp b/src/NEAR/Entry.cpp index 6fc0ccbd793..c33987cc422 100644 --- a/src/NEAR/Entry.cpp +++ b/src/NEAR/Entry.cpp @@ -9,23 +9,32 @@ #include "Address.h" #include "Signer.h" -using namespace TW::NEAR; +using namespace TW; using namespace std; +namespace TW::NEAR { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::normalizeAddress(TWCoinType coin, const string& address) const { +string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& address) const { return Address(address).string(); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Address(address); + return {addr.bytes.begin(), addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::NEAR diff --git a/src/NEAR/Entry.h b/src/NEAR/Entry.h index 46b568f4214..20a0870762c 100644 --- a/src/NEAR/Entry.h +++ b/src/NEAR/Entry.h @@ -12,13 +12,13 @@ namespace TW::NEAR { /// Entry point for implementation of NEAR coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeNEAR}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::NEAR diff --git a/src/NEAR/Serialization.cpp b/src/NEAR/Serialization.cpp index 4b6e24826de..9997fc84128 100644 --- a/src/NEAR/Serialization.cpp +++ b/src/NEAR/Serialization.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,10 +9,7 @@ #include "../BinaryCoding.h" #include "../PrivateKey.h" -using namespace TW; -using namespace TW::NEAR; -using namespace TW::NEAR::Proto; - +namespace TW::NEAR { static void writeU8(Data& data, uint8_t number) { data.push_back(number); @@ -30,7 +27,8 @@ static void writeU128(Data& data, const std::string& numberData) { data.insert(std::end(data), std::begin(numberData), std::end(numberData)); } -template static void writeRawBuffer(Data& data, const T& buf) { +template +static void writeRawBuffer(Data& data, const T& buf) { data.insert(std::end(data), std::begin(buf), std::end(buf)); } @@ -49,18 +47,90 @@ static void writeTransfer(Data& data, const Proto::Transfer& transfer) { writeU128(data, transfer.deposit()); } +static void writeFunctionCall(Data& data, const Proto::FunctionCall& functionCall) { + writeString(data, functionCall.method_name()); + + writeU32(data, static_cast(functionCall.args().size())); + writeRawBuffer(data, functionCall.args()); + + writeU64(data, functionCall.gas()); + writeU128(data, functionCall.deposit()); +} + +static void writeStake(Data& data, const Proto::Stake& stake) { + writeU128(data, stake.stake()); + writePublicKey(data, stake.public_key()); +} + +static void writeFunctionCallPermission(Data& data, const Proto::FunctionCallPermission& functionCallPermission) { + if (functionCallPermission.allowance().empty()) { + writeU8(data, 0); + } else { + writeU8(data, 1); + writeU128(data, functionCallPermission.allowance()); + } + writeString(data, functionCallPermission.receiver_id()); + writeU32(data, static_cast(functionCallPermission.method_names().size())); + for (auto&& methodName : functionCallPermission.method_names()) { + writeString(data, methodName); + } +} + +static void writeAccessKey(Data& data, const Proto::AccessKey& accessKey) { + writeU64(data, accessKey.nonce()); + switch (accessKey.permission_case()) { + case Proto::AccessKey::kFunctionCall: + writeU8(data, 0); + writeFunctionCallPermission(data, accessKey.function_call()); + break; + case Proto::AccessKey::kFullAccess: + writeU8(data, 1); + break; + case Proto::AccessKey::PERMISSION_NOT_SET: + break; + } +} + +static void writeAddKey(Data& data, const Proto::AddKey& addKey) { + writePublicKey(data, addKey.public_key()); + writeAccessKey(data, addKey.access_key()); +} + +static void writeDeleteKey(Data& data, const Proto::DeleteKey& deleteKey) { + writePublicKey(data, deleteKey.public_key()); +} + +static void writeDeleteAccount(Data& data, const Proto::DeleteAccount& deleteAccount) { + writeString(data, deleteAccount.beneficiary_id()); +} + static void writeAction(Data& data, const Proto::Action& action) { writeU8(data, action.payload_case() - Proto::Action::kCreateAccount); switch (action.payload_case()) { - case Proto::Action::kTransfer: - writeTransfer(data, action.transfer()); - return; - default: - return; + case Proto::Action::kTransfer: + writeTransfer(data, action.transfer()); + return; + case Proto::Action::kFunctionCall: + writeFunctionCall(data, action.function_call()); + return; + case Proto::Action::kStake: + writeStake(data, action.stake()); + return; + case Proto::Action::kAddKey: + writeAddKey(data, action.add_key()); + return; + case Proto::Action::kDeleteKey: + writeDeleteKey(data, action.delete_key()); + return; + case Proto::Action::kDeleteAccount: + writeDeleteAccount(data, action.delete_account()); + return; + default: + return; } } -Data TW::NEAR::transactionData(const Proto::SigningInput& input) { +Data transactionData(const Proto::SigningInput& input) { Data data; writeString(data, input.signer_id()); auto key = PrivateKey(input.private_key()); @@ -79,10 +149,12 @@ Data TW::NEAR::transactionData(const Proto::SigningInput& input) { return data; } -Data TW::NEAR::signedTransactionData(const Data& transactionData, const Data& signatureData) { +Data signedTransactionData(const Data& transactionData, const Data& signatureData) { Data data; writeRawBuffer(data, transactionData); writeU8(data, 0); writeRawBuffer(data, signatureData); return data; } + +} // namespace TW::NEAR diff --git a/src/NEAR/Serialization.h b/src/NEAR/Serialization.h index 1e2ecd9e493..54a9d7041d9 100644 --- a/src/NEAR/Serialization.h +++ b/src/NEAR/Serialization.h @@ -7,7 +7,7 @@ #pragma once #include "../proto/NEAR.pb.h" -#include "../Data.h" +#include "Data.h" namespace TW::NEAR { diff --git a/src/NEAR/Signer.cpp b/src/NEAR/Signer.cpp index 7434d646ea3..65031bce7a2 100644 --- a/src/NEAR/Signer.cpp +++ b/src/NEAR/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,8 +10,7 @@ #include "../Hash.h" #include "../PrivateKey.h" -using namespace TW; -using namespace TW::NEAR; +namespace TW::NEAR { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto transaction = transactionData(input); @@ -21,5 +20,8 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto output = Proto::SigningOutput(); auto signedTransaction = signedTransactionData(transaction, signature); output.set_signed_transaction(signedTransaction.data(), signedTransaction.size()); + output.set_hash(hash.data(), hash.size()); return output; } + +} // namespace TW::NEAR diff --git a/src/NEO/Address.cpp b/src/NEO/Address.cpp index 229e61f27d4..64c4c28a808 100644 --- a/src/NEO/Address.cpp +++ b/src/NEO/Address.cpp @@ -4,16 +4,17 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../Ontology/ParamsBuilder.h" +#include "OpCode.h" #include "../Base58.h" +#include "Data.h" #include "../Hash.h" -#include "../Data.h" -#include "OpCode.h" +#include "../Ontology/ParamsBuilder.h" #include "Address.h" using namespace TW; -using namespace TW::NEO; + +namespace TW::NEO { bool Address::isValid(const std::string& string) { const auto decoded = Base58::bitcoin.decodeCheck(string); @@ -22,7 +23,7 @@ bool Address::isValid(const std::string& string) { Address::Address() { Data keyHash; - for (int i = 0; i < Address::size; i++) { + for (auto i = 0ul; i < Address::size; i++) { keyHash.push_back(0); } std::copy(keyHash.data(), keyHash.data() + Address::size, bytes.begin()); @@ -36,7 +37,7 @@ Address::Address(const PublicKey& publicKey) { pkdata.push_back(CHECKSIG); auto keyHash = Hash::ripemd(Hash::sha256(pkdata)); - keyHash.insert(keyHash.begin(), (byte) Address::version); + keyHash.insert(keyHash.begin(), (byte)Address::version); if (keyHash.size() != Address::size) { throw std::invalid_argument("Invalid address key data"); @@ -60,3 +61,5 @@ Data Address::toScriptHash() const { std::copy(bytes.begin() + 1, bytes.begin() + Hash::ripemdSize + 1, data.begin()); return data; } + +} // namespace TW::NEO diff --git a/src/NEO/Address.h b/src/NEO/Address.h index d3d78ab7088..993cba859c3 100644 --- a/src/NEO/Address.h +++ b/src/NEO/Address.h @@ -9,7 +9,7 @@ #include #include "../Base58Address.h" -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" namespace TW::NEO { diff --git a/src/NEO/CoinReference.h b/src/NEO/CoinReference.h index ad185c0e143..8890f537d3d 100644 --- a/src/NEO/CoinReference.h +++ b/src/NEO/CoinReference.h @@ -7,7 +7,7 @@ #pragma once #include "../uint256.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../BinaryCoding.h" #include "ISerializable.h" @@ -19,6 +19,7 @@ class CoinReference : public Serializable { public: /// Number of bytes for prevIndex. static const size_t prevIndexSize = 2; + static const size_t prevHashSize = 32; uint256_t prevHash; uint16_t prevIndex = 0; @@ -35,7 +36,7 @@ class CoinReference : public Serializable { } Data serialize() const override { - auto resp = store(prevHash); + auto resp = store(prevHash, prevHashSize); encode16LE(prevIndex, resp); return resp; } @@ -46,4 +47,4 @@ class CoinReference : public Serializable { } }; -} \ No newline at end of file +} // namespace TW::NEO diff --git a/src/NEO/Constants.h b/src/NEO/Constants.h new file mode 100644 index 00000000000..817dc8840cf --- /dev/null +++ b/src/NEO/Constants.h @@ -0,0 +1,16 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +namespace TW::NEO { + +static const size_t assetIdSize = 32; +static const size_t contractHashSize = 32; +static const size_t valueSize = 8; +static const size_t scriptHashSize = 20; + +} // namespace TW::NEO diff --git a/src/NEO/Entry.cpp b/src/NEO/Entry.cpp index f51b0e07335..f9e55d72637 100644 --- a/src/NEO/Entry.cpp +++ b/src/NEO/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,21 +9,30 @@ #include "Address.h" #include "Signer.h" -using namespace TW::NEO; +using namespace TW; using namespace std; -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const { +namespace TW::NEO { + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const string& address, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] TW::byte p2sh, [[maybe_unused]] const char* hrp) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] const char* hrp) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Address(address); + return {addr.bytes.begin(), addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -void Entry::plan(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::plan([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { planTemplate(dataIn, dataOut); } + +} // namespace TW::NEO diff --git a/src/NEO/Entry.h b/src/NEO/Entry.h index ff632623175..6a396f753c2 100644 --- a/src/NEO/Entry.h +++ b/src/NEO/Entry.h @@ -12,13 +12,13 @@ namespace TW::NEO { /// NEO entry dispatcher. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeNEO}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::NEO diff --git a/src/NEO/ISerializable.h b/src/NEO/ISerializable.h index 64e30ee0754..ce2245a643d 100644 --- a/src/NEO/ISerializable.h +++ b/src/NEO/ISerializable.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../BinaryCoding.h" #include "ReadData.h" diff --git a/src/NEO/MinerTransaction.h b/src/NEO/MinerTransaction.h index ca73b306958..73ff8daf129 100644 --- a/src/NEO/MinerTransaction.h +++ b/src/NEO/MinerTransaction.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "Transaction.h" namespace TW::NEO { diff --git a/src/NEO/ReadData.cpp b/src/NEO/ReadData.cpp index e8f3487e4d8..f0889883487 100644 --- a/src/NEO/ReadData.cpp +++ b/src/NEO/ReadData.cpp @@ -4,13 +4,13 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../Data.h" +#include "Data.h" #include "ReadData.h" #include TW::Data TW::readBytes(const TW::Data& from, int max, int initial_pos) { - if (from.size() - initial_pos < max) { + if (from.size() - static_cast(initial_pos) < static_cast(max)) { throw std::invalid_argument("Data::Cannot read enough bytes!"); } return TW::Data(from.begin() + initial_pos, from.begin() + initial_pos + max); diff --git a/src/NEO/ReadData.h b/src/NEO/ReadData.h index 3c273d75dec..fbe297c9ba3 100644 --- a/src/NEO/ReadData.h +++ b/src/NEO/ReadData.h @@ -9,7 +9,7 @@ #include #include -#include "../Data.h" +#include "Data.h" #include "../BinaryCoding.h" namespace TW { @@ -27,7 +27,7 @@ template static std::vector concat(const std::vector& v1, const std::vector& v2) { std::vector v(v1); v.insert(v.end(), v2.begin(), v2.end()); - return std::move(v); + return v; } } // namespace TW diff --git a/src/NEO/Script.h b/src/NEO/Script.h index 64d47982754..80090432607 100644 --- a/src/NEO/Script.h +++ b/src/NEO/Script.h @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. #pragma once -#include "../Data.h" +#include "Data.h" namespace TW::NEO { diff --git a/src/NEO/Signer.cpp b/src/NEO/Signer.cpp index 1449f328d7d..1824af1b940 100644 --- a/src/NEO/Signer.cpp +++ b/src/NEO/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,19 +6,15 @@ #include "Signer.h" #include "Script.h" -#include "../Hash.h" #include "../HexCoding.h" -#include "../PrivateKey.h" -#include "../PublicKey.h" -#include "../proto/NEO.pb.h" -#include "../proto/Common.pb.h" -using namespace TW; -using namespace TW::NEO; using namespace std; +using namespace TW; +namespace TW::NEO { -Signer::Signer(const PrivateKey& priKey) : privateKey(std::move(priKey)) { +Signer::Signer(const PrivateKey& priKey) + : privateKey(std::move(priKey)) { auto pub = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1); publicKey = pub.bytes; address = Address(pub); @@ -82,7 +78,7 @@ Proto::TransactionPlan Signer::plan(const Proto::SigningInput& input) { for (int i = 0; i < input.outputs_size(); i++) { auto* outputPlan = plan.add_outputs(); - if (available.find(input.inputs(i).asset_id()) == available.end() || + if (available.find(input.outputs(i).asset_id()) == available.end() || available[input.outputs(i).asset_id()] < input.outputs(i).amount()) { throw Common::Proto::SigningError(Common::Proto::Error_low_balance); } @@ -224,3 +220,5 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { return output; } + +} // namespace TW::NEO diff --git a/src/NEO/Signer.h b/src/NEO/Signer.h index bb00b08e09e..55c68339ce2 100644 --- a/src/NEO/Signer.h +++ b/src/NEO/Signer.h @@ -8,7 +8,7 @@ #include "Address.h" #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/NEO.pb.h" diff --git a/src/NEO/Transaction.cpp b/src/NEO/Transaction.cpp index 66b88d9ef83..5108a85e581 100644 --- a/src/NEO/Transaction.cpp +++ b/src/NEO/Transaction.cpp @@ -1,28 +1,25 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include - #include "../uint256.h" -#include "../Data.h" #include "../Hash.h" #include "Transaction.h" #include "MinerTransaction.h" using namespace std; - using namespace TW; -using namespace TW::NEO; + +namespace TW::NEO { int64_t Transaction::size() const { return serialize().size(); } void Transaction::deserialize(const Data& data, int initial_pos) { - type = (TransactionType) data[initial_pos++]; + type = (TransactionType)data[initial_pos++]; version = data[initial_pos++]; initial_pos = deserializeExclusiveData(data, initial_pos); attributes.clear(); @@ -33,15 +30,15 @@ void Transaction::deserialize(const Data& data, int initial_pos) { Serializable::deserialize(outputs, data, initial_pos); } -Transaction * Transaction::deserializeFrom(const Data& data, int initial_pos) { - Transaction * resp = nullptr; - switch ((TransactionType) data[initial_pos]) { - case TransactionType::TT_MinerTransaction: - resp = new MinerTransaction(); - break; - default: - throw std::invalid_argument("Transaction::deserializeFrom Invalid transaction type"); - break; +Transaction* Transaction::deserializeFrom(const Data& data, int initial_pos) { + Transaction* resp = nullptr; + switch ((TransactionType)data[initial_pos]) { + case TransactionType::TT_MinerTransaction: + resp = new MinerTransaction(); + break; + default: + throw std::invalid_argument("Transaction::deserializeFrom Invalid transaction type"); + break; } resp->deserialize(data, initial_pos); return resp; @@ -49,27 +46,27 @@ Transaction * Transaction::deserializeFrom(const Data& data, int initial_pos) { Data Transaction::serialize() const { Data resp; - resp.push_back((byte) type); + resp.push_back((byte)type); resp.push_back(version); append(resp, serializeExclusiveData()); append(resp, Serializable::serialize(attributes)); append(resp, Serializable::serialize(inInputs)); append(resp, Serializable::serialize(outputs)); - if(witnesses.size()) - { - resp.push_back((byte) witnesses.size()); - for (const auto& witnesse : witnesses) - append(resp, witnesse.serialize()); - } + if (witnesses.size()) { + resp.push_back((byte)witnesses.size()); + for (const auto& witnesse : witnesses) + append(resp, witnesse.serialize()); + } return resp; } -bool Transaction::operator==(const Transaction &other) const { +bool Transaction::operator==(const Transaction& other) const { if (this == &other) { return true; } + // clang-format off return this->type == other.type && this->version == other.version && this->attributes.size() == other.attributes.size() @@ -78,6 +75,7 @@ bool Transaction::operator==(const Transaction &other) const { && this->attributes == other.attributes && this->inInputs == other.inInputs && this->outputs == other.outputs; + // clang-format on } Data Transaction::getHash() const { @@ -87,3 +85,5 @@ Data Transaction::getHash() const { uint256_t Transaction::getHashUInt256() const { return load(getHash()); } + +} // namespace TW::NEO diff --git a/src/NEO/Transaction.h b/src/NEO/Transaction.h index 50a311e3d9a..7523995398c 100644 --- a/src/NEO/Transaction.h +++ b/src/NEO/Transaction.h @@ -33,7 +33,7 @@ class Transaction : public Serializable { bool operator==(const Transaction &other) const; - virtual int deserializeExclusiveData(const Data& data, int initial_pos = 0) { return initial_pos; } + virtual int deserializeExclusiveData([[maybe_unused]] const Data& data, int initial_pos = 0) { return initial_pos; } virtual Data serializeExclusiveData() const { return Data(); } Data getHash() const; diff --git a/src/NEO/TransactionAttribute.h b/src/NEO/TransactionAttribute.h index 8d8c5976c0f..e1ac1235651 100644 --- a/src/NEO/TransactionAttribute.h +++ b/src/NEO/TransactionAttribute.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,56 +6,103 @@ #pragma once -#include "TransactionAttributeUsage.h" +#include "Constants.h" #include "ISerializable.h" #include "Serializable.h" -#include "../Data.h" +#include "TransactionAttributeUsage.h" +#include "Data.h" namespace TW::NEO { class TransactionAttribute : public Serializable { - public: +public: TransactionAttributeUsage usage = TAU_ContractHash; - Data data; + Data _data; virtual ~TransactionAttribute() {} int64_t size() const override { - return 1 + data.size(); + switch (usage) { + case TransactionAttributeUsage::TAU_ContractHash: + case TransactionAttributeUsage::TAU_ECDH02: + case TransactionAttributeUsage::TAU_ECDH03: + case TransactionAttributeUsage::TAU_Vote: + return 1 + contractHashSize; + case TransactionAttributeUsage::TAU_Script: + return 1 + scriptHashSize; + default: + if (usage >= TransactionAttributeUsage::TAU_Hash1 && + usage <= TransactionAttributeUsage::TAU_Hash15) { + return 1 + contractHashSize; + } + return 1 + varIntSize(_data.size()) + _data.size(); + } } void deserialize(const Data& data, int initial_pos = 0) override { - if (data.size() < initial_pos + 1) { + if (static_cast(data.size()) < initial_pos + 1) { throw std::invalid_argument("Invalid data for deserialization"); } - usage = (TransactionAttributeUsage) data[initial_pos]; - if (usage == TransactionAttributeUsage::TAU_ContractHash || usage == TransactionAttributeUsage::TAU_Vote || - (usage >= TransactionAttributeUsage::TAU_Hash1 && usage <= TransactionAttributeUsage::TAU_Hash15)) { - this->data = readBytes(data, 32, initial_pos + 1); - } else if (usage == TransactionAttributeUsage::TAU_ECDH02 || - usage == TransactionAttributeUsage::TAU_ECDH03) { - this->data = readBytes(data, 32, initial_pos + 1); - } else if (usage == TransactionAttributeUsage::TAU_Script) { - this->data = readBytes(data, 20, initial_pos + 1); - } else if (usage == TransactionAttributeUsage::TAU_DescriptionUrl) { - this->data = readBytes(data, 1, initial_pos + 1); - } else if (usage == TransactionAttributeUsage::TAU_Description || - usage >= TransactionAttributeUsage::TAU_Remark) { - this->data = readBytes(data, int(data.size()) - 1 - initial_pos, initial_pos + 1); - } else { + + // see: https://github.com/neo-project/neo/blob/v2.12.0/neo/Network/P2P/Payloads/TransactionAttribute.cs#L32 + usage = (TransactionAttributeUsage)data[initial_pos]; + switch (usage) { + case TransactionAttributeUsage::TAU_ECDH02: + case TransactionAttributeUsage::TAU_ECDH03: { + this->_data = concat({(TW::byte)usage}, readBytes(data, contractHashSize, initial_pos + 1)); + break; + } + + case TransactionAttributeUsage::TAU_Script: { + this->_data = readBytes(data, scriptHashSize, initial_pos + 1); + break; + } + + case TransactionAttributeUsage::TAU_DescriptionUrl: + case TransactionAttributeUsage::TAU_Description: + case TransactionAttributeUsage::TAU_Remark: { + this->_data = readVarBytes(data, initial_pos + 1); + break; + } + + default: + if (usage == TransactionAttributeUsage::TAU_ContractHash || + usage == TransactionAttributeUsage::TAU_Vote || + (usage >= TransactionAttributeUsage::TAU_Hash1 && usage <= TransactionAttributeUsage::TAU_Hash15)) { + this->_data = readBytes(data, contractHashSize, initial_pos + 1); + break; + } throw std::invalid_argument("TransactionAttribute Deserialize FormatException"); } } Data serialize() const override { - return concat(Data({static_cast(usage)}), data); + Data result; + result.push_back((TW::byte)usage); + + // see: https://github.com/neo-project/neo/blob/v2.12.0/neo/Network/P2P/Payloads/TransactionAttribute.cs#L49 + if (usage == TransactionAttributeUsage::TAU_DescriptionUrl || + usage == TransactionAttributeUsage::TAU_Description || + usage >= TransactionAttributeUsage::TAU_Remark) { + Data resp; + encodeVarInt((uint64_t)_data.size(), resp); + result.insert(result.end(), resp.begin(), resp.end()); + } + if (usage == TransactionAttributeUsage::TAU_ECDH02 || + usage == TransactionAttributeUsage::TAU_ECDH03) { + result.insert(result.end(), _data.begin() + 1, _data.begin() + 1 + contractHashSize); + } else { + result.insert(result.end(), _data.begin(), _data.end()); + } + + return result; } bool operator==(const TransactionAttribute &other) const { return this->usage == other.usage - && this->data.size() == other.data.size() - && this->data == other.data; + && _data.size() == other._data.size() + && _data == other._data; } }; -} \ No newline at end of file +} // namespace TW::NEO diff --git a/src/NEO/TransactionOutput.h b/src/NEO/TransactionOutput.h index 86b51af36bc..cc52b032301 100644 --- a/src/NEO/TransactionOutput.h +++ b/src/NEO/TransactionOutput.h @@ -7,7 +7,8 @@ #pragma once #include "../uint256.h" -#include "../Data.h" +#include "Constants.h" +#include "Data.h" #include "../BinaryCoding.h" #include "ReadData.h" #include "ISerializable.h" @@ -17,10 +18,6 @@ namespace TW::NEO { class TransactionOutput : public Serializable { public: - static const size_t assetIdSize = 32; - static const size_t valueSize = 8; - static const size_t scriptHashSize = 20; - uint256_t assetId; int64_t value = 0; uint256_t scriptHash; @@ -28,7 +25,7 @@ class TransactionOutput : public Serializable { virtual ~TransactionOutput() {} int64_t size() const override { - return store(assetId).size() + valueSize + store(scriptHash).size(); + return store(assetId, assetIdSize).size() + valueSize + store(scriptHash, scriptHashSize).size(); } void deserialize(const Data& data, int initial_pos = 0) override { @@ -38,9 +35,9 @@ class TransactionOutput : public Serializable { } Data serialize() const override { - auto resp = store(assetId); + auto resp = store(assetId, assetIdSize); encode64LE(value, resp); - return concat(resp, store(scriptHash)); + return concat(resp, store(scriptHash, scriptHashSize)); } bool operator==(const TransactionOutput &other) const { @@ -50,4 +47,4 @@ class TransactionOutput : public Serializable { } }; -} \ No newline at end of file +} // namespace TW::NEO diff --git a/src/NEO/Witness.h b/src/NEO/Witness.h index 6f0e7c9d139..2562bbb7f4e 100644 --- a/src/NEO/Witness.h +++ b/src/NEO/Witness.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "ISerializable.h" #include "Serializable.h" diff --git a/src/NULS/Address.cpp b/src/NULS/Address.cpp index 93917e257c5..494131dcb1e 100644 --- a/src/NULS/Address.cpp +++ b/src/NULS/Address.cpp @@ -12,7 +12,8 @@ #include "../HexCoding.h" using namespace TW; -using namespace TW::NULS; + +namespace TW::NULS { const std::string Address::prefix("NULSd"); const std::array Address::mainnetId = {0x01, 0x00}; @@ -21,7 +22,7 @@ bool Address::isValid(const std::string& string) { if (string.empty()) { return false; } - if (string.length() <= prefix.length()) { + if (string.length() <= prefix.length()) { return false; } @@ -46,15 +47,16 @@ Address::Address(const TW::PublicKey& publicKey) { bytes[1] = mainnetId[1]; // Address Type bytes[2] = addressType; - ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, &bytes[3]); + //ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, bytes.begin() + 3); + ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, &bytes[3]);//win bytes[23] = checksum(bytes); } Address::Address(const std::string& string) { - if (false == isValid(string)){ + if (false == isValid(string)) { throw std::invalid_argument("Invalid address string"); } - std::string address = string.substr(prefix.length(), string.length() - prefix.length()); + std::string address = string.substr(prefix.length(), string.length() - prefix.length()); const auto decoded = Base58::bitcoin.decode(address); std::copy(decoded.begin(), decoded.end(), bytes.begin()); } @@ -68,10 +70,11 @@ uint8_t Address::type() const { } std::string Address::string() const { - return prefix + Base58::bitcoin.encode(&bytes[0], &bytes[0] + bytes.size()); + return prefix + Base58::bitcoin.encode(&bytes[0], &bytes[0] + bytes.size());//win + //return prefix + Base58::bitcoin.encode(bytes.begin(), bytes.end()); } -uint8_t Address::checksum(std::array& byteArray) const{ +uint8_t Address::checksum(std::array& byteArray) const { uint8_t checkSum = 0x00; for (int i = 0; i < 23; ++i) { checkSum ^= byteArray[i]; @@ -79,4 +82,4 @@ uint8_t Address::checksum(std::array& byteArray) const{ return checkSum; } - +} // namespace TW::NULS diff --git a/src/NULS/BinaryCoding.h b/src/NULS/BinaryCoding.h index 077bf30cf85..51de36f5ec9 100644 --- a/src/NULS/BinaryCoding.h +++ b/src/NULS/BinaryCoding.h @@ -6,14 +6,15 @@ #pragma once -#include "../uint256.h" +#include "Address.h" #include "../BinaryCoding.h" -#include "../proto/NULS.pb.h" #include "../HexCoding.h" -#include "Address.h" +#include "../proto/NULS.pb.h" +#include "../uint256.h" using namespace TW; -using namespace TW::NULS; + +namespace TW::NULS { static inline void serializerRemark(std::string& remark, Data& data) { encodeVarInt(remark.length(), data); @@ -21,7 +22,7 @@ static inline void serializerRemark(std::string& remark, Data& data) { } static inline void serializerInput(const Proto::TransactionCoinFrom& input, Data& data) { - encodeVarInt(1, data); //there is one coinFrom + encodeVarInt(1, data); // there is one coinFrom const auto& fromAddress = input.from_address(); if (!NULS::Address::isValid(fromAddress)) { throw std::invalid_argument("Invalid address"); @@ -39,7 +40,7 @@ static inline void serializerInput(const Proto::TransactionCoinFrom& input, Data } static inline void serializerOutput(const Proto::TransactionCoinTo& output, Data& data) { - encodeVarInt(1, data); //there is one coinTo + encodeVarInt(1, data); // there is one coinTo const auto& toAddress = output.to_address(); if (!NULS::Address::isValid(toAddress)) { @@ -65,10 +66,10 @@ static inline Data makeTransactionSignature(PrivateKey& privateKey, Data& txHash Data transactionSignature = Data(); encodeVarInt(pubKey.bytes.size(), transactionSignature); std::copy(pubKey.bytes.begin(), pubKey.bytes.end(), std::back_inserter(transactionSignature)); - auto signature = privateKey.signAsDER(txHash, TWCurve::TWCurveSECP256k1); + auto signature = privateKey.signAsDER(txHash); encodeVarInt(signature.size(), transactionSignature); std::copy(signature.begin(), signature.end(), std::back_inserter(transactionSignature)); return transactionSignature; } - +} // namespace TW::NULS diff --git a/src/NULS/Entry.cpp b/src/NULS/Entry.cpp index 2dee29d9e31..db59dba1bb1 100644 --- a/src/NULS/Entry.cpp +++ b/src/NULS/Entry.cpp @@ -9,19 +9,22 @@ #include "Address.h" #include "Signer.h" -using namespace TW::NULS; using namespace std; +namespace TW::NULS { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::NULS diff --git a/src/NULS/Entry.h b/src/NULS/Entry.h index 283acb94797..b11bcc629bb 100644 --- a/src/NULS/Entry.h +++ b/src/NULS/Entry.h @@ -12,12 +12,11 @@ namespace TW::NULS { /// Entry point for implementation of NULS coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeNULS}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::NULS diff --git a/src/NULS/Signer.cpp b/src/NULS/Signer.cpp index 1e0c903d2a0..77b83e6ebae 100644 --- a/src/NULS/Signer.cpp +++ b/src/NULS/Signer.cpp @@ -12,7 +12,8 @@ #include "../PrivateKey.h" using namespace TW; -using namespace TW::NULS; + +namespace TW::NULS { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto output = Proto::SigningOutput(); @@ -20,20 +21,21 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto signer = Signer(input); auto data = signer.sign(); output.set_encoded(data.data(), data.size()); + } catch (...) { } - catch(...) {} return output; } -Signer::Signer(const Proto::SigningInput& input) : input(input) { +Signer::Signer(const Proto::SigningInput& input) + : input(input) { Proto::TransactionCoinFrom coinFrom; coinFrom.set_from_address(input.from()); coinFrom.set_assets_chainid(input.chain_id()); coinFrom.set_assets_id(input.idassets_id()); - //need to update with amount + fee + // need to update with amount + fee coinFrom.set_id_amount(input.amount()); coinFrom.set_nonce(input.nonce()); - //default unlocked + // default unlocked coinFrom.set_locked(0); *tx.mutable_input() = coinFrom; @@ -88,13 +90,13 @@ Data Signer::sign() const { encode16LE(static_cast(tx.type()), dataRet); // Timestamp encode32LE(tx.timestamp(), dataRet); - // Remark + // Remark std::string remark = tx.remark(); serializerRemark(remark, dataRet); // txData encodeVarInt(0, dataRet); - //coinFrom and coinTo size + // coinFrom and coinTo size encodeVarInt(TRANSACTION_INPUT_SIZE + TRANSACTION_OUTPUT_SIZE, dataRet); // CoinData Input @@ -105,7 +107,7 @@ Data Signer::sign() const { // Calc transaction hash Data txHash = calcTransactionDigest(dataRet); - + Data privKey = data(input.private_key()); auto priv = PrivateKey(privKey); auto transactionSignature = makeTransactionSignature(priv, txHash); @@ -117,7 +119,7 @@ Data Signer::sign() const { uint32_t Signer::CalculatorTransactionSize(uint32_t inputCount, uint32_t outputCount, uint32_t remarkSize) const { uint32_t size = TRANSACTION_FIX_SIZE + TRANSACTION_SIG_MAX_SIZE + TRANSACTION_INPUT_SIZE * inputCount + - TRANSACTION_OUTPUT_SIZE * outputCount + remarkSize; + TRANSACTION_OUTPUT_SIZE * outputCount + remarkSize; return size; } @@ -127,4 +129,6 @@ uint64_t Signer::CalculatorTransactionFee(uint64_t size) const { fee += MIN_PRICE_PRE_1024_BYTES; } return fee; -} \ No newline at end of file +} + +} // namespace TW::NULS diff --git a/src/Nano/Address.cpp b/src/Nano/Address.cpp index ba16435bfd6..9788472b089 100644 --- a/src/Nano/Address.cpp +++ b/src/Nano/Address.cpp @@ -1,5 +1,5 @@ // Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,7 +10,7 @@ #include -using namespace TW::Nano; +namespace TW::Nano { const std::string kPrefixNano{"nano_"}; const std::string kPrefixXrb{"xrb_"}; @@ -21,14 +21,12 @@ bool Address::isValid(const std::string& address) { valid = nano_validate_address( kPrefixNano.c_str(), kPrefixNano.length(), address.c_str(), address.length(), - nullptr - ); + nullptr); if (!valid) { valid = nano_validate_address( kPrefixXrb.c_str(), kPrefixXrb.length(), address.c_str(), address.length(), - nullptr - ); + nullptr); } return valid; @@ -68,11 +66,13 @@ std::string Address::string() const { std::array out = {0}; size_t count = nano_get_address( - bytes.data(), - kPrefixNano.c_str(), kPrefixNano.length(), - out.data(), out.size()); + bytes.data(), + kPrefixNano.c_str(), kPrefixNano.length(), + out.data(), out.size()); // closing \0 assert(count < out.size()); out[count] = 0; - return std::string(out.data()); + return {out.data()}; } + +} // namespace TW::Nano diff --git a/src/Nano/Entry.cpp b/src/Nano/Entry.cpp index 2c0493f1cd0..6cfdb73e52b 100644 --- a/src/Nano/Entry.cpp +++ b/src/Nano/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,23 +9,29 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Nano; -using namespace std; +namespace TW::Nano { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Address(address); + return {addr.bytes.begin(), addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +std::string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +} // namespace TW::Nano diff --git a/src/Nano/Entry.h b/src/Nano/Entry.h index a444938ad4a..1a27f029e23 100644 --- a/src/Nano/Entry.h +++ b/src/Nano/Entry.h @@ -12,14 +12,14 @@ namespace TW::Nano { /// Entry point for implementation of Nano coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeNano}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Nano diff --git a/src/Nano/Signer.h b/src/Nano/Signer.h index ea41ff542b2..8ca6fe318d5 100644 --- a/src/Nano/Signer.h +++ b/src/Nano/Signer.h @@ -7,7 +7,7 @@ #pragma once #include "Address.h" -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include diff --git a/src/Nebulas/Address.cpp b/src/Nebulas/Address.cpp index bbc81447f43..4b113b6cd8c 100644 --- a/src/Nebulas/Address.cpp +++ b/src/Nebulas/Address.cpp @@ -9,7 +9,7 @@ #include "../Hash.h" #include "../HexCoding.h" -using namespace TW::Nebulas; +namespace TW::Nebulas { bool Address::isValid(const std::string& string) { auto data = Base58::bitcoin.decode(string); @@ -46,14 +46,14 @@ Address::Address(const Data& data) { std::copy(data.begin(), data.end(), bytes.begin()); } -Address::Address(const PublicKey &publicKey) { +Address::Address(const PublicKey& publicKey) { if (publicKey.type != TWPublicKeyTypeSECP256k1Extended) { throw std::invalid_argument("Nebulas::Address needs an extended SECP256k1 public key."); } const auto data = publicKey.hash( {Address::AddressPrefix, Address::NormalType}, - static_cast(Hash::sha3_256ripemd), false); - + Hash::HasherSha3_256ripemd, false); + std::copy(data.begin(), data.end(), bytes.begin()); auto checksum = Hash::sha3_256(data); std::copy(checksum.begin(), checksum.begin() + 4, bytes.begin() + 22); @@ -62,3 +62,5 @@ Address::Address(const PublicKey &publicKey) { std::string Address::string() const { return Base58::bitcoin.encode(bytes); } + +} // namespace TW::Nebulas diff --git a/src/Nebulas/Entry.cpp b/src/Nebulas/Entry.cpp index a432250c5da..a8b4571e527 100644 --- a/src/Nebulas/Entry.cpp +++ b/src/Nebulas/Entry.cpp @@ -9,19 +9,21 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Nebulas; using namespace std; +namespace TW::Nebulas { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Nebulas diff --git a/src/Nebulas/Entry.h b/src/Nebulas/Entry.h index 5de8d9fd91b..0bdcaded71c 100644 --- a/src/Nebulas/Entry.h +++ b/src/Nebulas/Entry.h @@ -12,12 +12,11 @@ namespace TW::Nebulas { /// Entry point for implementation of Nebulas coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeNebulas}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Nebulas diff --git a/src/Nebulas/Signer.cpp b/src/Nebulas/Signer.cpp index 383ae6b3575..1d0159eeed4 100644 --- a/src/Nebulas/Signer.cpp +++ b/src/Nebulas/Signer.cpp @@ -9,21 +9,21 @@ #include "../HexCoding.h" using namespace TW; -using namespace TW::Nebulas; + +namespace TW::Nebulas { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto signer = Signer(load(input.chain_id())); auto tx = Transaction(Address(input.from_address()), - load(input.nonce()), - load(input.gas_price()), - load(input.gas_limit()), - Address(input.to_address()), - load(input.amount()), - load(input.timestamp()), - input.payload() - ); - + load(input.nonce()), + load(input.gas_price()), + load(input.gas_limit()), + Address(input.to_address()), + load(input.amount()), + load(input.timestamp()), + input.payload()); + auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); signer.sign(privateKey, tx); @@ -34,7 +34,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { return output; } -void Signer::sign(const PrivateKey &privateKey, Transaction &transaction) const noexcept { +void Signer::sign(const PrivateKey& privateKey, Transaction& transaction) const noexcept { transaction.hash = this->hash(transaction); transaction.chainID = chainID; transaction.algorithm = 1; @@ -42,12 +42,12 @@ void Signer::sign(const PrivateKey &privateKey, Transaction &transaction) const transaction.serializeToRaw(); } -Data Signer::hash(const Transaction &transaction) const noexcept { +Data Signer::hash(const Transaction& transaction) const noexcept { auto encoded = Data(); auto payload = Data(); auto* data = Transaction::newPayloadData(transaction.payload); payload.resize(data->ByteSizeLong()); - data->SerializePartialToArray(payload.data(),(int)payload.size()); + data->SerializePartialToArray(payload.data(), (int)payload.size()); delete data; encoded.insert(encoded.end(), transaction.from.bytes.begin(), transaction.from.bytes.end()); @@ -61,3 +61,5 @@ Data Signer::hash(const Transaction &transaction) const noexcept { encode256BE(encoded, transaction.gasLimit, 128); return Hash::sha3_256(encoded); } + +} // namespace TW::Nebulas diff --git a/src/Nebulas/Signer.h b/src/Nebulas/Signer.h index 86a74d2bed2..2beed1448ce 100644 --- a/src/Nebulas/Signer.h +++ b/src/Nebulas/Signer.h @@ -7,7 +7,7 @@ #pragma once #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/Nebulas.pb.h" #include "../uint256.h" diff --git a/src/Nebulas/Transaction.cpp b/src/Nebulas/Transaction.cpp index c243e3cd769..ce70cb617da 100644 --- a/src/Nebulas/Transaction.cpp +++ b/src/Nebulas/Transaction.cpp @@ -6,65 +6,73 @@ // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include + #include "Transaction.h" #include "../HexCoding.h" +#include using namespace TW; -using namespace TW::Nebulas; - -const char *Transaction::TxPayloadBinaryType = "binary"; -const char *Transaction::TxPayloadDeployType = "deploy"; -const char *Transaction::TxPayloadCallType = "call"; std::string htmlescape(const std::string& str) { std::string result; - for(size_t i=0; i': result += "\\u003e"; break; - case '<': result += "\\u003c"; break; - case 0x20: - if(i+1 < str.size()) { - if(str[i+1]==0x28) { - result += "\\u2028"; - ++i; - break; - } - else if (str[i+1]==0x29) { - result += "\\u2029"; - ++i; - break; - } + for (size_t i = 0; i < str.size(); ++i) { + switch (str[i]) { + case '&': + result += "\\u0026"; + break; + case '>': + result += "\\u003e"; + break; + case '<': + result += "\\u003c"; + break; + case 0x20: + if (i + 1 < str.size()) { + if (str[i + 1] == 0x28) { + result += "\\u2028"; + ++i; + break; + } else if (str[i + 1] == 0x29) { + result += "\\u2029"; + ++i; + break; } - default: result += str[i]; break; + } + default: + result += str[i]; + break; } } return result; } -Proto::Data* Transaction::newPayloadData(const std::string& payload){ +namespace TW::Nebulas { + +const char* Transaction::TxPayloadBinaryType = "binary"; +const char* Transaction::TxPayloadDeployType = "deploy"; +const char* Transaction::TxPayloadCallType = "call"; + +Proto::Data* Transaction::newPayloadData(const std::string& payload) { auto* data = new Proto::Data(); data->set_type(Transaction::TxPayloadBinaryType); nlohmann::json payloadData; - if(!payload.empty()) { + if (!payload.empty()) { auto json = nlohmann::json::parse(payload); - if(json.find("binary")!=json.end()) { + if (json.find("binary") != json.end()) { std::string binary_data = json["binary"]; - auto buff = Data(binary_data.begin(),binary_data.end()); + auto buff = Data(binary_data.begin(), binary_data.end()); payloadData["Data"]["type"] = "Buffer"; payloadData["Data"]["data"] = nlohmann::json(buff); } } - if(!payloadData.empty()) + if (!payloadData.empty()) data->set_payload(htmlescape(payloadData.dump())); return data; } -void Transaction::serializeToRaw(){ - if(signature.empty()) { +void Transaction::serializeToRaw() { + if (signature.empty()) { throw std::logic_error("The transaction is unsigned!"); } @@ -74,23 +82,25 @@ void Transaction::serializeToRaw(){ auto value = Data(); auto gas_price = Data(); auto gas_limit = Data(); - tx.set_hash(reinterpret_cast(hash.data()),hash.size()); - tx.set_from(from.bytes.data(),from.size); - tx.set_to(to.bytes.data(),to.size); + tx.set_hash(reinterpret_cast(hash.data()), hash.size()); + tx.set_from(from.bytes.data(), from.size); + tx.set_to(to.bytes.data(), to.size); encode256BE(value, amount, 128); - tx.set_value(value.data(),value.size()); + tx.set_value(value.data(), value.size()); tx.set_nonce((uint64_t)nonce); tx.set_timestamp((int64_t)timestamp); tx.set_allocated_data(data); tx.set_chain_id((uint32_t)chainID); encode256BE(gas_price, gasPrice, 128); - tx.set_gas_price(gas_price.data(),gas_price.size()); + tx.set_gas_price(gas_price.data(), gas_price.size()); encode256BE(gas_limit, gasLimit, 128); - tx.set_gas_limit(gas_limit.data(),gas_limit.size()); - + tx.set_gas_limit(gas_limit.data(), gas_limit.size()); + tx.set_alg((uint32_t)algorithm); - tx.set_sign(reinterpret_cast(signature.data()),signature.size()); + tx.set_sign(reinterpret_cast(signature.data()), signature.size()); raw.resize(tx.ByteSizeLong()); - tx.SerializeToArray(raw.data(),(int)raw.size()); -} \ No newline at end of file + tx.SerializeToArray(raw.data(), (int)raw.size()); +} + +} // namespace TW::Nebulas diff --git a/src/Nervos/Address.cpp b/src/Nervos/Address.cpp new file mode 100644 index 00000000000..7740a96657e --- /dev/null +++ b/src/Nervos/Address.cpp @@ -0,0 +1,177 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Address.h" +#include "Constants.h" + +#include "../Bech32.h" +#include "../Coin.h" + +#include +#include +#include + +namespace TW::Nervos { + +[[nodiscard]] bool Address::isValid(const std::string& string) noexcept { + return Address::isValid(string, HRP_NERVOS); +} + +[[nodiscard]] bool Address::isValid(const std::string& string, const char* hrp) noexcept { + return Address().decode(string, hrp); +} + +Address::Address(const std::string& string, const char* hrp) { + if (!decode(string, hrp)) { + throw std::invalid_argument("Invalid address string"); + } +} + +bool Address::decode(const std::string& string, const char* hrp) noexcept { + _hrp = hrp; + auto decoded = Bech32::decode(string); + auto&& [decodedHrp, decodedData, decodedVariant] = decoded; + if (decodedHrp.compare(hrp)) { + return false; + } + Data decodedPayload; + if (!Bech32::convertBits<5, 8, false>(decodedPayload, decodedData)) { + return false; + } + if (decodedPayload.empty()) { + return false; + } + addressType = AddressType(decodedPayload[0]); + switch (addressType) { + case AddressType::FullVersion: { + size_t codeHashOffset = 1; + size_t codeHashSize = 32; + size_t hashTypeOffset = codeHashOffset + codeHashSize; + size_t hashTypeSize = 1; + size_t argsOffset = hashTypeOffset + hashTypeSize; + if (decodedVariant != Bech32::ChecksumVariant::Bech32M) { + return false; + } + if (decodedPayload.size() < argsOffset) { + return false; + } + codeHashIndex = -1; + codeHash = Data(decodedPayload.begin() + codeHashOffset, + decodedPayload.begin() + codeHashOffset + codeHashSize); + hashType = HashType(decodedPayload[hashTypeOffset]); + args = Data(decodedPayload.begin() + argsOffset, decodedPayload.end()); + break; + } + case AddressType::HashIdx: { + size_t codeHashIndexOffset = 1; + size_t codeHashIndexSize = 1; + size_t argsOffset = codeHashIndexOffset + codeHashIndexSize; + size_t argsSize = 20; + if (decodedVariant != Bech32::ChecksumVariant::Bech32) { + return false; + } + if (decodedPayload.size() != argsOffset + argsSize) { + return false; + } + codeHashIndex = decodedPayload[codeHashIndexOffset]; + if (codeHashIndex != 0) { + return false; + } + codeHash = Constants::gSecp256k1CodeHash; + hashType = HashType::Type1; + args = Data(decodedPayload.begin() + argsOffset, decodedPayload.end()); + break; + } + case AddressType::DataCodeHash: + case AddressType::TypeCodeHash: { + size_t codeHashOffset = 1; + size_t codeHashSize = 32; + size_t argsOffset = codeHashOffset + codeHashSize; + if (decodedVariant != Bech32::ChecksumVariant::Bech32) { + return false; + } + if (decodedPayload.size() < argsOffset) { + return false; + } + codeHashIndex = -1; + codeHash = Data(decodedPayload.begin() + codeHashOffset, + decodedPayload.begin() + codeHashOffset + codeHashSize); + hashType = addressType == AddressType::DataCodeHash ? HashType::Data0 : HashType::Type1; + args = Data(decodedPayload.begin() + argsOffset, decodedPayload.end()); + break; + } + default: { + return false; + } + } + return true; +} + +Address::Address(const PublicKey& publicKey, const char* hrp) + : _hrp(hrp) { + if (publicKey.type != TWPublicKeyTypeSECP256k1) { + throw std::invalid_argument("Nervos::Address needs a SECP256k1 public key."); + } + addressType = AddressType::FullVersion; + codeHashIndex = -1; + codeHash = Constants::gSecp256k1CodeHash; + hashType = HashType::Type1; + Data publicKeyHash = Hash::blake2b(publicKey.bytes, 32, Constants::gHashPersonalization); + Data truncatedPublicKeyHash = Data(publicKeyHash.begin(), publicKeyHash.begin() + 20); + args = truncatedPublicKeyHash; +} + +std::string Address::string() const { + auto data = Data(); + data.emplace_back(addressType); + Bech32::ChecksumVariant checksumVariant; + switch (addressType) { + case AddressType::FullVersion: { + data.insert(data.end(), codeHash.begin(), codeHash.end()); + data.emplace_back(hashType); + data.insert(data.end(), args.begin(), args.end()); + checksumVariant = Bech32::ChecksumVariant::Bech32M; + break; + } + case AddressType::HashIdx: { + data.emplace_back(codeHashIndex); + data.insert(data.end(), args.begin(), args.end()); + checksumVariant = Bech32::ChecksumVariant::Bech32; + break; + } + case AddressType::DataCodeHash: + case AddressType::TypeCodeHash: { + data.insert(data.end(), codeHash.begin(), codeHash.end()); + data.insert(data.end(), args.begin(), args.end()); + checksumVariant = Bech32::ChecksumVariant::Bech32; + break; + } + default: { + return ""; + } + } + Data payload; + if (!Bech32::convertBits<8, 5, true>(payload, data)) { + return ""; + } + return Bech32::encode(_hrp, payload, checksumVariant); +} + +std::string Address::hashTypeString() const { + switch (hashType) { + case HashType::Data0: { + return HashTypeString[0]; + } + case HashType::Type1: { + return HashTypeString[1]; + } + case HashType::Data1: { + return HashTypeString[2]; + } + } +} + +} // namespace TW::Nervos diff --git a/src/Nervos/Address.h b/src/Nervos/Address.h new file mode 100644 index 00000000000..3b8c28fab7c --- /dev/null +++ b/src/Nervos/Address.h @@ -0,0 +1,80 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include + +#include "Data.h" +#include "../PublicKey.h" + +#include + +namespace TW::Nervos { + +enum HashType { + Data0 = 0, + Type1 = 1, + Data1 = 2 +}; + +static const char* HashTypeString[] { + "data", + "type", + "data1" +}; + +enum AddressType { + FullVersion = 0, // full version identifies the hash_type + HashIdx = 1, // short version for locks with popular codehash, deprecated + DataCodeHash = 2, // full version with hash type 'Data', deprecated + TypeCodeHash = 4, // full version with hash type 'Type', deprecated +}; + +class Address { +public: + const char* _hrp; + AddressType addressType; + TW::byte codeHashIndex; + Data codeHash; + HashType hashType; + Data args; + + /// Determines whether a string makes a valid address. + [[nodiscard]] static bool isValid(const std::string& string) noexcept; + [[nodiscard]] static bool isValid(const std::string& string, const char* hrp) noexcept; + + /// Initializes a Nervos address with a string representation. + explicit Address(const std::string& string) : Address(string, HRP_NERVOS) {} + explicit Address(const std::string& string, const char* hrp); + + /// Initializes a Nervos address with a public key. + explicit Address(const PublicKey& publicKey) : Address(publicKey, HRP_NERVOS) {} + explicit Address(const PublicKey& publicKey, const char* hrp); + + /// Returns a string representation of the address. + std::string string() const; + + std::string hashTypeString() const; + +private: + Address() = default; + + // Decodes address from string + bool decode(const std::string& string, const char* hrp) noexcept; +}; + +inline bool operator==(const Address& lhs, const Address& rhs) { + return (lhs.codeHash == rhs.codeHash) && (lhs.hashType == rhs.hashType) && + (lhs.args == rhs.args); +} + +} // namespace TW::Nervos + +/// Wrapper for C interface. +struct TWNervosAddress { + TW::Nervos::Address impl; +}; diff --git a/src/Nervos/Cell.h b/src/Nervos/Cell.h new file mode 100644 index 00000000000..750f1b0fc58 --- /dev/null +++ b/src/Nervos/Cell.h @@ -0,0 +1,108 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "OutPoint.h" +#include "Script.h" +#include "../proto/Nervos.pb.h" + +#include + +namespace TW::Nervos { + +struct Cell { + OutPoint outPoint; + uint64_t capacity; + Script lock; + Script type; + Data data; + uint64_t blockNumber; + Data blockHash; + uint64_t since; + Data inputType; + Data outputType; + + Cell() = default; + + // Copy constructor + Cell(const Cell& cell) + : outPoint(cell.outPoint) + , capacity(cell.capacity) + , lock(cell.lock) + , type(cell.type) + , data(cell.data) + , blockNumber(cell.blockNumber) + , blockHash(cell.blockHash) + , since(cell.since) + , inputType(cell.inputType) + , outputType(cell.outputType) {} + + // Move constructor + Cell(Cell&& cell) + : outPoint(std::move(cell.outPoint)) + , capacity(cell.capacity) + , lock(std::move(cell.lock)) + , type(std::move(cell.type)) + , data(std::move(cell.data)) + , blockNumber(cell.blockNumber) + , blockHash(std::move(cell.blockHash)) + , since(cell.since) + , inputType(std::move(cell.inputType)) + , outputType(std::move(cell.outputType)) {} + + // Copy assignment operator + Cell& operator=(const Cell& cell) { + outPoint = cell.outPoint; + capacity = cell.capacity; + lock = cell.lock; + type = cell.type; + data = cell.data; + blockNumber = cell.blockNumber; + blockHash = cell.blockHash; + since = cell.since; + inputType = cell.inputType; + outputType = cell.outputType; + return *this; + } + + Cell(const Proto::Cell& cell) + : outPoint(cell.out_point()) + , capacity(cell.capacity()) + , lock(cell.lock()) + , type(cell.type()) + , blockNumber(cell.block_number()) + , since(cell.since()) { + auto&& cellData = cell.data(); + data.insert(data.end(), cellData.begin(), cellData.end()); + auto&& cellBlockHash = cell.block_hash(); + blockHash.insert(blockHash.end(), cellBlockHash.begin(), cellBlockHash.end()); + auto&& cellInputType = cell.input_type(); + inputType.insert(inputType.end(), cellInputType.begin(), cellInputType.end()); + auto&& cellOutputType = cell.output_type(); + outputType.insert(outputType.end(), cellOutputType.begin(), cellOutputType.end()); + } + + Proto::Cell proto() const { + auto cell = Proto::Cell(); + *cell.mutable_out_point() = outPoint.proto(); + cell.set_capacity(capacity); + *cell.mutable_lock() = lock.proto(); + *cell.mutable_type() = type.proto(); + cell.set_data(std::string(data.begin(), data.end())); + cell.set_block_number(blockNumber); + cell.set_block_hash(std::string(blockHash.begin(), blockHash.end())); + cell.set_since(since); + cell.set_input_type(std::string(inputType.begin(), inputType.end())); + cell.set_output_type(std::string(outputType.begin(), outputType.end())); + return cell; + } +}; + +/// A list of Cell's +using Cells = std::vector; + +} // namespace TW::Nervos diff --git a/src/Nervos/CellDep.cpp b/src/Nervos/CellDep.cpp new file mode 100644 index 00000000000..a9acf139ae3 --- /dev/null +++ b/src/Nervos/CellDep.cpp @@ -0,0 +1,39 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "CellDep.h" + +#include "../BinaryCoding.h" + +namespace TW::Nervos { + +CellDep::CellDep(const Proto::CellDep& cellDep) + : outPoint(cellDep.out_point()) { + auto&& depTypeString = cellDep.dep_type(); + for (int i = 0; i < (int)(sizeof(DepTypeString) / sizeof(DepTypeString[0])); i++) { + if (depTypeString == DepTypeString[i]) { + depType = (DepType)i; + } + } +} + +Proto::CellDep CellDep::proto() const { + auto cellDep = Proto::CellDep(); + *cellDep.mutable_out_point() = outPoint.proto(); + cellDep.set_dep_type(DepTypeString[depType]); + return cellDep; +} + +void CellDep::encode(Data& data) const { + outPoint.encode(data); + data.emplace_back(depType); +} + +nlohmann::json CellDep::json() const { + return nlohmann::json{{"out_point", outPoint.json()}, {"dep_type", DepTypeString[depType]}}; +} + +} // namespace TW::Nervos diff --git a/src/Nervos/CellDep.h b/src/Nervos/CellDep.h new file mode 100644 index 00000000000..9318017803e --- /dev/null +++ b/src/Nervos/CellDep.h @@ -0,0 +1,45 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "OutPoint.h" +#include "../proto/Nervos.pb.h" +#include + +namespace TW::Nervos { + +enum DepType { + Code = 0, + DepGroup = 1 +}; + +static const char* DepTypeString[] { + "code", + "dep_group" +}; + +/// Nervos cell dep. +struct CellDep { + OutPoint outPoint; + DepType depType; + + /// Initializes a cell dep with a previous output and depType + CellDep(OutPoint outPoint, DepType depType) : outPoint(std::move(outPoint)), depType(depType) {} + + CellDep(const Proto::CellDep& cellDep); + + /// Encodes the transaction into the provided buffer. + void encode(Data& data) const; + nlohmann::json json() const; + + Proto::CellDep proto() const; +}; + +/// A list of Cell Deps +using CellDeps = std::vector; + +} // namespace TW::Nervos diff --git a/src/Nervos/CellInput.cpp b/src/Nervos/CellInput.cpp new file mode 100644 index 00000000000..61810b9686b --- /dev/null +++ b/src/Nervos/CellInput.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "CellInput.h" +#include "Serialization.h" + +namespace TW::Nervos { + +void CellInput::encode(Data& data) const { + encode64LE(since, data); + previousOutput.encode(data); +} + +nlohmann::json CellInput::json() const { + return nlohmann::json{{"previous_output", previousOutput.json()}, + {"since", Serialization::numberToHex(since)}}; +} + +} // namespace TW::Nervos diff --git a/src/Nervos/CellInput.h b/src/Nervos/CellInput.h new file mode 100644 index 00000000000..afb8488cf05 --- /dev/null +++ b/src/Nervos/CellInput.h @@ -0,0 +1,36 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "OutPoint.h" +#include + +namespace TW::Nervos { + +/// Nervos cell input. +struct CellInput { + /// Reference to the previous transaction's output. + OutPoint previousOutput; + + /// Prevents the transaction to be mined before an absolute or relative time. + uint64_t since; + + /// Initializes a cell input with a previous output and since + CellInput(OutPoint previousOutput, uint64_t since) + : previousOutput(std::move(previousOutput)), since(since) {} + + /// Encodes the transaction into the provided buffer. + void encode(Data& data) const; + + /// Encodes the output into json format. + nlohmann::json json() const; +}; + +/// A list of Cell Inputs +using CellInputs = std::vector; + +} // namespace TW::Nervos diff --git a/src/Nervos/CellOutput.cpp b/src/Nervos/CellOutput.cpp new file mode 100644 index 00000000000..ed1d98b86b8 --- /dev/null +++ b/src/Nervos/CellOutput.cpp @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "CellOutput.h" +#include "Serialization.h" + +namespace TW::Nervos { + +void CellOutput::encode(Data& data) const { + Data capacityData; + Data lockData; + Data typeData; + encode64LE(capacity, capacityData); + lock.encode(lockData); + type.encode(typeData); + Serialization::encodeDataArray(std::vector{capacityData, lockData, typeData}, data); +} + +nlohmann::json CellOutput::json() const { + return nlohmann::json{{"capacity", Serialization::numberToHex(capacity)}, + {"lock", lock.json()}, + {"type", type.json()}}; +} + +} // namespace TW::Nervos diff --git a/src/Nervos/CellOutput.h b/src/Nervos/CellOutput.h new file mode 100644 index 00000000000..ca2b267d0cf --- /dev/null +++ b/src/Nervos/CellOutput.h @@ -0,0 +1,53 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Script.h" +#include "Data.h" +#include "../proto/Nervos.pb.h" +#include + +#include + +namespace TW::Nervos { + +/// Nervos cell output. +struct CellOutput { + uint64_t capacity; + Script lock; + Script type; + + /// Initializes an empty cell output. + CellOutput() = default; + + /// Initializes a cell output with a capacity and scripts. + CellOutput(uint64_t capacity, Script&& lock, Script&& type) + : capacity(capacity), lock(std::move(lock)), type(std::move(type)) {} + + /// Initializes a CellInput from a Protobuf CellInput. + CellOutput(const Proto::CellOutput& cellOutput) + : capacity(cellOutput.capacity()), lock(cellOutput.lock()), type(cellOutput.type()) {} + + /// Encodes the output into the provided buffer. + void encode(Data& data) const; + + /// Encodes the output into json format. + nlohmann::json json() const; + + Proto::CellOutput proto() const { + auto cellOutput = Proto::CellOutput(); + cellOutput.set_capacity(capacity); + *cellOutput.mutable_lock() = lock.proto(); + *cellOutput.mutable_type() = type.proto(); + return cellOutput; + } +}; + +/// A list of Cell Outputs +using CellOutputs = std::vector; + +} // namespace TW::Nervos diff --git a/src/Nervos/Constants.h b/src/Nervos/Constants.h new file mode 100644 index 00000000000..97678b77bd7 --- /dev/null +++ b/src/Nervos/Constants.h @@ -0,0 +1,53 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Address.h" +#include "CellDep.h" +#include "OutPoint.h" +#include "Data.h" +#include "../HexCoding.h" + +#include +#include + +namespace TW::Nervos::Constants { + +static const uint64_t gTransactionBaseSize = 72; +static const uint64_t gCellDepSize = 37; +static const uint64_t gHeaderDepSize = 32; +static const uint64_t gSingleInputAndWitnessBaseSize = 44; +static const uint64_t gBlankWitnessBytes = 65; +static const uint64_t gUint32Size = 4; +static const uint64_t gMinCellCapacityForNativeToken = 6100000000; +static const uint64_t gMinCellCapacityForSUDT = 14400000000; + +static const Data gHashPersonalization{'c', 'k', 'b', '-', 'd', 'e', 'f', 'a', + 'u', 'l', 't', '-', 'h', 'a', 's', 'h'}; + +static const Data gSecp256k1CodeHash = + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"); + +static const CellDep gSecp256k1CellDep = CellDep( + OutPoint(parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c"), 0), + DepType::DepGroup); + +static const Data gSUDTCodeHash = + parse_hex("5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5"); + +static const CellDep gSUDTCellDep = CellDep( + OutPoint(parse_hex("c7813f6a415144643970c2e88e0bb6ca6a8edc5dd7c1022746f628284a9936d5"), 0), + DepType::Code); + +static const Data gDAOCodeHash = + parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"); + +static const CellDep gDAOCellDep = CellDep( + OutPoint(parse_hex("e2fb199810d49a4d8beec56718ba2593b665db9d52299a0f9e6e75416d73ff5c"), 2), + DepType::Code); + +} // namespace TW::Nervos::Constants diff --git a/src/Nervos/Entry.cpp b/src/Nervos/Entry.cpp new file mode 100644 index 00000000000..44e725ae6a3 --- /dev/null +++ b/src/Nervos/Entry.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Entry.h" + +#include "Address.h" +#include "Signer.h" + +namespace TW::Nervos { +using namespace std; + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, byte, byte, + const char* hrp) const { + return Address::isValid(address, hrp); +} + +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, byte, + const char* hrp) const { + return Address(publicKey, hrp).string(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const Data& dataIn, Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +void Entry::plan([[maybe_unused]] TWCoinType coin, const Data& dataIn, Data& dataOut) const { + planTemplate(dataIn, dataOut); +} + +} // namespace TW::Nervos diff --git a/src/Nervos/Entry.h b/src/Nervos/Entry.h new file mode 100644 index 00000000000..0d3919dcefb --- /dev/null +++ b/src/Nervos/Entry.h @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../CoinEntry.h" + +using namespace TW; + +namespace TW::Nervos { + +/// Entry point for implementation of Nervos coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific +/// includes in this file +class Entry final : public CoinEntry { +public: + bool validateAddress(TWCoinType coin, const std::string& address, byte p2pkh, byte p2sh, + const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, byte p2pkh, + const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; +}; + +} // namespace TW::Nervos diff --git a/src/Nervos/HeaderDep.h b/src/Nervos/HeaderDep.h new file mode 100644 index 00000000000..af94b115db8 --- /dev/null +++ b/src/Nervos/HeaderDep.h @@ -0,0 +1,19 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include + +namespace TW::Nervos { + +using HeaderDep = Data; + +/// A list of header deps +using HeaderDeps = std::vector; + +} // namespace TW::Nervos diff --git a/src/Nervos/OutPoint.cpp b/src/Nervos/OutPoint.cpp new file mode 100644 index 00000000000..a9cfbb1599d --- /dev/null +++ b/src/Nervos/OutPoint.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "OutPoint.h" +#include "Serialization.h" + +namespace TW::Nervos { + +void OutPoint::encode(Data& data) const { + data.insert(data.end(), txHash.begin(), txHash.end()); + encode32LE(index, data); +} + +nlohmann::json OutPoint::json() const { + return nlohmann::json{{"tx_hash", hexEncoded(txHash)}, + {"index", Serialization::numberToHex(uint64_t(index))}}; +} + +} // namespace TW::Nervos \ No newline at end of file diff --git a/src/Nervos/OutPoint.h b/src/Nervos/OutPoint.h new file mode 100644 index 00000000000..f6fc82e90f0 --- /dev/null +++ b/src/Nervos/OutPoint.h @@ -0,0 +1,54 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" +#include "../proto/Nervos.pb.h" +#include + +#include +#include +#include + +namespace TW::Nervos { + +/// Nervos transaction out-point reference. +struct OutPoint { + /// The hash of the referenced transaction. + Data txHash; + + /// The index of the specific output in the transaction. + uint32_t index; + + OutPoint() = default; + + /// Initializes an out-point reference with hash, index. + template + OutPoint(const T& h, uint32_t index) : txHash(std::begin(h), std::end(h)), index(index) {} + + /// Initializes an out-point from a Protobuf out-point. + OutPoint(const Proto::OutPoint& outPoint) + : txHash(std::begin(outPoint.tx_hash()), std::end(outPoint.tx_hash())) + , index(outPoint.index()) {} + + /// Encodes the out-point into the provided buffer. + void encode(Data& data) const; + nlohmann::json json() const; + + friend bool operator==(const OutPoint& lhs, const OutPoint& rhs) { + return (lhs.txHash == rhs.txHash && lhs.index == rhs.index); + } + + Proto::OutPoint proto() const { + auto outPoint = Proto::OutPoint(); + outPoint.set_tx_hash(std::string(txHash.begin(), txHash.end())); + outPoint.set_index(index); + return outPoint; + } +}; + +} // namespace TW::Nervos diff --git a/src/Nervos/Script.cpp b/src/Nervos/Script.cpp new file mode 100644 index 00000000000..b0f688f5797 --- /dev/null +++ b/src/Nervos/Script.cpp @@ -0,0 +1,49 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Script.h" +#include "Constants.h" +#include "Serialization.h" +#include "../Bech32.h" + +#include +#include + +namespace TW::Nervos { + +Data Script::hash() const { + Data data; + encode(data); + return Hash::blake2b(data, 32, Constants::gHashPersonalization); +} + +[[nodiscard]] bool Script::empty() const { + return std::all_of(codeHash.begin(), codeHash.end(), [](byte element) { return element == 0; }); +} + +void Script::encode(Data& data) const { + Data hashTypeData(1); + Data argsData; + if (empty()) { + return; + } + hashTypeData[0] = hashType; + encode32LE(uint32_t(args.size()), argsData); + argsData.insert(argsData.end(), args.begin(), args.end()); + Serialization::encodeDataArray(std::vector{codeHash, hashTypeData, argsData}, data); +} + +nlohmann::json Script::json() const { + if (empty()) { + return nullptr; + } else { + return nlohmann::json{{"code_hash", hexEncoded(codeHash)}, + {"hash_type", HashTypeString[hashType]}, + {"args", hexEncoded(args)}}; + } +} + +} // namespace TW::Nervos diff --git a/src/Nervos/Script.h b/src/Nervos/Script.h new file mode 100644 index 00000000000..7c0abffec5c --- /dev/null +++ b/src/Nervos/Script.h @@ -0,0 +1,107 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Address.h" +#include "Constants.h" +#include "Data.h" +#include "../proto/Nervos.pb.h" +#include + +#include +#include + +namespace TW::Nervos { + +struct Script { + Data codeHash; + HashType hashType; + Data args; + + /// Initializes an empty script. + Script() { + hashType = HashType::Data0; + } + + /// Copy constructor + Script(const Script& script) + : codeHash(script.codeHash), hashType(script.hashType), args(script.args) {} + + /// Move constructor + Script(Script&& script) + : codeHash(std::move(script.codeHash)) + , hashType(script.hashType) + , args(std::move(script.args)) {} + + /// Initializes a script with codeHash, args and hashType. + Script(const Data& codeHash, const HashType hashType, const Data& args) + : codeHash(codeHash), hashType(hashType), args(args) {} + + /// Initializes a script with the given address. + Script(const Address& address) + : codeHash(address.codeHash), hashType(address.hashType), args(address.args) {} + + // Copy assignment operator + Script& operator=(const Script& script) { + codeHash = script.codeHash; + hashType = script.hashType; + args = script.args; + return *this; + } + + // Move assignment operator + Script& operator=(Script&& script) { + codeHash = std::move(script.codeHash); + hashType = script.hashType; + args = std::move(script.args); + return *this; + } + + friend bool operator==(const Script& lhs, const Script& rhs) { + return (lhs.codeHash == rhs.codeHash) && (lhs.hashType == rhs.hashType) && + (lhs.args == rhs.args); + } + + friend bool operator!=(const Script& lhs, const Script& rhs) { return !(lhs == rhs); } + + /// Returns the script's script hash. + Data hash() const; + + /// Whether the script is empty. + [[nodiscard]] bool empty() const; + + /// Initializes an script from a Protobuf script. + Script(const Proto::Script& script) { + auto&& scriptCodeHash = script.code_hash(); + codeHash.insert(codeHash.end(), scriptCodeHash.begin(), scriptCodeHash.end()); + auto&& hashTypeString = script.hash_type(); + hashType = HashType::Data0; + for (int i = 0; i < (int)(sizeof(HashTypeString) / sizeof(HashTypeString[0])); i++) { + if (hashTypeString == HashTypeString[i]) { + hashType = (HashType)i; + } + } + auto&& scriptArgs = script.args(); + args.insert(args.end(), scriptArgs.begin(), scriptArgs.end()); + } + + /// Encodes the script. + void encode(Data& data) const; + + /// Encodes the script into json format. + nlohmann::json json() const; + + Proto::Script proto() const { + auto script = Proto::Script(); + script.set_code_hash(std::string(codeHash.begin(), codeHash.end())); + script.set_hash_type(HashTypeString[hashType]); + script.set_args(std::string(args.begin(), args.end())); + return script; + } +}; + +} // namespace TW::Nervos diff --git a/src/Nervos/Serialization.h b/src/Nervos/Serialization.h new file mode 100644 index 00000000000..9a2c66dee71 --- /dev/null +++ b/src/Nervos/Serialization.h @@ -0,0 +1,60 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../BinaryCoding.h" +#include "Data.h" +#include "../HexCoding.h" +#include "../uint256.h" + +#include +#include + +namespace TW::Nervos { + +struct Serialization { + static void encodeDataArray(const std::vector& dataArray, Data& data) { + uint32_t dataLength = std::accumulate(dataArray.begin(), dataArray.end(), uint32_t(0), + [](const uint32_t total, const Data& element) { + return total + uint32_t(element.size()); + }); + uint32_t headerLength = 4 + 4 * uint32_t(dataArray.size()); + uint32_t fullLength = headerLength + dataLength; + encode32LE(fullLength, data); + std::accumulate(dataArray.begin(), dataArray.end(), headerLength, + [&data](const uint32_t offset, const Data& element) { + encode32LE(offset, data); + return offset + uint32_t(element.size()); + }); + for (auto&& element : dataArray) { + data.insert(data.end(), element.begin(), element.end()); + } + } + + static Data encodeUint256(uint256_t number, byte minLen = 0) { + auto data = store(number, minLen); + std::reverse(data.begin(), data.end()); + return data; + } + + static uint256_t decodeUint256(const Data& data) { + auto data1 = Data(data); + std::reverse(data1.begin(), data1.end()); + return load(data1); + } + + static std::string numberToHex(uint64_t number) { + auto str = hex(number); + str.erase(0, str.find_first_not_of('0')); + if (str.length() == 0) { + return "0x0"; + } else { + return str.insert(0, "0x"); + } + } +}; +} // namespace TW::Nervos diff --git a/src/Nervos/Signer.cpp b/src/Nervos/Signer.cpp new file mode 100644 index 00000000000..3a39ed1eb45 --- /dev/null +++ b/src/Nervos/Signer.cpp @@ -0,0 +1,55 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Signer.h" +#include "Transaction.h" +#include "TransactionPlan.h" + +namespace TW::Nervos { + +Proto::TransactionPlan Signer::plan(const Proto::SigningInput& signingInput) noexcept { + TransactionPlan txPlan; + txPlan.plan(signingInput); + return txPlan.proto(); +} + +Proto::SigningOutput Signer::sign(const Proto::SigningInput& signingInput) noexcept { + Proto::SigningOutput output; + + TransactionPlan txPlan; + if (signingInput.has_plan()) { + txPlan = TransactionPlan(signingInput.plan()); + } else { + txPlan.plan(signingInput); + } + if (txPlan.error != Common::Proto::OK) { + // Planning failed + output.set_error(txPlan.error); + return output; + } + + Transaction tx; + tx.build(txPlan); + std::vector privateKeys; + privateKeys.reserve(signingInput.private_key_size()); + for (auto&& privateKey : signingInput.private_key()) { + privateKeys.emplace_back(privateKey); + } + auto error = tx.sign(privateKeys); + if (error != Common::Proto::OK) { + // Signing failed + output.set_error(error); + return output; + } + + output.set_transaction_json(tx.json().dump()); + output.set_transaction_id(hexEncoded(tx.hash())); + output.set_error(Common::Proto::OK); + + return output; +} + +} // namespace TW::Nervos diff --git a/src/Nervos/Signer.h b/src/Nervos/Signer.h new file mode 100644 index 00000000000..7c7c0c1d279 --- /dev/null +++ b/src/Nervos/Signer.h @@ -0,0 +1,26 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "CoinEntry.h" +#include "Data.h" +#include "../proto/Nervos.pb.h" + +namespace TW::Nervos { + +class Signer { +public: + Signer() = delete; + + /// Returns a transaction plan (utxo selection, fee estimation) + static Proto::TransactionPlan plan(const Proto::SigningInput& signingInputProto) noexcept; + + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& signingInputProto) noexcept; +}; + +} // namespace TW::Nervos diff --git a/src/Nervos/Transaction.cpp b/src/Nervos/Transaction.cpp new file mode 100644 index 00000000000..bf61915e2f8 --- /dev/null +++ b/src/Nervos/Transaction.cpp @@ -0,0 +1,212 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Transaction.h" +#include "Constants.h" +#include "Serialization.h" + +#include +#include +#include +#include + +#include + +namespace TW::Nervos { + +Data Transaction::hash() const { + Data data; + std::vector dataArray; + dataArray.reserve(6); + + // version + Data versionData; + encode32LE(version, versionData); + dataArray.emplace_back(versionData); + + // cell deps + Data cellDepsData; + encode32LE(uint32_t(cellDeps.size()), cellDepsData); + for (auto&& cellDep : cellDeps) { + cellDep.encode(cellDepsData); + } + dataArray.emplace_back(cellDepsData); + + // header deps + Data headerDepsData; + encode32LE(uint32_t(headerDeps.size()), headerDepsData); + for (auto&& headerDep : headerDeps) { + headerDepsData.insert(headerDepsData.end(), headerDep.begin(), headerDep.end()); + } + dataArray.emplace_back(headerDepsData); + + // inputs + Data inputsData; + encode32LE(uint32_t(inputs.size()), inputsData); + for (auto&& input : inputs) { + input.encode(inputsData); + } + dataArray.emplace_back(inputsData); + + // outputs + Data outputsData1; + std::vector outputsData1Array; + outputsData1Array.reserve(outputs.size()); + for (auto&& output : outputs) { + Data outputData1; + output.encode(outputData1); + outputsData1Array.emplace_back(outputData1); + } + Serialization::encodeDataArray(outputsData1Array, outputsData1); + dataArray.emplace_back(outputsData1); + + // outputs data + Data outputsData2; + std::vector outputsData2Array; + outputsData2Array.reserve(outputsData.size()); + for (auto&& outputData : outputsData) { + Data outputData2; + encode32LE(uint32_t(outputData.size()), outputData2); + outputData2.insert(outputData2.end(), outputData.begin(), outputData.end()); + outputsData2Array.emplace_back(outputData2); + } + Serialization::encodeDataArray(outputsData2Array, outputsData2); + dataArray.emplace_back(outputsData2); + + Serialization::encodeDataArray(dataArray, data); + + return Hash::blake2b(data, 32, Constants::gHashPersonalization); +} + +nlohmann::json Transaction::json() const { + auto json = nlohmann::json(); + json["version"] = "0x0"; + auto cellDepsJSON = nlohmann::json::array(); + for (auto&& cellDep : cellDeps) { + cellDepsJSON.push_back(cellDep.json()); + } + json["cell_deps"] = cellDepsJSON; + auto headerDepsJSON = nlohmann::json::array(); + for (auto&& headerDep : headerDeps) { + headerDepsJSON.push_back(hexEncoded(headerDep)); + } + json["header_deps"] = headerDepsJSON; + auto inputsJSON = nlohmann::json::array(); + for (auto&& input : inputs) { + inputsJSON.push_back(input.json()); + } + json["inputs"] = inputsJSON; + auto outputsJSON = nlohmann::json::array(); + for (auto&& output : outputs) { + outputsJSON.push_back(output.json()); + } + json["outputs"] = outputsJSON; + auto outputsDataJSON = nlohmann::json::array(); + for (auto&& outputData : outputsData) { + outputsDataJSON.push_back(hexEncoded(outputData)); + } + json["outputs_data"] = outputsDataJSON; + auto witnessesJSON = nlohmann::json::array(); + for (auto&& serializedWitness : serializedWitnesses) { + witnessesJSON.push_back(hexEncoded(serializedWitness)); + } + json["witnesses"] = witnessesJSON; + return json; +} + +void Transaction::build(const TransactionPlan& txPlan) { + cellDeps = txPlan.cellDeps; + headerDeps = txPlan.headerDeps; + selectedCells = txPlan.selectedCells; + outputs = txPlan.outputs; + outputsData = txPlan.outputsData; + for (auto&& cell : selectedCells) { + inputs.emplace_back(cell.outPoint, cell.since); + } +} + +Common::Proto::SigningError Transaction::sign(const std::vector& privateKeys) { + formGroups(); + return signGroups(privateKeys); +} + +void Transaction::formGroups() { + for (size_t index = 0; index < selectedCells.size(); index++) { + auto&& cell = selectedCells[index]; + auto lockHash = cell.lock.hash(); + int groupNum = -1; + for (size_t groupNum1 = 0; groupNum1 < m_groupNumToLockHash.size(); groupNum1++) { + if (lockHash == m_groupNumToLockHash[groupNum1]) { + // Group found. Add to existing group. + groupNum = int(groupNum1); + break; + } + } + if (groupNum == -1) { + // Group not found. Create new group. + groupNum = int(m_groupNumToLockHash.size()); + m_groupNumToLockHash.emplace_back(lockHash); + m_groupNumToInputIndices.emplace_back(); + m_groupNumToWitnesses.emplace_back(); + } + m_groupNumToInputIndices[groupNum].emplace_back(index); + m_groupNumToWitnesses[groupNum].emplace_back(Data(), cell.inputType, cell.outputType); + serializedWitnesses.emplace_back(); + } +} + +Common::Proto::SigningError Transaction::signGroups(const std::vector& privateKeys) { + const Data txHash = hash(); + for (size_t groupNum = 0; groupNum < m_groupNumToLockHash.size(); groupNum++) { + auto&& cell = selectedCells[m_groupNumToInputIndices[groupNum][0]]; + const PrivateKey* privateKey = nullptr; + for (auto&& privateKey1 : privateKeys) { + auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeSECP256k1); + auto address = Address(publicKey1, HRP_NERVOS); + auto script = Script(address); + if (script == cell.lock) { + privateKey = &privateKey1; + break; + } + } + if (!privateKey) { + return Common::Proto::Error_missing_private_key; + } + auto result = signWitnesses(*privateKey, txHash, m_groupNumToWitnesses[groupNum]); + if (result != Common::Proto::OK) { + return result; + } + m_groupNumToWitnesses[groupNum][0].encode(serializedWitnesses[m_groupNumToInputIndices[groupNum][0]]); + } + return Common::Proto::OK; +} + +Common::Proto::SigningError Transaction::signWitnesses(const PrivateKey& privateKey, + const Data& txHash, Witnesses& witnesses) { + Data message; + message.insert(message.end(), txHash.begin(), txHash.end()); + + witnesses[0].lock = Data(Constants::gBlankWitnessBytes, 0); + + for (auto&& witness : witnesses) { + Data serializedWitness; + witness.encode(serializedWitness); + encode64LE(serializedWitness.size(), message); + message.insert(message.end(), serializedWitness.begin(), serializedWitness.end()); + } + + auto messageHash = Hash::blake2b(message, 32, Constants::gHashPersonalization); + auto signature = privateKey.sign(messageHash, TWCurveSECP256k1); + if (signature.empty()) { + // Error: Failed to sign + return Common::Proto::Error_signing; + } + witnesses[0].lock = signature; + + return Common::Proto::OK; +} + +} // namespace TW::Nervos diff --git a/src/Nervos/Transaction.h b/src/Nervos/Transaction.h new file mode 100644 index 00000000000..6121b0a6866 --- /dev/null +++ b/src/Nervos/Transaction.h @@ -0,0 +1,76 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Address.h" +#include "Cell.h" +#include "CellDep.h" +#include "CellInput.h" +#include "CellOutput.h" +#include "HeaderDep.h" +#include "Script.h" +#include "TransactionPlan.h" +#include "Witness.h" + +#include "../Coin.h" +#include "../CoinEntry.h" +#include "Data.h" +#include "../Hash.h" +#include "../KeyPair.h" +#include "../PrivateKey.h" +#include "../PublicKey.h" +#include "../Result.h" + +#include + +namespace TW::Nervos { + +class Transaction { +public: + /// Transaction data format version (note, this is signed) + int32_t version = 0; + + // List of cell deps + CellDeps cellDeps; + + // List of header deps + HeaderDeps headerDeps; + + // List of cell inputs + CellInputs inputs; + + // List of cell outputs + CellOutputs outputs; + + // List of outputs data + std::vector outputsData; + + // List of serialized witnesses + std::vector serializedWitnesses; + + // List of cells selected for this transaction + Cells selectedCells; + + Transaction() = default; + + Data hash() const; + nlohmann::json json() const; + void build(const TransactionPlan& txPlan); + Common::Proto::SigningError sign(const std::vector& privateKeys); + +private: + std::vector m_groupNumToLockHash; + std::vector> m_groupNumToInputIndices; + std::vector m_groupNumToWitnesses; + + void formGroups(); + Common::Proto::SigningError signGroups(const std::vector& privateKeys); + Common::Proto::SigningError signWitnesses(const PrivateKey& privateKey, const Data& txHash, + Witnesses& witnesses); +}; + +} // namespace TW::Nervos diff --git a/src/Nervos/TransactionPlan.cpp b/src/Nervos/TransactionPlan.cpp new file mode 100644 index 00000000000..c4135505af8 --- /dev/null +++ b/src/Nervos/TransactionPlan.cpp @@ -0,0 +1,328 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TransactionPlan.h" +#include "Constants.h" +#include "Serialization.h" +#include "Witness.h" +#include "../BinaryCoding.h" +#include "../HexCoding.h" + +#include + +#include +#include +#include + +using namespace TW; +namespace TW::Nervos { + +void TransactionPlan::plan(const Proto::SigningInput& signingInput) { + error = Common::Proto::OK; + + m_byteFee = signingInput.byte_fee(); + + if (signingInput.cell_size() == 0) { + error = Common::Proto::Error_missing_input_utxos; + return; + } + for (auto&& cell : signingInput.cell()) { + m_availableCells.emplace_back(cell); + } + + switch (signingInput.operation_oneof_case()) { + case Proto::SigningInput::kNativeTransfer: { + planNativeTransfer(signingInput); + break; + } + case Proto::SigningInput::kSudtTransfer: { + planSudtTransfer(signingInput); + break; + } + case Proto::SigningInput::kDaoDeposit: { + planDaoDeposit(signingInput); + break; + } + case Proto::SigningInput::kDaoWithdrawPhase1: { + planDaoWithdrawPhase1(signingInput); + break; + } + case Proto::SigningInput::kDaoWithdrawPhase2: { + planDaoWithdrawPhase2(signingInput); + break; + } + default: { + error = Common::Proto::Error_invalid_params; + } + } +} + +void TransactionPlan::planNativeTransfer(const Proto::SigningInput& signingInput) { + auto useMaxAmount = signingInput.native_transfer().use_max_amount(); + auto amount = signingInput.native_transfer().amount(); + if ((amount == 0) && !useMaxAmount) { + error = Common::Proto::Error_zero_amount_requested; + return; + } + + cellDeps.emplace_back(Constants::gSecp256k1CellDep); + + outputs.emplace_back(amount, Script(Address(signingInput.native_transfer().to_address())), + Script()); + outputsData.emplace_back(); + + if (useMaxAmount) { + selectMaximumCapacity(); + } else { + auto changeAddress = Address(signingInput.native_transfer().change_address()); + selectRequiredCapacity(changeAddress); + } +} + +void TransactionPlan::planSudtTransfer(const Proto::SigningInput& signingInput) { + auto useMaxAmount = signingInput.sudt_transfer().use_max_amount(); + uint256_t amount = uint256_t(signingInput.sudt_transfer().amount()); + if ((amount == 0) && !useMaxAmount) { + error = Common::Proto::Error_zero_amount_requested; + return; + } + + cellDeps.emplace_back(Constants::gSecp256k1CellDep); + cellDeps.emplace_back(Constants::gSUDTCellDep); + + outputs.emplace_back(Constants::gMinCellCapacityForSUDT, + Script(Address(signingInput.sudt_transfer().to_address())), + Script(Constants::gSUDTCodeHash, HashType::Type1, + data(signingInput.sudt_transfer().sudt_address()))); + outputsData.emplace_back(); + + auto changeAddress = Address(signingInput.sudt_transfer().change_address()); + selectSudtTokens(useMaxAmount, amount, changeAddress); + selectRequiredCapacity(changeAddress); +} + +void TransactionPlan::planDaoDeposit(const Proto::SigningInput& signingInput) { + auto amount = signingInput.dao_deposit().amount(); + + cellDeps.emplace_back(Constants::gSecp256k1CellDep); + cellDeps.emplace_back(Constants::gDAOCellDep); + + outputs.emplace_back(amount, Script(Address(signingInput.dao_deposit().to_address())), + Script(Constants::gDAOCodeHash, HashType::Type1, Data())); + outputsData.emplace_back(); + encode64LE(0, outputsData[outputsData.size() - 1]); + + auto changeAddress = Address(signingInput.dao_deposit().change_address()); + selectRequiredCapacity(changeAddress); +} + +void TransactionPlan::planDaoWithdrawPhase1(const Proto::SigningInput& signingInput) { + cellDeps.emplace_back(Constants::gSecp256k1CellDep); + cellDeps.emplace_back(Constants::gDAOCellDep); + + auto depositCell = Cell(signingInput.dao_withdraw_phase1().deposit_cell()); + selectedCells.emplace_back(depositCell); + m_availableCells.erase(std::remove_if( + m_availableCells.begin(), m_availableCells.end(), + [&depositCell](const Cell& cell) { return cell.outPoint == depositCell.outPoint; })); + + headerDeps.emplace_back(depositCell.blockHash); + + outputs.emplace_back(depositCell.capacity, Script(depositCell.lock), Script(depositCell.type)); + outputsData.emplace_back(); + encode64LE(depositCell.blockNumber, outputsData[outputsData.size() - 1]); + + auto changeAddress = Address(signingInput.dao_withdraw_phase1().change_address()); + selectRequiredCapacity(changeAddress); +} + +void TransactionPlan::planDaoWithdrawPhase2(const Proto::SigningInput& signingInput) { + cellDeps.emplace_back(Constants::gSecp256k1CellDep); + cellDeps.emplace_back(Constants::gDAOCellDep); + + auto depositCell = Cell(signingInput.dao_withdraw_phase2().deposit_cell()); + auto withdrawingCell = Cell(signingInput.dao_withdraw_phase2().withdrawing_cell()); + selectedCells.emplace_back(withdrawingCell); + encode64LE(0, selectedCells[selectedCells.size() - 1].inputType); + + headerDeps.emplace_back(depositCell.blockHash); + headerDeps.emplace_back(withdrawingCell.blockHash); + + outputs.emplace_back(signingInput.dao_withdraw_phase2().amount(), Script(withdrawingCell.lock), + Script()); + outputsData.emplace_back(); + + outputs[0].capacity -= calculateFee(); +} + +void TransactionPlan::selectMaximumCapacity() { + uint64_t selectedCapacity = + std::accumulate(m_availableCells.begin(), m_availableCells.end(), uint64_t(0), + [&](const uint64_t total, const Cell& cell) { + if (cell.type.empty()) { + selectedCells.emplace_back(cell); + return total + cell.capacity; + } else { + return total; + } + }); + uint64_t fee = calculateFee(); + outputs[0].capacity = selectedCapacity - fee; +} + +void TransactionPlan::selectRequiredCapacity(const Address& changeAddress) { + uint64_t requiredCapacity = getRequiredCapacity(); + uint64_t fee = calculateFee(); + uint64_t feeForChangeOutput = sizeOfSingleOutput(changeAddress) * m_byteFee; + uint64_t selectedCapacity = getSelectedCapacity(); + uint64_t requiredCapacityPlusFees = requiredCapacity + fee + feeForChangeOutput; + if (selectedCapacity >= requiredCapacityPlusFees + Constants::gMinCellCapacityForNativeToken) { + outputs.emplace_back(selectedCapacity - requiredCapacityPlusFees, Script(changeAddress), + Script()); + outputsData.emplace_back(); + return; + } + sortAccordingToCapacity(); + bool gotEnough = false; + for (auto&& cell : m_availableCells) { + if (!cell.type.empty()) { + continue; + } + selectedCells.emplace_back(cell); + selectedCapacity += cell.capacity; + fee += sizeOfSingleInputAndWitness(cell.inputType, cell.outputType) * m_byteFee; + if (selectedCapacity >= requiredCapacity + fee) { + gotEnough = true; + uint64_t remainingCapacity = selectedCapacity - requiredCapacity - fee; + if (remainingCapacity >= + feeForChangeOutput + Constants::gMinCellCapacityForNativeToken) { + // If change is enough, add it to the change address + outputs.emplace_back(remainingCapacity - feeForChangeOutput, Script(changeAddress), + Script()); + outputsData.emplace_back(); + } else { + // If change is not enough, add it to the destination address + outputs[outputs.size() - 1].capacity += remainingCapacity; + } + break; + } + } + if (!gotEnough) { + error = Common::Proto::Error_not_enough_utxos; + } +} + +void TransactionPlan::selectSudtTokens(const bool useMaxAmount, const uint256_t amount, + const Address& changeAddress) { + uint256_t selectedSudtAmount = 0; + sortAccordingToTypeAndData(outputs[0].type); + bool gotEnough = false; + auto cell = m_availableCells.begin(); + while (cell != m_availableCells.end()) { + if (cell->type != outputs[0].type) { + cell++; + continue; + } + selectedCells.emplace_back(*cell); + selectedSudtAmount += Serialization::decodeUint256(cell->data); + cell = m_availableCells.erase(cell); + if (useMaxAmount) { + // Transfer maximum available tokens + gotEnough = true; + } else if (selectedSudtAmount >= amount) { + // Transfer exact number of tokens + gotEnough = true; + uint256_t changeValue = selectedSudtAmount - amount; + if (changeValue > 0) { + outputs.emplace_back(Constants::gMinCellCapacityForSUDT, Script(changeAddress), + Script(outputs[0].type)); + outputsData.emplace_back(Serialization::encodeUint256(changeValue, 16)); + } + break; + } + } + if (!gotEnough) { + error = Common::Proto::Error_not_enough_utxos; + return; + } + outputsData[0] = Serialization::encodeUint256(useMaxAmount ? selectedSudtAmount : amount, 16); +} + +uint64_t TransactionPlan::sizeWithoutInputs() { + uint64_t size = Constants::gTransactionBaseSize; + size += Constants::gCellDepSize * cellDeps.size(); + size += Constants::gHeaderDepSize * headerDeps.size(); + size += std::accumulate(outputs.begin(), outputs.end(), 0, + [](const uint64_t size, const CellOutput& output) { + Data outputData1; + output.encode(outputData1); + return size + outputData1.size() + Constants::gUint32Size; + }); + size += std::accumulate( + outputsData.begin(), outputsData.end(), 0, [](const uint64_t size, const Data& outputData) { + return size + Constants::gUint32Size + outputData.size() + Constants::gUint32Size; + }); + return size; +} + +uint64_t TransactionPlan::sizeOfSingleInputAndWitness(const Data& inputType, + const Data& outputType) { + uint64_t size = Constants::gSingleInputAndWitnessBaseSize; + auto witness = Witness(Data(Constants::gBlankWitnessBytes, 0), inputType, outputType); + Data witnessData; + witness.encode(witnessData); + size += Constants::gUint32Size + witnessData.size() + Constants::gUint32Size; + return size; +} + +uint64_t TransactionPlan::sizeOfSingleOutput(const Address& address) { + uint64_t size = 0; + auto output = CellOutput(0, Script(address), Script()); + Data outputData1; + output.encode(outputData1); + size += outputData1.size() + Constants::gUint32Size; // output + size += Constants::gUint32Size + 0 + Constants::gUint32Size; // blank outputData + return size; +} + +uint64_t TransactionPlan::calculateFee() { + uint64_t size = sizeWithoutInputs(); + size += std::accumulate(selectedCells.begin(), selectedCells.end(), uint64_t(0), + [&](const uint64_t total, const Cell& cell) { + return total + + sizeOfSingleInputAndWitness(cell.inputType, cell.outputType); + }); + return size * m_byteFee; +} + +void TransactionPlan::sortAccordingToCapacity() { + std::sort(m_availableCells.begin(), m_availableCells.end(), + [](const Cell& lhs, const Cell& rhs) { return lhs.capacity < rhs.capacity; }); +} + +void TransactionPlan::sortAccordingToTypeAndData(const Script& type) { + std::sort( + m_availableCells.begin(), m_availableCells.end(), [&](const Cell& lhs, const Cell& rhs) { + uint256_t lhsAmount = (lhs.type == type) ? Serialization::decodeUint256(lhs.data) : 0; + uint256_t rhsAmount = (rhs.type == type) ? Serialization::decodeUint256(rhs.data) : 0; + return lhsAmount < rhsAmount; + }); +} + +uint64_t TransactionPlan::getRequiredCapacity() { + return std::accumulate(outputs.begin(), outputs.end(), uint64_t(0), + [](const uint64_t total, const CellOutput& cellOutput) { + return total + cellOutput.capacity; + }); +} + +uint64_t TransactionPlan::getSelectedCapacity() { + return std::accumulate( + selectedCells.begin(), selectedCells.end(), uint64_t(0), + [](const uint64_t total, const Cell& cell) { return total + cell.capacity; }); +} + +} // namespace TW::Nervos diff --git a/src/Nervos/TransactionPlan.h b/src/Nervos/TransactionPlan.h new file mode 100644 index 00000000000..7fc800e44a3 --- /dev/null +++ b/src/Nervos/TransactionPlan.h @@ -0,0 +1,123 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Address.h" +#include "Cell.h" +#include "CellDep.h" +#include "CellInput.h" +#include "CellOutput.h" +#include "HeaderDep.h" +#include "Script.h" + +#include "../Coin.h" +#include "../CoinEntry.h" +#include "Data.h" +#include "../Hash.h" +#include "../KeyPair.h" +#include "../PrivateKey.h" +#include "../PublicKey.h" +#include "../Result.h" +#include "../proto/Nervos.pb.h" + +#include + +namespace TW::Nervos { + +class TransactionPlan { +public: + // List of cell deps + CellDeps cellDeps; + + // List of header deps + HeaderDeps headerDeps; + + // List of cells selected for this transaction + Cells selectedCells; + + // List of cell outputs + CellOutputs outputs; + + // List of outputs data + std::vector outputsData; + + // Error during transaction planning + Common::Proto::SigningError error; + + TransactionPlan() = default; + + /// Initializes a transaction from a Protobuf transaction. + TransactionPlan(const Proto::TransactionPlan& txPlan) { + for (auto&& cellDep : txPlan.cell_deps()) { + cellDeps.emplace_back(cellDep); + } + for (auto&& headerDep : txPlan.header_deps()) { + Data data; + data.insert(data.end(), headerDep.begin(), headerDep.end()); + headerDeps.emplace_back(data); + } + for (auto&& cell : txPlan.selected_cells()) { + selectedCells.emplace_back(cell); + } + for (auto&& output : txPlan.outputs()) { + outputs.emplace_back(output); + } + for (auto&& outputData : txPlan.outputs_data()) { + Data data; + data.insert(data.end(), outputData.begin(), outputData.end()); + outputsData.emplace_back(data); + } + error = txPlan.error(); + } + + /// Converts to Protobuf model + Proto::TransactionPlan proto() const { + auto txPlan = Proto::TransactionPlan(); + for (auto&& cellDep : cellDeps) { + *txPlan.add_cell_deps() = cellDep.proto(); + } + for (auto&& headerDep : headerDeps) { + txPlan.add_header_deps(headerDep.data(), headerDep.size()); + } + for (auto&& cell : selectedCells) { + *txPlan.add_selected_cells() = cell.proto(); + } + for (auto&& output : outputs) { + *txPlan.add_outputs() = output.proto(); + } + for (auto&& outputData : outputsData) { + txPlan.add_outputs_data(outputData.data(), outputData.size()); + } + return txPlan; + } + + void plan(const Proto::SigningInput& signingInput); + +private: + uint64_t m_byteFee; + Cells m_availableCells; + + void planNativeTransfer(const Proto::SigningInput& signingInput); + void planSudtTransfer(const Proto::SigningInput& signingInput); + void planDaoDeposit(const Proto::SigningInput& signingInput); + void planDaoWithdrawPhase1(const Proto::SigningInput& signingInput); + void planDaoWithdrawPhase2(const Proto::SigningInput& signingInput); + void selectMaximumCapacity(); + void selectRequiredCapacity(const Address& changeAddress); + void selectSudtTokens(const bool useMaxAmount, const uint256_t amount, + const Address& changeAddress); + uint64_t sizeWithoutInputs(); + uint64_t sizeOfSingleInputAndWitness(const Data& inputType, const Data& outputType); + uint64_t sizeOfSingleOutput(const Address& address); + uint64_t calculateFee(); + void sortAccordingToCapacity(); + void sortAccordingToTypeAndData(const Script& type); + uint64_t getRequiredCapacity(); + uint64_t getSelectedCapacity(); +}; + +} // namespace TW::Nervos diff --git a/src/Nervos/Witness.cpp b/src/Nervos/Witness.cpp new file mode 100644 index 00000000000..73a7e37fed8 --- /dev/null +++ b/src/Nervos/Witness.cpp @@ -0,0 +1,29 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Witness.h" +#include "Serialization.h" + +namespace TW::Nervos { + +void Witness::encode(Data& data) const { + if ((lock.empty()) && (inputType.empty()) && (outputType.empty())) { + return; + } + std::vector dataArray; + dataArray.reserve(3); + for (auto&& data1 : std::vector({lock, inputType, outputType})) { + Data data2; + if (!data1.empty()) { + encode32LE(uint32_t(data1.size()), data2); + data2.insert(data2.end(), data1.begin(), data1.end()); + } + dataArray.emplace_back(data2); + } + Serialization::encodeDataArray(dataArray, data); +} + +} // namespace TW::Nervos diff --git a/src/Nervos/Witness.h b/src/Nervos/Witness.h new file mode 100644 index 00000000000..f53aa398f7a --- /dev/null +++ b/src/Nervos/Witness.h @@ -0,0 +1,33 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" + +#include + +namespace TW::Nervos { + +struct Witness { + Data lock; + Data inputType; + Data outputType; + + Witness() = default; + + /// Initializes a witness with lock, inputType and outputType. + Witness(const Data& lock, const Data& inputType, const Data& outputType) + : lock(lock), inputType(inputType), outputType(outputType) {} + + /// Encodes the witness into the provided buffer. + void encode(Data& data) const; +}; + +/// A list of Witness's +using Witnesses = std::vector; + +} // namespace TW::Nervos diff --git a/src/Nimiq/Address.cpp b/src/Nimiq/Address.cpp index 458507aa506..46b6cb3ace7 100644 --- a/src/Nimiq/Address.cpp +++ b/src/Nimiq/Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,16 +7,12 @@ #include "Address.h" #include "../Base32.h" -#include "../Hash.h" -#include "../HexCoding.h" #include #include -#include -#include -using namespace TW::Nimiq; +namespace TW::Nimiq { static const char* BASE32_ALPHABET_NIMIQ = "0123456789ABCDEFGHJKLMNPQRSTUVXY"; @@ -150,3 +146,5 @@ static inline int check_add(int check, int num) { ; return (check + num) % 97; } + +} // namespace TW::Nimiq diff --git a/src/Nimiq/Address.h b/src/Nimiq/Address.h index 66acb288bad..ff60c756f9e 100644 --- a/src/Nimiq/Address.h +++ b/src/Nimiq/Address.h @@ -22,7 +22,7 @@ class Address { /// Address data consisting of a prefix byte followed by the public key /// hash. - std::array bytes; + std::array bytes{}; /// Determines whether a collection of bytes makes a valid address. static bool isValid(const std::vector& data) { return data.size() == size; } diff --git a/src/Nimiq/Entry.cpp b/src/Nimiq/Entry.cpp index 9e795074477..92279053f4f 100644 --- a/src/Nimiq/Entry.cpp +++ b/src/Nimiq/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,19 +9,21 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Nimiq; -using namespace std; +namespace TW::Nimiq { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Nimiq + diff --git a/src/Nimiq/Entry.h b/src/Nimiq/Entry.h index 59c18f3e890..cad09e0fc65 100644 --- a/src/Nimiq/Entry.h +++ b/src/Nimiq/Entry.h @@ -12,12 +12,11 @@ namespace TW::Nimiq { /// Entry point for implementation of Nimiq coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeNimiq}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Nimiq diff --git a/src/Nimiq/Signer.cpp b/src/Nimiq/Signer.cpp index 2d3201e2892..5433b88aa86 100644 --- a/src/Nimiq/Signer.cpp +++ b/src/Nimiq/Signer.cpp @@ -9,8 +9,7 @@ #include -using namespace TW; -using namespace TW::Nimiq; +namespace TW::Nimiq { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); @@ -39,3 +38,5 @@ void Signer::sign(const PrivateKey& privateKey, Transaction& transaction) const auto signature = privateKey.sign(preImage, TWCurveED25519); std::copy(signature.begin(), signature.end(), transaction.signature.begin()); } + +} // namespace TW::Nimiq diff --git a/src/Nimiq/Signer.h b/src/Nimiq/Signer.h index 52baa9f03f0..022f98ba424 100644 --- a/src/Nimiq/Signer.h +++ b/src/Nimiq/Signer.h @@ -7,7 +7,7 @@ #pragma once #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/Nimiq.pb.h" diff --git a/src/Nimiq/Transaction.cpp b/src/Nimiq/Transaction.cpp index 4498166bad3..c7828ed73a8 100644 --- a/src/Nimiq/Transaction.cpp +++ b/src/Nimiq/Transaction.cpp @@ -8,11 +8,8 @@ #include "Signer.h" #include "../BinaryCoding.h" -#include "../HexCoding.h" -#include "../PublicKey.h" -using namespace TW; -using namespace TW::Nimiq; +namespace TW::Nimiq { const uint8_t NETWORK_ID = 42; const uint8_t EMPTY_FLAGS = 0; @@ -50,3 +47,5 @@ std::vector Transaction::getPreImage() const { return data; } + +} // namespace TW::Nimiq diff --git a/src/NumericLiteral.h b/src/NumericLiteral.h new file mode 100644 index 00000000000..4674dda8d88 --- /dev/null +++ b/src/NumericLiteral.h @@ -0,0 +1,14 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include // std::size_t + +// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0330r8.html +inline constexpr std::size_t operator"" _uz(unsigned long long int n) { + return n; +} \ No newline at end of file diff --git a/src/Oasis/Address.cpp b/src/Oasis/Address.cpp index af150206a80..ee4102f4d8f 100644 --- a/src/Oasis/Address.cpp +++ b/src/Oasis/Address.cpp @@ -9,19 +9,21 @@ #include #define COIN_ADDRESS_CONTEXT "oasis-core/address: staking" -#define COIN_ADDRESS_VERSION 0 +#define COIN_ADDRESS_VERSION 0 -using namespace TW::Oasis; +namespace TW::Oasis { const std::string Address::hrp = HRP_OASIS; -Address::Address(const Data& keyHash) : Bech32Address(hrp, keyHash) { +Address::Address(const Data& keyHash) + : Bech32Address(hrp, keyHash) { if (getKeyHash().size() != Address::size) { throw std::invalid_argument("invalid address data"); } } -Address::Address(const TW::PublicKey& publicKey) : Bech32Address(hrp){ +Address::Address(const TW::PublicKey& publicKey) + : Bech32Address(hrp) { if (publicKey.type != TWPublicKeyTypeED25519) { throw std::invalid_argument("address may only be an extended ED25519 public key"); } @@ -39,8 +41,9 @@ Address::Address(const TW::PublicKey& publicKey) : Bech32Address(hrp){ setKey(key); } -Address::Address(const std::string& addr) : Bech32Address(addr) { - if(!isValid(addr)) { +Address::Address(const std::string& addr) + : Bech32Address(addr) { + if (!isValid(addr)) { throw std::invalid_argument("invalid address string"); } } @@ -49,3 +52,4 @@ bool Address::isValid(const std::string& addr) { return Bech32Address::isValid(addr, hrp); } +} // namespace TW::Oasis diff --git a/src/Oasis/Address.h b/src/Oasis/Address.h index d9784cd63f1..c4eb73f3ef0 100644 --- a/src/Oasis/Address.h +++ b/src/Oasis/Address.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include "../Bech32Address.h" diff --git a/src/Oasis/Entry.cpp b/src/Oasis/Entry.cpp index 44c756948a3..3dbaeeb4d24 100644 --- a/src/Oasis/Entry.cpp +++ b/src/Oasis/Entry.cpp @@ -9,19 +9,22 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Oasis; using namespace std; +namespace TW::Oasis { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Oasis diff --git a/src/Oasis/Entry.h b/src/Oasis/Entry.h index a82478f4050..c4b3fb6240f 100644 --- a/src/Oasis/Entry.h +++ b/src/Oasis/Entry.h @@ -12,12 +12,11 @@ namespace TW::Oasis { /// Entry point for implementation of Oasis coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeOasis}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Oasis diff --git a/src/Oasis/Signer.cpp b/src/Oasis/Signer.cpp index 7babc53ee84..b9263a7d8d9 100644 --- a/src/Oasis/Signer.cpp +++ b/src/Oasis/Signer.cpp @@ -6,14 +6,14 @@ #include -#include "Signer.h" #include "Address.h" +#include "Signer.h" #define TRANSFER_METHOD "staking.Transfer" using namespace TW; -using namespace TW::Oasis; +namespace TW::Oasis { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto output = Proto::SigningOutput(); @@ -40,7 +40,7 @@ Data Signer::build() const { gasAmountStream >> gasAmount; Transaction transaction( - /* to */ address, + /* to */ address, /* method */ TRANSFER_METHOD, /* gasPrice */ input.transfer().gas_price(), /* gasAmount */ gasAmount, @@ -48,7 +48,6 @@ Data Signer::build() const { /* nonce */ input.transfer().nonce(), /* context */ input.transfer().context()); - auto privateKey = PrivateKey(input.private_key()); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); @@ -70,3 +69,5 @@ Data Signer::sign(Transaction& tx) const { auto signature = privateKey.sign(hash, TWCurveED25519); return Data(signature.begin(), signature.end()); } + +} // namespace TW::Oasis diff --git a/src/Oasis/Signer.h b/src/Oasis/Signer.h index 76df838c029..90e92d0e5b9 100644 --- a/src/Oasis/Signer.h +++ b/src/Oasis/Signer.h @@ -9,7 +9,7 @@ #include -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/Oasis.pb.h" #include "Transaction.h" diff --git a/src/Oasis/Transaction.cpp b/src/Oasis/Transaction.cpp index c2f1cc88ffc..dece41bfb53 100644 --- a/src/Oasis/Transaction.cpp +++ b/src/Oasis/Transaction.cpp @@ -7,7 +7,10 @@ #include "Transaction.h" using namespace TW; -using namespace TW::Oasis; + +namespace TW::Oasis { + +// clang-format off // encodeVaruint encodes a 256-bit number into a big endian encoding, omitting leading zeros. static Data encodeVaruint(const uint256_t& value) { @@ -56,3 +59,7 @@ Data Transaction::serialize(Data& signature, PublicKey& publicKey) const { }); return signedMessage.encoded(); } + +// clang-format on + +} // namespace TW::Oasis diff --git a/src/Ontology/Address.cpp b/src/Ontology/Address.cpp index 979d204dc05..40333d62217 100644 --- a/src/Ontology/Address.cpp +++ b/src/Ontology/Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -16,14 +16,15 @@ #include using namespace TW; -using namespace TW::Ontology; + +namespace TW::Ontology { Address::Address(const PublicKey& publicKey) { std::vector builder(publicKey.bytes); builder.insert(builder.begin(), PUSH_BYTE_33); builder.push_back(CHECK_SIG); auto builderData = toScriptHash(builder); - std::copy(builderData.begin(), builderData.end(), data.begin()); + std::copy(builderData.begin(), builderData.end(), _data.begin()); } Address::Address(const std::string& b58Address) { @@ -32,19 +33,19 @@ Address::Address(const std::string& b58Address) { } Data addressWithVersion(size + 1); base58_decode_check(b58Address.c_str(), HASHER_SHA2D, addressWithVersion.data(), size + 1); - std::copy(addressWithVersion.begin() + 1, addressWithVersion.end(), data.begin()); + std::copy(addressWithVersion.begin() + 1, addressWithVersion.end(), _data.begin()); } Address::Address(const std::vector& bytes) { if (bytes.size() != size) { throw std::runtime_error("Invalid bytes data."); } - std::copy(bytes.begin(), bytes.end(), data.begin()); + std::copy(bytes.begin(), bytes.end(), _data.begin()); } Address::Address(uint8_t m, const std::vector& publicKeys) { auto builderData = toScriptHash(ParamsBuilder::fromMultiPubkey(m, publicKeys)); - std::copy(builderData.begin(), builderData.end(), data.begin()); + std::copy(builderData.begin(), builderData.end(), _data.begin()); } Data Address::toScriptHash(const Data& data) { @@ -64,10 +65,12 @@ bool Address::isValid(const std::string& b58Address) noexcept { std::string Address::string() const { std::vector encodeData(size + 1); encodeData[0] = version; - std::copy(data.begin(), data.end(), encodeData.begin() + 1); + std::copy(_data.begin(), _data.end(), encodeData.begin() + 1); size_t b58StrSize = 34; std::string b58Str(b58StrSize, ' '); base58_encode_check(encodeData.data(), (int)encodeData.size(), HASHER_SHA2D, &b58Str[0], (int)b58StrSize + 1); return b58Str; } + +} // namespace TW::Ontology diff --git a/src/Ontology/Address.h b/src/Ontology/Address.h index 7d5b2377b31..b55ebf1f3dc 100644 --- a/src/Ontology/Address.h +++ b/src/Ontology/Address.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -22,7 +22,7 @@ class Address { static const size_t size = 20; static const uint8_t version = 0x17; - std::array data; + std::array _data; /// Initializes an address with a public key. explicit Address(const PublicKey& publicKey); @@ -44,7 +44,7 @@ class Address { }; inline bool operator==(const Address& lhs, const Address& rhs) { - return lhs.data == rhs.data; + return lhs._data == rhs._data; } } // namespace TW::Ontology diff --git a/src/Ontology/Asset.h b/src/Ontology/Asset.h index 123b6738ab7..a393db0f18e 100644 --- a/src/Ontology/Asset.h +++ b/src/Ontology/Asset.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,7 +10,7 @@ #include "Signer.h" #include "Transaction.h" #include "../BinaryCoding.h" -#include "../Data.h" +#include "Data.h" #include #include @@ -19,18 +19,19 @@ namespace TW::Ontology { class Asset { - protected: +protected: const uint8_t txType = 0xD1; - public: +public: + virtual ~Asset() noexcept = default; virtual Data contractAddress() = 0; virtual Transaction decimals(uint32_t nonce) = 0; - virtual Transaction balanceOf(const Address &address, uint32_t nonce) = 0; + virtual Transaction balanceOf(const Address& address, uint32_t nonce) = 0; - virtual Transaction transfer(const Signer &from, const Address &to, uint64_t amount, - const Signer &payer, uint64_t gasPrice, uint64_t gasLimit, + virtual Transaction transfer(const Signer& from, const Address& to, uint64_t amount, + const Signer& payer, uint64_t gasPrice, uint64_t gasLimit, uint32_t nonce) = 0; }; } // namespace TW::Ontology diff --git a/src/Ontology/Entry.cpp b/src/Ontology/Entry.cpp index f0129ed1e08..9f4de742362 100644 --- a/src/Ontology/Entry.cpp +++ b/src/Ontology/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,19 +9,20 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Ontology; -using namespace std; +namespace TW::Ontology { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Ontology diff --git a/src/Ontology/Entry.h b/src/Ontology/Entry.h index ba3cf582d26..68df17b8baf 100644 --- a/src/Ontology/Entry.h +++ b/src/Ontology/Entry.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,12 +12,11 @@ namespace TW::Ontology { /// Entry point for implementation of Ontology coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeOntology}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Ontology diff --git a/src/Ontology/Oep4.cpp b/src/Ontology/Oep4.cpp new file mode 100644 index 00000000000..3c08c8af512 --- /dev/null +++ b/src/Ontology/Oep4.cpp @@ -0,0 +1,69 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Oep4.h" +#include + +namespace TW::Ontology { +Oep4::Oep4(const Address addr) noexcept + : oep4Contract(addr._data.begin(), addr._data.end()) { +} + +Oep4::Oep4(const Data bin) noexcept + : oep4Contract(bin) { +} + +Transaction Oep4::readOnlyMethod(std::string method_name, uint32_t nonce) { + Address addr(oep4Contract); + NeoVmParamValue::ParamArray args{}; + auto invokeCode = ParamsBuilder::buildOep4InvokeCode(addr, method_name, {args}); + + return Transaction(0, 0xD1, nonce, 0, 0, "", invokeCode); +} + +Transaction Oep4::name(uint32_t nonce) { + return Oep4::readOnlyMethod("name", nonce); +} + +Transaction Oep4::symbol(uint32_t nonce) { + return Oep4::readOnlyMethod("symbol", nonce); +} + +Transaction Oep4::decimals(uint32_t nonce) { + return Oep4::readOnlyMethod("decimals", nonce); +} + +Transaction Oep4::totalSupply(uint32_t nonce) { + return Oep4::readOnlyMethod("totalSupply", nonce); +} + +Transaction Oep4::balanceOf(const Address& user, uint32_t nonce) { + Address contract(oep4Contract); + Data d(std::begin(user._data), std::end(user._data)); + NeoVmParamValue::ParamArray args{d}; + auto invokeCode = ParamsBuilder::buildOep4InvokeCode(contract, "balanceOf", {args}); + return Transaction(0, 0xD1, nonce, 0, 0, "", invokeCode); +} + +Transaction Oep4::transfer(const Signer& from, const Address& to, uint64_t amount, + const Signer& payer, uint64_t gasPrice, uint64_t gasLimit, + uint32_t nonce) { + Address contract(oep4Contract); + + auto fromAddr = from.getAddress(); + NeoVmParamValue::ParamArray args{fromAddr._data, to._data, amount}; + // yes, invoke neovm is not like ont transfer + std::reverse(args.begin(), args.end()); + auto invokeCode = ParamsBuilder::buildOep4InvokeCode(contract, "transfer", {args}); + + auto tx = Transaction(0, 0xD1, nonce, gasPrice, gasLimit, payer.getAddress().string(), invokeCode); + from.sign(tx); + payer.addSign(tx); + + return tx; +} + +} // namespace TW::Ontology diff --git a/src/Ontology/Oep4.h b/src/Ontology/Oep4.h new file mode 100644 index 00000000000..d121d8a1ceb --- /dev/null +++ b/src/Ontology/Oep4.h @@ -0,0 +1,41 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Address.h" +#include "Asset.h" +#include "ParamsBuilder.h" +#include "Transaction.h" +#include "Data.h" + +namespace TW::Ontology { + +class Oep4 { +private: + static constexpr uint8_t version = 0x00; + + Data oep4Contract; + // building neovm instruction for oep4 readonly method(name, symbol...) + // are all the same except the method name + Transaction readOnlyMethod(std::string methodName, uint32_t nonce); + +public: + explicit Oep4(const Address addr) noexcept; + explicit Oep4(const Data bin) noexcept; + Oep4() = delete; + Data contractAddress() { return oep4Contract; } + Transaction name(uint32_t nonce); + Transaction symbol(uint32_t nonce); + Transaction decimals(uint32_t nonce); + Transaction totalSupply(uint32_t nonce); + Transaction balanceOf(const Address& address, uint32_t nonce); + Transaction transfer(const Signer& from, const Address& to, uint64_t amount, + const Signer& payer, uint64_t gasPrice, uint64_t gasLimit, + uint32_t nonce); +}; + +} // namespace TW::Ontology diff --git a/src/Ontology/Oep4TxBuilder.cpp b/src/Ontology/Oep4TxBuilder.cpp new file mode 100644 index 00000000000..04e5f2c108c --- /dev/null +++ b/src/Ontology/Oep4TxBuilder.cpp @@ -0,0 +1,51 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Oep4TxBuilder.h" +#include "../HexCoding.h" + +namespace TW::Ontology { + +Data Oep4TxBuilder::decimals(const Ontology::Proto::SigningInput& input) { + Oep4 oep4(parse_hex(input.contract())); + auto transaction = oep4.decimals(input.nonce()); + auto encoded = transaction.serialize(); + return encoded; +} + +Data Oep4TxBuilder::balanceOf(const Ontology::Proto::SigningInput& input) { + Oep4 oep4(parse_hex(input.contract())); + auto queryAddress = Address(input.query_address()); + auto transaction = oep4.balanceOf(queryAddress, input.nonce()); + auto encoded = transaction.serialize(); + return encoded; +} + +Data Oep4TxBuilder::transfer(const Ontology::Proto::SigningInput& input) { + Oep4 oep4(parse_hex(input.contract())); + auto payerSigner = Signer(PrivateKey(input.payer_private_key())); + auto fromSigner = Signer(PrivateKey(input.owner_private_key())); + auto toAddress = Address(input.to_address()); + auto tranferTx = oep4.transfer(fromSigner, toAddress, input.amount(), payerSigner, + input.gas_price(), input.gas_limit(), input.nonce()); + auto encoded = tranferTx.serialize(); + return encoded; +} + +Data Oep4TxBuilder::build(const Ontology::Proto::SigningInput& input) { + auto method = std::string(input.method().begin(), input.method().end()); + if (method == "transfer") { + return Oep4TxBuilder::transfer(input); + } else if (method == "balanceOf") { + return Oep4TxBuilder::balanceOf(input); + } else if (method == "decimals") { + return Oep4TxBuilder::decimals(input); + } + + return Data(); +} + +} // namespace TW::Ontology diff --git a/src/Ontology/Oep4TxBuilder.h b/src/Ontology/Oep4TxBuilder.h new file mode 100644 index 00000000000..138f955aa82 --- /dev/null +++ b/src/Ontology/Oep4TxBuilder.h @@ -0,0 +1,29 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Oep4.h" + +#include "../proto/Ontology.pb.h" + +#include + +namespace TW::Ontology { + +class Oep4TxBuilder { + +public: + static Data decimals(const Ontology::Proto::SigningInput& input); + + static Data balanceOf(const Ontology::Proto::SigningInput& input); + + static Data transfer(const Ontology::Proto::SigningInput& input); + + static Data build(const Ontology::Proto::SigningInput& input); +}; + +} // namespace TW::Ontology diff --git a/src/Ontology/Ong.cpp b/src/Ontology/Ong.cpp index 383a411e549..e37ff4ae6a7 100644 --- a/src/Ontology/Ong.cpp +++ b/src/Ontology/Ong.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,34 +10,33 @@ #include -using namespace TW; -using namespace TW::Ontology; +namespace TW::Ontology { Transaction Ong::decimals(uint32_t nonce) { auto builder = ParamsBuilder(); auto invokeCode = - ParamsBuilder::buildNativeInvokeCode(contractAddress(), version, "decimals", Data()); + ParamsBuilder::buildNativeInvokeCode(contractAddress(), version, "decimals", {Data()}); auto tx = Transaction(version, txType, nonce, (uint64_t)0, (uint64_t)0, (std::string) "", invokeCode); return tx; } -Transaction Ong::balanceOf(const Address &address, uint32_t nonce) { +Transaction Ong::balanceOf(const Address& address, uint32_t nonce) { auto builder = ParamsBuilder(); auto invokeCode = - ParamsBuilder::buildNativeInvokeCode(contractAddress(), version, "balanceOf", address.data); + ParamsBuilder::buildNativeInvokeCode(contractAddress(), version, "balanceOf", {address._data}); auto tx = Transaction(version, txType, nonce, (uint64_t)0, (uint64_t)0, (std::string) "", invokeCode); return tx; } -Transaction Ong::transfer(const Signer &from, const Address &to, uint64_t amount, - const Signer &payer, uint64_t gasPrice, uint64_t gasLimit, +Transaction Ong::transfer(const Signer& from, const Address& to, uint64_t amount, + const Signer& payer, uint64_t gasPrice, uint64_t gasLimit, uint32_t nonce) { - std::list transferParam{from.getAddress().data, to.data, amount}; - std::vector args{transferParam}; + NeoVmParamValue::ParamList transferParam{from.getAddress()._data, to._data, amount}; + NeoVmParamValue::ParamArray args{transferParam}; auto invokeCode = - ParamsBuilder::buildNativeInvokeCode(contractAddress(), 0x00, "transfer", args); + ParamsBuilder::buildNativeInvokeCode(contractAddress(), 0x00, "transfer", {args}); auto tx = Transaction(version, txType, nonce, gasPrice, gasLimit, payer.getAddress().string(), invokeCode); from.sign(tx); @@ -45,16 +44,18 @@ Transaction Ong::transfer(const Signer &from, const Address &to, uint64_t amount return tx; } -Transaction Ong::withdraw(const Signer &claimer, const Address &receiver, uint64_t amount, - const Signer &payer, uint64_t gasPrice, uint64_t gasLimit, +Transaction Ong::withdraw(const Signer& claimer, const Address& receiver, uint64_t amount, + const Signer& payer, uint64_t gasPrice, uint64_t gasLimit, uint32_t nonce) { auto ontContract = Address("AFmseVrdL9f9oyCzZefL9tG6UbvhUMqNMV"); - std::list args{claimer.getAddress().data, ontContract.data, receiver.data, amount}; + NeoVmParamValue::ParamList args{claimer.getAddress()._data, ontContract._data, receiver._data, amount}; auto invokeCode = - ParamsBuilder::buildNativeInvokeCode(contractAddress(), 0x00, "transferFrom", args); + ParamsBuilder::buildNativeInvokeCode(contractAddress(), 0x00, "transferFrom", {args}); auto tx = Transaction(version, txType, nonce, gasPrice, gasLimit, payer.getAddress().string(), invokeCode); claimer.sign(tx); payer.addSign(tx); return tx; -} \ No newline at end of file +} + +} // namespace TW::Ontology diff --git a/src/Ontology/Ong.h b/src/Ontology/Ong.h index 27de0c1d108..8537f605967 100644 --- a/src/Ontology/Ong.h +++ b/src/Ontology/Ong.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,7 +7,7 @@ #pragma once #include "Asset.h" -#include "../Data.h" +#include "Data.h" namespace TW::Ontology { diff --git a/src/Ontology/OngTxBuilder.cpp b/src/Ontology/OngTxBuilder.cpp index 8fc3c7f89d0..f44d46a14b9 100644 --- a/src/Ontology/OngTxBuilder.cpp +++ b/src/Ontology/OngTxBuilder.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,23 +6,22 @@ #include "OngTxBuilder.h" -using namespace TW; -using namespace TW::Ontology; +namespace TW::Ontology { -Data OngTxBuilder::decimals(const Ontology::Proto::SigningInput &input) { +Data OngTxBuilder::decimals(const Ontology::Proto::SigningInput& input) { auto transaction = Ong().decimals(input.nonce()); auto encoded = transaction.serialize(); return encoded; } -Data OngTxBuilder::balanceOf(const Ontology::Proto::SigningInput &input) { +Data OngTxBuilder::balanceOf(const Ontology::Proto::SigningInput& input) { auto queryAddress = Address(input.query_address()); auto transaction = Ong().balanceOf(queryAddress, input.nonce()); auto encoded = transaction.serialize(); return encoded; } -Data OngTxBuilder::transfer(const Ontology::Proto::SigningInput &input) { +Data OngTxBuilder::transfer(const Ontology::Proto::SigningInput& input) { auto payer = Signer(PrivateKey(input.payer_private_key())); auto owner = Signer(PrivateKey(input.owner_private_key())); auto toAddress = Address(input.to_address()); @@ -32,7 +31,7 @@ Data OngTxBuilder::transfer(const Ontology::Proto::SigningInput &input) { return encoded; } -Data OngTxBuilder::withdraw(const Ontology::Proto::SigningInput &input) { +Data OngTxBuilder::withdraw(const Ontology::Proto::SigningInput& input) { auto payer = Signer(PrivateKey(input.payer_private_key())); auto owner = Signer(PrivateKey(input.owner_private_key())); auto toAddress = Address(input.to_address()); @@ -42,7 +41,7 @@ Data OngTxBuilder::withdraw(const Ontology::Proto::SigningInput &input) { return encoded; } -Data OngTxBuilder::build(const Ontology::Proto::SigningInput &input) { +Data OngTxBuilder::build(const Ontology::Proto::SigningInput& input) { auto method = std::string(input.method().begin(), input.method().end()); if (method == "transfer") { return OngTxBuilder::transfer(input); @@ -55,3 +54,5 @@ Data OngTxBuilder::build(const Ontology::Proto::SigningInput &input) { } return Data(); } + +} // namespace TW::Ontology diff --git a/src/Ontology/OngTxBuilder.h b/src/Ontology/OngTxBuilder.h index e5843a0909b..a97c169c5fd 100644 --- a/src/Ontology/OngTxBuilder.h +++ b/src/Ontology/OngTxBuilder.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/src/Ontology/Ont.cpp b/src/Ontology/Ont.cpp index 08ce5847328..55a9bf5f2a8 100644 --- a/src/Ontology/Ont.cpp +++ b/src/Ontology/Ont.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,37 +10,38 @@ #include -using namespace TW; -using namespace TW::Ontology; +namespace TW::Ontology { Transaction Ont::decimals(uint32_t nonce) { auto builder = ParamsBuilder(); auto invokeCode = - ParamsBuilder::buildNativeInvokeCode(contractAddress(), version, "decimals", Data()); + ParamsBuilder::buildNativeInvokeCode(contractAddress(), version, "decimals", {Data()}); auto tx = Transaction((uint8_t)0, txType, nonce, (uint64_t)0, (uint64_t)0, (std::string) "", invokeCode); return tx; } -Transaction Ont::balanceOf(const Address &address, uint32_t nonce) { +Transaction Ont::balanceOf(const Address& address, uint32_t nonce) { auto builder = ParamsBuilder(); auto invokeCode = - ParamsBuilder::buildNativeInvokeCode(contractAddress(), version, "balanceOf", address.data); + ParamsBuilder::buildNativeInvokeCode(contractAddress(), version, "balanceOf", {address._data}); auto tx = Transaction((uint8_t)0, txType, nonce, (uint64_t)0, (uint64_t)0, (std::string) "", invokeCode); return tx; } -Transaction Ont::transfer(const Signer &from, const Address &to, uint64_t amount, - const Signer &payer, uint64_t gasPrice, uint64_t gasLimit, +Transaction Ont::transfer(const Signer& from, const Address& to, uint64_t amount, + const Signer& payer, uint64_t gasPrice, uint64_t gasLimit, uint32_t nonce) { - std::list transferParam{from.getAddress().data, to.data, amount}; - std::vector args{transferParam}; + NeoVmParamValue::ParamList transferParam{from.getAddress()._data, to._data, amount}; + NeoVmParamValue::ParamArray args{transferParam}; auto invokeCode = - ParamsBuilder::buildNativeInvokeCode(contractAddress(), 0x00, "transfer", args); + ParamsBuilder::buildNativeInvokeCode(contractAddress(), 0x00, "transfer", {args}); auto tx = Transaction(version, txType, nonce, gasPrice, gasLimit, payer.getAddress().string(), invokeCode); from.sign(tx); payer.addSign(tx); return tx; } + +} // namespace TW::Ontology diff --git a/src/Ontology/Ont.h b/src/Ontology/Ont.h index d0dce26e64d..88806c2a197 100644 --- a/src/Ontology/Ont.h +++ b/src/Ontology/Ont.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,7 +7,7 @@ #pragma once #include "Asset.h" -#include "../Data.h" +#include "Data.h" namespace TW::Ontology { diff --git a/src/Ontology/OntTxBuilder.cpp b/src/Ontology/OntTxBuilder.cpp index 9b727313fae..735681c14fc 100644 --- a/src/Ontology/OntTxBuilder.cpp +++ b/src/Ontology/OntTxBuilder.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,23 +6,22 @@ #include "OntTxBuilder.h" -using namespace TW; -using namespace TW::Ontology; +namespace TW::Ontology { -Data OntTxBuilder::decimals(const Ontology::Proto::SigningInput &input) { +Data OntTxBuilder::decimals(const Ontology::Proto::SigningInput& input) { auto transaction = Ont().decimals(input.nonce()); auto encoded = transaction.serialize(); return encoded; } -Data OntTxBuilder::balanceOf(const Ontology::Proto::SigningInput &input) { +Data OntTxBuilder::balanceOf(const Ontology::Proto::SigningInput& input) { auto queryAddress = Address(input.query_address()); auto transaction = Ont().balanceOf(queryAddress, input.nonce()); auto encoded = transaction.serialize(); return encoded; } -Data OntTxBuilder::transfer(const Ontology::Proto::SigningInput &input) { +Data OntTxBuilder::transfer(const Ontology::Proto::SigningInput& input) { auto payerSigner = Signer(PrivateKey(input.payer_private_key())); auto fromSigner = Signer(PrivateKey(input.owner_private_key())); auto toAddress = Address(input.to_address()); @@ -32,7 +31,7 @@ Data OntTxBuilder::transfer(const Ontology::Proto::SigningInput &input) { return encoded; } -Data OntTxBuilder::build(const Ontology::Proto::SigningInput &input) { +Data OntTxBuilder::build(const Ontology::Proto::SigningInput& input) { auto method = std::string(input.method().begin(), input.method().end()); if (method == "transfer") { return OntTxBuilder::transfer(input); @@ -43,3 +42,5 @@ Data OntTxBuilder::build(const Ontology::Proto::SigningInput &input) { } return Data(); } + +} // namespace TW::Ontology diff --git a/src/Ontology/OntTxBuilder.h b/src/Ontology/OntTxBuilder.h index aabc1cc8e40..5cf64cbe16c 100644 --- a/src/Ontology/OntTxBuilder.h +++ b/src/Ontology/OntTxBuilder.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/src/Ontology/OpCode.h b/src/Ontology/OpCode.h index 6c44454b1a3..3f7904242fd 100644 --- a/src/Ontology/OpCode.h +++ b/src/Ontology/OpCode.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -25,5 +25,6 @@ static const uint8_t TO_ALT_STACK{0x6B}; static const uint8_t FROM_ALT_STACK{0x6C}; static const uint8_t SWAP{0x7C}; static const uint8_t HAS_KEY{0xC8}; +static const uint8_t APP_CALL{0x67}; -} // namespace TW::Ontology \ No newline at end of file +} // namespace TW::Ontology diff --git a/src/Ontology/ParamsBuilder.cpp b/src/Ontology/ParamsBuilder.cpp index 367863b085e..c4db460cfb7 100644 --- a/src/Ontology/ParamsBuilder.cpp +++ b/src/Ontology/ParamsBuilder.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,34 +12,40 @@ #include #include -#include #include +#include -using namespace TW; -using namespace TW::Ontology; - -void ParamsBuilder::buildNeoVmParam(ParamsBuilder& builder, const boost::any& param) { - if (param.type() == typeid(std::string)) { - builder.push(boost::any_cast(param)); - } else if (param.type() == typeid(std::array)) { - builder.push(boost::any_cast>(param)); - } else if (param.type() == typeid(Data)) { - builder.push(boost::any_cast(param)); - } else if (param.type() == typeid(uint64_t)) { - builder.push(boost::any_cast(param)); - } else if (param.type() == typeid(std::vector)) { - auto paramVec = boost::any_cast>(param); - for (const auto& item : paramVec) { - ParamsBuilder::buildNeoVmParam(builder, item); +namespace TW::Ontology { + +void ParamsBuilder::buildNeoVmParam(ParamsBuilder& builder, const NeoVmParamValue& param) { + + if (auto* paramStr = std::get_if(¶m.params); paramStr) { + builder.push(*paramStr); + } else if (auto* paramFixedArray = std::get_if(¶m.params); paramFixedArray) { + builder.push(*paramFixedArray); + } else if (auto* paramData = std::get_if(¶m.params); paramData) { + builder.push(*paramData); + } else if (auto* paramInteger = std::get_if(¶m.params); paramInteger) { + builder.push(*paramInteger); + } else if (auto* paramArray = std::get_if(¶m.params); paramArray) { + for (auto&& item : *paramArray) { + std::visit([&builder](auto&& arg) { + NeoVmParamValue::ParamVariant value = arg; + ParamsBuilder::buildNeoVmParam(builder, {value}); + }, + item); } - builder.push(static_cast(paramVec.size())); + builder.push(static_cast(paramArray->size())); builder.pushBack(PACK); - } else if (param.type() == typeid(std::list)) { + } else if (auto* paramList = std::get_if(¶m.params); paramList) { builder.pushBack(PUSH0); builder.pushBack(NEW_STRUCT); builder.pushBack(TO_ALT_STACK); - for (auto const& p : boost::any_cast>(param)) { - ParamsBuilder::buildNeoVmParam(builder, p); + for (auto const& p : *paramList) { + std::visit([&builder](auto&& arg) { + NeoVmParamValue::ParamVariant value = arg; + ParamsBuilder::buildNeoVmParam(builder, {value}); + }, p); builder.pushBack(DUP_FROM_ALT_STACK); builder.pushBack(SWAP); builder.pushBack(HAS_KEY); @@ -221,7 +227,7 @@ Data ParamsBuilder::fromMultiPubkey(uint8_t m, const std::vector& pubKeys) } Data ParamsBuilder::buildNativeInvokeCode(const Data& contractAddress, uint8_t version, - const std::string& method, const boost::any& params) { + const std::string& method, const NeoVmParamValue& params) { ParamsBuilder builder; ParamsBuilder::buildNeoVmParam(builder, params); builder.push(Data(method.begin(), method.end())); @@ -231,4 +237,18 @@ Data ParamsBuilder::buildNativeInvokeCode(const Data& contractAddress, uint8_t v std::string nativeInvoke = "Ontology.Native.Invoke"; builder.push(Data(nativeInvoke.begin(), nativeInvoke.end())); return builder.getBytes(); -} \ No newline at end of file +} + +Data ParamsBuilder::buildOep4InvokeCode(const Address& contractAddress, const std::string& method, const NeoVmParamValue& params) { + ParamsBuilder builder; + ParamsBuilder::buildNeoVmParam(builder, params); + builder.push(method); + builder.pushBack(APP_CALL); + Address clone = contractAddress; + std::reverse(std::begin(clone._data), std::end(clone._data)); + builder.pushBack(clone._data); + + return builder.getBytes(); +} + +} // namespace TW::Ontology diff --git a/src/Ontology/ParamsBuilder.h b/src/Ontology/ParamsBuilder.h index cf2b3202003..0a4acfd55c5 100644 --- a/src/Ontology/ParamsBuilder.h +++ b/src/Ontology/ParamsBuilder.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,23 +7,35 @@ #pragma once #include "../BinaryCoding.h" -#include "../Data.h" - -#include +#include "Data.h" #include #include #include #include +#include +#include + +#include "Address.h" namespace TW::Ontology { +struct NeoVmParamValue; + +struct NeoVmParamValue { + using ParamFixedArray = std::array; + using ParamList = std::list>; + using ParamArray = std::vector>; + using ParamVariant = std::variant; + ParamVariant params; +}; + class ParamsBuilder { - private: +private: std::vector bytes; - public: +public: static const size_t MAX_PK_SIZE = 16; std::vector getBytes() { return bytes; } @@ -36,7 +48,7 @@ class ParamsBuilder { static Data fromMultiPubkey(uint8_t m, const std::vector& pubKeys); - static void buildNeoVmParam(ParamsBuilder& builder, const boost::any& param); + static void buildNeoVmParam(ParamsBuilder& builder, const NeoVmParamValue& param); static void buildNeoVmParam(ParamsBuilder& builder, const std::string& param); @@ -77,7 +89,9 @@ class ParamsBuilder { static std::vector buildNativeInvokeCode(const std::vector& contractAddress, uint8_t version, const std::string& method, - const boost::any& params); + const NeoVmParamValue& params); + + static std::vector buildOep4InvokeCode(const Address& contractAddress, const std::string& method, const NeoVmParamValue& params); }; -} // namespace TW::Ontology \ No newline at end of file +} // namespace TW::Ontology diff --git a/src/Ontology/SigData.cpp b/src/Ontology/SigData.cpp index e1f52e1b1e2..857504de0e2 100644 --- a/src/Ontology/SigData.cpp +++ b/src/Ontology/SigData.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,8 +9,7 @@ #include "ParamsBuilder.h" #include "SigData.h" -using namespace TW; -using namespace TW::Ontology; +namespace TW::Ontology { Data SigData::serialize() { auto sigInfo = ParamsBuilder::fromSigs(sigs); @@ -28,3 +27,5 @@ Data SigData::serialize() { builder.pushVar(verifyInfo); return builder.getBytes(); } + +} // namespace TW::Ontology diff --git a/src/Ontology/SigData.h b/src/Ontology/SigData.h index eaa054c4115..0ee703922c1 100644 --- a/src/Ontology/SigData.h +++ b/src/Ontology/SigData.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" namespace TW::Ontology { diff --git a/src/Ontology/Signer.cpp b/src/Ontology/Signer.cpp index 70fbc9e0661..876b97bf9c7 100644 --- a/src/Ontology/Signer.cpp +++ b/src/Ontology/Signer.cpp @@ -1,21 +1,21 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "Signer.h" + #include "HexCoding.h" #include "SigData.h" +#include "../Ontology/Oep4TxBuilder.h" + #include "../Ontology/OngTxBuilder.h" #include "../Ontology/OntTxBuilder.h" -#include "../Hash.h" - #include -using namespace TW; -using namespace TW::Ontology; +namespace TW::Ontology { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto contract = std::string(input.contract().begin(), input.contract().end()); @@ -27,20 +27,25 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { } else if (contract == "ONG") { auto encoded = OngTxBuilder::build(input); output.set_encoded(encoded.data(), encoded.size()); + } else { + // then assume it's oep4 address + auto encoded = Oep4TxBuilder::build(input); + output.set_encoded(encoded.data(), encoded.size()); } } catch (...) { } return output; } -Signer::Signer(TW::PrivateKey priKey) : privateKey(std::move(priKey)) { - auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1); +Signer::Signer(TW::PrivateKey priKey) + : privKey(std::move(priKey)) { + auto pubKey = privKey.getPublicKey(TWPublicKeyTypeNIST256p1); publicKey = pubKey.bytes; address = Address(pubKey).string(); } PrivateKey Signer::getPrivateKey() const { - return privateKey; + return privKey; } PublicKey Signer::getPublicKey() const { @@ -68,3 +73,5 @@ void Signer::addSign(Transaction& tx) const { signature.pop_back(); tx.sigVec.emplace_back(publicKey, signature, 1); } + +} // namespace TW::Ontology diff --git a/src/Ontology/Signer.h b/src/Ontology/Signer.h index d9e8166ae9d..4f2dc8459ca 100644 --- a/src/Ontology/Signer.h +++ b/src/Ontology/Signer.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -15,19 +15,19 @@ #include #include - namespace TW::Ontology { class Signer { - public: +public: /// Signs a Proto::SigningInput transaction static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; - private: + +private: Data publicKey; - TW::PrivateKey privateKey; + TW::PrivateKey privKey; std::string address; - public: +public: explicit Signer(TW::PrivateKey priKey); PrivateKey getPrivateKey() const; @@ -41,8 +41,3 @@ class Signer { void addSign(Transaction& tx) const; }; } // namespace TW::Ontology - -/// Wrapper for C interface. -struct TWOntologySigner { - TW::Ontology::Signer impl; -}; diff --git a/src/Ontology/Transaction.cpp b/src/Ontology/Transaction.cpp index a3306871b75..7bf47d716f4 100644 --- a/src/Ontology/Transaction.cpp +++ b/src/Ontology/Transaction.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,13 +8,9 @@ #include "Address.h" #include "ParamsBuilder.h" -#include "../Hash.h" -#include "../HexCoding.h" - #include -using namespace TW; -using namespace TW::Ontology; +namespace TW::Ontology { const std::string Transaction::ZERO_PAYER = "AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM"; @@ -25,7 +21,7 @@ std::vector Transaction::serializeUnsigned() { builder.pushBack(nonce); builder.pushBack(gasPrice); builder.pushBack(gasLimit); - builder.pushBack(Address(payer).data); + builder.pushBack(Address(payer)._data); if (!payload.empty()) { builder.pushVar(payload); } @@ -58,3 +54,5 @@ std::vector Transaction::serialize(const PublicKey& pk) { builder.pushBack((uint8_t)0xAC); return builder.getBytes(); } + +} // namespace TW::Ontology diff --git a/src/Ontology/Transaction.h b/src/Ontology/Transaction.h index e4f9796b4f6..0f1388b3eb7 100644 --- a/src/Ontology/Transaction.h +++ b/src/Ontology/Transaction.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the diff --git a/src/Polkadot/Address.h b/src/Polkadot/Address.h index 183018ea3db..10f3828c2a0 100644 --- a/src/Polkadot/Address.h +++ b/src/Polkadot/Address.h @@ -6,9 +6,9 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" -#include "../SS58Address.h" +#include "SS58Address.h" #include #include diff --git a/src/Polkadot/Entry.cpp b/src/Polkadot/Entry.cpp index 4db8a184a19..f97ae843ca9 100644 --- a/src/Polkadot/Entry.cpp +++ b/src/Polkadot/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,19 +9,25 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Polkadot; -using namespace std; +namespace TW::Polkadot { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Address(address); + return {addr.bytes.begin() + 1, addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Polkadot diff --git a/src/Polkadot/Entry.h b/src/Polkadot/Entry.h index e0932bba62a..d36ac8845af 100644 --- a/src/Polkadot/Entry.h +++ b/src/Polkadot/Entry.h @@ -12,12 +12,12 @@ namespace TW::Polkadot { /// Entry point for implementation of Polkadot coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypePolkadot}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Polkadot diff --git a/src/Polkadot/Extrinsic.cpp b/src/Polkadot/Extrinsic.cpp index e77eff45821..8cab2fb5cef 100644 --- a/src/Polkadot/Extrinsic.cpp +++ b/src/Polkadot/Extrinsic.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,8 +8,7 @@ #include #include -using namespace TW; -using namespace TW::Polkadot; +namespace TW::Polkadot { static constexpr uint8_t signedBit = 0x80; static constexpr uint8_t sigTypeEd25519 = 0x00; @@ -18,7 +17,7 @@ static constexpr uint32_t multiAddrSpecVersion = 28; static constexpr uint32_t multiAddrSpecVersionKsm = 2028; static const std::string balanceTransfer = "Balances.transfer"; -static const std::string utilityBatch = "Utility.batch"; +static const std::string utilityBatch = "Utility.batch_all"; static const std::string stakingBond = "Staking.bond"; static const std::string stakingBondExtra = "Staking.bond_extra"; static const std::string stakingUnbond = "Staking.unbond"; @@ -28,41 +27,40 @@ static const std::string stakingChill = "Staking.chill"; // Readable decoded call index can be found from https://polkascan.io static std::map polkadotCallIndices = { - {balanceTransfer, Data{0x05, 0x00}}, - {stakingBond, Data{0x07, 0x00}}, - {stakingBondExtra, Data{0x07, 0x01}}, - {stakingUnbond, Data{0x07, 0x02}}, + {balanceTransfer, Data{0x05, 0x00}}, + {stakingBond, Data{0x07, 0x00}}, + {stakingBondExtra, Data{0x07, 0x01}}, + {stakingUnbond, Data{0x07, 0x02}}, {stakingWithdrawUnbond, Data{0x07, 0x03}}, - {stakingNominate, Data{0x07, 0x05}}, - {stakingChill, Data{0x07, 0x06}}, - {utilityBatch, Data{0x1a, 0x02}}, + {stakingNominate, Data{0x07, 0x05}}, + {stakingChill, Data{0x07, 0x06}}, + {utilityBatch, Data{0x1a, 0x02}}, }; static std::map kusamaCallIndices = { - {balanceTransfer, Data{0x04, 0x00}}, - {stakingBond, Data{0x06, 0x00}}, - {stakingBondExtra, Data{0x06, 0x01}}, - {stakingUnbond, Data{0x06, 0x02}}, + {balanceTransfer, Data{0x04, 0x00}}, + {stakingBond, Data{0x06, 0x00}}, + {stakingBondExtra, Data{0x06, 0x01}}, + {stakingUnbond, Data{0x06, 0x02}}, {stakingWithdrawUnbond, Data{0x06, 0x03}}, - {stakingNominate, Data{0x06, 0x05}}, - {stakingChill, Data{0x06, 0x06}}, - {utilityBatch, Data{0x18, 0x02}}, + {stakingNominate, Data{0x06, 0x05}}, + {stakingChill, Data{0x06, 0x06}}, + {utilityBatch, Data{0x18, 0x02}}, }; static Data getCallIndex(TWSS58AddressType network, const std::string& key) { - switch (network) { - case TWSS58AddressTypePolkadot: + if (network == TWSS58AddressTypePolkadot) { return polkadotCallIndices[key]; - case TWSS58AddressTypeKusama: - return kusamaCallIndices[key]; } + // network == TWSS58AddressTypeKusama + return kusamaCallIndices[key]; } bool Extrinsic::encodeRawAccount(TWSS58AddressType network, uint32_t specVersion) { if ((network == TWSS58AddressTypePolkadot && specVersion >= multiAddrSpecVersion) || (network == TWSS58AddressTypeKusama && specVersion >= multiAddrSpecVersionKsm)) { - return false; - } + return false; + } return true; } @@ -113,103 +111,115 @@ Data Extrinsic::encodeBatchCall(const std::vector& calls, TWSS58AddressTyp Data Extrinsic::encodeStakingCall(const Proto::Staking& staking, TWSS58AddressType network, uint32_t specVersion) { Data data; switch (staking.message_oneof_case()) { - case Proto::Staking::kBond: - { - auto address = SS58Address(staking.bond().controller(), byte(network)); - auto value = load(staking.bond().value()); - auto reward = byte(staking.bond().reward_destination()); - // call index - append(data, getCallIndex(network, stakingBond)); - // controller - append(data, encodeAccountId(address.keyBytes(), encodeRawAccount(network, specVersion))); - // value - append(data, encodeCompact(value)); - // reward destination - append(data, reward); - } - break; - - case Proto::Staking::kBondAndNominate: - { - // encode call1 - Data call1; - { - auto staking1 = Proto::Staking(); - auto* bond = staking1.mutable_bond(); - bond->set_controller(staking.bond_and_nominate().controller()); - bond->set_value(staking.bond_and_nominate().value()); - bond->set_reward_destination(staking.bond_and_nominate().reward_destination()); - // recursive call - call1 = encodeStakingCall(staking1, network, specVersion); - } - - // encode call2 - Data call2; - { - auto staking2 = Proto::Staking(); - auto* nominate = staking2.mutable_nominate(); - for (auto i = 0; i < staking.bond_and_nominate().nominators_size(); ++i) { - nominate->add_nominators(staking.bond_and_nominate().nominators(i)); - } - // recursive call - call2 = encodeStakingCall(staking2, network, specVersion); - } - - auto calls = std::vector{call1, call2}; - data = encodeBatchCall(calls, network); - } - break; - - case Proto::Staking::kBondExtra: - { - auto value = load(staking.bond_extra().value()); - // call index - append(data, getCallIndex(network, stakingBondExtra)); - // value - append(data, encodeCompact(value)); - } - break; - - case Proto::Staking::kUnbond: - { - auto value = load(staking.unbond().value()); - // call index - append(data, getCallIndex(network, stakingUnbond)); - // value - append(data, encodeCompact(value)); - } - break; - - case Proto::Staking::kWithdrawUnbonded: - { - auto spans = staking.withdraw_unbonded().slashing_spans(); - // call index - append(data, getCallIndex(network, stakingWithdrawUnbond)); - // num_slashing_spans - encode32LE(spans, data); - } - break; - - case Proto::Staking::kNominate: - { - std::vector accountIds; - for (auto& n : staking.nominate().nominators()) { - accountIds.emplace_back(SS58Address(n, network)); - } - // call index - append(data, getCallIndex(network, stakingNominate)); - // nominators - append(data, encodeAccountIds(accountIds, encodeRawAccount(network, specVersion))); + case Proto::Staking::kBond: { + auto address = SS58Address(staking.bond().controller(), byte(network)); + auto value = load(staking.bond().value()); + auto reward = byte(staking.bond().reward_destination()); + // call index + append(data, getCallIndex(network, stakingBond)); + // controller + append(data, encodeAccountId(address.keyBytes(), encodeRawAccount(network, specVersion))); + // value + append(data, encodeCompact(value)); + // reward destination + append(data, reward); + } break; + + case Proto::Staking::kBondAndNominate: { + // encode call1 + Data call1; + { + auto staking1 = Proto::Staking(); + auto* bond = staking1.mutable_bond(); + bond->set_controller(staking.bond_and_nominate().controller()); + bond->set_value(staking.bond_and_nominate().value()); + bond->set_reward_destination(staking.bond_and_nominate().reward_destination()); + // recursive call + call1 = encodeStakingCall(staking1, network, specVersion); + } + + // encode call2 + Data call2; + { + auto staking2 = Proto::Staking(); + auto* nominate = staking2.mutable_nominate(); + for (auto i = 0; i < staking.bond_and_nominate().nominators_size(); ++i) { + nominate->add_nominators(staking.bond_and_nominate().nominators(i)); } - break; + // recursive call + call2 = encodeStakingCall(staking2, network, specVersion); + } + + auto calls = std::vector{call1, call2}; + data = encodeBatchCall(calls, network); + } break; - case Proto::Staking::kChill: - // call index - append(data, getCallIndex(network, stakingChill)); - break; + case Proto::Staking::kBondExtra: { + auto value = load(staking.bond_extra().value()); + // call index + append(data, getCallIndex(network, stakingBondExtra)); + // value + append(data, encodeCompact(value)); + } break; - default: - break; + case Proto::Staking::kUnbond: { + auto value = load(staking.unbond().value()); + // call index + append(data, getCallIndex(network, stakingUnbond)); + // value + append(data, encodeCompact(value)); + } break; + + case Proto::Staking::kWithdrawUnbonded: { + auto spans = staking.withdraw_unbonded().slashing_spans(); + // call index + append(data, getCallIndex(network, stakingWithdrawUnbond)); + // num_slashing_spans + encode32LE(spans, data); + } break; + + case Proto::Staking::kNominate: { + std::vector accountIds; + for (auto& n : staking.nominate().nominators()) { + accountIds.emplace_back(SS58Address(n, network)); + } + // call index + append(data, getCallIndex(network, stakingNominate)); + // nominators + append(data, encodeAccountIds(accountIds, encodeRawAccount(network, specVersion))); + } break; + + case Proto::Staking::kChill: + // call index + append(data, getCallIndex(network, stakingChill)); + break; + + case Proto::Staking::kChillAndUnbond: { + // encode call1 + Data call1; + { + auto staking1 = Proto::Staking(); + staking1.mutable_chill(); + // recursive call + call1 = encodeStakingCall(staking1, network, specVersion); + } + + // encode call2 + Data call2; + { + auto staking2 = Proto::Staking(); + auto* unbond = staking2.mutable_unbond(); + unbond->set_value(staking.chill_and_unbond().value()); + // recursive call + call2 = encodeStakingCall(staking2, network, specVersion); + } + + auto calls = std::vector{call1, call2}; + data = encodeBatchCall(calls, network); + } break; + + default: + break; } return data; } @@ -249,3 +259,5 @@ Data Extrinsic::encodeSignature(const PublicKey& signer, const Data& signature) encodeLengthPrefix(data); return data; } + +} // namespace TW::Polkadot diff --git a/src/Polkadot/Extrinsic.h b/src/Polkadot/Extrinsic.h index dc20eb75908..ccac8260998 100644 --- a/src/Polkadot/Extrinsic.h +++ b/src/Polkadot/Extrinsic.h @@ -7,7 +7,7 @@ #pragma once #include "Address.h" -#include "../Data.h" +#include "Data.h" #include "../proto/Polkadot.pb.h" #include "../uint256.h" #include "ScaleCodec.h" diff --git a/src/Polkadot/SS58Address.cpp b/src/Polkadot/SS58Address.cpp new file mode 100644 index 00000000000..34a25f7c263 --- /dev/null +++ b/src/Polkadot/SS58Address.cpp @@ -0,0 +1,117 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "SS58Address.h" + +using namespace TW; +using namespace std; + +bool SS58Address::isValid(const std::string& string, uint32_t network) { + const auto decoded = Base58::bitcoin.decode(string); + byte decodedNetworkSize = 0; + uint32_t decodedNetwork = 0; + if (!decodeNetwork(decoded, decodedNetworkSize, decodedNetwork)) { + return false; + } + // check size + if ((decodedNetworkSize + PublicKey::ed25519Size + checksumSize) != decoded.size()) { + return false; + } + // compare network + if (decodedNetwork != network) { + return false; + } + auto checksum = computeChecksum(Data(decoded.begin(), decoded.end() - checksumSize)); + // compare checksum + if (!std::equal(decoded.end() - checksumSize, decoded.end(), checksum.begin())) { + return false; + } + return true; +} + +template +Data SS58Address::computeChecksum(const T& data) { + auto prefix = Data(SS58Prefix.begin(), SS58Prefix.end()); + append(prefix, Data(data.begin(), data.end())); + auto hash = Hash::blake2b(prefix, 64); + auto checksum = Data(checksumSize); + std::copy(hash.begin(), hash.begin() + checksumSize, checksum.data()); + return checksum; +} + +/// Initializes an address with a string representation. +SS58Address::SS58Address(const std::string& string, uint32_t network) { + if (!isValid(string, network)) { + throw std::invalid_argument("Invalid address string"); + } + const auto decoded = Base58::bitcoin.decode(string); + bytes.resize(decoded.size() - checksumSize); + std::copy(decoded.begin(), decoded.end() - checksumSize, bytes.begin()); +} + +/// Initializes an address with a public key and network. +SS58Address::SS58Address(const PublicKey& publicKey, uint32_t network) { + if (publicKey.type != TWPublicKeyTypeED25519) { + throw std::invalid_argument("SS58Address expects an ed25519 public key."); + } + if (!encodeNetwork(network, bytes)) { + throw std::invalid_argument(std::string("network out of range ") + std::to_string(network)); + } + TW::append(bytes, publicKey.bytes); +} + +/// Returns a string representation of the address. +std::string SS58Address::string() const { + auto result = Data(bytes.begin(), bytes.end()); + auto checksum = computeChecksum(bytes); + append(result, checksum); + return Base58::bitcoin.encode(result); +} + +/// Returns public key bytes +Data SS58Address::keyBytes() const { + byte networkSize; + uint32_t networkTemp; + decodeNetwork(bytes, networkSize, networkTemp); + return Data(bytes.begin() + networkSize, bytes.end()); +} + +// Return true and the network size (1 or 2) and network if input is valid +bool SS58Address::decodeNetwork(const Data& data, byte& networkSize, uint32_t& network) { + networkSize = 0; + network = 0; + if (data.size() >= 1 && data[0] < networkSimpleLimit) { // 0 -- 63 + networkSize = 1; + network = (uint32_t)(data[0]); + return true; + } + // src https://github.com/paritytech/substrate/blob/master/primitives/core/src/crypto.rs + if (data.size() >= 2 && data[0] >= networkSimpleLimit && data[0] < networkFullLimit) { // 64 -- 127 + networkSize = 2; + byte lower = (byte)((data[0] & 0b00111111) << 2) | (byte)((data[1] & 0b11000000) >> 6); + byte upper = data[1] & 0b00111111; + network = ((uint32_t)upper << 8) + lower; + return (network >= networkSimpleLimit); + } + return false; +} + +bool SS58Address::encodeNetwork(uint32_t network, Data& data) { + if (network < networkSimpleLimit) { // 0 -- 63 + // Simple account/address/network + data = {(byte)network}; + return true; + } + if (network < 0x4000) { // 64 -- 16383 + // Full address/address/network identifier. + byte first = networkSimpleLimit + (byte)((network & 0b0000000011111100) >> 2); + byte second = (byte)((network & 0b0011111100000000) >> 8) | (byte)((byte)(network & 0b0000000000000011) << 6); + data = {first, second}; + return true; + } + // not supported + return false; +} diff --git a/src/Polkadot/SS58Address.h b/src/Polkadot/SS58Address.h new file mode 100644 index 00000000000..20d4e292f70 --- /dev/null +++ b/src/Polkadot/SS58Address.h @@ -0,0 +1,64 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Base58.h" +#include "Data.h" +#include "PublicKey.h" + +#include + +inline const std::string SS58Prefix{"SS58PRE"}; + +namespace TW { + +/// Base-58-encodeed Substrate address. +class SS58Address { + public: + static const size_t checksumSize = 2; + + // networks 0 -- 63 are encoded in one byte (00aaaaaa) + static const byte networkSimpleLimit = 0x40; + // networks 64 -- 16383 are encoded in 2 bytes: network 00cccccc_aaaaaabb is encoded as 01aaaaaa, bbcccccc (first byte between 64 and 127) + // see: https://github.com/paritytech/substrate/blob/master/primitives/core/src/crypto.rs + // https://docs.substrate.io/v3/advanced/ss58/#address-type + static const byte networkFullLimit = 0x80; + + /// Address data consisting of one or more network byte(s) followed by the public key. + Data bytes; + + /// Determines whether a string makes a valid address + static bool isValid(const std::string& string, uint32_t network); + + template + static Data computeChecksum(const T& data); + + SS58Address() = default; + + /// Initializes an address with a string representation. + SS58Address(const std::string& string, uint32_t network); + + /// Initializes an address with a public key and network. + SS58Address(const PublicKey& publicKey, uint32_t network); + + /// Returns a string representation of the address. + std::string string() const; + + /// Returns public key bytes + Data keyBytes() const; + + // Return true and the network size (1 or 2) and network if input is valid + static bool decodeNetwork(const Data& data, byte& networkSize, uint32_t& network); + + static bool encodeNetwork(uint32_t network, Data& data); +}; + +inline bool operator==(const SS58Address& lhs, const SS58Address& rhs) { + return lhs.bytes == rhs.bytes; +} + +} // namespace TW diff --git a/src/Polkadot/ScaleCodec.h b/src/Polkadot/ScaleCodec.h index 6a244383440..e0d1c3ca20d 100644 --- a/src/Polkadot/ScaleCodec.h +++ b/src/Polkadot/ScaleCodec.h @@ -7,9 +7,9 @@ #pragma once #include "../BinaryCoding.h" -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" -#include "../SS58Address.h" +#include "SS58Address.h" #include #include #include diff --git a/src/Polkadot/Signer.cpp b/src/Polkadot/Signer.cpp index c7f708adcc7..b5f083a522b 100644 --- a/src/Polkadot/Signer.cpp +++ b/src/Polkadot/Signer.cpp @@ -9,8 +9,7 @@ #include "../Hash.h" #include "../PrivateKey.h" -using namespace TW; -using namespace TW::Polkadot; +namespace TW::Polkadot { static constexpr size_t hashTreshold = 256; @@ -30,3 +29,5 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { protoOutput.set_encoded(encoded.data(), encoded.size()); return protoOutput; } + +} // namespace TW::Polkadot diff --git a/src/Polkadot/Signer.h b/src/Polkadot/Signer.h index 4b790bd8b42..d893d64a010 100644 --- a/src/Polkadot/Signer.h +++ b/src/Polkadot/Signer.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/Polkadot.pb.h" diff --git a/src/PrivateKey.cpp b/src/PrivateKey.cpp index 4c98f968263..898953100ed 100644 --- a/src/PrivateKey.cpp +++ b/src/PrivateKey.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -15,22 +15,22 @@ #include #include #include -#include #include #include +#include #include using namespace TW; bool PrivateKey::isValid(const Data& data) { - // Check length. Extended key needs 3*32 bytes. - if (data.size() != size && data.size() != extendedSize) { + // Check length + if (data.size() != _size && data.size() != cardanoKeySize) { return false; } // Check for zero address - for (size_t i = 0; i < size; ++i) { + for (size_t i = 0; i < _size; ++i) { if (data[i] != 0) { return true; } @@ -39,17 +39,15 @@ bool PrivateKey::isValid(const Data& data) { return false; } -bool PrivateKey::isValid(const Data& data, TWCurve curve) -{ +bool PrivateKey::isValid(const Data& data, TWCurve curve) { // check size bool valid = isValid(data); if (!valid) { return false; } - const ecdsa_curve *ec_curve = nullptr; - switch (curve) - { + const ecdsa_curve* ec_curve = nullptr; + switch (curve) { case TWCurveSECP256k1: ec_curve = &secp256k1; break; @@ -58,7 +56,7 @@ bool PrivateKey::isValid(const Data& data, TWCurve curve) break; case TWCurveED25519: case TWCurveED25519Blake2bNano: - case TWCurveED25519Extended: + case TWCurveED25519ExtendedCardano: case TWCurveCurve25519: case TWCurveNone: default: @@ -77,29 +75,35 @@ bool PrivateKey::isValid(const Data& data, TWCurve curve) return true; } +TWPrivateKeyType PrivateKey::getType(TWCurve curve) noexcept { + switch (curve) { + case TWCurve::TWCurveED25519ExtendedCardano: + return TWPrivateKeyTypeCardano; + default: + return TWPrivateKeyTypeDefault; + } +} + PrivateKey::PrivateKey(const Data& data) { if (!isValid(data)) { throw std::invalid_argument("Invalid private key data"); } - if (data.size() == extendedSize) { - // special extended case - *this = PrivateKey( - TW::data(data.data(), 32), - TW::data(data.data() + 32, 32), - TW::data(data.data() + 64, 32)); - } else { - // default case - bytes = data; - } + bytes = data; } -PrivateKey::PrivateKey(const Data& data, const Data& ext, const Data& chainCode) { - if (!isValid(data) || !isValid(ext) || !isValid(chainCode)) { +PrivateKey::PrivateKey( + const Data& key1, const Data& extension1, const Data& chainCode1, + const Data& key2, const Data& extension2, const Data& chainCode2) { + if (key1.size() != _size || extension1.size() != _size || chainCode1.size() != _size || + key2.size() != _size || extension2.size() != _size || chainCode2.size() != _size) { throw std::invalid_argument("Invalid private key or extended key data"); } - bytes = data; - extensionBytes = ext; - chainCodeBytes = chainCode; + bytes = key1; + append(bytes, extension1); + append(bytes, chainCode1); + append(bytes, key2); + append(bytes, extension2); + append(bytes, chainCode2); } PublicKey PrivateKey::getPublicKey(TWPublicKeyType type) const { @@ -107,38 +111,47 @@ PublicKey PrivateKey::getPublicKey(TWPublicKeyType type) const { switch (type) { case TWPublicKeyTypeSECP256k1: result.resize(PublicKey::secp256k1Size); - ecdsa_get_public_key33(&secp256k1, bytes.data(), result.data()); + ecdsa_get_public_key33(&secp256k1, key().data(), result.data()); break; case TWPublicKeyTypeSECP256k1Extended: result.resize(PublicKey::secp256k1ExtendedSize); - ecdsa_get_public_key65(&secp256k1, bytes.data(), result.data()); + ecdsa_get_public_key65(&secp256k1, key().data(), result.data()); break; case TWPublicKeyTypeNIST256p1: result.resize(PublicKey::secp256k1Size); - ecdsa_get_public_key33(&nist256p1, bytes.data(), result.data()); + ecdsa_get_public_key33(&nist256p1, key().data(), result.data()); break; case TWPublicKeyTypeNIST256p1Extended: result.resize(PublicKey::secp256k1ExtendedSize); - ecdsa_get_public_key65(&nist256p1, bytes.data(), result.data()); + ecdsa_get_public_key65(&nist256p1, key().data(), result.data()); break; case TWPublicKeyTypeED25519: result.resize(PublicKey::ed25519Size); - ed25519_publickey(bytes.data(), result.data()); + ed25519_publickey(key().data(), result.data()); break; case TWPublicKeyTypeED25519Blake2b: result.resize(PublicKey::ed25519Size); - ed25519_publickey_blake2b(bytes.data(), result.data()); + ed25519_publickey_blake2b(key().data(), result.data()); break; - case TWPublicKeyTypeED25519Extended: - // must be extended key - if (bytes.size() + extensionBytes.size() + chainCodeBytes.size() != extendedSize) { + case TWPublicKeyTypeED25519Cardano: { + // must be double extended key + if (bytes.size() != cardanoKeySize) { throw std::invalid_argument("Invalid extended key"); } - result.resize(PublicKey::ed25519ExtendedSize); - ed25519_publickey_ext(bytes.data(), extensionBytes.data(), result.data()); - // append chainCode to the end of the public key - std::copy(chainCodeBytes.begin(), chainCodeBytes.end(), result.begin() + 32); - break; + Data pubKey(PublicKey::ed25519Size); + + // first key + ed25519_publickey_ext(key().data(), pubKey.data()); + append(result, pubKey); + // copy chainCode + append(result, chainCode()); + + // second key + ed25519_publickey_ext(secondKey().data(), pubKey.data()); + append(result, pubKey); + append(result, secondChainCode()); + } break; + case TWPublicKeyTypeCURVE25519: result.resize(PublicKey::ed25519Size); PublicKey ed25519PublicKey = getPublicKey(TWPublicKeyTypeED25519); @@ -154,7 +167,7 @@ Data PrivateKey::getSharedKey(const PublicKey& pubKey, TWCurve curve) const { } Data result(PublicKey::secp256k1ExtendedSize); - bool success = ecdh_multiply(&secp256k1, bytes.data(), + bool success = ecdh_multiply(&secp256k1, key().data(), pubKey.bytes.data(), result.data()) == 0; if (success) { @@ -166,7 +179,7 @@ Data PrivateKey::getSharedKey(const PublicKey& pubKey, TWCurve curve) const { return {}; } -int ecdsa_sign_digest_checked(const ecdsa_curve *curve, const uint8_t *priv_key, const uint8_t *digest, size_t digest_size, uint8_t *sig, uint8_t *pby, int (*is_canonical)(uint8_t by, uint8_t sig[64])) { +int ecdsa_sign_digest_checked(const ecdsa_curve* curve, const uint8_t* priv_key, const uint8_t* digest, size_t digest_size, uint8_t* sig, uint8_t* pby, int (*is_canonical)(uint8_t by, uint8_t sig[64])) { if (digest_size < 32) { return -1; } @@ -180,33 +193,27 @@ Data PrivateKey::sign(const Data& digest, TWCurve curve) const { switch (curve) { case TWCurveSECP256k1: { result.resize(65); - success = ecdsa_sign_digest_checked(&secp256k1, bytes.data(), digest.data(), digest.size(), result.data(), - result.data() + 64, nullptr) == 0; + success = ecdsa_sign_digest_checked(&secp256k1, key().data(), digest.data(), digest.size(), result.data(), result.data() + 64, nullptr) == 0; } break; case TWCurveED25519: { result.resize(64); - const auto publicKey = getPublicKey(TWPublicKeyTypeED25519); - ed25519_sign(digest.data(), digest.size(), bytes.data(), publicKey.bytes.data(), result.data()); + ed25519_sign(digest.data(), digest.size(), key().data(), result.data()); success = true; } break; case TWCurveED25519Blake2bNano: { result.resize(64); - const auto publicKey = getPublicKey(TWPublicKeyTypeED25519Blake2b); - ed25519_sign_blake2b(digest.data(), digest.size(), bytes.data(), - publicKey.bytes.data(), result.data()); + ed25519_sign_blake2b(digest.data(), digest.size(), key().data(), result.data()); success = true; } break; - case TWCurveED25519Extended: { + case TWCurveED25519ExtendedCardano: { result.resize(64); - const auto publicKey = getPublicKey(TWPublicKeyTypeED25519Extended); - ed25519_sign_ext(digest.data(), digest.size(), bytes.data(), extensionBytes.data(), publicKey.bytes.data(), result.data()); + ed25519_sign_ext(digest.data(), digest.size(), key().data(), extension().data(), result.data()); success = true; } break; case TWCurveCurve25519: { result.resize(64); const auto publicKey = getPublicKey(TWPublicKeyTypeED25519); - ed25519_sign(digest.data(), digest.size(), bytes.data(), publicKey.bytes.data(), - result.data()); + ed25519_sign(digest.data(), digest.size(), key().data(), result.data()); const auto sign_bit = publicKey.bytes[31] & 0x80; result[63] = result[63] & 127; result[63] |= sign_bit; @@ -214,11 +221,10 @@ Data PrivateKey::sign(const Data& digest, TWCurve curve) const { } break; case TWCurveNIST256p1: { result.resize(65); - success = ecdsa_sign_digest_checked(&nist256p1, bytes.data(), digest.data(), digest.size(), result.data(), - result.data() + 64, nullptr) == 0; + success = ecdsa_sign_digest_checked(&nist256p1, key().data(), digest.data(), digest.size(), result.data(), result.data() + 64, nullptr) == 0; } break; case TWCurveNone: - default: + default: break; } @@ -228,24 +234,22 @@ Data PrivateKey::sign(const Data& digest, TWCurve curve) const { return result; } -Data PrivateKey::sign(const Data& digest, TWCurve curve, int(*canonicalChecker)(uint8_t by, uint8_t sig[64])) const { +Data PrivateKey::sign(const Data& digest, TWCurve curve, int (*canonicalChecker)(uint8_t by, uint8_t sig[64])) const { Data result; bool success = false; switch (curve) { case TWCurveSECP256k1: { result.resize(65); - success = ecdsa_sign_digest_checked(&secp256k1, bytes.data(), digest.data(), digest.size(), result.data() + 1, - result.data(), canonicalChecker) == 0; + success = ecdsa_sign_digest_checked(&secp256k1, key().data(), digest.data(), digest.size(), result.data() + 1, result.data(), canonicalChecker) == 0; } break; - case TWCurveED25519: // not supported - case TWCurveED25519Blake2bNano: // not supported - case TWCurveED25519Extended: // not supported - case TWCurveCurve25519: // not supported + case TWCurveED25519: // not supported + case TWCurveED25519Blake2bNano: // not supported + case TWCurveED25519ExtendedCardano: // not supported + case TWCurveCurve25519: // not supported break; case TWCurveNIST256p1: { result.resize(65); - success = ecdsa_sign_digest_checked(&nist256p1, bytes.data(), digest.data(), digest.size(), result.data() + 1, - result.data(), canonicalChecker) == 0; + success = ecdsa_sign_digest_checked(&nist256p1, key().data(), digest.data(), digest.size(), result.data() + 1, result.data(), canonicalChecker) == 0; } break; case TWCurveNone: default: @@ -261,10 +265,10 @@ Data PrivateKey::sign(const Data& digest, TWCurve curve, int(*canonicalChecker)( return result; } -Data PrivateKey::signAsDER(const Data& digest, TWCurve curve) const { +Data PrivateKey::signAsDER(const Data& digest) const { Data sig(64); bool success = - ecdsa_sign_digest(&secp256k1, bytes.data(), digest.data(), sig.data(), nullptr, nullptr) == 0; + ecdsa_sign_digest(&secp256k1, key().data(), digest.data(), sig.data(), nullptr, nullptr) == 0; if (!success) { return {}; } @@ -277,24 +281,9 @@ Data PrivateKey::signAsDER(const Data& digest, TWCurve curve) const { return result; } -Data PrivateKey::signSchnorr(const Data& message, TWCurve curve) const { - bool success = false; +Data PrivateKey::signZilliqa(const Data& message) const { Data sig(64); - switch (curve) { - case TWCurveSECP256k1: { - success = zil_schnorr_sign(&secp256k1, bytes.data(), message.data(), static_cast(message.size()), sig.data()) == 0; - } break; - - case TWCurveNIST256p1: - case TWCurveED25519: - case TWCurveED25519Blake2bNano: - case TWCurveED25519Extended: - case TWCurveCurve25519: - case TWCurveNone: - default: - // not support - break; - } + bool success = zil_schnorr_sign(&secp256k1, key().data(), message.data(), static_cast(message.size()), sig.data()) == 0; if (!success) { return {}; @@ -304,6 +293,4 @@ Data PrivateKey::signSchnorr(const Data& message, TWCurve curve) const { void PrivateKey::cleanup() { std::fill(bytes.begin(), bytes.end(), 0); - std::fill(extensionBytes.begin(), extensionBytes.end(), 0); - std::fill(chainCodeBytes.begin(), chainCodeBytes.end(), 0); } diff --git a/src/PrivateKey.h b/src/PrivateKey.h index 08d01d76d9d..32b2877a0ff 100644 --- a/src/PrivateKey.h +++ b/src/PrivateKey.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,6 +9,7 @@ #include "Data.h" #include "PublicKey.h" +#include #include namespace TW { @@ -16,16 +17,22 @@ namespace TW { class PrivateKey { public: /// The number of bytes in a private key. - static const size_t size = 32; - /// The number of bytes in an extended private key. - static const size_t extendedSize = 3 * 32; + static const size_t _size = 32; + /// The number of bytes in a Cardano key (two extended ed25519 keys + chain code) + static const size_t cardanoKeySize = 2 * 3 * 32; - /// The private key bytes. + /// The private key bytes: + /// - common case: 'size' bytes + /// - double extended case: 'cardanoKeySize' bytes, key+extension+chainCode+second+secondExtension+secondChainCode Data bytes; - /// Optional extended part of the key (additional 32 bytes) - Data extensionBytes; - /// Optional chain code (additional 32 bytes) - Data chainCodeBytes; + + /// Optional members for extended keys and second extended keys + Data key() const { return subData(bytes, 0, 32); } + Data extension() const { return subData(bytes, 32, 32); } + Data chainCode() const { return subData(bytes, 2*32, 32); } + Data secondKey() const { return subData(bytes, 3*32, 32); } + Data secondExtension() const { return subData(bytes, 4*32, 32); } + Data secondChainCode() const { return subData(bytes, 5*32, 32); } /// Determines if a collection of bytes makes a valid private key. static bool isValid(const Data& data); @@ -33,14 +40,19 @@ class PrivateKey { /// Determines if a collection of bytes and curve make a valid private key. static bool isValid(const Data& data, TWCurve curve); - /// Initializes a private key with an array of bytes. Size must be exact (normally 32, or 96 for extended) + // obtain private key type used by the curve/coin + static TWPrivateKeyType getType(TWCurve curve) noexcept; + + /// Initializes a private key with an array of bytes. Size must be exact (normally 32, or 192 for extended) explicit PrivateKey(const Data& data); - /// Initializes a private key from a string of bytes (convenience method). + /// Initializes a private key from a string of bytes. explicit PrivateKey(const std::string& data) : PrivateKey(TW::data(data)) {} - /// Initializes an extended private key with key, extended key, and chain code. - explicit PrivateKey(const Data& data, const Data& ext, const Data& chainCode); + /// Initializes a Cardano style key + explicit PrivateKey( + const Data& bytes1, const Data& extension1, const Data& chainCode1, + const Data& bytes2, const Data& extension2, const Data& chainCode2); PrivateKey(const PrivateKey& other) = default; PrivateKey& operator=(const PrivateKey& other) = default; @@ -66,10 +78,10 @@ class PrivateKey { /// Signs a digest using the given ECDSA curve. The result is encoded with /// DER. - Data signAsDER(const Data& digest, TWCurve curve) const; + Data signAsDER(const Data& digest) const; - /// Signs a digest using given ECDSA curve, returns schnorr signature - Data signSchnorr(const Data& message, TWCurve curve) const; + /// Signs a digest using given ECDSA curve, returns Zilliqa schnorr signature + Data signZilliqa(const Data& message) const; /// Cleanup contents (fill with 0s), called before destruction void cleanup(); diff --git a/src/PublicKey.cpp b/src/PublicKey.cpp index 8c4cf532eef..0fcd53c3a3b 100644 --- a/src/PublicKey.cpp +++ b/src/PublicKey.cpp @@ -1,18 +1,20 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "PublicKey.h" +#include "PrivateKey.h" #include "Data.h" #include #include +#include #include #include #include -#include +#include #include @@ -31,8 +33,8 @@ bool PublicKey::isValid(const Data& data, enum TWPublicKeyType type) { case TWPublicKeyTypeCURVE25519: case TWPublicKeyTypeED25519Blake2b: return size == ed25519Size; - case TWPublicKeyTypeED25519Extended: - return size == ed25519ExtendedSize; + case TWPublicKeyTypeED25519Cardano: + return size == cardanoKeySize; case TWPublicKeyTypeSECP256k1: case TWPublicKeyTypeNIST256p1: return size == secp256k1Size && (data[0] == 0x02 || data[0] == 0x03); @@ -46,8 +48,9 @@ bool PublicKey::isValid(const Data& data, enum TWPublicKeyType type) { /// Initializes a public key with a collection of bytes. /// -/// @throws std::invalid_argument if the data is not a valid public key. -PublicKey::PublicKey(const Data& data, enum TWPublicKeyType type) : type(type) { +/// \throws std::invalid_argument if the data is not a valid public key. +PublicKey::PublicKey(const Data& data, enum TWPublicKeyType type) + : type(type) { if (!isValid(data, type)) { throw std::invalid_argument("Invalid public key data"); } @@ -74,8 +77,8 @@ PublicKey::PublicKey(const Data& data, enum TWPublicKeyType type) : type(type) { assert(data.size() == ed25519Size); // ensured by isValid() above std::copy(std::begin(data), std::end(data), std::back_inserter(bytes)); break; - case TWPublicKeyTypeED25519Extended: - bytes.reserve(ed25519ExtendedSize); + case TWPublicKeyTypeED25519Cardano: + bytes.reserve(cardanoKeySize); std::copy(std::begin(data), std::end(data), std::back_inserter(bytes)); } } @@ -118,8 +121,10 @@ PublicKey PublicKey::extended() const { case TWPublicKeyTypeED25519: case TWPublicKeyTypeCURVE25519: case TWPublicKeyTypeED25519Blake2b: - case TWPublicKeyTypeED25519Extended: - return *this; + case TWPublicKeyTypeED25519Cardano: + return *this; + default: + return *this; } } @@ -135,10 +140,11 @@ bool PublicKey::verify(const Data& signature, const Data& message) const { return ed25519_sign_open(message.data(), message.size(), bytes.data(), signature.data()) == 0; case TWPublicKeyTypeED25519Blake2b: return ed25519_sign_open_blake2b(message.data(), message.size(), bytes.data(), signature.data()) == 0; - case TWPublicKeyTypeED25519Extended: - throw std::logic_error("Not yet implemented"); - //ed25519_sign_open(message.data(), message.size(), bytes.data(), signature.data()) == 0; - case TWPublicKeyTypeCURVE25519: + case TWPublicKeyTypeED25519Cardano: { + const auto key = subData(bytes, 0, ed25519Size); + return ed25519_sign_open(message.data(), message.size(), key.data(), signature.data()) == 0; + } + case TWPublicKeyTypeCURVE25519: { auto ed25519PublicKey = Data(); ed25519PublicKey.resize(PublicKey::ed25519Size); curve25519_pk_to_ed25519(ed25519PublicKey.data(), bytes.data()); @@ -150,12 +156,31 @@ bool PublicKey::verify(const Data& signature, const Data& message) const { auto verifyBuffer = Data(); append(verifyBuffer, signature); verifyBuffer[63] &= 127; - return ed25519_sign_open(message.data(), message.size(), ed25519PublicKey.data(), - verifyBuffer.data()) == 0; + return ed25519_sign_open(message.data(), message.size(), ed25519PublicKey.data(), verifyBuffer.data()) == 0; + } + default: + throw std::logic_error("Not yet implemented"); } } -bool PublicKey::verifySchnorr(const Data& signature, const Data& message) const { +bool PublicKey::verifyAsDER(const Data& signature, const Data& message) const { + switch (type) { + case TWPublicKeyTypeSECP256k1: + case TWPublicKeyTypeSECP256k1Extended: { + Data sig(64); + int ret = ecdsa_sig_from_der(signature.data(), signature.size(), sig.data()); + if (ret) { + return false; + } + return ecdsa_verify_digest(&secp256k1, bytes.data(), sig.data(), message.data()) == 0; + } + + default: + return false; + } +} + +bool PublicKey::verifyZilliqa(const Data& signature, const Data& message) const { switch (type) { case TWPublicKeyTypeSECP256k1: case TWPublicKeyTypeSECP256k1Extended: @@ -164,7 +189,7 @@ bool PublicKey::verifySchnorr(const Data& signature, const Data& message) const case TWPublicKeyTypeNIST256p1Extended: case TWPublicKeyTypeED25519: case TWPublicKeyTypeED25519Blake2b: - case TWPublicKeyTypeED25519Extended: + case TWPublicKeyTypeED25519Cardano: case TWPublicKeyTypeCURVE25519: default: return false; @@ -173,7 +198,7 @@ bool PublicKey::verifySchnorr(const Data& signature, const Data& message) const Data PublicKey::hash(const Data& prefix, Hash::Hasher hasher, bool skipTypeByte) const { const auto offset = std::size_t(skipTypeByte ? 1 : 0); - const auto hash = hasher(bytes.data() + offset, bytes.size() - offset); + const auto hash = Hash::hash(hasher, bytes.data() + offset, bytes.size() - offset); auto result = Data(); result.reserve(prefix.size() + hash.size()); @@ -182,21 +207,35 @@ Data PublicKey::hash(const Data& prefix, Hash::Hasher hasher, bool skipTypeByte) return result; } -PublicKey PublicKey::recover(const Data& signature, const Data& message) { - if (signature.size() < 65) { +PublicKey PublicKey::recoverRaw(const Data& signatureRS, byte recId, const Data& messageDigest) { + if (signatureRS.size() < 2 * PrivateKey::_size) { throw std::invalid_argument("signature too short"); } - auto v = signature[64]; - if (v >= 27) { - v -= 27; + if (recId >= 4) { + throw std::invalid_argument("Invalid recId (>=4)"); } - TW::Data result(65); - if (ecdsa_recover_pub_from_sig(&secp256k1, result.data(), signature.data(), message.data(), v) != 0) { - throw std::invalid_argument("recover failed"); + if (messageDigest.size() < PrivateKey::_size) { + throw std::invalid_argument("digest too short"); + } + TW::Data result(secp256k1SignatureSize); + if (auto ret = ecdsa_recover_pub_from_sig(&secp256k1, result.data(), signatureRS.data(), messageDigest.data(), recId); ret != 0) { + throw std::invalid_argument("recover failed " + std::to_string(ret)); } return PublicKey(result, TWPublicKeyTypeSECP256k1Extended); } +PublicKey PublicKey::recover(const Data& signature, const Data& messageDigest) { + if (signature.size() < secp256k1SignatureSize) { + throw std::invalid_argument("signature too short"); + } + auto v = signature[secp256k1SignatureSize - 1]; + // handle EIP155 Eth encoding of V, of the form 27+v, or 35+chainID*2+v + if (v >= PublicKey::SignatureVOffset) { + v = !(v & 0x01); + } + return recoverRaw(signature, v, messageDigest); +} + bool PublicKey::isValidED25519() const { if (type != TWPublicKeyTypeED25519) { return false; diff --git a/src/PublicKey.h b/src/PublicKey.h index 27daa26a267..111f0fdf306 100644 --- a/src/PublicKey.h +++ b/src/PublicKey.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -21,14 +21,21 @@ class PublicKey { /// The number of bytes in a secp256k1 and nist256p1 public key. static const size_t secp256k1Size = 33; - /// The number of bytes in a ed25519 public key. + /// The number of bytes in an ed25519 public key. static const size_t ed25519Size = 32; - static const size_t ed25519ExtendedSize = 64; + /// The number of bytes in a Cardano public key (two ed25519 public key + chain code). + static const size_t cardanoKeySize = 2 * 2 * 32; /// The number of bytes in a secp256k1 and nist256p1 extended public key. static const size_t secp256k1ExtendedSize = 65; + /// The number of bytes in a secp256k1 signature. + static const size_t secp256k1SignatureSize = 65; + + /// Magic number used in V compnent encoding + static const byte SignatureVOffset = 27; + /// The public key bytes. Data bytes; @@ -44,7 +51,7 @@ class PublicKey { /// Initializes a public key with a collection of bytes. /// - /// @throws std::invalid_argument if the data is not a valid public key. + /// \throws std::invalid_argument if the data is not a valid public key. explicit PublicKey(const Data& data, enum TWPublicKeyType type); /// Determines if this is a compressed public key. @@ -61,17 +68,31 @@ class PublicKey { /// Verifies a signature for the provided message. bool verify(const Data& signature, const Data& message) const; - /// Verifies a schnorr signature for the provided message. - bool verifySchnorr(const Data& signature, const Data& message) const; + /// Verifies a signature in DER format. + bool verifyAsDER(const Data& signature, const Data& message) const; + + /// Verifies a Zilliqa schnorr signature for the provided message. + bool verifyZilliqa(const Data& signature, const Data& message) const; /// Computes the public key hash. /// /// The public key hash is computed by applying the hasher to the public key /// bytes and then prepending the prefix. - Data hash(const Data& prefix, Hash::Hasher hasher = Hash::sha256ripemd, bool skipTypeByte = false) const; + Data hash(const Data& prefix, Hash::Hasher hasher = Hash::HasherSha256ripemd, bool skipTypeByte = false) const; + + /// Recover public key (SECP256k1Extended) from signature R, S, V values + /// signatureRS: 2x32 bytes with the R and S values + /// recId: the recovery ID, a.k.a. V value, 0 <= v < 4 + /// messageDigest: message digest (hash) to be signed + /// Throws on invalid data. + static PublicKey recoverRaw(const Data& signatureRS, byte recId, const Data& messageDigest); /// Recover public key from signature (SECP256k1Extended) - static PublicKey recover(const Data& signature, const Data& message); + /// signature: 65-byte signature (R, S, and V). V can have higher value bits, as used by Ethereum (for values over 27 the negated last bit is taken). + /// messageDigest: message digest (hash) to be signed + /// Throws on invalid data. + /// Naming is kept for backwards compatibility. + static PublicKey recover(const Data& signature, const Data& messageDigest); /// Check if this key makes a valid ED25519 key (it is on the curve) bool isValidED25519() const; diff --git a/src/Result.h b/src/Result.h index c7faede9896..1ce611525a1 100644 --- a/src/Result.h +++ b/src/Result.h @@ -42,7 +42,7 @@ struct Result { using Storage = typename std::aligned_storage::type; /// Wether the operation succeeded. - bool success_; + bool success_{false}; Storage storage_; public: @@ -73,6 +73,7 @@ struct Result { } else { new (&storage_) E(other.get()); } + return *this; } Result(Result&& other) { @@ -96,6 +97,7 @@ struct Result { } else { new (&storage_) E(std::move(other.get())); } + return *this; } ~Result() { @@ -150,7 +152,7 @@ struct Result { public: /// Initializes a success result with a payload. - Result(Types::Success payload) : success_(true), error_() {} + Result([[maybe_unused]] Types::Success payload) : success_(true), error_() {} /// Initializes a failure result. Result(Types::Failure error) : success_(false), error_(error.val) {} diff --git a/src/Ripple/Address.cpp b/src/Ripple/Address.cpp deleted file mode 100644 index 4f4a601f775..00000000000 --- a/src/Ripple/Address.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Address.h" -#include "../Base58.h" -#include - -using namespace TW::Ripple; - -bool Address::isValid(const std::string& string) { - const auto decoded = Base58::ripple.decodeCheck(string); - if (decoded.size() != Address::size) { - return false; - } - return true; -} - -Address::Address(const std::string& string) { - const auto decoded = Base58::ripple.decodeCheck(string); - if (decoded.size() != Address::size) { - throw std::invalid_argument("Invalid address string"); - } - std::copy(decoded.begin(), decoded.end(), bytes.begin()); -} - -Address::Address(const PublicKey& publicKey) { - /// see type prefix: https://developers.ripple.com/base58-encodings.html - bytes[0] = 0x00; - ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, bytes.data() + 1); -} - -std::string Address::string() const { - return Base58::ripple.encodeCheck(bytes); -} diff --git a/src/Ripple/Address.h b/src/Ripple/Address.h deleted file mode 100644 index 5aa88a4c168..00000000000 --- a/src/Ripple/Address.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "../Data.h" -#include "../PublicKey.h" - -#include - -namespace TW::Ripple { - -class Address { - public: - /// Number of bytes in an address. - static const size_t size = 21; - - /// Address data consisting of a prefix byte followed by the public key hash - std::array bytes; - - /// Determines whether a string makes a valid address. - static bool isValid(const std::string& string); - - /// Initializes a Ripple address with a string representation. - explicit Address(const std::string& string); - - /// Initializes a Ripple address with a public key. - explicit Address(const PublicKey& publicKey); - - /// Returns a string representation of the address. - std::string string() const; -}; - -inline bool operator==(const Address& lhs, const Address& rhs) { - return lhs.bytes == rhs.bytes; -} - -} // namespace TW::Ripple diff --git a/src/Ripple/Entry.cpp b/src/Ripple/Entry.cpp deleted file mode 100644 index a2948f7073e..00000000000 --- a/src/Ripple/Entry.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Entry.h" - -#include "Address.h" -#include "XAddress.h" -#include "Signer.h" - -using namespace TW::Ripple; -using namespace std; - -// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. - -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { - return Address::isValid(address) || XAddress::isValid(address); -} - -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { - return Address(publicKey).string(); -} - -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { - signTemplate(dataIn, dataOut); -} diff --git a/src/Ripple/Entry.h b/src/Ripple/Entry.h deleted file mode 100644 index adcd80e90fc..00000000000 --- a/src/Ripple/Entry.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "../CoinEntry.h" - -namespace TW::Ripple { - -/// Entry point for implementation of Ripple (XRP) coin. -/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { -public: - virtual const std::vector coinTypes() const { return {TWCoinTypeXRP}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; -}; - -} // namespace TW::Ripple diff --git a/src/Ripple/Signer.cpp b/src/Ripple/Signer.cpp deleted file mode 100644 index 0be0b41f62f..00000000000 --- a/src/Ripple/Signer.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Signer.h" -#include "../BinaryCoding.h" -#include "../Hash.h" - -using namespace TW; -using namespace TW::Ripple; - -static const int64_t fullyCanonical = 0x80000000; - -Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { - auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - auto transaction = Transaction( - /* amount */input.amount(), - /* fee */input.fee(), - /* flags */input.flags(), - /* sequence */input.sequence(), - /* last_ledger_sequence */input.last_ledger_sequence(), - /* account */Address(input.account()), - /* destination */input.destination(), - /* destination_tag*/input.destination_tag() - ); - - auto signer = Signer(); - signer.sign(key, transaction); - - auto output = Proto::SigningOutput(); - auto encoded = transaction.serialize(); - output.set_encoded(encoded.data(), encoded.size()); - return output; -} - -void Signer::sign(const PrivateKey& privateKey, Transaction& transaction) const noexcept { - /// See https://github.com/trezor/trezor-core/blob/master/src/apps/ripple/sign_tx.py#L59 - transaction.flags |= fullyCanonical; - transaction.pub_key = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1).bytes; - - auto unsignedTx = transaction.getPreImage(); - auto hash = Hash::sha512(unsignedTx); - auto half = Data(hash.begin(), hash.begin() + 32); - - transaction.signature = privateKey.signAsDER(half, TWCurveSECP256k1); -} diff --git a/src/Ripple/Signer.h b/src/Ripple/Signer.h deleted file mode 100644 index 9674fdb5cd2..00000000000 --- a/src/Ripple/Signer.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Transaction.h" -#include "../Data.h" -#include "../Hash.h" -#include "../PrivateKey.h" - -namespace TW::Ripple { - -/// Helper class that performs Ripple transaction signing. -class Signer { - public: - /// Signs a Proto::SigningInput transaction - static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; - - /// Signs the given transaction. - void sign(const PrivateKey& privateKey, Transaction& transaction) const noexcept; -}; - -} // namespace TW::Ripple diff --git a/src/Ripple/Transaction.cpp b/src/Ripple/Transaction.cpp deleted file mode 100644 index ef2d595854f..00000000000 --- a/src/Ripple/Transaction.cpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "BinaryCoding.h" -#include "Transaction.h" -#include "../BinaryCoding.h" -#include "../HexCoding.h" - -using namespace TW; -using namespace TW::Ripple; - -const int NETWORK_PREFIX = 0x53545800; - -Data Transaction::serialize() const { - auto data = Data(); - /// field must be sorted by field type then by field name - /// "type" - encodeType(FieldType::int16, 2, data); - encode16BE(uint16_t(TransactionType::payment), data); - /// "flags" - encodeType(FieldType::int32, 2, data); - encode32BE(static_cast(flags), data); - /// "sequence" - encodeType(FieldType::int32, 4, data); - encode32BE(sequence, data); - /// "destinationTag" - if (encode_tag) { - encodeType(FieldType::int32, 14, data); - encode32BE(static_cast(destination_tag), data); - } - /// "lastLedgerSequence" - if (last_ledger_sequence > 0) { - encodeType(FieldType::int32, 27, data); - encode32BE(last_ledger_sequence, data); - } - /// "amount" - encodeType(FieldType::amount, 1, data); - append(data, serializeAmount(amount)); - /// "fee" - encodeType(FieldType::amount, 8, data); - append(data, serializeAmount(fee)); - /// "signingPubKey" - if (!pub_key.empty()) { - encodeType(FieldType::vl, 3, data); - encodeBytes(pub_key, data); - } - /// "txnSignature" - if (!signature.empty()) { - encodeType(FieldType::vl, 4, data); - encodeBytes(signature, data); - } - /// "account" - encodeType(FieldType::account, 1, data); - encodeBytes(serializeAddress(account), data); - /// "destination" - encodeType(FieldType::account, 3, data); - encodeBytes(destination, data); - return data; -} - -Data Transaction::getPreImage() const { - auto preImage = Data(); - encode32BE(NETWORK_PREFIX, preImage); - append(preImage, serialize()); - return preImage; -} - -Data Transaction::serializeAmount(int64_t amount) { - if (amount < 0) { - return Data(); - } - auto data = Data(); - encode64BE(uint64_t(amount), data); - /// clear first bit to indicate XRP - data[0] &= 0x7F; - /// set second bit to indicate positive number - data[0] |= 0x40; - return data; -} - -Data Transaction::serializeAddress(Address address) { - auto data = Data(20); - if (!address.bytes.empty()) { - std::copy(&address.bytes[0] + 1, &address.bytes[0] + (address.bytes.size() < 21 ? address.bytes.size() : 21), &data[0]); - } - return data; -} diff --git a/src/Ripple/Transaction.h b/src/Ripple/Transaction.h deleted file mode 100644 index e718f319de4..00000000000 --- a/src/Ripple/Transaction.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Address.h" -#include "XAddress.h" -#include "../Data.h" -#include "../proto/Ripple.pb.h" - -namespace TW::Ripple { - -enum class FieldType: int { - int16 = 1, - int32 = 2, - amount = 6, - vl = 7, - account = 8 -}; - -enum class TransactionType { payment = 0 }; - -class Transaction { - /// We only support transaction types other than the Payment transaction. - /// Non-XRP currencies are not supported. Float and negative amounts are not supported. - /// See https://github.com/trezor/trezor-core/tree/master/src/apps/ripple#transactions - public: - int64_t amount; - int64_t fee; - int64_t flags; - int32_t sequence; - int32_t last_ledger_sequence; - Address account; - Data destination; - bool encode_tag; - int64_t destination_tag; - Data pub_key; - Data signature; - - Transaction(int64_t amount, int64_t fee, int64_t flags, int32_t sequence, - int32_t last_ledger_sequence, Address account, const std::string& destination, - int64_t destination_tag) - : amount(amount) - , fee(fee) - , flags(flags) - , sequence(sequence) - , last_ledger_sequence(last_ledger_sequence) - , account(account) { - try { - auto address = Address(destination); - encode_tag = destination_tag > 0; - this->destination_tag = destination_tag; - this->destination = Data(address.bytes.begin() + 1, address.bytes.end()); - } catch(const std::exception& e) { - auto xAddress = XAddress(destination); - encode_tag = xAddress.flag != TagFlag::none; - this->destination_tag = xAddress.tag; - this->destination = Data(xAddress.bytes.begin(), xAddress.bytes.end()); - } - } - - public: - /// simplified serialization format tailored for Payment transaction type - /// exclusively. - Data serialize() const; - Data getPreImage() const; - - static Data serializeAmount(int64_t amount); - static Data serializeAddress(Address address); -}; - -} // namespace TW::Ripple diff --git a/src/Ronin/Address.cpp b/src/Ronin/Address.cpp new file mode 100644 index 00000000000..74555fb553a --- /dev/null +++ b/src/Ronin/Address.cpp @@ -0,0 +1,55 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Address.h" +#include "Ethereum/Address.h" +#include "Ethereum/AddressChecksum.h" +#include "../Hash.h" +#include "../HexCoding.h" + +const std::string prefix = "ronin:"; + +namespace TW::Ronin { + +bool Address::isValid(const std::string& string) { + // check prefix + if (string.compare(0, prefix.length(), prefix) == 0) { + const auto suffix = string.substr(prefix.length()); + const auto data = parse_hex(suffix); + return Ethereum::Address::isValid(data); + } + // accept Ethereum format as well + if (Ethereum::Address::isValid(string)) { + return true; + } + return false; +} + +Address::Address(const std::string& string) { + // check prefix + if (string.compare(0, prefix.length(), prefix) == 0) { + const auto suffix = string.substr(prefix.length()); + const auto data = parse_hex(suffix); + std::copy(data.begin(), data.end(), bytes.begin()); + } else if (Ethereum::Address::isValid(string)) { + // accept Ethereum format as well + Ethereum::Address ethereumAddress(string); + bytes = ethereumAddress.bytes; + } else { + throw std::invalid_argument("Invalid address data"); + } +} + +// Normalized: with ronin prefix, checksummed hex address, no 0x prefix +std::string Address::string() const { + std::string address = Ethereum::checksumed(*this); + if (address.size() >= 2 && address.substr(0, 2) == "0x") { + address = address.substr(2); + } // skip 0x + return prefix + address; +} + +} // namespace TW::Ronin diff --git a/src/Ronin/Address.h b/src/Ronin/Address.h new file mode 100644 index 00000000000..ac28eb1372f --- /dev/null +++ b/src/Ronin/Address.h @@ -0,0 +1,31 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../PublicKey.h" +#include "../Ethereum/Address.h" +#include +#include + +namespace TW::Ronin { + +class Address: public Ethereum::Address { + public: + /// Determines whether a string makes a valid address. + static bool isValid(const std::string& string); + + /// Initializes an address with a string representation. + explicit Address(const std::string& string); + + /// Initializes an address with a public key. + explicit Address(const PublicKey& publicKey): Ethereum::Address(publicKey) {} + + /// Returns a string representation of the address. + std::string string() const; +}; + +} // namespace TW::Ronin diff --git a/src/Ronin/Entry.cpp b/src/Ronin/Entry.cpp new file mode 100644 index 00000000000..d69ac356390 --- /dev/null +++ b/src/Ronin/Entry.cpp @@ -0,0 +1,42 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Entry.h" + +#include "Address.h" +#include "../Ethereum/Signer.h" + +using namespace TW; +using namespace std; + +namespace TW::Ronin { + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { + return Address::isValid(address); +} + +string Entry::normalizeAddress([[maybe_unused]] TWCoinType coin, const string& address) const { + return Address(address).string(); +} + +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { + return Address(publicKey).string(); +} + +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = Address(address); + return {addr.bytes.begin(), addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { + return Ethereum::Signer::signJSON(json, key); +} + +} // namespace TW::Ronin diff --git a/src/Ronin/Entry.h b/src/Ronin/Entry.h new file mode 100644 index 00000000000..9bb7f96aa40 --- /dev/null +++ b/src/Ronin/Entry.h @@ -0,0 +1,25 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../CoinEntry.h" + +namespace TW::Ronin { + +/// Entry point for Ronin (EVM side chain) +class Entry final : public CoinEntry { +public: + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string normalizeAddress(TWCoinType coin, const std::string& address) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; +}; + +} // namespace TW::Ronin diff --git a/src/SS58Address.h b/src/SS58Address.h deleted file mode 100644 index b9ccfa627b6..00000000000 --- a/src/SS58Address.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include "Base58.h" -#include "Data.h" -#include "PublicKey.h" - -#include -#include -#include - -const std::string SS58Prefix = "SS58PRE"; - -namespace TW { - -class SS58Address { - public: - /// Number of bytes in an address. - static const size_t size = 33; - - static const size_t checksumSize = 2; - - /// Address data consisting of a network byte followed by the public key. - std::array bytes; - - /// Determines whether a string makes a valid address - static bool isValid(const std::string& string, byte network) { - const auto decoded = Base58::bitcoin.decode(string); - if (decoded.size() != SS58Address::size + checksumSize) { - return false; - } - // check network - if (decoded[0] != network) { - return false; - } - auto checksum = computeChecksum(Data(decoded.begin(), decoded.end() - checksumSize)); - // compare checksum - if (!std::equal(decoded.end() - checksumSize, decoded.end(), checksum.begin())) { - return false; - } - return true; - } - - template - static Data computeChecksum(const T& data) { - auto prefix = Data(SS58Prefix.begin(), SS58Prefix.end()); - append(prefix, Data(data.begin(), data.end())); - auto hash = Hash::blake2b(prefix, 64); - auto checksum = Data(checksumSize); - std::copy(hash.begin(), hash.begin() + checksumSize, checksum.data()); - return checksum; - } - - SS58Address() = default; - - /// Initializes an address with a string representation. - SS58Address(const std::string& string, byte network) { - if (!isValid(string, network)) { - throw std::invalid_argument("Invalid address string"); - } - const auto decoded = Base58::bitcoin.decode(string); - std::copy(decoded.begin(), decoded.end() - checksumSize, bytes.begin()); - } - - /// Initializes an address with a public key and network. - SS58Address(const PublicKey& publicKey, byte network) { - if (publicKey.type != TWPublicKeyTypeED25519) { - throw std::invalid_argument("SS58Address expects an ed25519 public key."); - } - bytes[0] = network; - std::copy(publicKey.bytes.begin(), publicKey.bytes.end(), bytes.begin() + 1); - } - - /// Returns a string representation of the address. - std::string string() const { - auto result = Data(bytes.begin(), bytes.end()); - auto checksum = computeChecksum(bytes); - append(result, checksum); - return Base58::bitcoin.encode(result); - } - - /// Returns public key bytes - Data keyBytes() const { - return Data(bytes.begin() + 1, bytes.end()); - } -}; - -inline bool operator==(const SS58Address& lhs, const SS58Address& rhs) { - return lhs.bytes == rhs.bytes; -} - -} // namespace TW diff --git a/src/Solana/Address.cpp b/src/Solana/Address.cpp index c8a633eb853..3e4f77cface 100644 --- a/src/Solana/Address.cpp +++ b/src/Solana/Address.cpp @@ -5,8 +5,8 @@ // file LICENSE at the root of the source code distribution tree. #include "Address.h" -#include "Transaction.h" #include "Program.h" +#include "Transaction.h" #include "../Base58.h" #include "../Base58Address.h" #include "../Hash.h" @@ -16,7 +16,8 @@ #include using namespace TW; -using namespace TW::Solana; + +namespace TW::Solana { bool Address::isValid(const std::string& string) { const auto data = Base58::bitcoin.decode(string); @@ -58,3 +59,5 @@ Data Address::vector() const { Address Address::defaultTokenAddress(const Address& tokenMintAddress) { return TokenProgram::defaultTokenAddress(*this, tokenMintAddress); } + +} // namespace TW::Solana diff --git a/src/Solana/Entry.cpp b/src/Solana/Entry.cpp index 41c06ca31e1..765c9c1927f 100644 --- a/src/Solana/Entry.cpp +++ b/src/Solana/Entry.cpp @@ -9,23 +9,31 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Solana; +using namespace TW; using namespace std; +namespace TW::Solana { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + return Address(address).vector(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +} // namespace TW::Solana diff --git a/src/Solana/Entry.h b/src/Solana/Entry.h index 18f27424c69..b6bc8b4f1d4 100644 --- a/src/Solana/Entry.h +++ b/src/Solana/Entry.h @@ -12,14 +12,14 @@ namespace TW::Solana { /// Entry point for implementation of Solana coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeSolana}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Solana diff --git a/src/Solana/Program.cpp b/src/Solana/Program.cpp index ae33d08bd3d..bac0340caa8 100644 --- a/src/Solana/Program.cpp +++ b/src/Solana/Program.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,18 +7,13 @@ #include "Program.h" #include "Address.h" #include "Transaction.h" -#include "../Base58.h" -#include "../Hash.h" -#include +#include -#include - -using namespace TW; -using namespace TW::Solana; +namespace TW::Solana { Address StakeProgram::addressFromValidatorSeed(const Address& fromAddress, const Address& validatorAddress, - const Address& programId) { + const Address& programId) { Data extended = fromAddress.vector(); std::string seed = validatorAddress.string(); Data vecSeed(seed.begin(), seed.end()); @@ -52,8 +47,7 @@ Address TokenProgram::defaultTokenAddress(const Address& mainAddress, const Addr std::vector seeds = { TW::data(mainAddress.bytes.data(), mainAddress.bytes.size()), TW::data(programId.bytes.data(), programId.bytes.size()), - TW::data(tokenMintAddress.bytes.data(), tokenMintAddress.bytes.size()) - }; + TW::data(tokenMintAddress.bytes.data(), tokenMintAddress.bytes.size())}; return findProgramAddress(seeds, Address(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS)); } @@ -61,23 +55,20 @@ Address TokenProgram::defaultTokenAddress(const Address& mainAddress, const Addr * Based on solana code, find_program_address() * https://github.com/solana-labs/solana/blob/master/sdk/program/src/pubkey.rs#L193 */ -Address TokenProgram::findProgramAddress(const std::vector& seeds, const Address& programId) { +Address TokenProgram::findProgramAddress(const std::vector& seeds, [[maybe_unused]] const Address& programId) { Address result(Data(32)); // cycle through seeds for the rare case when result is not valid - for (uint8_t seed = 255; seed >= 0; --seed) { - std::vector seedsCopy; - for (auto& s: seeds) { - seedsCopy.push_back(s); - } - // add extra seed - seedsCopy.push_back({seed}); - Address address = createProgramAddress(seedsCopy, Address(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS)); + auto bumpSeed = Data{std::numeric_limits::max()}; + for (std::uint8_t seed = 0; seed <= std::numeric_limits::max(); ++seed) { + std::vector seedsCopy = seeds; + seedsCopy.emplace_back(bumpSeed); + Address address = createProgramAddress(seedsCopy, programId); PublicKey publicKey = PublicKey(TW::data(address.bytes.data(), address.bytes.size()), TWPublicKeyTypeED25519); if (!publicKey.isValidED25519()) { result = address; break; } - // try next seed + bumpSeed[0] -= 1; } return result; } @@ -89,7 +80,7 @@ Address TokenProgram::findProgramAddress(const std::vector& seeds, con Address TokenProgram::createProgramAddress(const std::vector& seeds, const Address& programId) { // concatenate seeds Data hashInput; - for (auto& seed: seeds) { + for (auto& seed : seeds) { append(hashInput, seed); } // append programId @@ -99,3 +90,6 @@ Address TokenProgram::createProgramAddress(const std::vector& seeds, c Data hash = TW::Hash::sha256(hashInput.data(), hashInput.size()); return Address(hash); } + +} // namespace TW::Solana + diff --git a/src/Solana/Signer.cpp b/src/Solana/Signer.cpp index f999e893001..f5839bc0e8f 100644 --- a/src/Solana/Signer.cpp +++ b/src/Solana/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,16 +7,12 @@ #include "Signer.h" #include "Address.h" #include "Program.h" -#include "../Base58.h" -#include #include -#include #include -using namespace TW; -using namespace TW::Solana; +namespace TW::Solana { void Signer::sign(const std::vector& privateKeys, Transaction& transaction) { for (auto privateKey : privateKeys) { @@ -28,149 +24,146 @@ void Signer::sign(const std::vector& privateKeys, Transaction& trans } } +// Helper to convert protobuf-string-collection references to Address vector +std::vector
convertReferences(const google::protobuf::RepeatedPtrField& references) { + std::vector
ret; + for (auto i = 0; i < references.size(); ++i) { + if (Address::isValid(references[i])) { + ret.emplace_back(references[i]); + } + } + return ret; +} + Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto blockhash = Solana::Hash(input.recent_blockhash()); auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); Message message; - std::string stakePubkey; std::vector signerKeys; switch (input.transaction_type_case()) { - case Proto::SigningInput::TransactionTypeCase::kTransferTransaction: - { - auto protoMessage = input.transfer_transaction(); - message = Message::createTransfer( - /* from */ Address(key.getPublicKey(TWPublicKeyTypeED25519)), - /* to */ Address(protoMessage.recipient()), - /* value */ protoMessage.value(), - /* recent_blockhash */ blockhash); - signerKeys.push_back(key); - } - break; - - case Proto::SigningInput::TransactionTypeCase::kDelegateStakeTransaction: - { - auto protoMessage = input.delegate_stake_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto validatorAddress = Address(protoMessage.validator_pubkey()); - auto stakeProgramId = Address(STAKE_PROGRAM_ID_ADDRESS); - std::optional
stakeAddress; - if (protoMessage.stake_account().size() == 0) { - // no stake address specified, generate a new unique - stakeAddress = StakeProgram::addressFromRecentBlockhash(userAddress, blockhash, stakeProgramId); - } else { - // stake address specified, use it - stakeAddress = Address(protoMessage.stake_account()); - } - message = Message::createStake( - /* signer */ userAddress, - /* stakeAddress */ stakeAddress.value(), - /* voteAddress */ validatorAddress, - /* value */ protoMessage.value(), - /* recent_blockhash */ blockhash); - signerKeys.push_back(key); - } - break; - - case Proto::SigningInput::TransactionTypeCase::kDeactivateStakeTransaction: - { - auto protoMessage = input.deactivate_stake_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto stakeAddress = Address(protoMessage.stake_account()); - message = Message::createStakeDeactivate( - /* signer */ userAddress, - /* stakeAddress */ stakeAddress, - /* recent_blockhash */ blockhash); - signerKeys.push_back(key); - } - break; - - case Proto::SigningInput::TransactionTypeCase::kDeactivateAllStakeTransaction: - { - auto protoMessage = input.deactivate_all_stake_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - std::vector
addresses; - for (auto i = 0; i < protoMessage.stake_accounts_size(); ++i) { - addresses.emplace_back(Address(protoMessage.stake_accounts(i))); - } - message = Message::createStakeDeactivateAll(userAddress, addresses, blockhash); - signerKeys.push_back(key); - } - break; - - case Proto::SigningInput::TransactionTypeCase::kWithdrawTransaction: - { - auto protoMessage = input.withdraw_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto stakeAddress = Address(protoMessage.stake_account()); - message = Message::createStakeWithdraw( - /* signer */ userAddress, - /* stakeAddress */ stakeAddress, - /* value */ protoMessage.value(), - /* recent_blockhash */ blockhash); - signerKeys.push_back(key); - } - break; - - case Proto::SigningInput::TransactionTypeCase::kWithdrawAllTransaction: - { - auto protoMessage = input.withdraw_all_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - std::vector> stakes; - for (auto i = 0; i < protoMessage.stake_accounts_size(); ++i) { - stakes.push_back(std::make_pair( - Address(protoMessage.stake_accounts(i).stake_account()), - protoMessage.stake_accounts(i).value() - )); - } - message = Message::createStakeWithdrawAll(userAddress, stakes, blockhash); - signerKeys.push_back(key); - } - break; - - case Proto::SigningInput::TransactionTypeCase::kCreateTokenAccountTransaction: - { - auto protoMessage = input.create_token_account_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto mainAddress = Address(protoMessage.main_address()); - auto tokenMintAddress = Address(protoMessage.token_mint_address()); - auto tokenAddress = Address(protoMessage.token_address()); - message = Message::createTokenCreateAccount(userAddress, TokenInstruction::CreateTokenAccount, mainAddress, tokenMintAddress, tokenAddress, blockhash); - signerKeys.push_back(key); - } - break; - - case Proto::SigningInput::TransactionTypeCase::kTokenTransferTransaction: - { - auto protoMessage = input.token_transfer_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto tokenMintAddress = Address(protoMessage.token_mint_address()); - auto senderTokenAddress = Address(protoMessage.sender_token_address()); - auto recipientTokenAddress = Address(protoMessage.recipient_token_address()); - auto amount = protoMessage.amount(); - auto decimals = static_cast(protoMessage.decimals()); - message = Message::createTokenTransfer(userAddress, TokenInstruction::TokenTransfer, tokenMintAddress, senderTokenAddress, recipientTokenAddress, amount, decimals, blockhash); - signerKeys.push_back(key); - } - break; - - case Proto::SigningInput::TransactionTypeCase::kCreateAndTransferTokenTransaction: - { - auto protoMessage = input.create_and_transfer_token_transaction(); - auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); - auto recipientMainAddress = Address(protoMessage.recipient_main_address()); - auto tokenMintAddress = Address(protoMessage.token_mint_address()); - auto recipientTokenAddress = Address(protoMessage.recipient_token_address()); - auto senderTokenAddress = Address(protoMessage.sender_token_address()); - auto amount = protoMessage.amount(); - auto decimals = static_cast(protoMessage.decimals()); - message = Message::createTokenCreateAndTransfer(userAddress, recipientMainAddress, tokenMintAddress, recipientTokenAddress, senderTokenAddress, amount, decimals, blockhash); - signerKeys.push_back(key); - } - break; - - default: - assert(input.transaction_type_case() != Proto::SigningInput::TransactionTypeCase::TRANSACTION_TYPE_NOT_SET); + case Proto::SigningInput::TransactionTypeCase::kTransferTransaction: { + auto protoMessage = input.transfer_transaction(); + message = Message::createTransfer( + /* from */ Address(key.getPublicKey(TWPublicKeyTypeED25519)), + /* to */ Address(protoMessage.recipient()), + /* value */ protoMessage.value(), + /* recent_blockhash */ blockhash, + /* memo */ protoMessage.memo(), + convertReferences(protoMessage.references())); + signerKeys.push_back(key); + } break; + + case Proto::SigningInput::TransactionTypeCase::kDelegateStakeTransaction: { + auto protoMessage = input.delegate_stake_transaction(); + auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); + auto validatorAddress = Address(protoMessage.validator_pubkey()); + auto stakeProgramId = Address(STAKE_PROGRAM_ID_ADDRESS); + std::optional
stakeAddress; + if (protoMessage.stake_account().size() == 0) { + // no stake address specified, generate a new unique + stakeAddress = StakeProgram::addressFromRecentBlockhash(userAddress, blockhash, stakeProgramId); + } else { + // stake address specified, use it + stakeAddress = Address(protoMessage.stake_account()); + } + message = Message::createStake( + /* signer */ userAddress, + /* stakeAddress */ stakeAddress.value(), + /* voteAddress */ validatorAddress, + /* value */ protoMessage.value(), + /* recent_blockhash */ blockhash); + signerKeys.push_back(key); + } break; + + case Proto::SigningInput::TransactionTypeCase::kDeactivateStakeTransaction: { + auto protoMessage = input.deactivate_stake_transaction(); + auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); + auto stakeAddress = Address(protoMessage.stake_account()); + message = Message::createStakeDeactivate( + /* signer */ userAddress, + /* stakeAddress */ stakeAddress, + /* recent_blockhash */ blockhash); + signerKeys.push_back(key); + } break; + + case Proto::SigningInput::TransactionTypeCase::kDeactivateAllStakeTransaction: { + auto protoMessage = input.deactivate_all_stake_transaction(); + auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); + std::vector
addresses; + for (auto i = 0; i < protoMessage.stake_accounts_size(); ++i) { + addresses.emplace_back(Address(protoMessage.stake_accounts(i))); + } + message = Message::createStakeDeactivateAll(userAddress, addresses, blockhash); + signerKeys.push_back(key); + } break; + + case Proto::SigningInput::TransactionTypeCase::kWithdrawTransaction: { + auto protoMessage = input.withdraw_transaction(); + auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); + auto stakeAddress = Address(protoMessage.stake_account()); + message = Message::createStakeWithdraw( + /* signer */ userAddress, + /* stakeAddress */ stakeAddress, + /* value */ protoMessage.value(), + /* recent_blockhash */ blockhash); + signerKeys.push_back(key); + } break; + + case Proto::SigningInput::TransactionTypeCase::kWithdrawAllTransaction: { + auto protoMessage = input.withdraw_all_transaction(); + auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); + std::vector> stakes; + for (auto i = 0; i < protoMessage.stake_accounts_size(); ++i) { + stakes.push_back(std::make_pair( + Address(protoMessage.stake_accounts(i).stake_account()), + protoMessage.stake_accounts(i).value())); + } + message = Message::createStakeWithdrawAll(userAddress, stakes, blockhash); + signerKeys.push_back(key); + } break; + + case Proto::SigningInput::TransactionTypeCase::kCreateTokenAccountTransaction: { + auto protoMessage = input.create_token_account_transaction(); + auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); + auto mainAddress = Address(protoMessage.main_address()); + auto tokenMintAddress = Address(protoMessage.token_mint_address()); + auto tokenAddress = Address(protoMessage.token_address()); + message = Message::createTokenCreateAccount(userAddress, mainAddress, tokenMintAddress, tokenAddress, blockhash); + signerKeys.push_back(key); + } break; + + case Proto::SigningInput::TransactionTypeCase::kTokenTransferTransaction: { + auto protoMessage = input.token_transfer_transaction(); + auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); + auto tokenMintAddress = Address(protoMessage.token_mint_address()); + auto senderTokenAddress = Address(protoMessage.sender_token_address()); + auto recipientTokenAddress = Address(protoMessage.recipient_token_address()); + auto amount = protoMessage.amount(); + auto decimals = static_cast(protoMessage.decimals()); + const auto memo = protoMessage.memo(); + message = Message::createTokenTransfer(userAddress, tokenMintAddress, senderTokenAddress, recipientTokenAddress, amount, decimals, blockhash, + memo, convertReferences(protoMessage.references())); + signerKeys.push_back(key); + } break; + + case Proto::SigningInput::TransactionTypeCase::kCreateAndTransferTokenTransaction: { + auto protoMessage = input.create_and_transfer_token_transaction(); + auto userAddress = Address(key.getPublicKey(TWPublicKeyTypeED25519)); + auto recipientMainAddress = Address(protoMessage.recipient_main_address()); + auto tokenMintAddress = Address(protoMessage.token_mint_address()); + auto recipientTokenAddress = Address(protoMessage.recipient_token_address()); + auto senderTokenAddress = Address(protoMessage.sender_token_address()); + auto amount = protoMessage.amount(); + auto decimals = static_cast(protoMessage.decimals()); + const auto memo = protoMessage.memo(); + message = Message::createTokenCreateAndTransfer(userAddress, recipientMainAddress, tokenMintAddress, recipientTokenAddress, senderTokenAddress, amount, decimals, blockhash, + memo, convertReferences(protoMessage.references())); + signerKeys.push_back(key); + } break; + + default: + assert(input.transaction_type_case() != Proto::SigningInput::TransactionTypeCase::TRANSACTION_TYPE_NOT_SET); } auto transaction = Transaction(message); @@ -214,3 +207,5 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { input.set_private_key(key.data(), key.size()); return Signer::sign(input).encoded(); } + +} // namespace TW::Solana diff --git a/src/Solana/Signer.h b/src/Solana/Signer.h index 8b86befe832..46bed571ffb 100644 --- a/src/Solana/Signer.h +++ b/src/Solana/Signer.h @@ -7,7 +7,7 @@ #pragma once #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/Solana.pb.h" diff --git a/src/Solana/Transaction.cpp b/src/Solana/Transaction.cpp index dd5dc5c0c05..ac345134dc6 100644 --- a/src/Solana/Transaction.cpp +++ b/src/Solana/Transaction.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,14 +8,10 @@ #include "Hash.h" #include "Signer.h" -#include "../BinaryCoding.h" -#include "../PublicKey.h" #include -using namespace TW; -using namespace TW::Solana; -using namespace std; +namespace TW::Solana { uint8_t CompiledInstruction::findAccount(const Address& address) { auto it = std::find(addresses.begin(), addresses.end(), address); @@ -54,31 +50,30 @@ void Message::addAccountKeys(const Address& account) { } void Message::compileAccounts() { - for (auto& instr: instructions) { - for (auto& address: instr.accounts) { + for (auto& instr : instructions) { + for (auto& address : instr.accounts) { addAccount(address); } } // add programIds (read-only, at end) - for (auto& instr: instructions) { + for (auto& instr : instructions) { addAccount(AccountMeta{instr.programId, false, true}); } header = MessageHeader{ (uint8_t)signedAccounts.size(), 0, - (uint8_t)readOnlyAccounts.size() - }; + (uint8_t)readOnlyAccounts.size()}; // merge the three buckets accountKeys.clear(); - for(auto& a: signedAccounts) { + for (auto& a : signedAccounts) { addAccountKeys(a); } - for(auto& a: unsignedAccounts) { + for (auto& a : unsignedAccounts) { addAccountKeys(a); } - for(auto& a: readOnlyAccounts) { + for (auto& a : readOnlyAccounts) { addAccountKeys(a); } @@ -87,7 +82,7 @@ void Message::compileAccounts() { void Message::compileInstructions() { compiledInstructions.clear(); - for (auto instruction: instructions) { + for (auto instruction : instructions) { compiledInstructions.emplace_back(CompiledInstruction(instruction, accountKeys)); } } @@ -145,3 +140,5 @@ uint8_t Transaction::getAccountIndex(Address publicKey) { bool Signature::operator==(const Signature& v) const { return bytes == v.bytes; } + +} // namespace TW::Solana diff --git a/src/Solana/Transaction.h b/src/Solana/Transaction.h index 701d90c245b..8ea3e6c5fca 100644 --- a/src/Solana/Transaction.h +++ b/src/Solana/Transaction.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,11 +9,10 @@ #include "Address.h" #include "../Base58.h" #include "../BinaryCoding.h" -#include "../Data.h" +#include "Data.h" #include #include -#include namespace TW::Solana { @@ -28,6 +27,7 @@ const std::string SYSVAR_CLOCK_ID_ADDRESS = "SysvarC1ock111111111111111111111111 const std::string STAKE_CONFIG_ID_ADDRESS = "StakeConfig11111111111111111111111111111111"; const std::string NULL_ID_ADDRESS = "11111111111111111111111111111111"; const std::string SYSVAR_STAKE_HISTORY_ID_ADDRESS = "SysvarStakeHistory1111111111111111111111111"; +const std::string MEMO_PROGRAM_ID_ADDRESS = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"; template Data shortVecLength(std::vector vec) { @@ -98,25 +98,19 @@ struct Instruction { Instruction(const Address& programId, const std::vector& accounts, const Data& data) : programId(programId), accounts(accounts), data(data) {} - // This constructor creates a default System Transfer instruction - Instruction(const std::vector& accounts, uint64_t value) : - programId(Address(SYSTEM_PROGRAM_ID_ADDRESS)), - accounts(accounts) - { - SystemInstruction type = Transfer; + // This creator creates a default System Transfer instruction + static Instruction createTransfer(const std::vector& accounts, uint64_t value) { + const SystemInstruction type = Transfer; auto data = Data(); encode32LE(static_cast(type), data); encode64LE(static_cast(value), data); - this->data = data; + + return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); } - // This constructor creates a System CreateAccountWithSeed instruction - Instruction(const std::vector& accounts, uint64_t value, uint64_t space, const Address& programId, - const Address& voteAddress, uint64_t seedLength, const Address& signer) : - programId(Address(SYSTEM_PROGRAM_ID_ADDRESS)), - accounts(accounts) - { - SystemInstruction type = CreateAccountWithSeed; + static Instruction createAccountWithSeed(const std::vector& accounts, uint64_t value, uint64_t space, const Address& programId, + const Address& voteAddress, uint64_t seedLength, const Address& signer) { + const SystemInstruction type = CreateAccountWithSeed; auto data = Data(); std::string seed = voteAddress.string(); Data vecSeed(seed.begin(), seed.end()); @@ -128,62 +122,62 @@ struct Instruction { encode64LE(static_cast(value), data); encode64LE(static_cast(space), data); append(data, programId.vector()); - this->data = data; + + return Instruction(Address(SYSTEM_PROGRAM_ID_ADDRESS), accounts, data); } - // This constructor creates an Initialize Stake instruction - Instruction(StakeInstruction type, const std::vector& accounts, const Address& signer) : - programId(Address(STAKE_PROGRAM_ID_ADDRESS)), - accounts(accounts) - { + // creates an Initialize Stake instruction + static Instruction createStakeInitialize(const std::vector& accounts, const Address& signer) { + const StakeInstruction type = Initialize; auto data = Data(); encode32LE(static_cast(type), data); append(data, signer.vector()); append(data, signer.vector()); auto lockup = Data(48); append(data, lockup); - this->data = data; + + return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); } - // This constructor creates a Withdraw Stake instruction - Instruction(StakeInstruction type, const std::vector& accounts, uint64_t value) : - programId(Address(STAKE_PROGRAM_ID_ADDRESS)), - accounts(accounts) - { + // creates a Withdraw Stake instruction + static Instruction createStakeWithdraw(const std::vector& accounts, uint64_t value) { + const StakeInstruction type = Withdraw; auto data = Data(); encode32LE(static_cast(type), data); encode64LE(static_cast(value), data); - this->data = data; + + return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); } - // This constructor creates a Stake instruction - Instruction(StakeInstruction type, const std::vector& accounts) : - programId(Address(STAKE_PROGRAM_ID_ADDRESS)), - accounts(accounts) - { + // creates a Stake instruction + static Instruction createStake(StakeInstruction type, const std::vector& accounts) { auto data = Data(); encode32LE(static_cast(type), data); - this->data = data; + + return Instruction(Address(STAKE_PROGRAM_ID_ADDRESS), accounts, data); } - // This constructor creates a createAccount token instruction. - Instruction(TokenInstruction type, const std::vector& accounts) : - programId(Address(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS)), - accounts(accounts) - { - this->data = Data(); + // creates a createAccount token instruction. + static Instruction createTokenCreateAccount(const std::vector& accounts) { + auto data = Data(); + return Instruction(Address(ASSOCIATED_TOKEN_PROGRAM_ID_ADDRESS), accounts, data); } - // This constructor creates a transfer token instruction. - Instruction(TokenInstruction type, const std::vector& accounts, uint64_t value, uint8_t decimals) : - programId(Address(TOKEN_PROGRAM_ID_ADDRESS)), - accounts(accounts) - { + // creates a transfer token instruction. + static Instruction createTokenTransfer(const std::vector& accounts, uint64_t value, uint8_t decimals) { + const TokenInstruction type = TokenTransfer; auto data = Data(); data.push_back(static_cast(type)); encode64LE(value, data); data.push_back(static_cast(decimals)); - this->data = data; + + return Instruction(Address(TOKEN_PROGRAM_ID_ADDRESS), accounts, data); + } + + static Instruction createMemo(std::string memo) { + auto data = TW::data(memo); + std::vector accounts; // empty + return Instruction(Address(MEMO_PROGRAM_ID_ADDRESS), accounts, data); } }; @@ -290,14 +284,28 @@ class Message { // compile the instructions; replace instruction accounts with indices void compileInstructions(); + static void appendReferences(std::vector& accountMetas, const std::vector
& references) { + for (auto &&reference: references) { + accountMetas.emplace_back(reference, false, true); + } + } + // This constructor creates a default single-signer Transfer message - static Message createTransfer(const Address& from, const Address& to, uint64_t value, Hash recentBlockhash) { - auto instruction = Instruction(std::vector{ + static Message createTransfer(const Address& from, const Address& to, uint64_t value, Hash recentBlockhash, + std::string memo = "", std::vector
references = {} + ) { + std::vector instructions; + if (memo.length() > 0) { + // Optional memo. Order: before transfer, as per documentation. + instructions.push_back(Instruction::createMemo(memo)); + } + std::vector accountMetas = { AccountMeta(from, true, false), AccountMeta(to, false, false), - }, value); - std::vector instructions = {instruction}; - return Message(recentBlockhash, {instruction}); + }; + appendReferences(accountMetas, references); + instructions.push_back(Instruction::createTransfer(accountMetas, value)); + return Message(recentBlockhash, instructions); } // This constructor creates a create_account_with_seed_and_delegate_stake message @@ -309,22 +317,23 @@ class Message { auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); auto stakeProgramId = Address(STAKE_PROGRAM_ID_ADDRESS); std::vector instructions; + instructions.reserve(3); // create_account_with_seed instruction Address seed = Address(data(recentBlockhash.bytes.data(), recentBlockhash.bytes.size())); - auto createAccountInstruction = Instruction(std::vector{ + auto createAccountInstruction = Instruction::createAccountWithSeed(std::vector{ AccountMeta(signer, true, true), AccountMeta(stakeAddress, false, false), AccountMeta(signer, true, true), }, value, 200, stakeProgramId, seed, 32, signer); instructions.push_back(createAccountInstruction); // initialize instruction - auto initializeInstruction = Instruction(Initialize, std::vector{ + auto initializeInstruction = Instruction::createStakeInitialize(std::vector{ AccountMeta(stakeAddress, false, false), AccountMeta(sysvarRentId, false, true) }, signer); instructions.push_back(initializeInstruction); // delegate_stake instruction - auto delegateInstruction = Instruction(DelegateStake, + auto delegateInstruction = Instruction::createStake(DelegateStake, std::vector{ AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Initialized stake account to be delegated AccountMeta(voteAddress, false, true), // 1. `[]` Vote account to which this stake will be delegated @@ -340,7 +349,7 @@ class Message { // This constructor creates a deactivate_stake message static Message createStakeDeactivate(const Address& signer, const Address& stakeAddress, Hash recentBlockhash) { auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); - auto instruction = Instruction(Deactivate, std::vector{ + auto instruction = Instruction::createStake(Deactivate, std::vector{ AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Delegated stake account AccountMeta(sysvarClockId, false, true), // 1. `[]` Clock sysvar AccountMeta(signer, true, false), // 2. `[SIGNER]` Stake authority @@ -353,7 +362,7 @@ class Message { auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); std::vector instructions; for(auto& address: stakeAddresses) { - auto instruction = Instruction(Deactivate, std::vector{ + auto instruction = Instruction::createStake(Deactivate, std::vector{ AccountMeta(address, false, false), // 0. `[WRITE]` Delegated stake account AccountMeta(sysvarClockId, false, true), // 1. `[]` Clock sysvar AccountMeta(signer, true, false), // 2. `[SIGNER]` Stake authority @@ -367,7 +376,7 @@ class Message { static Message createStakeWithdraw(const Address& signer, const Address& stakeAddress, uint64_t value, Hash recentBlockhash) { auto sysvarClockId = Address(SYSVAR_CLOCK_ID_ADDRESS); auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); - auto instruction = Instruction(Withdraw, std::vector{ + auto instruction = Instruction::createStakeWithdraw(std::vector{ AccountMeta(stakeAddress, false, false), // 0. `[WRITE]` Stake account from which to withdraw AccountMeta(signer, false, false), // 1. `[WRITE]` Recipient account AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar @@ -383,7 +392,7 @@ class Message { auto sysvarStakeHistoryId = Address(SYSVAR_STAKE_HISTORY_ID_ADDRESS); std::vector instructions; for(auto& stake: stakes) { - auto instruction = Instruction(Withdraw, std::vector{ + auto instruction = Instruction::createStakeWithdraw(std::vector{ AccountMeta(stake.first, false, false), // 0. `[WRITE]` Stake account from which to withdraw AccountMeta(signer, false, false), // 1. `[WRITE]` Recipient account AccountMeta(sysvarClockId, false, true), // 2. `[]` Clock sysvar @@ -397,12 +406,11 @@ class Message { // This constructor creates a createAccount token message // see create_associated_token_account() solana-program-library/associated-token-account/program/src/lib.rs - static Message createTokenCreateAccount(const Address& signer, TokenInstruction type, const Address& otherMainAccount, const Address& tokenMintAddress, const Address& tokenAddress, Hash recentBlockhash) { - assert(type == TokenInstruction::CreateTokenAccount); + static Message createTokenCreateAccount(const Address& signer, const Address& otherMainAccount, const Address& tokenMintAddress, const Address& tokenAddress, Hash recentBlockhash) { auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); auto tokenProgramId = Address(TOKEN_PROGRAM_ID_ADDRESS); - auto instruction = Instruction(type, std::vector{ + auto instruction = Instruction::createTokenCreateAccount(std::vector{ AccountMeta(signer, true, false), // fundingAddress, AccountMeta(tokenAddress, false, false), AccountMeta(otherMainAccount, false, true), @@ -416,25 +424,37 @@ class Message { // This constructor creates a transfer token message. // see transfer_checked() solana-program-library/token/program/src/instruction.rs - static Message createTokenTransfer(const Address& signer, TokenInstruction type, const Address& tokenMintAddress, - const Address& senderTokenAddress, const Address& recipientTokenAddress, uint64_t amount, uint8_t decimals, Hash recentBlockhash) { - assert(type == TokenInstruction::TokenTransfer); - auto instruction = Instruction(type, std::vector{ + static Message createTokenTransfer(const Address& signer, const Address& tokenMintAddress, + const Address& senderTokenAddress, const Address& recipientTokenAddress, uint64_t amount, uint8_t decimals, Hash recentBlockhash, + std::string memo = "", std::vector
references = {} + ) { + std::vector instructions; + if (memo.length() > 0) { + // Optional memo. Order: before transfer, as per documentation. + instructions.push_back(Instruction::createMemo(memo)); + } + std::vector accountMetas = { AccountMeta(senderTokenAddress, false, false), AccountMeta(tokenMintAddress, false, true), AccountMeta(recipientTokenAddress, false, false), AccountMeta(signer, true, false), - }, amount, decimals); - return Message(recentBlockhash, {instruction}); + }; + appendReferences(accountMetas, references); + instructions.push_back(Instruction::createTokenTransfer(accountMetas, amount, decimals)); + return Message(recentBlockhash, instructions); } // This constructor creates a createAndTransferToken message, combining createAccount and transfer. static Message createTokenCreateAndTransfer(const Address& signer, const Address& recipientMainAddress, const Address& tokenMintAddress, - const Address& recipientTokenAddress, const Address& senderTokenAddress, uint64_t amount, uint8_t decimals, Hash recentBlockhash) { - auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); - auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); - auto tokenProgramId = Address(TOKEN_PROGRAM_ID_ADDRESS); - auto createInstruction = Instruction(TokenInstruction::CreateTokenAccount, std::vector{ + const Address& recipientTokenAddress, const Address& senderTokenAddress, uint64_t amount, uint8_t decimals, Hash recentBlockhash, + std::string memo = "", std::vector
references = {} + ) { + const auto sysvarRentId = Address(SYSVAR_RENT_ID_ADDRESS); + const auto systemProgramId = Address(SYSTEM_PROGRAM_ID_ADDRESS); + const auto tokenProgramId = Address(TOKEN_PROGRAM_ID_ADDRESS); + std::vector instructions; + instructions.reserve(3); + instructions.emplace_back(Instruction::createTokenCreateAccount(std::vector{ AccountMeta(signer, true, false), // fundingAddress, AccountMeta(recipientTokenAddress, false, false), AccountMeta(recipientMainAddress, false, true), @@ -442,14 +462,20 @@ class Message { AccountMeta(systemProgramId, false, true), AccountMeta(tokenProgramId, false, true), AccountMeta(sysvarRentId, false, true), - }); - auto transferInstruction = Instruction(TokenInstruction::TokenTransfer, std::vector{ + })); + if (memo.length() > 0) { + // Optional memo. Order: before transfer, as per documentation. + instructions.emplace_back(Instruction::createMemo(memo)); + } + std::vector accountMetas = { AccountMeta(senderTokenAddress, false, false), AccountMeta(tokenMintAddress, false, true), AccountMeta(recipientTokenAddress, false, false), AccountMeta(signer, true, false), - }, amount, decimals); - return Message(recentBlockhash, {createInstruction, transferInstruction}); + }; + appendReferences(accountMetas, references); + instructions.push_back(Instruction::createTokenTransfer(accountMetas, amount, decimals)); + return Message(recentBlockhash, instructions); } }; @@ -465,8 +491,8 @@ class Transaction { } // Default basic transfer transaction - Transaction(const Address& from, const Address& to, uint64_t value, Hash recentBlockhash) - : message(Message::createTransfer(from, to, value, recentBlockhash)) { + Transaction(const Address& from, const Address& to, uint64_t value, Hash recentBlockhash, std::string memo = "", std::vector
references = {}) + : message(Message::createTransfer(from, to, value, recentBlockhash, memo, references)) { this->signatures.resize(1, Signature(defaultSignature)); } @@ -480,8 +506,3 @@ class Transaction { }; } // namespace TW::Solana - -/// Wrapper for C interface. -struct TWSolanaTransaction { - TW::Solana::Transaction impl; -}; diff --git a/src/Stellar/Address.cpp b/src/Stellar/Address.cpp index c7197b89451..84b99a62bae 100644 --- a/src/Stellar/Address.cpp +++ b/src/Stellar/Address.cpp @@ -5,9 +5,9 @@ // file LICENSE at the root of the source code distribution tree. #include "Address.h" +#include "Crc.h" #include "../Base32.h" #include "../HexCoding.h" -#include "Crc.h" #include #include @@ -15,7 +15,7 @@ #include #include -using namespace TW::Stellar; +namespace TW::Stellar { bool Address::isValid(const std::string& string) { bool valid = false; @@ -82,3 +82,5 @@ std::string Address::string() const { auto out = Base32::encode(bytesAsData); return out; } + +} // namespace TW::Stellar diff --git a/src/Stellar/Address.h b/src/Stellar/Address.h index 6183aaeaace..6f42dd14926 100644 --- a/src/Stellar/Address.h +++ b/src/Stellar/Address.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/Stellar/Entry.cpp b/src/Stellar/Entry.cpp index 0e1beb09ca3..a2412dcb5dd 100644 --- a/src/Stellar/Entry.cpp +++ b/src/Stellar/Entry.cpp @@ -9,19 +9,20 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Stellar; -using namespace std; +namespace TW::Stellar { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Stellar diff --git a/src/Stellar/Entry.h b/src/Stellar/Entry.h index ed070efadf1..f4236c325df 100644 --- a/src/Stellar/Entry.h +++ b/src/Stellar/Entry.h @@ -12,12 +12,11 @@ namespace TW::Stellar { /// Entry point for implementation of Stellar coin, and Kin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeStellar, TWCoinTypeKin}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Stellar diff --git a/src/Stellar/Signer.cpp b/src/Stellar/Signer.cpp index ba11047c747..5d48a71551b 100644 --- a/src/Stellar/Signer.cpp +++ b/src/Stellar/Signer.cpp @@ -4,8 +4,8 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Base64.h" #include "Signer.h" +#include "Base64.h" #include "../BinaryCoding.h" #include "../Hash.h" #include "../HexCoding.h" @@ -14,8 +14,8 @@ #include using namespace TW; -using namespace TW::Stellar; +namespace TW::Stellar { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto signer = Signer(input); auto output = Proto::SigningOutput(); @@ -25,12 +25,12 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { std::string Signer::sign() const noexcept { - auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); - auto account = Address(input.account()); - auto encoded = encode(input); + auto key = PrivateKey(Data(_input.private_key().begin(), _input.private_key().end())); + auto account = Address(_input.account()); + auto encoded = encode(_input); auto encodedWithHeaders = Data(); - auto publicNetwork = input.passphrase(); // Header + auto publicNetwork = _input.passphrase(); // Header auto passphrase = Hash::sha256(publicNetwork); encodedWithHeaders.insert(encodedWithHeaders.end(), passphrase.begin(), passphrase.end()); auto transactionType = Data{0, 0, 0, 2}; // Header @@ -64,7 +64,7 @@ Data Signer::encode(const Proto::SigningInput& input) const { // Time bounds if (input.has_op_change_trust() && input.op_change_trust().valid_before() != 0) { encode32BE(1, data); - encode64BE(0, data); // from + encode64BE(0, data); // from encode64BE(input.op_change_trust().valid_before(), data); // to } else { encode32BE(0, data); // missing @@ -91,27 +91,51 @@ Data Signer::encode(const Proto::SigningInput& input) const { } // Operations - encode32BE(1, data); // Operation list size. Only 1 operation. - encode32BE(0, data); // Source equals account + encode32BE(1, data); // Operation list size. Only 1 operation. + encode32BE(0, data); // Source equals account encode32BE(operationType(input), data); // Operation type switch (input.operation_oneof_case()) { - case Proto::SigningInput::kOpCreateAccount: - default: - encodeAddress(Address(input.op_create_account().destination()), data); - encode64BE(input.op_create_account().amount(), data); - break; - - case Proto::SigningInput::kOpPayment: - encodeAddress(Address(input.op_payment().destination()), data); - encodeAsset(input.op_payment().asset(), data); - encode64BE(input.op_payment().amount(), data); - break; - - case Proto::SigningInput::kOpChangeTrust: - encodeAsset(input.op_change_trust().asset(), data); - encode64BE(0x7fffffffffffffff, data); // limit MAX - break; + case Proto::SigningInput::kOpCreateAccount: + default: + encodeAddress(Address(input.op_create_account().destination()), data); + encode64BE(input.op_create_account().amount(), data); + break; + + case Proto::SigningInput::kOpPayment: + encodeAddress(Address(input.op_payment().destination()), data); + encodeAsset(input.op_payment().asset(), data); + encode64BE(input.op_payment().amount(), data); + break; + + case Proto::SigningInput::kOpChangeTrust: + encodeAsset(input.op_change_trust().asset(), data); + encode64BE(0x7fffffffffffffff, data); // limit MAX + break; + + case Proto::SigningInput::kOpCreateClaimableBalance: { + const auto ClaimantTypeV0 = 0; + encodeAsset(input.op_create_claimable_balance().asset(), data); + encode64BE(input.op_create_claimable_balance().amount(), data); + auto nClaimants = input.op_create_claimable_balance().claimants_size(); + encode32BE((uint32_t)nClaimants, data); + for (auto i = 0; i < nClaimants; ++i) { + encode32BE((uint32_t)ClaimantTypeV0, data); + encodeAddress(Address(input.op_create_claimable_balance().claimants(i).account()), data); + encode32BE((uint32_t)input.op_create_claimable_balance().claimants(i).predicate(), data); + // Note: other predicates not supported, predicate-specific data would follow here + } + } break; + + case Proto::SigningInput::kOpClaimClaimableBalance: { + const auto ClaimableBalanceIdTypeClaimableBalanceIdTypeV0 = 0; + encode32BE((uint32_t)ClaimableBalanceIdTypeClaimableBalanceIdTypeV0, data); + const auto balanceId = input.op_claim_claimable_balance().balance_id(); + if (balanceId.size() != 32) { + return Data(); + } + data.insert(data.end(), balanceId.begin(), balanceId.end()); + } break; } encode32BE(0, data); // Ext @@ -120,13 +144,17 @@ Data Signer::encode(const Proto::SigningInput& input) const { uint32_t Signer::operationType(const Proto::SigningInput& input) { switch (input.operation_oneof_case()) { - case Proto::SigningInput::kOpCreateAccount: - default: - return 0; - case Proto::SigningInput::kOpPayment: - return 1; - case Proto::SigningInput::kOpChangeTrust: - return 6; + case Proto::SigningInput::kOpCreateAccount: + default: + return 0; + case Proto::SigningInput::kOpPayment: + return 1; + case Proto::SigningInput::kOpChangeTrust: + return 6; + case Proto::SigningInput::kOpCreateClaimableBalance: + return 14; + case Proto::SigningInput::kOpClaimClaimableBalance: + return 15; } } @@ -144,7 +172,7 @@ void Signer::encodeAsset(const Proto::Asset& asset, Data& data) { } encode32BE(assetType, data); if (assetType > 0) { - for (auto i = 0; i < 4; ++i) { + for (auto i = 0ul; i < 4; ++i) { if (alphaUse.length() > i) { data.push_back(alphaUse[i]); } else { @@ -165,3 +193,5 @@ void Signer::pad(Data& data) const { data.insert(data.end(), 0); } } + +} // namespace TW::Stellar diff --git a/src/Stellar/Signer.h b/src/Stellar/Signer.h index c38896000b2..7cf60a41094 100644 --- a/src/Stellar/Signer.h +++ b/src/Stellar/Signer.h @@ -6,7 +6,7 @@ #pragma once #include "Address.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/Stellar.pb.h" @@ -20,9 +20,9 @@ class Signer { /// Signs a Proto::SigningInput transaction static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; public: - const Proto::SigningInput& input; + const Proto::SigningInput& _input; - Signer(const Proto::SigningInput& input) : input(input) {} + Signer(const Proto::SigningInput& input) : _input(input) {} /// Signs the given transaction. std::string sign() const noexcept; diff --git a/src/THORChain/Entry.cpp b/src/THORChain/Entry.cpp index 67faa8a23dc..02c1d4f97c0 100644 --- a/src/THORChain/Entry.cpp +++ b/src/THORChain/Entry.cpp @@ -9,18 +9,21 @@ #include "Signer.h" #include "../proto/Cosmos.pb.h" -using namespace TW::THORChain; using namespace std; +namespace TW::THORChain { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { auto input = Cosmos::Proto::SigningInput(); input.ParseFromArray(dataIn.data(), (int)dataIn.size()); auto serializedOut = Signer::sign(input).SerializeAsString(); dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end()); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +} // namespace TW::THORChain diff --git a/src/THORChain/Entry.h b/src/THORChain/Entry.h index 5d5bd14c1de..a5650e5b94a 100644 --- a/src/THORChain/Entry.h +++ b/src/THORChain/Entry.h @@ -13,13 +13,10 @@ namespace TW::THORChain { /// Entry point for implementation of THORChain coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public Cosmos::Entry { +class Entry final : public Cosmos::Entry { public: - virtual const std::vector coinTypes() const { - return { TWCoinTypeTHORChain }; - } - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::THORChain diff --git a/src/THORChain/Signer.cpp b/src/THORChain/Signer.cpp index c6f768cc197..30ebb84217b 100644 --- a/src/THORChain/Signer.cpp +++ b/src/THORChain/Signer.cpp @@ -8,11 +8,12 @@ #include "../Cosmos/Signer.h" #include "../proto/Cosmos.pb.h" +#include #include using namespace TW; -using namespace TW::THORChain; +namespace TW::THORChain { const std::string TYPE_PREFIX_MSG_SEND = "thorchain/MsgSend"; Cosmos::Proto::SigningOutput Signer::sign(Cosmos::Proto::SigningInput& input) noexcept { @@ -21,7 +22,7 @@ Cosmos::Proto::SigningOutput Signer::sign(Cosmos::Proto::SigningInput& input) no input.mutable_messages(i)->mutable_send_coins_message()->set_type_prefix(TYPE_PREFIX_MSG_SEND); } } - return Cosmos::Signer::sign(input); + return Cosmos::Signer::sign(input, TWCoinTypeTHORChain); } std::string Signer::signJSON(const std::string& json, const Data& key) { @@ -31,3 +32,5 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { auto output = Signer::sign(input); return output.json(); } + +} // namespace TW::THORChain diff --git a/src/THORChain/Signer.h b/src/THORChain/Signer.h index 5488f281291..e524abfb5ee 100644 --- a/src/THORChain/Signer.h +++ b/src/THORChain/Signer.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../proto/Cosmos.pb.h" #include diff --git a/src/THORChain/Swap.cpp b/src/THORChain/Swap.cpp new file mode 100644 index 00000000000..17d47ebf51e --- /dev/null +++ b/src/THORChain/Swap.cpp @@ -0,0 +1,233 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Swap.h" + +#include +#include "Coin.h" +#include "proto/THORChainSwap.pb.h" +// BTC +#include "Bitcoin/SigHashType.h" +#include "../proto/Bitcoin.pb.h" +// ETH +#include "Ethereum/Address.h" +#include "Ethereum/ABI/Function.h" +#include "Ethereum/ABI/ParamBase.h" +#include "Ethereum/ABI/ParamAddress.h" +#include "uint256.h" +#include "../proto/Ethereum.pb.h" +// BNB +#include "Binance/Address.h" +#include "../proto/Binance.pb.h" + +#include + +/* + * References: + * https://gitlab.com/thorchain/asgardex-common/asgardex-util + */ + +namespace TW::THORChainSwap { + +TWCoinType chainCoinType(Chain chain) { + switch (chain) { + case Chain::ETH: return TWCoinTypeEthereum; + case Chain::BNB: return TWCoinTypeBinance; + case Chain::BTC: return TWCoinTypeBitcoin; + case Chain::THOR: + default: + return TWCoinTypeTHORChain; + } +} + +std::string chainName(Chain chain) { + switch (chain) { + case Chain::ETH: return "ETH"; + case Chain::BNB: return "BNB"; + case Chain::BTC: return "BTC"; + case Chain::THOR: + default: + return "THOR"; + } +} + +std::string Swap::buildMemo(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& toAddress, uint64_t limit) { + std::string prefix = "SWAP"; + if (toChain == Chain::ETH) { + prefix = "="; + } + const auto toCoinToken = (!toTokenId.empty() && toTokenId != "0x0000000000000000000000000000000000000000") ? toTokenId : toSymbol; + return prefix + ":" + chainName(toChain) + "." + toCoinToken + ":" + toAddress + ":" + std::to_string(limit); +} + +bool validateAddress(Chain chain, const std::string& address) { + return TW::validateAddress(chainCoinType(chain), address); +} + +std::tuple Swap::build( + Chain fromChain, + Chain toChain, + const std::string& fromAddress, + const std::string& toSymbol, + const std::string& toTokenId, + const std::string& toAddress, + const std::string& vaultAddress, + const std::string& routerAddress, + const std::string& fromAmount, + const std::string& toAmountLimit +) { + if (!validateAddress(fromChain, fromAddress)) { + return std::make_tuple({}, static_cast(Proto::ErrorCode::Error_Invalid_from_address), "Invalid from address"); + } + if (!validateAddress(toChain, toAddress)) { + return std::make_tuple({}, static_cast(Proto::ErrorCode::Error_Invalid_to_address), "Invalid to address"); + } + + uint64_t fromAmountNum = std::atoll(fromAmount.c_str()); + uint64_t toAmountLimitNum = std::atoll(toAmountLimit.c_str()); + const auto memo = buildMemo(toChain, toSymbol, toTokenId, toAddress, toAmountLimitNum); + + switch (fromChain) { + case Chain::BTC: { + Data out; + auto res = buildBitcoin(toChain, toSymbol, toTokenId, fromAddress, toAddress, vaultAddress, fromAmountNum, memo, out); + return std::make_tuple(std::move(out), std::move(std::get<0>(res)), std::move(std::get<1>(res))); + } + + case Chain::ETH: { + Data out; + auto res = buildEthereum(toChain, toSymbol, toTokenId, fromAddress, toAddress, vaultAddress, routerAddress, fromAmountNum, memo, out); + return std::make_tuple(std::move(out), std::move(std::get<0>(res)), std::move(std::get<1>(res))); + } + + case Chain::BNB: { + Data out; + auto res = buildBinance(toChain, toSymbol, toTokenId, fromAddress, toAddress, vaultAddress, fromAmountNum, memo, out); + return std::make_tuple(std::move(out), std::move(std::get<0>(res)), std::move(std::get<1>(res))); + } + + case Chain::THOR: + default: + return std::make_tuple({}, static_cast(Proto::ErrorCode::Error_Unsupported_from_chain), "Unsupported from chain: " + std::to_string(toChain)); + } +} + +std::pair Swap::buildBitcoin([[maybe_unused]] Chain toChain, [[maybe_unused]] const std::string& toSymbol, [[maybe_unused]] const std::string& toTokenId, const std::string& fromAddress, [[maybe_unused]] const std::string& toAddress, const std::string& vaultAddress, uint64_t amount, const std::string& memo, Data& out) { + auto input = Bitcoin::Proto::SigningInput(); + + // Following fields must be set afterwards, before signing ... + input.set_hash_type(TWBitcoinSigHashTypeAll); + input.set_byte_fee(1); + input.set_use_max_amount(false); + // private_key[] + // utxo[] + // scripts[] + // ... end + + input.set_amount(amount); + input.set_to_address(vaultAddress); + input.set_change_address(fromAddress); + input.set_coin_type(TWCoinTypeBitcoin); + input.set_output_op_return(memo); + + auto serialized = input.SerializeAsString(); + out.insert(out.end(), serialized.begin(), serialized.end()); + return std::make_pair(0, ""); +} + +Data ethAddressStringToData(const std::string& asString) { + Data asData(20); + if (asString.empty() || !Ethereum::Address::isValid(asString)) { + return asData; + } + auto address = Ethereum::Address(asString); + std::copy(address.bytes.begin(), address.bytes.end(), asData.data()); + return asData; +} + +std::pair Swap::buildEthereum([[maybe_unused]] Chain toChain, [[maybe_unused]] const std::string& toSymbol, const std::string& toTokenId, [[maybe_unused]] const std::string& fromAddress, [[maybe_unused]] const std::string& toAddress, const std::string& vaultAddress, const std::string& routerAddress, uint64_t amount, const std::string& memo, Data& out) { + auto input = Ethereum::Proto::SigningInput(); + + // some sanity check / address conversion + Data vaultAddressBin = ethAddressStringToData(vaultAddress); + if (!Ethereum::Address::isValid(vaultAddress) || vaultAddressBin.size() != Ethereum::Address::size) { + return std::make_pair(static_cast(Proto::ErrorCode::Error_Invalid_vault_address), "Invalid vault address: " + vaultAddress); + } + if (!Ethereum::Address::isValid(routerAddress)) { + return std::make_pair(static_cast(Proto::ErrorCode::Error_Invalid_router_address), "Invalid router address: " + routerAddress); + } + Data toAssetAddressBin = ethAddressStringToData(toTokenId); + + // Following fields must be set afterwards, before signing ... + const auto chainId = store(uint256_t(0)); + input.set_chain_id(chainId.data(), chainId.size()); + const auto nonce = store(uint256_t(0)); + input.set_nonce(nonce.data(), nonce.size()); + const auto gasPrice = store(uint256_t(0)); + input.set_gas_price(gasPrice.data(), gasPrice.size()); + const auto gasLimit = store(uint256_t(0)); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_private_key(""); + // ... end + + input.set_to_address(routerAddress); + auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); + auto func = Ethereum::ABI::Function("deposit", std::vector>{ + std::make_shared(vaultAddressBin), + std::make_shared(toAssetAddressBin), + std::make_shared(uint256_t(amount)), + std::make_shared(memo) + }); + Data payload; + func.encode(payload); + transfer.set_data(payload.data(), payload.size()); + Data amountData = store(uint256_t(amount)); + transfer.set_amount(amountData.data(), amountData.size()); + + auto serialized = input.SerializeAsString(); + out.insert(out.end(), serialized.begin(), serialized.end()); + return std::make_pair(0, ""); +} + +std::pair Swap::buildBinance([[maybe_unused]] Chain toChain, [[maybe_unused]] const std::string& toSymbol, [[maybe_unused]] const std::string& toTokenId, const std::string& fromAddress, [[maybe_unused]] const std::string& toAddress, const std::string& vaultAddress, uint64_t amount, const std::string& memo, Data& out) { + auto input = Binance::Proto::SigningInput(); + + // Following fields must be set afterwards, before signing ... + input.set_chain_id(""); + input.set_account_number(0); + input.set_sequence(0); + input.set_source(0); + input.set_private_key(""); + // ... end + + input.set_memo(memo); + + auto& order = *input.mutable_send_order(); + + auto token = Binance::Proto::SendOrder::Token(); + token.set_denom("BNB"); + token.set_amount(amount); + { + Binance::Address fromAddressBin; + Binance::Address::decode(fromAddress, fromAddressBin); + auto input_ = order.add_inputs(); + input_->set_address(fromAddressBin.getKeyHash().data(), fromAddressBin.getKeyHash().size()); + *input_->add_coins() = token; + } + { + Binance::Address vaultAddressBin; + Binance::Address::decode(vaultAddress, vaultAddressBin); + auto output = order.add_outputs(); + output->set_address(vaultAddressBin.getKeyHash().data(), vaultAddressBin.getKeyHash().size()); + *output->add_coins() = token; + } + + auto serialized = input.SerializeAsString(); + out.insert(out.end(), serialized.begin(), serialized.end()); + return std::make_pair(0, ""); +} + +} // namespace TW diff --git a/src/THORChain/Swap.h b/src/THORChain/Swap.h new file mode 100644 index 00000000000..723b4ee83f2 --- /dev/null +++ b/src/THORChain/Swap.h @@ -0,0 +1,52 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" + +#include +#include + +namespace TW::THORChainSwap { + +/// Supported blockchains +enum Chain { + THOR = 0, + BTC = 1, + ETH = 2, + BNB = 3, +}; + +/// Building THORChain cross-chain transactions +class Swap { +public: + /// Logic to build a native transaction on the source chain for a swap + /// Returns serialized SigningInput proto message, on the source chain, + /// and an optional error code + message + static std::tuple build( + Chain fromChain, + Chain toChain, + const std::string& fromAddress, // source address, on source chain, string format + const std::string& toSymbol, // destination coin symbol + const std::string& toTokenId, // destination token ID, on the destination chain, in case destination is a token, empty otherwise + const std::string& toAddress, // destination address, on destination chain, string format + const std::string& vaultAddress, // ThorChainSwap vault, on the source chain. Should be queried afresh, as it may change + const std::string& routerAddress, // ThorChain router, only in case of Ethereum source network + const std::string& fromAmount, // The source amount, as integer in the smallest native unit of the chain + const std::string& toAmountLimit // The minimum accepted destination amount. Actual destination amount will depend on current rates, limit amount can be used to prevent using very unfavorable rates. + ); + +protected: + static std::pair buildBitcoin(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& fromAddress, const std::string& toAddress, const std::string& vaultAddress, uint64_t amount, const std::string& memo, Data& out); + static std::pair buildEthereum(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& fromAddress, const std::string& toAddress, const std::string& vaultAddress, const std::string& routerAddress, uint64_t amount, const std::string& memo, Data& out); + static std::pair buildBinance(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& fromAddress, const std::string& toAddress, const std::string& vaultAddress, uint64_t amount, const std::string& memo, Data& out); + +public: + static std::string buildMemo(Chain toChain, const std::string& toSymbol, const std::string& toTokenId, const std::string& toAddress, uint64_t limit); +}; + +} // namespace TW diff --git a/src/THORChain/TWSwap.cpp b/src/THORChain/TWSwap.cpp new file mode 100644 index 00000000000..9f1341c50fe --- /dev/null +++ b/src/THORChain/TWSwap.cpp @@ -0,0 +1,92 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Data.h" +#include "Swap.h" +#include "proto/THORChainSwap.pb.h" +#include + +using namespace TW; + +TWData* _Nonnull TWTHORChainSwapBuildSwap(TWData* _Nonnull input) { + THORChainSwap::Proto::SwapInput inputProto; + THORChainSwap::Proto::SwapOutput outputProto; + if (!inputProto.ParseFromArray(TWDataBytes(input), static_cast(TWDataSize(input)))) { + // error + outputProto.mutable_error()->set_code(THORChainSwap::Proto::ErrorCode::Error_Input_proto_deserialization); + outputProto.mutable_error()->set_message("Could not deserialize input proto"); + auto outputData = TW::data(outputProto.SerializeAsString()); + return TWDataCreateWithBytes(outputData.data(), outputData.size()); + } + + const auto fromChain = inputProto.from_chain(); + const auto toChain = inputProto.to_asset().chain(); + auto res = THORChainSwap::Swap::build( + static_cast(static_cast(fromChain)), + static_cast(static_cast(toChain)), + inputProto.from_address(), + inputProto.to_asset().symbol(), + inputProto.to_asset().token_id(), + inputProto.to_address(), + inputProto.vault_address(), + inputProto.router_address(), + inputProto.from_amount(), + inputProto.to_amount_limit()); + + outputProto.set_from_chain(fromChain); + outputProto.set_to_chain(toChain); + if (std::get<1>(res) != 0) { + // error + outputProto.mutable_error()->set_code(static_cast(std::get<1>(res))); + outputProto.mutable_error()->set_message(std::get<2>(res)); + } else { + // no error + outputProto.mutable_error()->set_code(THORChainSwap::Proto::ErrorCode::OK); + outputProto.mutable_error()->set_message(""); + + const Data& txInput = std::get<0>(res); + switch (fromChain) { + case THORChainSwap::Proto::BTC: { + Bitcoin::Proto::SigningInput btcInput; + if (!btcInput.ParseFromArray(txInput.data(), static_cast(txInput.size()))) { + outputProto.mutable_error()->set_code(THORChainSwap::Proto::ErrorCode::Error_Input_proto_deserialization); + outputProto.mutable_error()->set_message("Could not deserialize BTC input"); + } else { + *outputProto.mutable_bitcoin() = btcInput; + } + } break; + + case THORChainSwap::Proto::ETH: { + Ethereum::Proto::SigningInput ethInput; + if (!ethInput.ParseFromArray(txInput.data(), static_cast(txInput.size()))) { + outputProto.mutable_error()->set_code(THORChainSwap::Proto::ErrorCode::Error_Input_proto_deserialization); + outputProto.mutable_error()->set_message("Could not deserialize ETH input"); + } else { + *outputProto.mutable_ethereum() = ethInput; + } + } break; + + case THORChainSwap::Proto::BNB: { + Binance::Proto::SigningInput bnbInput; + if (!bnbInput.ParseFromArray(txInput.data(), static_cast(txInput.size()))) { + outputProto.mutable_error()->set_code(THORChainSwap::Proto::ErrorCode::Error_Input_proto_deserialization); + outputProto.mutable_error()->set_message("Could not deserialize BNB input"); + } else { + *outputProto.mutable_binance() = bnbInput; + } + } break; + + default: + outputProto.mutable_error()->set_code(THORChainSwap::Proto::ErrorCode::Error_Unsupported_from_chain); + outputProto.mutable_error()->set_message(std::string("Unsupported from chain ") + std::to_string(fromChain)); + break; + } + } + + // serialize output + auto outputData = TW::data(outputProto.SerializeAsString()); + return TWDataCreateWithBytes(outputData.data(), outputData.size()); +} diff --git a/src/Tezos/Address.cpp b/src/Tezos/Address.cpp index 046bab28ce6..e351ce4d776 100644 --- a/src/Tezos/Address.cpp +++ b/src/Tezos/Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -15,13 +15,12 @@ #include -using namespace TW; -using namespace TW::Tezos; +namespace TW::Tezos { /// Address prefixes. -const std::array tz1Prefix{6, 161, 159}; -const std::array tz2Prefix{6, 161, 161}; -const std::array tz3Prefix{6, 161, 164}; +const std::array tz1Prefix{6, 161, 159}; +const std::array tz2Prefix{6, 161, 161}; +const std::array tz3Prefix{6, 161, 164}; bool Address::isValid(const std::string& string) { const auto decoded = Base58::bitcoin.decodeCheck(string); @@ -67,3 +66,5 @@ Data Address::forge() const { std::string s = string(); return forgePublicKeyHash(s); } + +} // namespace TW::Tezos diff --git a/src/Tezos/Address.h b/src/Tezos/Address.h index 88fb46bc79e..eb360abc6bd 100644 --- a/src/Tezos/Address.h +++ b/src/Tezos/Address.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,7 +7,7 @@ #pragma once #include "../Base58Address.h" -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/Tezos/BinaryCoding.cpp b/src/Tezos/BinaryCoding.cpp index 35f2d9cee2b..1f030be4cfd 100644 --- a/src/Tezos/BinaryCoding.cpp +++ b/src/Tezos/BinaryCoding.cpp @@ -1,11 +1,11 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "../Base58.h" -#include "../Data.h" +#include "Data.h" #include "../HexCoding.h" #include "../PublicKey.h" #include "../PrivateKey.h" @@ -13,9 +13,9 @@ #include #include -using namespace TW; +namespace TW::Tezos { -std::string base58ToHex(const std::string& string, size_t prefixLength, uint8_t* prefix) { +std::string base58ToHex(const std::string& string, size_t prefixLength) { const auto decoded = Base58::bitcoin.decodeCheck(string); if (decoded.size() < prefixLength) { return ""; @@ -40,11 +40,13 @@ PublicKey parsePublicKey(const std::string& publicKey) { PrivateKey parsePrivateKey(const std::string& privateKey) { const auto decoded = Base58::bitcoin.decodeCheck(privateKey); auto pk = Data(); - auto prefix_size = 4; + auto prefix_size = 4ul; if (decoded.size() != 32 + prefix_size) { throw std::invalid_argument("Invalid Public Key"); } append(pk, Data(decoded.begin() + prefix_size, decoded.end())); return PrivateKey(pk); -} \ No newline at end of file +} + +} // namespace TW::Tezos diff --git a/src/Tezos/BinaryCoding.h b/src/Tezos/BinaryCoding.h index 837a4e48b94..9bee6a3bd46 100644 --- a/src/Tezos/BinaryCoding.h +++ b/src/Tezos/BinaryCoding.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,14 +6,16 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include "../PrivateKey.h" #include -using namespace TW; +namespace TW::Tezos { PublicKey parsePublicKey(const std::string& publicKey); PrivateKey parsePrivateKey(const std::string& privateKey); -std::string base58ToHex(const std::string& data, size_t prefixLength, uint8_t* prefix); +std::string base58ToHex(const std::string& data, size_t prefixLength); + +} // namespace TW::Tezos diff --git a/src/Tezos/Entry.cpp b/src/Tezos/Entry.cpp index 2597237bb92..71aa8246a81 100644 --- a/src/Tezos/Entry.cpp +++ b/src/Tezos/Entry.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,23 +9,24 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Tezos; -using namespace std; +namespace TW::Tezos { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +std::string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +} // namespace TW::Tezos diff --git a/src/Tezos/Entry.h b/src/Tezos/Entry.h index 2a705849f32..bea87ca0aa3 100644 --- a/src/Tezos/Entry.h +++ b/src/Tezos/Entry.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -12,14 +12,13 @@ namespace TW::Tezos { /// Entry point for implementation of Tezos coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeTezos}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Tezos diff --git a/src/Tezos/Forging.cpp b/src/Tezos/Forging.cpp index b288b0935bf..83088b3f3c0 100644 --- a/src/Tezos/Forging.cpp +++ b/src/Tezos/Forging.cpp @@ -1,21 +1,29 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#include "Forging.h" #include "Address.h" #include "BinaryCoding.h" -#include "../Base58.h" -#include "../Data.h" #include "../HexCoding.h" #include "../proto/Tezos.pb.h" - #include -using namespace TW; -using namespace TW::Tezos; -using namespace TW::Tezos::Proto; +namespace TW::Tezos { + +namespace { + +constexpr const char* gTezosContractAddressPrefix{"KT1"}; + +void encodePrefix(const std::string& address, Data& forged) { + const auto decoded = Base58::bitcoin.decodeCheck(address); + constexpr auto prefixSize{3}; + forged.insert(forged.end(), decoded.begin() + prefixSize, decoded.end()); +} + +} // namespace // Forge the given boolean into a hex encoded string. Data forgeBool(bool input) { @@ -23,6 +31,39 @@ Data forgeBool(bool input) { return Data{result}; } +Data forgeInt32(int value, int len) { + Data out(len); + for (int i = len - 1; i >= 0; i--, value >>= 8) { + out[i] = (value & 0xFF); + } + return out; +} + +Data forgeString(const std::string& value, std::size_t len) { + auto bytes = data(value); + auto result = forgeInt32(static_cast(bytes.size()), static_cast(len)); + append(result, bytes); + return result; +} + +Data forgeEntrypoint(const std::string& value) { + if (value == "default") + return Data{0x00}; + else if (value == "root") + return Data{0x01}; + else if (value == "do") + return Data{0x02}; + else if (value == "set_delegate") + return Data{0x03}; + else if (value == "remove_delegate") + return Data{0x04}; + else { + Data forged{0xff}; + append(forged, forgeString(value, 1)); + return forged; + } +} + // Forge the given public key hash into a hex encoded string. // Note: This function supports tz1, tz2 and tz3 addresses. Data forgePublicKeyHash(const std::string& publicKeyHash) { @@ -41,12 +82,31 @@ Data forgePublicKeyHash(const std::string& publicKeyHash) { default: throw std::invalid_argument("Invalid Prefix"); } - const auto decoded = Base58::bitcoin.decodeCheck(publicKeyHash); - const auto prefixSize = 3; - forged.insert(forged.end(), decoded.begin() + prefixSize, decoded.end()); + encodePrefix(publicKeyHash, forged); return forged; } +Data forgeAddress(const std::string& address) { + if (address.size() < 3) { + throw std::invalid_argument("Invalid address size"); + } + auto prefix = address.substr(0, 3); + + if (prefix == "tz1" || prefix == "tz2" || prefix == "tz3") { + Data forged{0x00}; + append(forged, forgePublicKeyHash(address)); + return forged; + } + + if (prefix == gTezosContractAddressPrefix) { + Data forged{0x01}; + encodePrefix(address, forged); + forged.emplace_back(0x00); + return forged; + } + throw std::invalid_argument("Invalid Prefix"); +} + // Forge the given public key into a hex encoded string. Data forgePublicKey(PublicKey publicKey) { std::array prefix = {13, 15, 37, 217}; @@ -55,7 +115,7 @@ Data forgePublicKey(PublicKey publicKey) { append(data, bytes); auto pk = Base58::bitcoin.encodeCheck(data); - auto decoded = "00" + base58ToHex(pk, 4, prefix.data()); + auto decoded = "00" + base58ToHex(pk, 4); return parse_hex(decoded); } @@ -63,15 +123,16 @@ Data forgePublicKey(PublicKey publicKey) { Data forgeZarith(uint64_t input) { Data forged = Data(); while (input >= 0x80) { - forged.push_back(static_cast((input & 0xff) | 0x80)); + forged.push_back(static_cast((input & 0xff) | 0x80)); input >>= 7; } - forged.push_back(static_cast(input)); + forged.push_back(static_cast(input)); return forged; } // Forge the given operation. -Data forgeOperation(const Operation& operation) { +Data forgeOperation(const Proto::Operation& operation) { + using namespace Proto; auto forged = Data(); auto source = Address(operation.source()); auto forgedSource = source.forge(); @@ -83,7 +144,7 @@ Data forgeOperation(const Operation& operation) { if (operation.kind() == Operation_OperationKind_REVEAL) { auto publicKey = PublicKey(data(operation.reveal_operation_data().public_key()), TWPublicKeyTypeED25519); auto forgedPublicKey = forgePublicKey(publicKey); - + forged.push_back(Operation_OperationKind_REVEAL); append(forged, forgedSource); append(forged, forgedFee); @@ -118,18 +179,110 @@ Data forgeOperation(const Operation& operation) { auto forgedAmount = forgeZarith(operation.transaction_operation_data().amount()); auto forgedDestination = Address(operation.transaction_operation_data().destination()).forge(); - forged.push_back(Operation_OperationKind_TRANSACTION); + forged.emplace_back(Operation_OperationKind_TRANSACTION); append(forged, forgedSource); append(forged, forgedFee); append(forged, forgedCounter); append(forged, forgedGasLimit); append(forged, forgedStorageLimit); append(forged, forgedAmount); - append(forged, forgeBool(false)); - append(forged, forgedDestination); - append(forged, forgeBool(false)); + if (!operation.transaction_operation_data().has_parameters()) { + append(forged, forgeBool(false)); + append(forged, forgedDestination); + append(forged, forgeBool(false)); + } else if (operation.transaction_operation_data().has_parameters()) { + append(forged, forgeAddress(operation.transaction_operation_data().destination())); + append(forged, forgeBool(true)); + auto& parameters = operation.transaction_operation_data().parameters(); + switch (parameters.parameters_case()) { + case OperationParameters::kFa12Parameters: + append(forged, forgeEntrypoint(parameters.fa12_parameters().entrypoint())); + append(forged, forgeArray(forgeMichelson(FA12ParameterToMichelson(parameters.fa12_parameters())))); + break; + case OperationParameters::kFa2Parameters: + append(forged, forgeEntrypoint(parameters.fa2_parameters().entrypoint())); + append(forged, forgeArray(forgeMichelson(FA2ParameterToMichelson(parameters.fa2_parameters())))); + break; + case OperationParameters::PARAMETERS_NOT_SET: + break; + } + } return forged; } throw std::invalid_argument("Invalid operation kind"); } + +Data forgePrim(const PrimValue& value) { + Data forged; + if (value.prim == "Pair") { + // https://tezos.gitlab.io/developer/encodings.html?highlight=pair#pairs + forged.reserve(2); + constexpr uint8_t nbArgs = 2; + // https://github.com/ecadlabs/taquito/blob/fd84d627171d24ce7ba81dd7b18763a95f16a99c/packages/taquito-local-forging/src/michelson/codec.ts#L195 + // https://github.com/baking-bad/netezos/blob/0bfd6db4e85ab1c99fb55503e476fe67cebd2dc5/Netezos/Forging/Local/LocalForge.Forgers.cs#L199 + const uint8_t preamble = static_cast(std::min(2 * nbArgs + static_cast(value.anots.size()) + 0x03, 9)); + forged.emplace_back(preamble); + forged.emplace_back(PrimType::Pair); + Data subForged; + for (auto&& cur : value.args) { + append(subForged, forgeMichelson(cur.value)); + } + append(forged, subForged); + } + return forged; +} + +Data forgeMichelson(const MichelsonValue::MichelsonVariant& value) { + auto visit_functor = [](const MichelsonValue::MichelsonVariant& value) -> Data { + if (std::holds_alternative(value)) { + return forgePrim(std::get(value)); + } else if (std::holds_alternative(value)) { + Data forged{1}; + append(forged, forgeString(std::get(value).string)); + return forged; + } else if (std::holds_alternative(value)) { + Data forged{0}; + auto res = int256_t(std::get(value)._int); + append(forged, forgeMichelInt(res)); + return forged; + } else if (std::holds_alternative(value)) { + return {}; + } else if (std::holds_alternative(value)) { + // array + Data forged{2}; + Data subForged; + auto array = std::get(value); + for (auto&& cur : array) { + std::visit([&subForged](auto&& arg) { append(subForged, forgeMichelson(arg)); }, cur); + } + append(forged, forgeArray(subForged)); + return forged; + } else { + throw std::invalid_argument("Invalid variant"); + } + }; + + return std::visit(visit_functor, value); +} + +Data forgeArray(const Data& data) { + auto forged = forgeInt32(static_cast(data.size())); + append(forged, data); + return forged; +} + +Data forgeMichelInt(const TW::int256_t& value) { + Data forged; + auto abs = boost::multiprecision::abs(value); + forged.emplace_back(static_cast(value.sign() < 0 ? (abs & 0x3f - 0x40) : (abs & 0x3f))); + abs >>= 6; + while (abs > 0) { + forged[forged.size() - 1] |= 0x80; + forged.emplace_back(static_cast(abs & 0x7F)); + abs >>= 7; + } + return forged; +} + +} // namespace TW::Tezos diff --git a/src/Tezos/Forging.h b/src/Tezos/Forging.h index 45f4624e282..721546db1e9 100644 --- a/src/Tezos/Forging.h +++ b/src/Tezos/Forging.h @@ -1,19 +1,36 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#pragma once + +#include "Michelson.h" +#include "uint256.h" #include "../PublicKey.h" #include "../proto/Tezos.pb.h" #include +#include +#include using namespace TW; -using namespace TW::Tezos::Proto; + +namespace TW::Tezos { Data forgeBool(bool input); -Data forgeOperation(const Operation& operation); +Data forgeOperation(const Proto::Operation& operation); +Data forgeAddress(const std::string& address); +Data forgeArray(const Data& data); Data forgePublicKeyHash(const std::string& publicKeyHash); Data forgePublicKey(PublicKey publicKey); Data forgeZarith(uint64_t input); +Data forgeInt32(int value, int len = 4); +Data forgeString(const std::string& value, std::size_t len = 4); +Data forgeEntrypoint(const std::string& value); +Data forgeMichelson(const MichelsonValue::MichelsonVariant& value); +Data forgeMichelInt(const TW::int256_t& value); +Data forgePrim(const PrimValue& value); + +} // namespace TW::Tezos diff --git a/src/Tezos/Michelson.cpp b/src/Tezos/Michelson.cpp new file mode 100644 index 00000000000..0b573885ebd --- /dev/null +++ b/src/Tezos/Michelson.cpp @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Michelson.h" + +namespace TW::Tezos { + +MichelsonValue::MichelsonVariant FA12ParameterToMichelson(const Proto::FA12Parameters& data) { + MichelsonValue::MichelsonVariant address = StringValue{.string = data.from()}; + MichelsonValue::MichelsonVariant to = StringValue{.string = data.to()}; + MichelsonValue::MichelsonVariant amount = IntValue{._int = data.value()}; + auto primTransferInfos = PrimValue{.prim = "Pair", .args{{to}, {amount}}}; + return PrimValue{.prim = "Pair", .args{{address}, {primTransferInfos}}}; +} + +MichelsonValue::MichelsonVariant FA2ParameterToMichelson(const Proto::FA2Parameters& data) { + auto& txObj = *data.txs_object().begin(); + MichelsonValue::MichelsonVariant from = StringValue{.string = txObj.from()}; + auto& txTransferInfos = txObj.txs(0); + MichelsonValue::MichelsonVariant tokenId = IntValue{._int = txTransferInfos.token_id()}; + MichelsonValue::MichelsonVariant amount = IntValue{._int = txTransferInfos.amount()}; + auto primTransferInfos = PrimValue{.prim = "Pair", .args{{tokenId}, {amount}}}; + MichelsonValue::MichelsonVariant to = StringValue{.string = txTransferInfos.to()}; + MichelsonValue::MichelsonVariant txs = MichelsonValue::MichelsonArray{PrimValue{.prim = "Pair", .args{{to}, {primTransferInfos}}}}; + auto primTxs = PrimValue{.prim = "Pair", .args{{from}, {txs}}}; + return MichelsonValue::MichelsonArray{primTxs}; +} + +} // namespace TW::Tezos diff --git a/src/Tezos/Michelson.h b/src/Tezos/Michelson.h new file mode 100644 index 00000000000..1e6d944c59d --- /dev/null +++ b/src/Tezos/Michelson.h @@ -0,0 +1,56 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include +#include + +#include "../proto/Tezos.pb.h" + +#pragma once + +namespace TW::Tezos { + +enum PrimType : std::uint8_t { + Pair = 7, +}; + +struct MichelsonValue; + +struct PrimValue { + std::string prim; + std::vector args; + std::vector anots; +}; + +struct BytesValue { + std::string bytes; +}; + +struct StringValue { + std::string string; +}; + +struct IntValue { + std::string _int; +}; + +struct MichelsonValue { + using MichelsonArray = std::vector>; + using MichelsonVariant = std::variant< + PrimValue, + BytesValue, + StringValue, + IntValue, + MichelsonArray>; + MichelsonVariant value; +}; + +MichelsonValue::MichelsonVariant FA12ParameterToMichelson(const Proto::FA12Parameters& data); +MichelsonValue::MichelsonVariant FA2ParameterToMichelson(const Proto::FA2Parameters& data); + +} // namespace TW::Tezos diff --git a/src/Tezos/OperationList.cpp b/src/Tezos/OperationList.cpp index ff7780ed385..fc2e7ed3897 100644 --- a/src/Tezos/OperationList.cpp +++ b/src/Tezos/OperationList.cpp @@ -1,19 +1,14 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "OperationList.h" -#include "BinaryCoding.h" #include "Forging.h" -#include "HexCoding.h" #include "../Base58.h" -#include "../proto/Tezos.pb.h" -using namespace TW; -using namespace TW::Tezos; -using namespace TW::Tezos::Proto; +namespace TW::Tezos { Tezos::OperationList::OperationList(const std::string& str) { branch = str; @@ -53,3 +48,5 @@ Data Tezos::OperationList::forge(const PrivateKey& privateKey) const { return forged; } + +} // namespace TW::Tezos \ No newline at end of file diff --git a/src/Tezos/OperationList.h b/src/Tezos/OperationList.h index f7ddd5615ed..a6f3a4a9f70 100644 --- a/src/Tezos/OperationList.h +++ b/src/Tezos/OperationList.h @@ -1,15 +1,20 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + #pragma once -#include "../Data.h" +#include "Data.h" #include "proto/Tezos.pb.h" #include "../PrivateKey.h" #include -using namespace TW::Tezos; -using namespace TW::Tezos::Proto; - namespace TW::Tezos { +using TW::Tezos::Proto::Operation; + class OperationList { public: std::string branch; diff --git a/src/Tezos/Signer.cpp b/src/Tezos/Signer.cpp index b0732fd78c9..248df7fd115 100644 --- a/src/Tezos/Signer.cpp +++ b/src/Tezos/Signer.cpp @@ -1,12 +1,11 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "OperationList.h" #include "Signer.h" -#include "../Hash.h" +#include "OperationList.h" #include "../HexCoding.h" #include @@ -15,12 +14,13 @@ #include using namespace TW; -using namespace TW::Tezos; + +namespace TW::Tezos { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto operationList = Tezos::OperationList(input.operation_list().branch()); for (Proto::Operation operation : input.operation_list().operations()) { - operationList.addOperation(operation); + operationList.addOperation(operation); } auto signer = Signer(); @@ -58,3 +58,5 @@ Data Signer::signData(const PrivateKey& privateKey, const Data& data) { append(signedData, signature); return signedData; } + +} // namespace TW::Tezos diff --git a/src/Tezos/Signer.h b/src/Tezos/Signer.h index 798d703ab21..a6e16cf4733 100644 --- a/src/Tezos/Signer.h +++ b/src/Tezos/Signer.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,7 +7,7 @@ #pragma once #include "OperationList.h" -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/Tezos.pb.h" diff --git a/src/Theta/Entry.cpp b/src/Theta/Entry.cpp index 8e391ec086b..634a661a059 100644 --- a/src/Theta/Entry.cpp +++ b/src/Theta/Entry.cpp @@ -9,22 +9,10 @@ #include "Ethereum/Address.h" #include "Signer.h" -using namespace TW::Theta; -using namespace std; +namespace TW::Theta { -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { - return Ethereum::Address::isValid(address); -} - -string Entry::normalizeAddress(TWCoinType coin, const string& address) const { - // normalized with EIP55 checksum - return Ethereum::Address(address).string(); -} - -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { - return Ethereum::Address(publicKey).string(); -} - -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Theta diff --git a/src/Theta/Entry.h b/src/Theta/Entry.h index 90d33acdf14..429190dee81 100644 --- a/src/Theta/Entry.h +++ b/src/Theta/Entry.h @@ -7,18 +7,15 @@ #pragma once #include "../CoinEntry.h" +#include "Ethereum/Entry.h" namespace TW::Theta { /// Entry point for Theta. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public Ethereum::Entry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeTheta}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Theta diff --git a/src/Theta/Signer.cpp b/src/Theta/Signer.cpp index e2bc16b1188..c50c74f264e 100755 --- a/src/Theta/Signer.cpp +++ b/src/Theta/Signer.cpp @@ -9,9 +9,9 @@ #include "../Ethereum/RLP.h" #include "../Hash.h" -using namespace TW; -using namespace TW::Theta; -using RLP = Ethereum::RLP; +using RLP = TW::Ethereum::RLP; + +namespace TW::Theta { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto pkFrom = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); @@ -66,3 +66,5 @@ Data Signer::sign(const PrivateKey& privateKey, const Transaction& transaction) auto signature = privateKey.sign(hash, TWCurveSECP256k1); return signature; } + +} // namespace TW::Theta diff --git a/src/Theta/Signer.h b/src/Theta/Signer.h index 8b16c4957f5..e4dbe975fd4 100644 --- a/src/Theta/Signer.h +++ b/src/Theta/Signer.h @@ -9,7 +9,7 @@ #include #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/Theta.pb.h" @@ -37,8 +37,3 @@ class Signer { }; } // namespace TW::Theta - -/// Wrapper for C interface. -struct TWThetaSigner { - TW::Theta::Signer impl; -}; diff --git a/src/Theta/Transaction.cpp b/src/Theta/Transaction.cpp index 7cfa42e3ad2..380f30084ea 100644 --- a/src/Theta/Transaction.cpp +++ b/src/Theta/Transaction.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,8 +8,8 @@ #include "../Ethereum/RLP.h" -using namespace TW; -using namespace TW::Theta; +namespace TW::Theta { + using RLP = Ethereum::RLP; Data encode(const Coins& coins) noexcept { @@ -52,15 +52,15 @@ Data encode(const std::vector& outputs) noexcept { } Transaction::Transaction(Ethereum::Address from, Ethereum::Address to, - uint256_t thetaAmount, uint256_t tfuelAmount, - uint64_t sequence, uint256_t feeAmount /* = 1000000000000*/) { + const uint256_t& thetaAmount, const uint256_t& tfuelAmount, + uint64_t sequence, const uint256_t& feeAmount /* = 1000000000000*/) { auto fee = Coins(0, feeAmount); auto coinsInput = Coins(thetaAmount, tfuelAmount + feeAmount); auto coinsOutput = Coins(thetaAmount, tfuelAmount); - auto input = TxInput(std::move(from), coinsInput, sequence); - auto output = TxOutput(std::move(to), coinsOutput); + auto input = TxInput(from, coinsInput, sequence); + auto output = TxOutput(to, coinsOutput); - this->fee = fee; + this->_fee = fee; this->inputs.push_back(input); this->outputs.push_back(output); } @@ -70,9 +70,9 @@ Data Transaction::encode() const noexcept { uint16_t txType = 2; // TxSend append(encoded, RLP::encode(txType)); auto encodedData = Data(); - append(encodedData, ::encode(fee)); - append(encodedData, ::encode(inputs)); - append(encodedData, ::encode(outputs)); + append(encodedData, Theta::encode(_fee)); + append(encodedData, Theta::encode(inputs)); + append(encodedData, Theta::encode(outputs)); append(encoded, RLP::encodeList(encodedData)); return encoded; } @@ -86,3 +86,5 @@ bool Transaction::setSignature(const Ethereum::Address& address, const Data& sig } return false; } + +} // namespace TW::Theta diff --git a/src/Theta/Transaction.h b/src/Theta/Transaction.h index 735c21ab1a8..20edd2d0de6 100644 --- a/src/Theta/Transaction.h +++ b/src/Theta/Transaction.h @@ -10,7 +10,7 @@ #include #include "Coins.h" -#include "../Data.h" +#include "Data.h" #include "../Ethereum/Address.h" namespace TW::Theta { @@ -39,17 +39,17 @@ class TxOutput { class Transaction { public: - Coins fee; + Coins _fee; std::vector inputs; std::vector outputs; Transaction() = default; Transaction(Coins fee, std::vector inputs, std::vector outputs) - : fee(std::move(fee)), inputs(std::move(inputs)), outputs(std::move(outputs)) {} + : _fee(std::move(fee)), inputs(std::move(inputs)), outputs(std::move(outputs)) {} Transaction(Ethereum::Address from, Ethereum::Address to, - uint256_t thetaAmount, uint256_t tfuelAmount, uint64_t sequence, - uint256_t feeAmount = 1000000000000); + const uint256_t& thetaAmount, const uint256_t& tfuelAmount, uint64_t sequence, + const uint256_t& feeAmount = 1000000000000); /// Encodes the transaction Data encode() const noexcept; diff --git a/src/TransactionCompiler.cpp b/src/TransactionCompiler.cpp new file mode 100644 index 00000000000..2f725ab6780 --- /dev/null +++ b/src/TransactionCompiler.cpp @@ -0,0 +1,38 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TransactionCompiler.h" + +#include "Coin.h" + +using namespace TW; + + +Data TransactionCompiler::buildInput(TWCoinType coinType, const std::string& from, const std::string& to, const std::string& amount, const std::string& asset, const std::string& memo, const std::string& chainId) { + // parse amount + uint256_t amount256 { amount }; + return anyCoinBuildTransactionInput(coinType, from, to, amount256, asset, memo, chainId); +} + +Data TransactionCompiler::preImageHashes(TWCoinType coinType, const Data& txInputData) { + return anyCoinPreImageHashes(coinType, txInputData); +} + +Data TransactionCompiler::compileWithSignatures(TWCoinType coinType, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys) { + // input parameter conversion + const auto publicKeyType = ::publicKeyType(coinType); + std::vector pubs; + for (auto& p: publicKeys) { + if (!PublicKey::isValid(p, publicKeyType)) { + throw std::invalid_argument("Invalid public key"); + } + pubs.emplace_back(p, publicKeyType); + } + + Data txOutput; + anyCoinCompileWithSignatures(coinType, txInputData, signatures, pubs, txOutput); + return txOutput; +} diff --git a/src/TransactionCompiler.h b/src/TransactionCompiler.h new file mode 100644 index 00000000000..21b968717b6 --- /dev/null +++ b/src/TransactionCompiler.h @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include "Data.h" +#include "CoinEntry.h" + +#include +#include + +namespace TW { + +/// Non-core transaction utility methods, like building a transaction using an external signature +class TransactionCompiler { +public: + /// Build a coin-specific SigningInput protobuf transaction input, from simple transaction parameters + static Data buildInput(TWCoinType coinType, const std::string& from, const std::string& to, const std::string& amount, const std::string& asset, const std::string& memo, const std::string& chainId); + + /// Obtain pre-signing hash of a transaction. + /// It will return a proto object named `PreSigningOutput` which will include hash. + /// We provide a default `PreSigningOutput` in TransactionCompiler.proto. + /// For some special coins, such as bitcoin, we will create a custom `PreSigningOutput` object in its proto file. + static Data preImageHashes(TWCoinType coinType, const Data& txInputData); + + /// Compile a complete transation with an external signature, put together from transaction input and provided public key and signature + static Data compileWithSignatures(TWCoinType coinType, const Data& txInputData, const std::vector& signatures, const std::vector& publicKeys); +}; + +} // namespace TW diff --git a/src/Tron/Address.cpp b/src/Tron/Address.cpp index 7fdac65b817..a67ea8fd1c2 100644 --- a/src/Tron/Address.cpp +++ b/src/Tron/Address.cpp @@ -12,7 +12,7 @@ #include #include -using namespace TW::Tron; +namespace TW::Tron { bool Address::isValid(const std::string& string) { const auto decoded = Base58::bitcoin.decodeCheck(string); @@ -36,3 +36,5 @@ Address::Address(const PublicKey& publicKey) { bytes[0] = prefix; std::copy(keyhash.end() - size + 1, keyhash.end(), bytes.begin() + 1); } + +} // namespace TW::Tron diff --git a/src/Tron/Address.h b/src/Tron/Address.h index 7577171a5b3..2c7a37128ee 100644 --- a/src/Tron/Address.h +++ b/src/Tron/Address.h @@ -7,7 +7,7 @@ #pragma once #include "../Base58Address.h" -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/Tron/Entry.cpp b/src/Tron/Entry.cpp index 0f69e92d865..056728c4bc6 100644 --- a/src/Tron/Entry.cpp +++ b/src/Tron/Entry.cpp @@ -9,19 +9,21 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Tron; using namespace std; +namespace TW::Tron { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Tron diff --git a/src/Tron/Entry.h b/src/Tron/Entry.h index 0e13d37855e..0d25c65da8b 100644 --- a/src/Tron/Entry.h +++ b/src/Tron/Entry.h @@ -12,12 +12,11 @@ namespace TW::Tron { /// Entry point for implementation of Tron coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeTron}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Tron diff --git a/src/Tron/Serialization.cpp b/src/Tron/Serialization.cpp index a753e52e5d9..36bfe251f69 100644 --- a/src/Tron/Serialization.cpp +++ b/src/Tron/Serialization.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,17 +10,15 @@ #include #include -using namespace TW; -using namespace TW::Tron; -using namespace std; +namespace TW::Tron { using json = nlohmann::json; -string typeName(const protocol::Transaction::Contract::ContractType type) { +std::string typeName(const protocol::Transaction::Contract::ContractType type) { return protocol::Transaction::Contract::ContractType_Name(type); } -string typeUrl(const protocol::Transaction::Contract::ContractType type) { +std::string typeUrl(const protocol::Transaction::Contract::ContractType type) { std::ostringstream stringStream; stringStream << "type.googleapis.com/protocol." << typeName(type); return stringStream.str(); @@ -47,9 +45,9 @@ json valueJSON(const protocol::TransferAssetContract& contract) { json valueJSON(const protocol::VoteAssetContract& contract) { json valueJSON; - - vector vote_address; - for (const string& addr : contract.vote_address()) { + + std::vector vote_address; + for (const std::string& addr : contract.vote_address()) { vote_address.push_back(hex(addr)); } @@ -57,7 +55,7 @@ json valueJSON(const protocol::VoteAssetContract& contract) { valueJSON["vote_address"] = vote_address; valueJSON["support"] = contract.support(); valueJSON["count"] = contract.count(); - + return valueJSON; } @@ -72,7 +70,7 @@ json voteJSON(const protocol::VoteWitnessContract::Vote& vote) { json valueJSON(const protocol::VoteWitnessContract& contract) { json valueJSON; - vector votes; + std::vector votes; for (const protocol::VoteWitnessContract::Vote& vote : contract.votes()) { votes.push_back(voteJSON(vote)); } @@ -136,81 +134,81 @@ json valueJSON(const protocol::TriggerSmartContract& contract) { return valueJSON; } -json parameterJSON(const google::protobuf::Any ¶meter, const protocol::Transaction::Contract::ContractType type) { +json parameterJSON(const google::protobuf::Any& parameter, const protocol::Transaction::Contract::ContractType type) { json paramJSON; paramJSON["type_url"] = typeUrl(type); switch (type) { - case protocol::Transaction::Contract::TransferContract: { - protocol::TransferContract contract; - parameter.UnpackTo(&contract); - paramJSON["value"] = valueJSON(contract); - break; - } - case protocol::Transaction::Contract::TransferAssetContract: { - protocol::TransferAssetContract contract; - parameter.UnpackTo(&contract); - paramJSON["value"] = valueJSON(contract); - break; - } - case protocol::Transaction::Contract::VoteAssetContract: { - protocol::VoteAssetContract contract; - parameter.UnpackTo(&contract); - paramJSON["value"] = valueJSON(contract); - break; - } - case protocol::Transaction::Contract::VoteWitnessContract: { - protocol::VoteWitnessContract contract; - parameter.UnpackTo(&contract); - paramJSON["value"] = valueJSON(contract); - break; - } - case protocol::Transaction::Contract::FreezeBalanceContract: { - protocol::FreezeBalanceContract contract; - parameter.UnpackTo(&contract); - paramJSON["value"] = valueJSON(contract); - break; - } - case protocol::Transaction::Contract::UnfreezeBalanceContract: { - protocol::UnfreezeBalanceContract contract; - parameter.UnpackTo(&contract); - paramJSON["value"] = valueJSON(contract); - break; - } - case protocol::Transaction::Contract::WithdrawBalanceContract: { - protocol::WithdrawBalanceContract contract; - parameter.UnpackTo(&contract); - paramJSON["value"] = valueJSON(contract); - break; - } - case protocol::Transaction::Contract::UnfreezeAssetContract: { - protocol::UnfreezeAssetContract contract; - parameter.UnpackTo(&contract); - paramJSON["value"] = valueJSON(contract); - break; - } - case protocol::Transaction::Contract::TriggerSmartContract: { - protocol::TriggerSmartContract contract; - parameter.UnpackTo(&contract); - paramJSON["value"] = valueJSON(contract); - break; - } - case protocol::Transaction::Contract::AccountCreateContract: - default: - break; + case protocol::Transaction::Contract::TransferContract: { + protocol::TransferContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::TransferAssetContract: { + protocol::TransferAssetContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::VoteAssetContract: { + protocol::VoteAssetContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::VoteWitnessContract: { + protocol::VoteWitnessContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::FreezeBalanceContract: { + protocol::FreezeBalanceContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::UnfreezeBalanceContract: { + protocol::UnfreezeBalanceContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::WithdrawBalanceContract: { + protocol::WithdrawBalanceContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::UnfreezeAssetContract: { + protocol::UnfreezeAssetContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::TriggerSmartContract: { + protocol::TriggerSmartContract contract; + parameter.UnpackTo(&contract); + paramJSON["value"] = valueJSON(contract); + break; + } + case protocol::Transaction::Contract::AccountCreateContract: + default: + break; } return paramJSON; } -json contractJSON(const protocol::Transaction::Contract &contract) { +json contractJSON(const protocol::Transaction::Contract& contract) { json contractJSON; contractJSON["type"] = typeName(contract.type()); contractJSON["parameter"] = parameterJSON(contract.parameter(), contract.type()); return contractJSON; } -json raw_dataJSON(const protocol::Transaction::raw &raw) { +json raw_dataJSON(const protocol::Transaction::raw& raw) { json raw_dataJSON; raw_dataJSON["ref_block_bytes"] = hex(raw.ref_block_bytes()); @@ -223,16 +221,18 @@ json raw_dataJSON(const protocol::Transaction::raw &raw) { } raw_dataJSON["timestamp"] = raw.timestamp(); raw_dataJSON["expiration"] = raw.expiration(); - raw_dataJSON["contract"] = json::array({ contractJSON(raw.contract(0)) }); + raw_dataJSON["contract"] = json::array({contractJSON(raw.contract(0))}); return raw_dataJSON; } -json TW::Tron::transactionJSON(const protocol::Transaction& transaction, const TW::Data& txID, const TW::Data& signature) { +json transactionJSON(const protocol::Transaction& transaction, const TW::Data& txID, const TW::Data& signature) { json transactionJSON; transactionJSON["raw_data"] = raw_dataJSON(transaction.raw_data()); transactionJSON["txID"] = hex(txID); - transactionJSON["signature"] = json::array({ hex(signature) }); + transactionJSON["signature"] = json::array({hex(signature)}); return transactionJSON; } + +} // namespace TW::Tron diff --git a/src/Tron/Serialization.h b/src/Tron/Serialization.h index 13181c7ac47..7f3c074cfd5 100644 --- a/src/Tron/Serialization.h +++ b/src/Tron/Serialization.h @@ -7,7 +7,7 @@ #pragma once #include "./Protobuf/TronInternal.pb.h" -#include "../Data.h" +#include "Data.h" #include namespace TW::Tron { diff --git a/src/Tron/Signer.cpp b/src/Tron/Signer.cpp index 2680046135f..87ed70b30f5 100644 --- a/src/Tron/Signer.cpp +++ b/src/Tron/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,21 +10,16 @@ #include "../Base58.h" #include "../BinaryCoding.h" -#include "../Hash.h" #include "../HexCoding.h" #include "Serialization.h" #include #include -using namespace TW; -using namespace TW::Tron; -using namespace std::chrono; +namespace TW::Tron { const std::string TRANSFER_TOKEN_FUNCTION = "0xa9059cbb"; -size_t base58Capacity = 128; - /// Converts an external TransferContract to an internal one used for signing. protocol::TransferContract to_internal(const Proto::TransferContract& transfer) { auto internal = protocol::TransferContract(); @@ -106,7 +101,7 @@ protocol::VoteAssetContract to_internal(const Proto::VoteAssetContract& voteCont internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); internal.set_support(voteContract.support()); internal.set_count(voteContract.count()); - for(int i = 0; i < voteContract.vote_address_size(); i++) { + for (int i = 0; i < voteContract.vote_address_size(); i++) { auto voteAddress = Base58::bitcoin.decodeCheck(voteContract.vote_address(i)); internal.add_vote_address(voteAddress.data(), voteAddress.size()); } @@ -120,7 +115,7 @@ protocol::VoteWitnessContract to_internal(const Proto::VoteWitnessContract& vote internal.set_owner_address(ownerAddress.data(), ownerAddress.size()); internal.set_support(voteContract.support()); - for(int i = 0; i < voteContract.votes_size(); i++) { + for (int i = 0; i < voteContract.votes_size(); i++) { auto voteAddress = Base58::bitcoin.decodeCheck(voteContract.votes(i).vote_address()); auto* vote = internal.add_votes(); @@ -293,15 +288,15 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { } // Get default timestamp and expiration - const uint64_t now = duration_cast< milliseconds >( - system_clock::now().time_since_epoch() - ).count(); + const uint64_t now = duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); const uint64_t timestamp = input.transaction().timestamp() == 0 - ? now - : input.transaction().timestamp(); + ? now + : input.transaction().timestamp(); const uint64_t expiration = input.transaction().expiration() == 0 - ? timestamp + 10 * 60 * 60 * 1000 // 10 hours - : input.transaction().expiration(); + ? timestamp + 10 * 60 * 60 * 1000 // 10 hours + : input.transaction().expiration(); internal.mutable_raw_data()->set_timestamp(timestamp); internal.mutable_raw_data()->set_expiration(expiration); @@ -325,3 +320,5 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { return output; } + +} // namespace TW::Tron diff --git a/src/Tron/Signer.h b/src/Tron/Signer.h index 2336c3c8fc2..33a06fff775 100644 --- a/src/Tron/Signer.h +++ b/src/Tron/Signer.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/Tron.pb.h" diff --git a/src/VeChain/Clause.h b/src/VeChain/Clause.h index 9bbb1bc6435..71d56d7d785 100644 --- a/src/VeChain/Clause.h +++ b/src/VeChain/Clause.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../Ethereum/Address.h" #include "../proto/VeChain.pb.h" #include "../uint256.h" diff --git a/src/VeChain/Entry.cpp b/src/VeChain/Entry.cpp index 96a0ef25dab..0a01f790321 100644 --- a/src/VeChain/Entry.cpp +++ b/src/VeChain/Entry.cpp @@ -9,22 +9,10 @@ #include "Ethereum/Address.h" #include "Signer.h" -using namespace TW::VeChain; -using namespace std; - -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { - return Ethereum::Address::isValid(address); -} - -string Entry::normalizeAddress(TWCoinType coin, const string& address) const { - // normalized with EIP55 checksum - return Ethereum::Address(address).string(); -} - -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { - return Ethereum::Address(publicKey).string(); -} - -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +namespace TW::VeChain { + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::VeChain diff --git a/src/VeChain/Entry.h b/src/VeChain/Entry.h index b856f4ebe28..48ec8b5db78 100644 --- a/src/VeChain/Entry.h +++ b/src/VeChain/Entry.h @@ -7,18 +7,15 @@ #pragma once #include "../CoinEntry.h" +#include "Ethereum/Entry.h" namespace TW::VeChain { /// Entry point for VeChain. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public Ethereum::Entry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeVeChain}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string normalizeAddress(TWCoinType coin, const std::string& address) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::VeChain diff --git a/src/VeChain/Signer.cpp b/src/VeChain/Signer.cpp index 3b12d7818bd..7cbfd00d333 100644 --- a/src/VeChain/Signer.cpp +++ b/src/VeChain/Signer.cpp @@ -9,7 +9,8 @@ #include "../Hash.h" using namespace TW; -using namespace TW::VeChain; + +namespace TW::VeChain { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); @@ -41,3 +42,5 @@ Data Signer::sign(const PrivateKey& privateKey, Transaction& transaction) noexce auto signature = privateKey.sign(hash, TWCurveSECP256k1); return Data(signature.begin(), signature.end()); } + +} // namespace TW::VeChain diff --git a/src/VeChain/Signer.h b/src/VeChain/Signer.h index 5b193a3385d..f0a2a92d22b 100644 --- a/src/VeChain/Signer.h +++ b/src/VeChain/Signer.h @@ -8,7 +8,7 @@ #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" @@ -32,8 +32,3 @@ class Signer { }; } // namespace TW::VeChain - -/// Wrapper for C interface. -struct TWVeChainSigner { - TW::VeChain::Signer impl; -}; diff --git a/src/VeChain/Transaction.cpp b/src/VeChain/Transaction.cpp index 383531bf8a5..aeca93d352f 100644 --- a/src/VeChain/Transaction.cpp +++ b/src/VeChain/Transaction.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,8 +8,8 @@ #include "../Ethereum/RLP.h" -using namespace TW; -using namespace TW::VeChain; +namespace TW::VeChain { + using RLP = Ethereum::RLP; Data encode(const Clause& clause) noexcept { @@ -45,3 +45,5 @@ Data Transaction::encode() const noexcept { } return RLP::encodeList(encoded); } + +} // namespace TW::VeChain diff --git a/src/VeChain/Transaction.h b/src/VeChain/Transaction.h index ba905028550..c4229b795d3 100644 --- a/src/VeChain/Transaction.h +++ b/src/VeChain/Transaction.h @@ -7,7 +7,7 @@ #pragma once #include "Clause.h" -#include "../Data.h" +#include "Data.h" #include #include diff --git a/src/Wasm.h b/src/Wasm.h new file mode 100644 index 00000000000..afb9c67aa88 --- /dev/null +++ b/src/Wasm.h @@ -0,0 +1,19 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#ifndef __USE_WASM +#define __USE_WASM +#endif // __USE_WASM + +#ifndef __USE_MISC +#ifdef __USE_WASM + +typedef unsigned long int ulong; +typedef unsigned short int ushort; +typedef unsigned int uint; + +#endif // __USE_WASM +#endif // __USE_MISC diff --git a/src/Waves/Address.cpp b/src/Waves/Address.cpp index 685f4327fb5..2f5ad758be7 100644 --- a/src/Waves/Address.cpp +++ b/src/Waves/Address.cpp @@ -7,7 +7,7 @@ #include "Address.h" #include "../Base58.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include @@ -15,8 +15,7 @@ #include #include -using namespace TW; -using namespace TW::Waves; +namespace TW::Waves { template Data Address::secureHash(const T &data) { @@ -40,8 +39,6 @@ bool Address::isValid(const Data& decoded) { const auto data_checksum = Data(decoded.end() - 4, decoded.end()); const auto calculated_hash = secureHash(data); const auto calculated_checksum = Data(calculated_hash.begin(), calculated_hash.begin() + 4); - const auto h = hex(data); - const auto h2 = hex(calculated_hash); return std::memcmp(data_checksum.data(), calculated_checksum.data(), 4) == 0; } @@ -83,4 +80,6 @@ Address::Address(const PublicKey &publicKey) { std::string Address::string() const { return Base58::bitcoin.encode(bytes); -} \ No newline at end of file +} + +} // namespace TW::Waves diff --git a/src/Waves/Address.h b/src/Waves/Address.h index df518415334..fed1743c9de 100644 --- a/src/Waves/Address.h +++ b/src/Waves/Address.h @@ -7,7 +7,7 @@ #pragma once #include "../Base58Address.h" -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include diff --git a/src/Waves/Entry.cpp b/src/Waves/Entry.cpp index 6162485901d..9635e2d30cd 100644 --- a/src/Waves/Entry.cpp +++ b/src/Waves/Entry.cpp @@ -9,19 +9,22 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Waves; using namespace std; +namespace TW::Waves { + // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } + +} // namespace TW::Waves diff --git a/src/Waves/Entry.h b/src/Waves/Entry.h index 8418548c2b6..ea114e04c45 100644 --- a/src/Waves/Entry.h +++ b/src/Waves/Entry.h @@ -12,12 +12,11 @@ namespace TW::Waves { /// Entry point for implementation of Waves coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeWaves}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Waves diff --git a/src/Waves/Signer.cpp b/src/Waves/Signer.cpp index 701d47e869b..93716de50d4 100644 --- a/src/Waves/Signer.cpp +++ b/src/Waves/Signer.cpp @@ -9,7 +9,8 @@ #include "../Hash.h" using namespace TW; -using namespace TW::Waves; + +namespace TW::Waves { Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { auto privateKey = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); @@ -19,12 +20,12 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { Data signature = Signer::sign(privateKey, transaction); Proto::SigningOutput output = Proto::SigningOutput(); - output.set_signature(reinterpret_cast(signature.data()), signature.size()); + output.set_signature(reinterpret_cast(signature.data()), signature.size()); output.set_json(transaction.buildJson(signature).dump()); return output; } -Data Signer::sign(const PrivateKey &privateKey, Transaction &transaction) noexcept { +Data Signer::sign(const PrivateKey& privateKey, Transaction& transaction) noexcept { try { auto bytesToSign = transaction.serializeToSign(); auto signature = privateKey.sign(bytesToSign, TWCurveCurve25519); @@ -33,3 +34,5 @@ Data Signer::sign(const PrivateKey &privateKey, Transaction &transaction) noexce return Data(); } } + +} // namespace TW::Waves diff --git a/src/Waves/Signer.h b/src/Waves/Signer.h index 4540029e28c..f7504d61132 100644 --- a/src/Waves/Signer.h +++ b/src/Waves/Signer.h @@ -7,7 +7,7 @@ #pragma once #include "Transaction.h" -#include "../Data.h" +#include "Data.h" #include "../Hash.h" #include "../PrivateKey.h" #include "../proto/Waves.pb.h" diff --git a/src/Waves/Transaction.cpp b/src/Waves/Transaction.cpp index ee9d2a5af44..36ac5cb3447 100644 --- a/src/Waves/Transaction.cpp +++ b/src/Waves/Transaction.cpp @@ -11,7 +11,8 @@ #include "../HexCoding.h" using namespace TW; -using namespace TW::Waves; + +namespace TW::Waves { using json = nlohmann::json; @@ -20,7 +21,7 @@ const std::string Transaction::WAVES = "WAVES"; Data serializeTransfer(int64_t amount, std::string asset, int64_t fee, std::string fee_asset, Address to, const Data& attachment, int64_t timestamp, const Data& pub_key) { auto data = Data(); if (asset.empty()) { - asset = Transaction::WAVES; + asset = Transaction::WAVES; } if (fee_asset.empty()) { fee_asset = Transaction::WAVES; @@ -46,7 +47,7 @@ Data serializeTransfer(int64_t amount, std::string asset, int64_t fee, std::stri encode64BE(fee, data); append(data, Data(std::begin(to.bytes), std::end(to.bytes))); encodeDynamicLengthBytes(attachment, data); - + return data; } @@ -61,7 +62,7 @@ Data serializeLease(int64_t amount, int64_t fee, Address to, int64_t timestamp, encode64BE(amount, data); encode64BE(fee, data); encode64BE(timestamp, data); - + return data; } @@ -75,13 +76,13 @@ Data serializeCancelLease(const Data& leaseId, int64_t fee, int64_t timestamp, c encode64BE(fee, data); encode64BE(timestamp, data); append(data, leaseId); - + return data; } json jsonTransfer(const Data& signature, int64_t amount, const std::string& asset, int64_t fee, const std::string& fee_asset, Address to, const Data& attachment, int64_t timestamp, const Data& pub_key) { json jsonTx; - + jsonTx["type"] = TransactionType::transfer; jsonTx["version"] = TransactionVersion::V2; jsonTx["fee"] = fee; @@ -97,13 +98,13 @@ json jsonTransfer(const Data& signature, int64_t amount, const std::string& asse } jsonTx["amount"] = amount; jsonTx["attachment"] = Base58::bitcoin.encode(attachment); - + return jsonTx; } json jsonLease(const Data& signature, int64_t amount, int64_t fee, Address to, int64_t timestamp, const Data& pub_key) { json jsonTx; - + jsonTx["type"] = TransactionType::lease; jsonTx["version"] = TransactionVersion::V2; jsonTx["fee"] = fee; @@ -112,13 +113,13 @@ json jsonLease(const Data& signature, int64_t amount, int64_t fee, Address to, i jsonTx["proofs"] = json::array({Base58::bitcoin.encode(signature)}); jsonTx["recipient"] = Address(to).string(); jsonTx["amount"] = amount; - + return jsonTx; } json jsonCancelLease(const Data& signature, const Data& leaseId, int64_t fee, int64_t timestamp, const Data& pub_key) { json jsonTx; - + jsonTx["type"] = TransactionType::cancelLease; jsonTx["version"] = TransactionVersion::V2; jsonTx["fee"] = fee; @@ -127,7 +128,7 @@ json jsonCancelLease(const Data& signature, const Data& leaseId, int64_t fee, in jsonTx["chainId"] = 87; // mainnet jsonTx["timestamp"] = timestamp; jsonTx["proofs"] = json::array({Base58::bitcoin.encode(signature)}); - + return jsonTx; } @@ -154,49 +155,44 @@ Data Transaction::serializeToSign() const { auto leaseId = Base58::bitcoin.decode(message.lease_id()); return serializeCancelLease(leaseId, message.fee(), input.timestamp(), pub_key); } - + return Data(); } - - - - json Transaction::buildJson(const Data& signature) const { if (input.has_transfer_message()) { auto message = input.transfer_message(); auto attachment = Data(message.attachment().begin(), message.attachment().end()); return jsonTransfer( - signature, - message.amount(), - message.asset(), - message.fee(), - message.fee_asset(), - Address(message.to()), - attachment, - input.timestamp(), - pub_key); + signature, + message.amount(), + message.asset(), + message.fee(), + message.fee_asset(), + Address(message.to()), + attachment, + input.timestamp(), + pub_key); } else if (input.has_lease_message()) { auto message = input.lease_message(); return jsonLease( - signature, - message.amount(), - message.fee(), - Address(message.to()), - input.timestamp(), - pub_key); + signature, + message.amount(), + message.fee(), + Address(message.to()), + input.timestamp(), + pub_key); } else if (input.has_cancel_lease_message()) { auto message = input.cancel_lease_message(); auto leaseId = Base58::bitcoin.decode(message.lease_id()); return jsonCancelLease( - signature, - leaseId, - message.fee(), - input.timestamp(), - pub_key); + signature, + leaseId, + message.fee(), + input.timestamp(), + pub_key); } return nullptr; } - - +} // namespace TW::Waves diff --git a/src/Waves/Transaction.h b/src/Waves/Transaction.h index b22fd54a030..539af362566 100644 --- a/src/Waves/Transaction.h +++ b/src/Waves/Transaction.h @@ -7,7 +7,7 @@ #pragma once #include "Address.h" -#include "../Data.h" +#include "Data.h" #include "../proto/Waves.pb.h" #include diff --git a/src/XRP/Address.cpp b/src/XRP/Address.cpp new file mode 100644 index 00000000000..2ff0026deba --- /dev/null +++ b/src/XRP/Address.cpp @@ -0,0 +1,39 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Address.h" +#include "../Base58.h" +#include + +namespace TW::Ripple { + +bool Address::isValid(const std::string& string) { + const auto decoded = Base58::ripple.decodeCheck(string); + if (decoded.size() != Address::size) { + return false; + } + return true; +} + +Address::Address(const std::string& string) { + const auto decoded = Base58::ripple.decodeCheck(string); + if (decoded.size() != Address::size) { + throw std::invalid_argument("Invalid address string"); + } + std::copy(decoded.begin(), decoded.end(), bytes.begin()); +} + +Address::Address(const PublicKey& publicKey) { + /// see type prefix: https://developers.ripple.com/base58-encodings.html + bytes[0] = 0x00; + ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, bytes.data() + 1); +} + +std::string Address::string() const { + return Base58::ripple.encodeCheck(bytes); +} + +} // namespace TW::Ripple diff --git a/src/XRP/Address.h b/src/XRP/Address.h new file mode 100644 index 00000000000..ffa8acbec2d --- /dev/null +++ b/src/XRP/Address.h @@ -0,0 +1,41 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" +#include "../PublicKey.h" + +#include + +namespace TW::Ripple { + +class Address { + public: + /// Number of bytes in an address. + static const size_t size = 21; + + /// Address data consisting of a prefix byte followed by the public key hash + std::array bytes; + + /// Determines whether a string makes a valid address. + static bool isValid(const std::string& string); + + /// Initializes a Ripple address with a string representation. + explicit Address(const std::string& string); + + /// Initializes a Ripple address with a public key. + explicit Address(const PublicKey& publicKey); + + /// Returns a string representation of the address. + std::string string() const; +}; + +inline bool operator==(const Address& lhs, const Address& rhs) { + return lhs.bytes == rhs.bytes; +} + +} // namespace TW::Ripple diff --git a/src/Ripple/BinaryCoding.h b/src/XRP/BinaryCoding.h similarity index 100% rename from src/Ripple/BinaryCoding.h rename to src/XRP/BinaryCoding.h diff --git a/src/XRP/Entry.cpp b/src/XRP/Entry.cpp new file mode 100644 index 00000000000..b28a843c670 --- /dev/null +++ b/src/XRP/Entry.cpp @@ -0,0 +1,29 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Entry.h" + +#include "Address.h" +#include "XAddress.h" +#include "Signer.h" + +namespace TW::Ripple { + +// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. + +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, TW::byte, TW::byte, const char*) const { + return Address::isValid(address) || XAddress::isValid(address); +} + +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { + return Address(publicKey).string(); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +} // namespace TW::Ripple diff --git a/src/XRP/Entry.h b/src/XRP/Entry.h new file mode 100644 index 00000000000..6736133286e --- /dev/null +++ b/src/XRP/Entry.h @@ -0,0 +1,22 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "../CoinEntry.h" + +namespace TW::Ripple { + +/// Entry point for implementation of Ripple (XRP) coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry final : public CoinEntry { +public: + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; +}; + +} // namespace TW::Ripple diff --git a/src/XRP/Signer.cpp b/src/XRP/Signer.cpp new file mode 100644 index 00000000000..52237c2fdba --- /dev/null +++ b/src/XRP/Signer.cpp @@ -0,0 +1,55 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Signer.h" +#include "../BinaryCoding.h" +#include + +namespace TW::Ripple { + +static const int64_t fullyCanonical = 0x80000000; + +Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { + auto output = Proto::SigningOutput(); + + const int64_t tag = input.destination_tag(); + if (tag > std::numeric_limits::max() || tag < 0) { + output.set_error(Common::Proto::SigningError::Error_invalid_memo); + return output; + } + + auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); + auto transaction = Transaction( + /* amount */ input.amount(), + /* fee */ input.fee(), + /* flags */ input.flags(), + /* sequence */ input.sequence(), + /* last_ledger_sequence */ input.last_ledger_sequence(), + /* account */ Address(input.account()), + /* destination */ input.destination(), + /* destination_tag*/ tag); + + auto signer = Signer(); + signer.sign(key, transaction); + + auto encoded = transaction.serialize(); + output.set_encoded(encoded.data(), encoded.size()); + return output; +} + +void Signer::sign(const PrivateKey& privateKey, Transaction& transaction) const noexcept { + /// See https://github.com/trezor/trezor-core/blob/master/src/apps/ripple/sign_tx.py#L59 + transaction.flags |= fullyCanonical; + transaction.pub_key = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1).bytes; + + auto unsignedTx = transaction.getPreImage(); + auto hash = Hash::sha512(unsignedTx); + auto half = Data(hash.begin(), hash.begin() + 32); + + transaction.signature = privateKey.signAsDER(half); +} + +} // namespace TW::Ripple diff --git a/src/XRP/Signer.h b/src/XRP/Signer.h new file mode 100644 index 00000000000..ab49851839b --- /dev/null +++ b/src/XRP/Signer.h @@ -0,0 +1,26 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Transaction.h" +#include "Data.h" +#include "../Hash.h" +#include "../PrivateKey.h" + +namespace TW::Ripple { + +/// Helper class that performs Ripple transaction signing. +class Signer { + public: + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; + + /// Signs the given transaction. + void sign(const PrivateKey& privateKey, Transaction& transaction) const noexcept; +}; + +} // namespace TW::Ripple diff --git a/src/XRP/Transaction.cpp b/src/XRP/Transaction.cpp new file mode 100644 index 00000000000..00846a905c7 --- /dev/null +++ b/src/XRP/Transaction.cpp @@ -0,0 +1,90 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "BinaryCoding.h" +#include "Transaction.h" +#include "../BinaryCoding.h" + +#include + +namespace TW::Ripple { + +const int NETWORK_PREFIX = 0x53545800; + +Data Transaction::serialize() const { + auto data = Data(); + /// field must be sorted by field type then by field name + /// "type" + encodeType(FieldType::int16, 2, data); + encode16BE(uint16_t(TransactionType::payment), data); + /// "flags" + encodeType(FieldType::int32, 2, data); + encode32BE(static_cast(flags), data); + /// "sequence" + encodeType(FieldType::int32, 4, data); + encode32BE(sequence, data); + /// "destinationTag" + if (encode_tag) { + encodeType(FieldType::int32, 14, data); + encode32BE(static_cast(destination_tag), data); + } + /// "lastLedgerSequence" + if (last_ledger_sequence > 0) { + encodeType(FieldType::int32, 27, data); + encode32BE(last_ledger_sequence, data); + } + /// "amount" + encodeType(FieldType::amount, 1, data); + append(data, serializeAmount(amount)); + /// "fee" + encodeType(FieldType::amount, 8, data); + append(data, serializeAmount(fee)); + /// "signingPubKey" + if (!pub_key.empty()) { + encodeType(FieldType::vl, 3, data); + encodeBytes(pub_key, data); + } + /// "txnSignature" + if (!signature.empty()) { + encodeType(FieldType::vl, 4, data); + encodeBytes(signature, data); + } + /// "account" + encodeType(FieldType::account, 1, data); + encodeBytes(serializeAddress(account), data); + /// "destination" + encodeType(FieldType::account, 3, data); + encodeBytes(destination, data); + return data; +} + +Data Transaction::getPreImage() const { + auto preImage = Data(); + encode32BE(NETWORK_PREFIX, preImage); + append(preImage, serialize()); + return preImage; +} + +Data Transaction::serializeAmount(int64_t amount) { + if (amount < 0) { + return Data(); + } + auto data = Data(); + encode64BE(uint64_t(amount), data); + /// clear first bit to indicate XRP + data[0] &= 0x7F; + /// set second bit to indicate positive number + data[0] |= 0x40; + return data; +} + +Data Transaction::serializeAddress(Address address) { + auto data = Data(20); + std::copy(&address.bytes[0] + 1, &address.bytes[0] + std::min(address.bytes.size(), size_t(21)), &data[0]); + return data; +} + +} // namespace TW::Ripple diff --git a/src/XRP/Transaction.h b/src/XRP/Transaction.h new file mode 100644 index 00000000000..a51f82eefab --- /dev/null +++ b/src/XRP/Transaction.h @@ -0,0 +1,75 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Address.h" +#include "XAddress.h" +#include "Data.h" +#include "../proto/Ripple.pb.h" + +namespace TW::Ripple { + +enum class FieldType: int { + int16 = 1, + int32 = 2, + amount = 6, + vl = 7, + account = 8 +}; + +enum class TransactionType { payment = 0 }; + +class Transaction { + /// We only support transaction types other than the Payment transaction. + /// Non-XRP currencies are not supported. Float and negative amounts are not supported. + /// See https://github.com/trezor/trezor-core/tree/master/src/apps/ripple#transactions + public: + int64_t amount; + int64_t fee; + int64_t flags; + int32_t sequence; + int32_t last_ledger_sequence; + Address account; + Data destination; + bool encode_tag; + int64_t destination_tag; + Data pub_key; + Data signature; + + Transaction(int64_t amount, int64_t fee, int64_t flags, int32_t sequence, + int32_t last_ledger_sequence, Address account, const std::string& destination, + int64_t destination_tag) + : amount(amount) + , fee(fee) + , flags(flags) + , sequence(sequence) + , last_ledger_sequence(last_ledger_sequence) + , account(account) { + try { + auto address = Address(destination); + encode_tag = destination_tag > 0; + this->destination_tag = destination_tag; + this->destination = Data(address.bytes.begin() + 1, address.bytes.end()); + } catch(const std::exception& e) { + auto xAddress = XAddress(destination); + encode_tag = xAddress.flag != TagFlag::none; + this->destination_tag = xAddress.tag; + this->destination = Data(xAddress.bytes.begin(), xAddress.bytes.end()); + } + } + + public: + /// simplified serialization format tailored for Payment transaction type + /// exclusively. + Data serialize() const; + Data getPreImage() const; + + static Data serializeAmount(int64_t amount); + static Data serializeAddress(Address address); +}; + +} // namespace TW::Ripple diff --git a/src/Ripple/XAddress.cpp b/src/XRP/XAddress.cpp similarity index 84% rename from src/Ripple/XAddress.cpp rename to src/XRP/XAddress.cpp index b6f522d7319..0eb5e6978f8 100644 --- a/src/Ripple/XAddress.cpp +++ b/src/XRP/XAddress.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,12 +7,10 @@ #include "XAddress.h" #include "../Base58.h" -#include "../BinaryCoding.h" -#include "../Data.h" +#include "../BinaryCoding.h" #include -using namespace TW; -using namespace TW::Ripple; +namespace TW::Ripple { const Data prefixMainnet = {0x05, 0x44}; @@ -21,7 +19,7 @@ bool XAddress::isValid(const std::string& string) { if (decoded.size() != XAddress::size) { return false; } - if(!std::equal(decoded.begin(), decoded.begin() + 2, prefixMainnet.begin())) { + if (!std::equal(decoded.begin(), decoded.begin() + 2, prefixMainnet.begin())) { return false; } if (!(decoded[22] == byte(TagFlag::none) || decoded[22] == byte(TagFlag::classic))) { @@ -45,12 +43,13 @@ XAddress::XAddress(const std::string& string) { } } -XAddress::XAddress(const PublicKey& publicKey, const uint32_t destination): tag(destination) { +XAddress::XAddress(const PublicKey& publicKey, const uint32_t destination) + : tag(destination) { ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, bytes.data()); } std::string XAddress::string() const { - /// see https://github.com/ripple/ripple-address-codec/blob/master/src/index.ts + /// \see https://github.com/ripple/ripple-address-codec/blob/master/src/index.ts /// base58check(2 bytes prefix + 20 bytes keyhash + 1 byte flag + 4 bytes + 32bit tag + 4 bytes reserved) Data result; append(result, prefixMainnet); @@ -60,3 +59,5 @@ std::string XAddress::string() const { append(result, Data{0x00, 0x00, 0x00, 0x00}); return Base58::ripple.encodeCheck(result); } + +} // namespace TW::Ripple diff --git a/src/Ripple/XAddress.h b/src/XRP/XAddress.h similarity index 96% rename from src/Ripple/XAddress.h rename to src/XRP/XAddress.h index dc60e67a57d..1d1e38a5271 100644 --- a/src/Ripple/XAddress.h +++ b/src/XRP/XAddress.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include "../PublicKey.h" #include @@ -20,7 +20,7 @@ class XAddress { /// Number of bytes in a X-address. static const size_t size = 31; - /// Publick key hash length. + /// Public key hash length. static const size_t keyHashSize = 20; /// Address data consisting of public key hash diff --git a/src/XXHash64.h b/src/XXHash64.h deleted file mode 100644 index 6147ff42759..00000000000 --- a/src/XXHash64.h +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright © 2016 Stephan Brumme. All rights reserved, see http://create.stephan-brumme.com/disclaimer.html -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once -#include // for uint32_t and uint64_t - -/// XXHash (64 bit), based on Yann Collet's descriptions, see http://cyan4973.github.io/xxHash/ -/** How to use: - uint64_t myseed = 0; - XXHash64 myhash(myseed); - myhash.add(pointerToSomeBytes, numberOfBytes); - myhash.add(pointerToSomeMoreBytes, numberOfMoreBytes); // call add() as often as you like to ... - // and compute hash: - uint64_t result = myhash.hash(); - - // or all of the above in one single line: - uint64_t result2 = XXHash64::hash(mypointer, numBytes, myseed); - - Note: my code is NOT endian-aware ! -**/ -class XXHash64 -{ -public: - /// create new XXHash (64 bit) - /** @param seed your seed value, even zero is a valid seed **/ - explicit XXHash64(uint64_t seed) - { - state[0] = seed + Prime1 + Prime2; - state[1] = seed + Prime2; - state[2] = seed; - state[3] = seed - Prime1; - buffer[0] = 0; - bufferSize = 0; - totalLength = 0; - } - - /// add a chunk of bytes - /** @param input pointer to a continuous block of data - @param length number of bytes - @return false if parameters are invalid / zero **/ - bool add(const void* input, uint64_t length) - { - // no data ? - if (!input || length == 0) - return false; - - totalLength += length; - // byte-wise access - const unsigned char* data = (const unsigned char*)input; - - // unprocessed old data plus new data still fit in temporary buffer ? - if (bufferSize + length < MaxBufferSize) - { - // just add new data - while (length-- > 0) - buffer[bufferSize++] = *data++; - return true; - } - - // point beyond last byte - const unsigned char* stop = data + length; - const unsigned char* stopBlock = stop - MaxBufferSize; - - // some data left from previous update ? - if (bufferSize > 0) - { - // make sure temporary buffer is full (16 bytes) - while (bufferSize < MaxBufferSize) - buffer[bufferSize++] = *data++; - - // process these 32 bytes (4x8) - process(buffer, state[0], state[1], state[2], state[3]); - } - - // copying state to local variables helps optimizer A LOT - uint64_t s0 = state[0], s1 = state[1], s2 = state[2], s3 = state[3]; - // 32 bytes at once - while (data <= stopBlock) - { - // local variables s0..s3 instead of state[0]..state[3] are much faster - process(data, s0, s1, s2, s3); - data += 32; - } - // copy back - state[0] = s0; state[1] = s1; state[2] = s2; state[3] = s3; - - // copy remainder to temporary buffer - bufferSize = uint32_t(stop - data); - for (unsigned int i = 0; i < bufferSize; i++) - buffer[i] = data[i]; - - // done - return true; - } - - /// get current hash - /** @return 64 bit XXHash **/ - uint64_t hash() const - { - // fold 256 bit state into one single 64 bit value - uint64_t result; - if (totalLength >= MaxBufferSize) - { - result = rotateLeft(state[0], 1) + - rotateLeft(state[1], 7) + - rotateLeft(state[2], 12) + - rotateLeft(state[3], 18); - result = (result ^ processSingle(0, state[0])) * Prime1 + Prime4; - result = (result ^ processSingle(0, state[1])) * Prime1 + Prime4; - result = (result ^ processSingle(0, state[2])) * Prime1 + Prime4; - result = (result ^ processSingle(0, state[3])) * Prime1 + Prime4; - } - else - { - // internal state wasn't set in add(), therefore original seed is still stored in state2 - result = state[2] + Prime5; - } - - result += totalLength; - - // process remaining bytes in temporary buffer - const unsigned char* data = buffer; - // point beyond last byte - const unsigned char* stop = data + bufferSize; - - // at least 8 bytes left ? => eat 8 bytes per step - for (; data + 8 <= stop; data += 8) - result = rotateLeft(result ^ processSingle(0, *(uint64_t*)data), 27) * Prime1 + Prime4; - - // 4 bytes left ? => eat those - if (data + 4 <= stop) - { - result = rotateLeft(result ^ (*(uint32_t*)data) * Prime1, 23) * Prime2 + Prime3; - data += 4; - } - - // take care of remaining 0..3 bytes, eat 1 byte per step - while (data != stop) - result = rotateLeft(result ^ (*data++) * Prime5, 11) * Prime1; - - // mix bits - result ^= result >> 33; - result *= Prime2; - result ^= result >> 29; - result *= Prime3; - result ^= result >> 32; - return result; - } - - - /// combine constructor, add() and hash() in one static function (C style) - /** @param input pointer to a continuous block of data - @param length number of bytes - @param seed your seed value, e.g. zero is a valid seed - @return 64 bit XXHash **/ - static uint64_t hash(const void* input, uint64_t length, uint64_t seed) - { - XXHash64 hasher(seed); - hasher.add(input, length); - return hasher.hash(); - } - -private: - /// magic constants :-) - static const uint64_t Prime1 = 11400714785074694791ULL; - static const uint64_t Prime2 = 14029467366897019727ULL; - static const uint64_t Prime3 = 1609587929392839161ULL; - static const uint64_t Prime4 = 9650029242287828579ULL; - static const uint64_t Prime5 = 2870177450012600261ULL; - - /// temporarily store up to 31 bytes between multiple add() calls - static const uint64_t MaxBufferSize = 31+1; - - uint64_t state[4]; - unsigned char buffer[MaxBufferSize]; - uint32_t bufferSize; - uint64_t totalLength; - - /// rotate bits, should compile to a single CPU instruction (ROL) - static inline uint64_t rotateLeft(uint64_t x, unsigned char bits) - { - return (x << bits) | (x >> (64 - bits)); - } - - /// process a single 64 bit value - static inline uint64_t processSingle(uint64_t previous, uint64_t input) - { - return rotateLeft(previous + input * Prime2, 31) * Prime1; - } - - /// process a block of 4x4 bytes, this is the main part of the XXHash32 algorithm - static inline void process(const void* data, uint64_t& state0, uint64_t& state1, uint64_t& state2, uint64_t& state3) - { - const uint64_t* block = (const uint64_t*) data; - state0 = processSingle(state0, block[0]); - state1 = processSingle(state1, block[1]); - state2 = processSingle(state2, block[2]); - state3 = processSingle(state3, block[3]); - } -}; diff --git a/src/Zcash/Entry.cpp b/src/Zcash/Entry.cpp index cbab0176d74..ff5f1ac21ce 100644 --- a/src/Zcash/Entry.cpp +++ b/src/Zcash/Entry.cpp @@ -6,24 +6,30 @@ #include "Entry.h" -#include "TAddress.h" #include "Signer.h" +#include "TAddress.h" -using namespace TW::Zcash; -using namespace std; +namespace TW::Zcash { -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const std::string& address, [[maybe_unused]] TW::byte p2pkh, [[maybe_unused]] TW::byte p2sh, [[maybe_unused]] const char* hrp) const { return TAddress::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, [[maybe_unused]] const char* hrp) const { return TAddress(publicKey, p2pkh).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + const auto addr = TAddress(address); + return {addr.bytes.begin() + 2, addr.bytes.end()}; +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { signTemplate(dataIn, dataOut); } -void Entry::plan(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +void Entry::plan([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { planTemplate(dataIn, dataOut); } + +} // namespace TW::Zcash diff --git a/src/Zcash/Entry.h b/src/Zcash/Entry.h index 503b13544d5..626f8105131 100644 --- a/src/Zcash/Entry.h +++ b/src/Zcash/Entry.h @@ -12,13 +12,13 @@ namespace TW::Zcash { /// Zcash entry dispatcher. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeZcash, TWCoinTypeZelcash}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + void plan(TWCoinType coin, const Data& dataIn, Data& dataOut) const; }; } // namespace TW::Zcash diff --git a/src/Zcash/Signer.cpp b/src/Zcash/Signer.cpp index 9387f58a8b7..37db9183ee8 100644 --- a/src/Zcash/Signer.cpp +++ b/src/Zcash/Signer.cpp @@ -11,8 +11,7 @@ #include "Transaction.h" #include "TransactionBuilder.h" -using namespace TW; -using namespace TW::Zcash; +namespace TW::Zcash { TransactionPlan Signer::plan(const SigningInput& input) noexcept { auto plan = Bitcoin::TransactionSigner::plan(input); @@ -38,3 +37,5 @@ SigningOutput Signer::sign(const SigningInput& input) noexcept { } return output; } + +} // namespace TW::Zcash diff --git a/src/Zcash/TAddress.cpp b/src/Zcash/TAddress.cpp deleted file mode 100644 index 9ebb2471ef5..00000000000 --- a/src/Zcash/TAddress.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "TAddress.h" - -using namespace TW::Zcash; diff --git a/src/Zcash/TAddress.h b/src/Zcash/TAddress.h index 6d6f5ac74a7..3452746b13f 100644 --- a/src/Zcash/TAddress.h +++ b/src/Zcash/TAddress.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -45,8 +45,3 @@ class TAddress : public TW::Base58Address<22> { }; } // namespace TW::Zcash - -/// Wrapper for C interface. -struct TWZcashTAddress { - TW::Zcash::TAddress impl; -}; diff --git a/src/Zcash/Transaction.cpp b/src/Zcash/Transaction.cpp index b023b1284f0..291c3384370 100644 --- a/src/Zcash/Transaction.cpp +++ b/src/Zcash/Transaction.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,26 +8,23 @@ #include "../Bitcoin/SigHashType.h" #include "../BinaryCoding.h" -#include "../Hash.h" -#include "../HexCoding.h" #include -using namespace TW; -using namespace TW::Zcash; +namespace TW::Zcash { -const auto sigHashPersonalization = Data({'Z','c','a','s','h','S','i','g','H','a','s','h'}); -const auto prevoutsHashPersonalization = Data({'Z','c','a','s','h','P','r','e','v','o','u','t','H','a','s','h'}); -const auto sequenceHashPersonalization = Data({'Z','c','a','s','h','S','e','q','u','e','n','c','H','a','s','h'}); -const auto outputsHashPersonalization = Data({'Z','c','a','s','h','O','u','t','p','u','t','s','H','a','s','h'}); -const auto joinsplitsHashPersonalization = Data({'Z','c','a','s','h','J','S','p','l','i','t','s','H','a','s','h'}); -const auto shieldedSpendHashPersonalization = Data({'Z','c','a','s','h','S','S','p','e','n','d','s','H','a','s','h'}); -const auto shieldedOutputsHashPersonalization = Data({'Z','c','a','s','h','S','O','u','t','p','u','t','H','a','s','h'}); +const auto sigHashPersonalization = Data({'Z', 'c', 'a', 's', 'h', 'S', 'i', 'g', 'H', 'a', 's', 'h'}); +const auto prevoutsHashPersonalization = Data({'Z', 'c', 'a', 's', 'h', 'P', 'r', 'e', 'v', 'o', 'u', 't', 'H', 'a', 's', 'h'}); +const auto sequenceHashPersonalization = Data({'Z', 'c', 'a', 's', 'h', 'S', 'e', 'q', 'u', 'e', 'n', 'c', 'H', 'a', 's', 'h'}); +const auto outputsHashPersonalization = Data({'Z', 'c', 'a', 's', 'h', 'O', 'u', 't', 'p', 'u', 't', 's', 'H', 'a', 's', 'h'}); +const auto joinsplitsHashPersonalization = Data({'Z', 'c', 'a', 's', 'h', 'J', 'S', 'p', 'l', 'i', 't', 's', 'H', 'a', 's', 'h'}); +const auto shieldedSpendHashPersonalization = Data({'Z', 'c', 'a', 's', 'h', 'S', 'S', 'p', 'e', 'n', 'd', 's', 'H', 'a', 's', 'h'}); +const auto shieldedOutputsHashPersonalization = Data({'Z', 'c', 'a', 's', 'h', 'S', 'O', 'u', 't', 'p', 'u', 't', 'H', 'a', 's', 'h'}); /// See https://github.com/zcash/zips/blob/master/zip-0205.rst#sapling-deployment BRANCH_ID section -const std::array Zcash::SaplingBranchID = {0xbb, 0x09, 0xb8, 0x76}; +const std::array SaplingBranchID = {0xbb, 0x09, 0xb8, 0x76}; /// See https://github.com/zcash/zips/blob/master/zip-0206.rst#blossom-deployment BRANCH_ID section -const std::array Zcash::BlossomBranchID = {0x60, 0x0e, 0xb4, 0x2b}; +const std::array BlossomBranchID = {0x60, 0x0e, 0xb4, 0x2b}; Data Transaction::getPreImage(const Bitcoin::Script& scriptCode, size_t index, enum TWBitcoinSigHashType hashType, uint64_t amount) const { @@ -36,7 +33,7 @@ Data Transaction::getPreImage(const Bitcoin::Script& scriptCode, size_t index, e auto data = Data{}; // header - encode32LE(version, data); + encode32LE(_version, data); // nVersionGroupId encode32LE(versionGroupId, data); @@ -152,7 +149,7 @@ Data Transaction::getShieldedOutputsHash() const { } void Transaction::encode(Data& data) const { - encode32LE(version, data); + encode32LE(_version, data); encode32LE(versionGroupId, data); // vin @@ -181,7 +178,7 @@ void Transaction::encode(Data& data) const { Data Transaction::getSignatureHash(const Bitcoin::Script& scriptCode, size_t index, enum TWBitcoinSigHashType hashType, uint64_t amount, - Bitcoin::SignatureVersion version) const { + [[maybe_unused]] Bitcoin::SignatureVersion version) const { Data personalization; personalization.reserve(16); std::copy(sigHashPersonalization.begin(), sigHashPersonalization.begin() + 12, @@ -194,7 +191,7 @@ Data Transaction::getSignatureHash(const Bitcoin::Script& scriptCode, size_t ind Bitcoin::Proto::Transaction Transaction::proto() const { auto protoTx = Bitcoin::Proto::Transaction(); - protoTx.set_version(version); + protoTx.set_version(_version); protoTx.set_locktime(lockTime); for (const auto& input : inputs) { @@ -214,3 +211,5 @@ Bitcoin::Proto::Transaction Transaction::proto() const { return protoTx; } + +} // namespace TW::Zcash diff --git a/src/Zcash/Transaction.h b/src/Zcash/Transaction.h index e42b77ef106..4086f3ddbea 100644 --- a/src/Zcash/Transaction.h +++ b/src/Zcash/Transaction.h @@ -23,7 +23,7 @@ extern const std::array BlossomBranchID; /// Only supports transparent transaction right now /// See also https://github.com/zcash/zips/blob/master/zip-0243.rst struct Transaction { - uint32_t version = 0x80000004; + uint32_t _version = 0x80000004; uint32_t versionGroupId = 0x892F2085; uint32_t lockTime = 0; uint32_t expiryHeight = 0; @@ -40,7 +40,7 @@ struct Transaction { Transaction(uint32_t version, uint32_t versionGroupId, uint32_t lockTime, uint32_t expiryHeight, uint64_t valueBalance, std::array branchId) - : version(version) + : _version(version) , versionGroupId(versionGroupId) , lockTime(lockTime) , expiryHeight(expiryHeight) diff --git a/src/Zilliqa/Address.cpp b/src/Zilliqa/Address.cpp index bf010554bd0..af0d14ac1bd 100644 --- a/src/Zilliqa/Address.cpp +++ b/src/Zilliqa/Address.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,6 +8,8 @@ #include -using namespace TW::Zilliqa; +namespace TW::Zilliqa { const std::string Address::hrp = HRP_ZILLIQA; + +} // namespace TW::Zilliqa diff --git a/src/Zilliqa/Address.h b/src/Zilliqa/Address.h index be7a7ef234f..8214e6aaebb 100644 --- a/src/Zilliqa/Address.h +++ b/src/Zilliqa/Address.h @@ -26,7 +26,7 @@ class Address : public Bech32Address { Address(const Data& keyHash) : Bech32Address(hrp, keyHash) {} /// Initializes an address with a public key. - Address(const PublicKey& publicKey) : Bech32Address(hrp, HASHER_SHA2, publicKey) {} + Address(const PublicKey& publicKey) : Bech32Address(hrp, Hash::HasherSha256, publicKey) {} std::string checksumed() const { return checksum(getKeyHash()); diff --git a/src/Zilliqa/AddressChecksum.cpp b/src/Zilliqa/AddressChecksum.cpp index 5332bee2363..bbd072f6268 100644 --- a/src/Zilliqa/AddressChecksum.cpp +++ b/src/Zilliqa/AddressChecksum.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,11 +11,10 @@ #include "../uint256.h" #include -using namespace TW; -using namespace TW::Zilliqa; +namespace TW::Zilliqa { -/// see https://github.com/Zilliqa/Zilliqa/blob/1c53b792c7ae44f7b77366536a7e2f73a3eade6a/src/libServer/AddressChecksum.h -std::string Zilliqa::checksum(const Data& bytes) { +/// \see https://github.com/Zilliqa/Zilliqa/blob/1c53b792c7ae44f7b77366536a7e2f73a3eade6a/src/libServer/AddressChecksum.h +std::string checksum(const Data& bytes) { const auto addressString = hex(bytes); const auto hash = hex(Hash::sha256(bytes)); @@ -23,7 +22,7 @@ std::string Zilliqa::checksum(const Data& bytes) { uint256_t v("0x" + hash); std::string string = ""; - for (auto i = 0; i < addressString.size(); i += 1) { + for (auto i = 0ul; i < addressString.size(); i += 1) { const auto a = addressString[i]; if (a >= '0' && a <= '9') { string.push_back(a); @@ -38,3 +37,5 @@ std::string Zilliqa::checksum(const Data& bytes) { return string; } + +} // namespace TW::Zilliqa diff --git a/src/Zilliqa/AddressChecksum.h b/src/Zilliqa/AddressChecksum.h index c2f32e63152..a91665deda1 100644 --- a/src/Zilliqa/AddressChecksum.h +++ b/src/Zilliqa/AddressChecksum.h @@ -6,7 +6,7 @@ #pragma once -#include "../Data.h" +#include "Data.h" #include namespace TW::Zilliqa { diff --git a/src/Zilliqa/Entry.cpp b/src/Zilliqa/Entry.cpp index c249ba827ba..9ab22662f0d 100644 --- a/src/Zilliqa/Entry.cpp +++ b/src/Zilliqa/Entry.cpp @@ -9,23 +9,33 @@ #include "Address.h" #include "Signer.h" -using namespace TW::Zilliqa; -using namespace std; +namespace TW::Zilliqa { // Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. -bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { +bool Entry::validateAddress([[maybe_unused]] TWCoinType coin, const std::string& address, byte, byte, const char*) const { return Address::isValid(address); } -string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { +std::string Entry::deriveAddress([[maybe_unused]] TWCoinType coin, const PublicKey& publicKey, byte, const char*) const { return Address(publicKey).string(); } -void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { +Data Entry::addressToData([[maybe_unused]] TWCoinType coin, const std::string& address) const { + Address addr; + if (!Address::decode(address, addr)) { + return {}; + } + // data in Zilliqa is a checksummed string without 0x + return data(checksum(addr.getKeyHash())); +} + +void Entry::sign([[maybe_unused]] TWCoinType coin, const Data& dataIn, Data& dataOut) const { signTemplate(dataIn, dataOut); } -string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { +std::string Entry::signJSON([[maybe_unused]] TWCoinType coin, const std::string& json, const Data& key) const { return Signer::signJSON(json, key); } + +} // namespace TW::Zilliqa diff --git a/src/Zilliqa/Entry.h b/src/Zilliqa/Entry.h index 05cc698c96f..fa1335f5838 100644 --- a/src/Zilliqa/Entry.h +++ b/src/Zilliqa/Entry.h @@ -12,14 +12,14 @@ namespace TW::Zilliqa { /// Entry point for implementation of Zilliqa coin. /// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file -class Entry: public CoinEntry { +class Entry final : public CoinEntry { public: - virtual const std::vector coinTypes() const { return {TWCoinTypeZilliqa}; } - virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; - virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; - virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; - virtual bool supportsJSONSigning() const { return true; } - virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; + bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + Data addressToData(TWCoinType coin, const std::string& address) const; + void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + bool supportsJSONSigning() const { return true; } + std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; }; } // namespace TW::Zilliqa diff --git a/src/Zilliqa/Signer.cpp b/src/Zilliqa/Signer.cpp index 2de41566d73..521ffd5c556 100644 --- a/src/Zilliqa/Signer.cpp +++ b/src/Zilliqa/Signer.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -18,8 +18,8 @@ #include #include -using namespace TW; -using namespace TW::Zilliqa; +namespace TW::Zilliqa { + using ByteArray = ZilliqaMessage::ByteArray; static inline Data prependZero(Data& data) { @@ -95,7 +95,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { const auto preImage = Signer::getPreImage(input, address); const auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); const auto pubKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); - const auto signature = key.signSchnorr(preImage, TWCurveSECP256k1); + const auto signature = key.signZilliqa(preImage); const auto transaction = input.transaction(); // build json @@ -137,3 +137,5 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { input.set_private_key(key.data(), key.size()); return hex(Signer::sign(input).json()); } + +} // namespace TW::Zilliqa diff --git a/src/Zilliqa/Signer.h b/src/Zilliqa/Signer.h index d4089e4e485..c1ce6f9cfe6 100644 --- a/src/Zilliqa/Signer.h +++ b/src/Zilliqa/Signer.h @@ -7,7 +7,7 @@ #pragma once #include "Address.h" -#include "../Data.h" +#include "Data.h" #include "../PrivateKey.h" #include "../proto/Zilliqa.pb.h" diff --git a/src/algorithm/erase.h b/src/algorithm/erase.h new file mode 100644 index 00000000000..a01fe425ce2 --- /dev/null +++ b/src/algorithm/erase.h @@ -0,0 +1,33 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include + +namespace TW { + +// C++17 Implementation of https://en.cppreference.com/w/cpp/container/vector/erase2 +template +constexpr typename std::vector::size_type erase(std::vector& c, + const U& value) { + auto it = std::remove(c.begin(), c.end(), value); + auto r = std::distance(it, c.end()); + c.erase(it, c.end()); + return r; +} + +template +constexpr typename std::vector::size_type erase_if(std::vector& c, + Functor&& pred) { + auto it = std::remove_if(c.begin(), c.end(), std::forward(pred)); + auto r = std::distance(it, c.end()); + c.erase(it, c.end()); + return r; +} + +} // namespace TW \ No newline at end of file diff --git a/src/algorithm/sort_copy.h b/src/algorithm/sort_copy.h new file mode 100644 index 00000000000..0b4dfeefe05 --- /dev/null +++ b/src/algorithm/sort_copy.h @@ -0,0 +1,27 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include + +namespace TW { + +template +inline Container sortCopy(const Container& container) noexcept { + Container result = container; + std::sort(begin(result), end(result)); + return result; +} + +template +inline Container sortCopy(const Container& container, Func&& func) noexcept { + Container result = container; + std::sort(begin(result), end(result), std::forward(func)); + return result; +} + +} // namespace TW \ No newline at end of file diff --git a/src/algorithm/to_array.h b/src/algorithm/to_array.h new file mode 100644 index 00000000000..06f302cd7e1 --- /dev/null +++ b/src/algorithm/to_array.h @@ -0,0 +1,21 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include + +namespace TW { + +template +constexpr std::array to_array(Collection&& collection) { + std::array out{}; + std::copy(begin(collection), end(collection), out.begin()); + return out; +} + +} // namespace TW diff --git a/src/concepts/tw_concepts.h b/src/concepts/tw_concepts.h new file mode 100644 index 00000000000..57e2455a078 --- /dev/null +++ b/src/concepts/tw_concepts.h @@ -0,0 +1,19 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include + +namespace TW { + +template +concept integral = std::is_integral_v; + +template +concept floating_point = std::is_floating_point_v; + +} // namespace TW diff --git a/src/interface/TWAccount.cpp b/src/interface/TWAccount.cpp index cbe49c3f321..73a624738c7 100644 --- a/src/interface/TWAccount.cpp +++ b/src/interface/TWAccount.cpp @@ -10,30 +10,45 @@ using namespace TW; -struct TWAccount *_Nonnull TWAccountCreate(TWString *_Nonnull address, enum TWCoinType coin, TWString *_Nonnull derivationPath, TWString *_Nonnull extendedPublicKey) { +struct TWAccount* _Nonnull TWAccountCreate(TWString* _Nonnull address, enum TWCoinType coin, + enum TWDerivation derivation, + TWString* _Nonnull derivationPath, + TWString* _Nonnull publicKey, + TWString* _Nonnull extendedPublicKey) { auto& addressString = *reinterpret_cast(address); auto& derivationPathString = *reinterpret_cast(derivationPath); + auto& publicKeyString = *reinterpret_cast(publicKey); auto& extendedPublicKeyString = *reinterpret_cast(extendedPublicKey); const auto dp = DerivationPath(derivationPathString); - return new TWAccount{ Keystore::Account(addressString, coin, dp, extendedPublicKeyString) }; + return new TWAccount{ + Keystore::Account(addressString, coin, derivation, dp, publicKeyString, extendedPublicKeyString) + }; } -void TWAccountDelete(struct TWAccount *_Nonnull account) { +void TWAccountDelete(struct TWAccount* _Nonnull account) { delete account; } -TWString *_Nonnull TWAccountAddress(struct TWAccount *_Nonnull account) { +TWString* _Nonnull TWAccountAddress(struct TWAccount* _Nonnull account) { return TWStringCreateWithUTF8Bytes(account->impl.address.c_str()); } -TWString *_Nonnull TWAccountDerivationPath(struct TWAccount *_Nonnull account) { +enum TWDerivation TWAccountDerivation(struct TWAccount* _Nonnull account) { + return account->impl.derivation; +} + +TWString* _Nonnull TWAccountDerivationPath(struct TWAccount* _Nonnull account) { return TWStringCreateWithUTF8Bytes(account->impl.derivationPath.string().c_str()); } -TWString *_Nonnull TWAccountExtendedPublicKey(struct TWAccount *_Nonnull account) { +TWString* _Nonnull TWAccountPublicKey(struct TWAccount* _Nonnull account) { + return TWStringCreateWithUTF8Bytes(account->impl.publicKey.c_str()); +} + +TWString* _Nonnull TWAccountExtendedPublicKey(struct TWAccount* _Nonnull account) { return TWStringCreateWithUTF8Bytes(account->impl.extendedPublicKey.c_str()); } -enum TWCoinType TWAccountCoin(struct TWAccount *_Nonnull account) { +enum TWCoinType TWAccountCoin(struct TWAccount* _Nonnull account) { return account->impl.coin; } diff --git a/src/interface/TWAnyAddress.cpp b/src/interface/TWAnyAddress.cpp index 7361584ef85..6ad7a9fb9ce 100644 --- a/src/interface/TWAnyAddress.cpp +++ b/src/interface/TWAnyAddress.cpp @@ -7,35 +7,13 @@ #include #include #include -#include -#include "../Bitcoin/Address.h" -#include "../Bitcoin/CashAddress.h" -#include "../Bitcoin/SegwitAddress.h" -#include "../Cosmos/Address.h" -#include "../Decred/Address.h" -#include "../Kusama/Address.h" -#include "../Polkadot/Address.h" -#include "../Zcash/TAddress.h" -#include "../Zilliqa/Address.h" -#include "../Cardano/AddressV3.h" -#include "../NEO/Address.h" -#include "../Nano/Address.h" -#include "../Elrond/Address.h" -#include "../NEAR/Address.h" - -#include "../Coin.h" -#include "../HexCoding.h" - -using namespace TW; - -struct TWAnyAddress { - TWString* address; - enum TWCoinType coin; -}; +#include "Data.h" +#include "Coin.h" +#include "AnyAddress.h" bool TWAnyAddressEqual(struct TWAnyAddress* _Nonnull lhs, struct TWAnyAddress* _Nonnull rhs) { - return TWStringEqual(lhs->address, rhs->address) && lhs->coin == rhs->coin; + return *lhs->impl == *rhs->impl; } bool TWAnyAddressIsValid(TWString* _Nonnull string, enum TWCoinType coin) { @@ -43,183 +21,58 @@ bool TWAnyAddressIsValid(TWString* _Nonnull string, enum TWCoinType coin) { return TW::validateAddress(coin, address); } +bool TWAnyAddressIsValidBech32(TWString* _Nonnull string, enum TWCoinType coin, TWString* _Nonnull hrp) { + const auto& address = *reinterpret_cast(string); + const auto& hrpStr = *reinterpret_cast(hrp); + return TW::validateAddress(coin, address, hrpStr.c_str()); +} + struct TWAnyAddress* _Nullable TWAnyAddressCreateWithString(TWString* _Nonnull string, enum TWCoinType coin) { const auto& address = *reinterpret_cast(string); - auto normalized = TW::normalizeAddress(coin, address); - if (normalized.empty()) { return nullptr; } - return new TWAnyAddress{TWStringCreateWithUTF8Bytes(normalized.c_str()), coin}; + auto *impl = TW::AnyAddress::createAddress(address, coin); + if (impl == nullptr) { + return nullptr; + } + return new TWAnyAddress{impl}; +} + +struct TWAnyAddress* _Nullable TWAnyAddressCreateBech32(TWString* _Nonnull string, + enum TWCoinType coin, TWString* _Nonnull hrp) { + const auto& address = *reinterpret_cast(string); + const auto& hrpStr = *reinterpret_cast(hrp); + auto *impl = TW::AnyAddress::createAddress(address, coin, hrpStr); + if (impl == nullptr) { + return nullptr; + } + return new TWAnyAddress{impl}; } struct TWAnyAddress* _Nonnull TWAnyAddressCreateWithPublicKey( struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin) { - auto address = TW::deriveAddress(coin, publicKey->impl); - return new TWAnyAddress{TWStringCreateWithUTF8Bytes(address.c_str()), coin}; + return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin)}; +} + +struct TWAnyAddress* _Nonnull TWAnyAddressCreateBech32WithPublicKey( + struct TWPublicKey* _Nonnull publicKey, enum TWCoinType coin, TWString* _Nonnull hrp) { + const auto& hrpStr = *reinterpret_cast(hrp); + return new TWAnyAddress{TW::AnyAddress::createAddress(publicKey->impl, coin, hrpStr)}; } void TWAnyAddressDelete(struct TWAnyAddress* _Nonnull address) { - TWStringDelete(address->address); + delete address->impl; delete address; } TWString* _Nonnull TWAnyAddressDescription(struct TWAnyAddress* _Nonnull address) { - return TWStringCreateWithUTF8Bytes(TWStringUTF8Bytes(address->address)); + return TWStringCreateWithUTF8Bytes(address->impl->address.c_str()); } enum TWCoinType TWAnyAddressCoin(struct TWAnyAddress* _Nonnull address) { - return address->coin; + return address->impl->coin; } TWData* _Nonnull TWAnyAddressData(struct TWAnyAddress* _Nonnull address) { - auto& string = *reinterpret_cast(address->address); - Data data; - switch (address->coin) { - case TWCoinTypeBinance: - case TWCoinTypeCosmos: - case TWCoinTypeKava: - case TWCoinTypeTerra: - case TWCoinTypeBandChain: - case TWCoinTypeTHORChain: - case TWCoinTypeBluzelle: - case TWCoinTypeIoTeX: - case TWCoinTypeCryptoOrg: - case TWCoinTypeHarmony: - { - Cosmos::Address addr; - if (!Cosmos::Address::decode(string, addr)) { - break; - } - data = addr.getKeyHash(); - break; - } - - case TWCoinTypeBitcoin: - case TWCoinTypeDigiByte: - case TWCoinTypeGroestlcoin: - case TWCoinTypeLitecoin: - case TWCoinTypeViacoin: { - auto decoded = Bitcoin::SegwitAddress::decode(string); - if (!std::get<2>(decoded)) { - break; - } - data = std::get<0>(decoded).witnessProgram; - break; - } - - case TWCoinTypeBitcoinCash: { - auto addr = Bitcoin::CashAddress(string); - data.resize(Bitcoin::Address::size); - size_t outlen = 0; - cash_data_to_addr(data.data(), &outlen, addr.bytes.data(), 34); - data = Data(data.begin() + 1, data.end()); - break; - } - - case TWCoinTypeDash: - case TWCoinTypeDogecoin: - case TWCoinTypeMonacoin: - case TWCoinTypeQtum: - case TWCoinTypeRavencoin: - case TWCoinTypeZcoin: { - auto addr = Bitcoin::Address(string); - data = Data(addr.bytes.begin() + 1, addr.bytes.end()); - break; - } - - case TWCoinTypeDecred: { - auto addr = Decred::Address(string); - data = Data(addr.bytes.begin() + 2, addr.bytes.end()); - break; - } - - case TWCoinTypeZcash: - case TWCoinTypeZelcash: { - auto addr = Zcash::TAddress(string); - data = Data(addr.bytes.begin() + 2, addr.bytes.end()); - break; - } - - case TWCoinTypeCallisto: - case TWCoinTypeEthereum: - case TWCoinTypeEthereumClassic: - case TWCoinTypeGoChain: - case TWCoinTypePOANetwork: - case TWCoinTypeThunderToken: - case TWCoinTypeTomoChain: - case TWCoinTypeVeChain: - case TWCoinTypeTheta: - case TWCoinTypeWanchain: - case TWCoinTypeAion: - case TWCoinTypeSmartChainLegacy: - case TWCoinTypeSmartChain: - case TWCoinTypePolygon: - case TWCoinTypeOptimism: - case TWCoinTypeArbitrum: - case TWCoinTypeECOChain: - case TWCoinTypeXDai: - case TWCoinTypeAvalancheCChain: - case TWCoinTypeFantom: - case TWCoinTypeCelo: - data = parse_hex(string); - break; - - case TWCoinTypeNano: { - auto addr = Nano::Address(string); - data = Data(addr.bytes.begin(), addr.bytes.end()); - break; - } - - case TWCoinTypeZilliqa: { - Zilliqa::Address addr; - if (!Zilliqa::Address::decode(string, addr)) { - break; - } - // data in Zilliqa is a checksummed string without 0x - auto str = Zilliqa::checksum(addr.getKeyHash()); - data = Data(str.begin(), str.end()); - break; - } - - case TWCoinTypeKusama: { - auto addr = Kusama::Address(string); - data = Data(addr.bytes.begin() + 1, addr.bytes.end()); - break; - } - - case TWCoinTypePolkadot: { - auto addr = Polkadot::Address(string); - data = Data(addr.bytes.begin() + 1, addr.bytes.end()); - break; - } - - case TWCoinTypeCardano: { - auto addr = Cardano::AddressV3(string); - data = addr.data(); - break; - } - - case TWCoinTypeNEO: { - auto addr = NEO::Address(string); - data = Data(addr.bytes.begin(), addr.bytes.end()); - break; - } - - case TWCoinTypeElrond: { - Elrond::Address addr; - if (Elrond::Address::decode(string, addr)) { - data = addr.getKeyHash(); - } - - break; - } - - case TWCoinTypeNEAR: { - auto addr = NEAR::Address(string); - data = Data(addr.bytes.begin(), addr.bytes.end()); - break; - } - - default: break; - } + auto data = address->impl->getData(); return TWDataCreateWithBytes(data.data(), data.size()); } diff --git a/src/interface/TWBase32.cpp b/src/interface/TWBase32.cpp new file mode 100644 index 00000000000..6d2e89c342c --- /dev/null +++ b/src/interface/TWBase32.cpp @@ -0,0 +1,42 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "../Base32.h" + +#include + +using namespace TW; + +TWData* TWBase32DecodeWithAlphabet(TWString* _Nonnull string, TWString* _Nullable alphabet) { + Data decodedOut; + auto cppString = *reinterpret_cast(string); + const char* alphabetRaw = nullptr; + if (alphabet != nullptr) { + alphabetRaw = TWStringUTF8Bytes(alphabet); + } + auto result = Base32::decode(cppString, decodedOut, alphabetRaw); + return result ? TWDataCreateWithData(&decodedOut) : nullptr; +} + +TWData* _Nullable TWBase32Decode(TWString* _Nonnull string) { + return TWBase32DecodeWithAlphabet(string, nullptr); +} + +TWString* _Nonnull TWBase32EncodeWithAlphabet(TWData* _Nonnull data, TWString* _Nullable alphabet) { + auto cppData = *reinterpret_cast(data); + const char* alphabetRaw = nullptr; + if (alphabet != nullptr) { + alphabetRaw = TWStringUTF8Bytes(alphabet); + } + auto result = Base32::encode(cppData, alphabetRaw); + return TWStringCreateWithUTF8Bytes(result.c_str()); +} + +TWString* _Nonnull TWBase32Encode(TWData* _Nonnull data) { + return TWBase32EncodeWithAlphabet(data, nullptr); +} diff --git a/src/interface/TWBase64.cpp b/src/interface/TWBase64.cpp new file mode 100644 index 00000000000..c57a713e6e2 --- /dev/null +++ b/src/interface/TWBase64.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "../Base64.h" + +#include + +using namespace TW; + +TWData* _Nullable TWBase64Decode(TWString* _Nonnull string) { + auto cppString = *reinterpret_cast(string); + Data decodedOut = Base64::decode(cppString); + return TWDataCreateWithData(&decodedOut); +} + +TWData* _Nullable TWBase64DecodeUrl(TWString* _Nonnull string) { + auto cppString = *reinterpret_cast(string); + Data decodedOut = Base64::decodeBase64Url(cppString); + return TWDataCreateWithData(&decodedOut); +} + +TWString *_Nonnull TWBase64Encode(TWData *_Nonnull data) { + auto cppData = *reinterpret_cast(data); + auto result = Base64::encode(cppData); + return TWStringCreateWithUTF8Bytes(result.c_str()); +} + +TWString *_Nonnull TWBase64EncodeUrl(TWData *_Nonnull data) { + auto cppData = *reinterpret_cast(data); + auto result = Base64::encodeBase64Url(cppData); + return TWStringCreateWithUTF8Bytes(result.c_str()); +} diff --git a/src/interface/TWBitcoinAddress.cpp b/src/interface/TWBitcoinAddress.cpp index 8df8de2a37f..cefd940e992 100644 --- a/src/interface/TWBitcoinAddress.cpp +++ b/src/interface/TWBitcoinAddress.cpp @@ -13,25 +13,23 @@ #include -using namespace TW::Bitcoin; - bool TWBitcoinAddressEqual(struct TWBitcoinAddress *_Nonnull lhs, struct TWBitcoinAddress *_Nonnull rhs) { return lhs->impl == rhs->impl; } bool TWBitcoinAddressIsValid(TWData *_Nonnull data) { - return TWDataSize(data) == Address::size; + return TWDataSize(data) == TW::Bitcoin::Address::size; } bool TWBitcoinAddressIsValidString(TWString *_Nonnull string) { auto& s = *reinterpret_cast(string); - return Address::isValid(s); + return TW::Bitcoin::Address::isValid(s); } struct TWBitcoinAddress *_Nullable TWBitcoinAddressCreateWithString(TWString *_Nonnull string) { auto& s = *reinterpret_cast(string); try { - return new TWBitcoinAddress{ Address(s) }; + return new TWBitcoinAddress{ TW::Bitcoin::Address(s) }; } catch (...) { return nullptr; } @@ -40,7 +38,7 @@ struct TWBitcoinAddress *_Nullable TWBitcoinAddressCreateWithString(TWString *_N struct TWBitcoinAddress *_Nullable TWBitcoinAddressCreateWithData(TWData *_Nonnull data) { const auto& d = *reinterpret_cast(data); try { - return new TWBitcoinAddress{ Address(d) }; + return new TWBitcoinAddress{ TW::Bitcoin::Address(d) }; } catch (...) { return nullptr; } @@ -48,7 +46,7 @@ struct TWBitcoinAddress *_Nullable TWBitcoinAddressCreateWithData(TWData *_Nonnu struct TWBitcoinAddress *_Nullable TWBitcoinAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey, uint8_t prefix) { try { - return new TWBitcoinAddress{ Address(publicKey->impl, prefix) }; + return new TWBitcoinAddress{ TW::Bitcoin::Address(publicKey->impl, prefix) }; } catch (...) { return nullptr; } @@ -67,5 +65,5 @@ uint8_t TWBitcoinAddressPrefix(struct TWBitcoinAddress *_Nonnull address) { } TWData *_Nonnull TWBitcoinAddressKeyhash(struct TWBitcoinAddress *_Nonnull address) { - return TWDataCreateWithBytes(address->impl.bytes.data() + 1, Address::size - 1); + return TWDataCreateWithBytes(address->impl.bytes.data() + 1, TW::Bitcoin::Address::size - 1); } diff --git a/src/interface/TWBitcoinMessageSigner.cpp b/src/interface/TWBitcoinMessageSigner.cpp new file mode 100644 index 00000000000..d5b0985fbb1 --- /dev/null +++ b/src/interface/TWBitcoinMessageSigner.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "Bitcoin/MessageSigner.h" + +TWString* _Nonnull TWBitcoinMessageSignerSignMessage(const struct TWPrivateKey* _Nonnull privateKey, TWString* _Nonnull address, TWString* _Nonnull message) { + try { + const auto signature = TW::Bitcoin::MessageSigner::signMessage(privateKey->impl, TWStringUTF8Bytes(address), TWStringUTF8Bytes(message), true); + return TWStringCreateWithUTF8Bytes(signature.c_str()); + } catch (...) { + return TWStringCreateWithUTF8Bytes(""); + } +} + +bool TWBitcoinMessageSignerVerifyMessage(TWString* _Nonnull address, TWString* _Nonnull message, TWString* _Nonnull signature) { + return TW::Bitcoin::MessageSigner::verifyMessage(TWStringUTF8Bytes(address), TWStringUTF8Bytes(message), TWStringUTF8Bytes(signature)); +} diff --git a/src/interface/TWBitcoinScript.cpp b/src/interface/TWBitcoinScript.cpp index 32601fc5238..8b685973143 100644 --- a/src/interface/TWBitcoinScript.cpp +++ b/src/interface/TWBitcoinScript.cpp @@ -11,8 +11,6 @@ #include -using namespace TW::Bitcoin; - struct TWBitcoinScript *_Nonnull TWBitcoinScriptCreate() { auto* script = new TWBitcoinScript{}; return script; @@ -122,38 +120,44 @@ TWData *TWBitcoinScriptEncode(const struct TWBitcoinScript *script) { struct TWBitcoinScript *TWBitcoinScriptBuildPayToPublicKey(TWData *pubkey) { auto* v = reinterpret_cast*>(pubkey); - auto script = Script::buildPayToPublicKey(*v); - return new TWBitcoinScript{ script }; + auto script = TW::Bitcoin::Script::buildPayToPublicKey(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; } struct TWBitcoinScript *TWBitcoinScriptBuildPayToPublicKeyHash(TWData *hash) { auto* v = reinterpret_cast*>(hash); - auto script = Script::buildPayToPublicKeyHash(*v); - return new TWBitcoinScript{ script }; + auto script = TW::Bitcoin::Script::buildPayToPublicKeyHash(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; } struct TWBitcoinScript *TWBitcoinScriptBuildPayToScriptHash(TWData *scriptHash) { auto* v = reinterpret_cast*>(scriptHash); - auto script = Script::buildPayToScriptHash(*v); - return new TWBitcoinScript{ script }; + auto script = TW::Bitcoin::Script::buildPayToScriptHash(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; } struct TWBitcoinScript *TWBitcoinScriptBuildPayToWitnessPubkeyHash(TWData *hash) { auto* v = reinterpret_cast*>(hash); - auto script = Script::buildPayToWitnessPublicKeyHash(*v); - return new TWBitcoinScript{ script }; + auto script = TW::Bitcoin::Script::buildPayToWitnessPublicKeyHash(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; } struct TWBitcoinScript *TWBitcoinScriptBuildPayToWitnessScriptHash(TWData *scriptHash) { auto* v = reinterpret_cast*>(scriptHash); - auto script = Script::buildPayToWitnessScriptHash(*v); - return new TWBitcoinScript{ script }; + auto script = TW::Bitcoin::Script::buildPayToWitnessScriptHash(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; } struct TWBitcoinScript *_Nonnull TWBitcoinScriptLockScriptForAddress(TWString *_Nonnull address, enum TWCoinType coin) { auto* s = reinterpret_cast(address); - auto script = Script::lockScriptForAddress(*s, coin); - return new TWBitcoinScript{ script }; + auto script = TW::Bitcoin::Script::lockScriptForAddress(*s, coin); + //return new TWBitcoinScript{ .impl = script }; + return new TWBitcoinScript{ script };//win } uint32_t TWBitcoinScriptHashTypeForCoin(enum TWCoinType coinType) { diff --git a/src/interface/TWBitcoin.cpp b/src/interface/TWBitcoinSigHashType.cpp similarity index 100% rename from src/interface/TWBitcoin.cpp rename to src/interface/TWBitcoinSigHashType.cpp diff --git a/src/interface/TWCardano.cpp b/src/interface/TWCardano.cpp new file mode 100644 index 00000000000..9c1c43ed16d --- /dev/null +++ b/src/interface/TWCardano.cpp @@ -0,0 +1,31 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "Cardano/Transaction.h" +#include "Cardano/AddressV3.h" +#include "proto/Cardano.pb.h" + +using namespace TW; + +uint64_t TWCardanoMinAdaAmount(TWData *_Nonnull tokenBundle) { + const Data* bundleData = static_cast(tokenBundle); + TW::Cardano::Proto::TokenBundle bundleProto; + if (bundleData && bundleProto.ParseFromArray(bundleData->data(), (int)bundleData->size())) { + return TW::Cardano::TokenBundle::fromProto(bundleProto).minAdaAmount(); + } + return 0; +} + +TWString *_Nonnull TWCardanoGetStakingAddress(TWString *_Nonnull baseAddress) { + const auto& address = *reinterpret_cast(baseAddress); + try { + return TWStringCreateWithUTF8Bytes(TW::Cardano::AddressV3(address).getStakingAddress().c_str()); + } catch (...) { + return TWStringCreateWithUTF8Bytes(""); + } +} diff --git a/src/interface/TWCoinType.cpp b/src/interface/TWCoinType.cpp index a99ecee7c75..2d58d5a7be5 100644 --- a/src/interface/TWCoinType.cpp +++ b/src/interface/TWCoinType.cpp @@ -39,6 +39,12 @@ TWString *_Nonnull TWCoinTypeDerivationPath(enum TWCoinType coin) { return TWStringCreateWithUTF8Bytes(string.c_str()); } +TWString* TWCoinTypeDerivationPathWithDerivation(enum TWCoinType coin, enum TWDerivation derivation) { + const auto path = TW::derivationPath(coin, derivation); + const auto string = path.string(); + return TWStringCreateWithUTF8Bytes(string.c_str()); +} + TWString *_Nonnull TWCoinTypeDeriveAddress(enum TWCoinType coin, struct TWPrivateKey *_Nonnull privateKey) { const auto string = TW::deriveAddress(coin, privateKey->impl); return TWStringCreateWithUTF8Bytes(string.c_str()); @@ -65,6 +71,14 @@ uint8_t TWCoinTypeStaticPrefix(enum TWCoinType coin) { return TW::staticPrefix(coin); } +TWString* _Nonnull TWCoinTypeChainId(enum TWCoinType coin) { + return TWStringCreateWithUTF8Bytes(TW::chainId(coin)); +} + uint32_t TWCoinTypeSlip44Id(enum TWCoinType coin) { return TW::slip44Id(coin); } + +enum TWPublicKeyType TWCoinTypePublicKeyType(enum TWCoinType coin) { + return TW::publicKeyType(coin); +} diff --git a/src/interface/TWData.cpp b/src/interface/TWData.cpp index da2b67fd14e..b08b5e7cb94 100644 --- a/src/interface/TWData.cpp +++ b/src/interface/TWData.cpp @@ -14,20 +14,20 @@ using namespace TW; TWData *_Nonnull TWDataCreateWithBytes(const uint8_t *_Nonnull bytes, size_t size) { - auto* data = new std::vector(); + auto* data = new Data(); data->reserve(size); std::copy(bytes, bytes + size, std::back_inserter(*data)); return data; } TWData *_Nonnull TWDataCreateWithSize(size_t size) { - auto* data = new std::vector(size, 0); + auto* data = new Data(size, 0); return data; } TWData *_Nonnull TWDataCreateWithData(TWData *_Nonnull data) { - auto* other = reinterpret_cast*>(data); - auto* copy = new std::vector(*other); + auto* other = reinterpret_cast(data); + auto* copy = new Data(*other); return copy; } @@ -40,69 +40,69 @@ TWData* TWDataCreateWithHexString(const TWString* hex) { } size_t TWDataSize(TWData *_Nonnull data) { - auto* v = reinterpret_cast*>(data); + auto* v = reinterpret_cast(data); return v->size(); } uint8_t *_Nonnull TWDataBytes(TWData *_Nonnull data) { - auto* v = const_cast*>(reinterpret_cast*>(data)); + auto* v = const_cast(reinterpret_cast(data)); return v->data(); } uint8_t TWDataGet(TWData *_Nonnull data, size_t index) { - auto* v = reinterpret_cast*>(data); + auto* v = reinterpret_cast(data); return (*v)[index]; } void TWDataSet(TWData *_Nonnull data, size_t index, uint8_t byte) { - auto* v = const_cast*>(reinterpret_cast*>(data)); + auto* v = const_cast(reinterpret_cast(data)); (*v)[index] = byte; } void TWDataCopyBytes(TWData *_Nonnull data, size_t start, size_t size, uint8_t *_Nonnull output) { - auto* v = reinterpret_cast*>(data); + auto* v = reinterpret_cast(data); std::copy(std::begin(*v) + start, std::begin(*v) + start + size, output); } void TWDataReplaceBytes(TWData *_Nonnull data, size_t start, size_t size, const uint8_t *_Nonnull bytes) { - auto* v = const_cast*>(reinterpret_cast*>(data)); + auto* v = const_cast(reinterpret_cast(data)); std::copy(bytes, bytes + size, std::begin(*v) + start); } void TWDataAppendBytes(TWData *_Nonnull data, const uint8_t *_Nonnull bytes, size_t size) { - auto* v = const_cast*>(reinterpret_cast*>(data)); - for (auto i = 0; i < size; i += 1) + auto* v = const_cast(reinterpret_cast(data)); + for (auto i = 0ul; i < size; i += 1) v->push_back(bytes[i]); } void TWDataAppendByte(TWData *_Nonnull data, uint8_t byte) { - auto* v = const_cast*>(reinterpret_cast*>(data)); + auto* v = const_cast(reinterpret_cast(data)); v->push_back(byte); } void TWDataAppendData(TWData *_Nonnull data, TWData *_Nonnull append) { - auto* v = const_cast*>(reinterpret_cast*>(data)); - auto* av = reinterpret_cast*>(append); + auto* v = const_cast(reinterpret_cast(data)); + auto* av = reinterpret_cast(append); std::copy(av->begin(), av->end(), std::back_inserter(*v)); } void TWDataReverse(TWData *_Nonnull data) { - auto* v = const_cast*>(reinterpret_cast*>(data)); + auto* v = const_cast(reinterpret_cast(data)); std::reverse(std::begin(*v), std::end(*v)); } void TWDataReset(TWData *_Nonnull data) { - auto* v = const_cast*>(reinterpret_cast*>(data)); + auto* v = const_cast(reinterpret_cast(data)); std::fill(std::begin(*v), std::end(*v), 0); } void TWDataDelete(TWData *_Nonnull data) { - auto* v = reinterpret_cast*>(data); + auto* v = reinterpret_cast(data); delete v; } bool TWDataEqual(TWData *_Nonnull lhs, TWData *_Nonnull rhs) { - auto* lv = reinterpret_cast*>(lhs); - auto* rv = reinterpret_cast*>(rhs); + auto* lv = reinterpret_cast(lhs); + auto* rv = reinterpret_cast(rhs); return *lv == *rv; } diff --git a/src/interface/TWDataVector.cpp b/src/interface/TWDataVector.cpp new file mode 100644 index 00000000000..bea89cc1dac --- /dev/null +++ b/src/interface/TWDataVector.cpp @@ -0,0 +1,54 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "Data.h" + +#include +#include + +using namespace TW; + + +struct TWDataVector { + std::vector impl; +}; + + +struct TWDataVector *_Nonnull TWDataVectorCreate() { + auto* obj = new struct TWDataVector(); + assert(obj != nullptr); + return obj; +} + +struct TWDataVector *_Nonnull TWDataVectorCreateWithData(TWData *_Nonnull data) { + auto* obj = new struct TWDataVector(); + assert(obj != nullptr); + + TWDataVectorAdd(obj, data); + return obj; +} + +void TWDataVectorDelete(struct TWDataVector *_Nonnull dataVector) { + delete dataVector; +} + +void TWDataVectorAdd(struct TWDataVector *_Nonnull dataVector, TWData *_Nonnull data) { + dataVector->impl.push_back(TW::data(TWDataBytes(data), TWDataSize(data))); +} + +size_t TWDataVectorSize(const struct TWDataVector *_Nonnull dataVector) { + return dataVector->impl.size(); +} + +TWData *_Nullable TWDataVectorGet(const struct TWDataVector *_Nonnull dataVector, size_t index) { + if (index >= dataVector->impl.size()) { + return nullptr; + } + auto& elem = dataVector->impl[index]; + return TWDataCreateWithBytes(elem.data(), elem.size()); +} diff --git a/src/interface/TWDerivationPath.cpp b/src/interface/TWDerivationPath.cpp new file mode 100644 index 00000000000..f48bc051d77 --- /dev/null +++ b/src/interface/TWDerivationPath.cpp @@ -0,0 +1,65 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include + +#include "../DerivationPath.h" + +using namespace TW; + +struct TWDerivationPath* _Nonnull TWDerivationPathCreate(enum TWPurpose purpose, uint32_t coin, uint32_t account, uint32_t change, uint32_t address) { + return new TWDerivationPath{DerivationPath(purpose, coin, account, change, address)}; +} + +struct TWDerivationPath* _Nullable TWDerivationPathCreateWithString(TWString* _Nonnull string) { + auto& str = *reinterpret_cast(string); + try { + return new TWDerivationPath{DerivationPath(str)}; + } catch (...) { + return nullptr; + } +} + +void TWDerivationPathDelete(struct TWDerivationPath* _Nonnull path) { + delete path; +} + +uint32_t TWDerivationPathIndicesCount(struct TWDerivationPath* _Nonnull path) { + return static_cast(path->impl.indices.size()); +} + +struct TWDerivationPathIndex* _Nullable TWDerivationPathIndexAt(struct TWDerivationPath* _Nonnull path, uint32_t index) { + if (index >= path->impl.indices.size()) { + return nullptr; + } + return new TWDerivationPathIndex{path->impl.indices[index]}; +} + +enum TWPurpose TWDerivationPathPurpose(struct TWDerivationPath* _Nonnull path) { + return path->impl.purpose(); +} + +uint32_t TWDerivationPathCoin(struct TWDerivationPath* _Nonnull path) { + return path->impl.coin(); +} + +uint32_t TWDerivationPathAccount(struct TWDerivationPath* _Nonnull path) { + return path->impl.account(); +} + +uint32_t TWDerivationPathChange(struct TWDerivationPath* _Nonnull path) { + return path->impl.change(); +} + +uint32_t TWDerivationPathAddress(struct TWDerivationPath* _Nonnull path) { + return path->impl.address(); +} + +TWString* _Nonnull TWDerivationPathDescription(struct TWDerivationPath* _Nonnull path) { + return TWStringCreateWithUTF8Bytes(path->impl.string().c_str()); +} diff --git a/src/interface/TWDerivationPathIndex.cpp b/src/interface/TWDerivationPathIndex.cpp new file mode 100644 index 00000000000..f443955191e --- /dev/null +++ b/src/interface/TWDerivationPathIndex.cpp @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include + +#include "../DerivationPath.h" + +using namespace TW; + +struct TWDerivationPathIndex* _Nonnull TWDerivationPathIndexCreate(uint32_t value, bool hardened) { + return new TWDerivationPathIndex{DerivationPathIndex(value, hardened)}; +} + +void TWDerivationPathIndexDelete(struct TWDerivationPathIndex* _Nonnull index) { + delete index; +} + +uint32_t TWDerivationPathIndexValue(struct TWDerivationPathIndex* _Nonnull index) { + return index->impl.value; +} + +bool TWDerivationPathIndexHardened(struct TWDerivationPathIndex* _Nonnull index) { + return index->impl.hardened; +} + +TWString* _Nonnull TWDerivationPathIndexDescription(struct TWDerivationPathIndex* _Nonnull index) { + return TWStringCreateWithUTF8Bytes(index->impl.string().c_str()); +} diff --git a/src/interface/TWEthereumAbi.cpp b/src/interface/TWEthereumAbi.cpp index 9156b9ea315..2ea781199eb 100644 --- a/src/interface/TWEthereumAbi.cpp +++ b/src/interface/TWEthereumAbi.cpp @@ -17,14 +17,12 @@ #include #include -using namespace TW::Ethereum::ABI; -using namespace TW::Ethereum; using namespace TW; - +namespace EthAbi = TW::Ethereum::ABI; TWData* _Nonnull TWEthereumAbiEncode(struct TWEthereumAbiFunction* _Nonnull func_in) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; Data encoded; function.encode(encoded); @@ -34,7 +32,7 @@ TWData* _Nonnull TWEthereumAbiEncode(struct TWEthereumAbiFunction* _Nonnull func bool TWEthereumAbiDecodeOutput(struct TWEthereumAbiFunction* _Nonnull func_in, TWData* _Nonnull encoded) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; assert(encoded != nullptr); Data encData = data(TWDataBytes(encoded), TWDataSize(encoded)); @@ -45,9 +43,9 @@ bool TWEthereumAbiDecodeOutput(struct TWEthereumAbiFunction* _Nonnull func_in, TWString* _Nullable TWEthereumAbiDecodeCall(TWData* _Nonnull callData, TWString* _Nonnull abiString) { const Data& call = *(reinterpret_cast(callData)); const auto& jsonString = *reinterpret_cast(abiString); - try { + try { auto abi = nlohmann::json::parse(jsonString); - auto string = decodeCall(call, abi); + auto string = EthAbi::decodeCall(call, abi); if (!string.has_value()) { return nullptr; } @@ -61,9 +59,7 @@ TWString* _Nullable TWEthereumAbiDecodeCall(TWData* _Nonnull callData, TWString* TWData* _Nonnull TWEthereumAbiEncodeTyped(TWString* _Nonnull messageJson) { Data data; try { - data = ParamStruct::hashStructJson(TWStringUTF8Bytes(messageJson)); - } catch (...) { - // return empty - } + data = EthAbi::ParamStruct::hashStructJson(TWStringUTF8Bytes(messageJson)); + } catch (...) {} // return empty return TWDataCreateWithBytes(data.data(), data.size()); } diff --git a/src/interface/TWEthereumAbiFunction.cpp b/src/interface/TWEthereumAbiFunction.cpp index 93b451c2a51..3951b949f5b 100644 --- a/src/interface/TWEthereumAbiFunction.cpp +++ b/src/interface/TWEthereumAbiFunction.cpp @@ -16,11 +16,10 @@ #include using namespace TW; -using namespace TW::Ethereum; -using namespace TW::Ethereum::ABI; +namespace EthAbi = TW::Ethereum::ABI; struct TWEthereumAbiFunction *_Nonnull TWEthereumAbiFunctionCreateWithString(TWString *_Nonnull name) { - auto func = Function(TWStringUTF8Bytes(name)); + auto func = EthAbi::Function(TWStringUTF8Bytes(name)); return new TWEthereumAbiFunction{ func }; } @@ -40,173 +39,173 @@ TWString *_Nonnull TWEthereumAbiFunctionGetType(struct TWEthereumAbiFunction *_N int TWEthereumAbiFunctionAddParamUInt8(struct TWEthereumAbiFunction *_Nonnull func_in, uint8_t val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); + auto param = std::make_shared(val); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamUInt16(struct TWEthereumAbiFunction *_Nonnull func_in, uint16_t val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); + auto param = std::make_shared(val); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamUInt32(struct TWEthereumAbiFunction *_Nonnull func_in, uint32_t val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); + auto param = std::make_shared(val); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamUInt64(struct TWEthereumAbiFunction *_Nonnull func_in, uint64_t val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); + auto param = std::make_shared(val); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamUInt256(struct TWEthereumAbiFunction *_Nonnull func_in, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; uint256_t val2 = load(*static_cast(val)); - auto param = std::make_shared(val2); + auto param = std::make_shared(val2); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamUIntN(struct TWEthereumAbiFunction *_Nonnull func_in, int bits, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; uint256_t val2 = load(*static_cast(val)); - auto param = std::make_shared(bits, val2); + auto param = std::make_shared(bits, val2); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamInt8(struct TWEthereumAbiFunction *_Nonnull func_in, int8_t val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); + auto param = std::make_shared(val); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamInt16(struct TWEthereumAbiFunction *_Nonnull func_in, int16_t val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); + auto param = std::make_shared(val); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamInt32(struct TWEthereumAbiFunction *_Nonnull func_in, int32_t val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); + auto param = std::make_shared(val); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamInt64(struct TWEthereumAbiFunction *_Nonnull func_in, int64_t val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); + auto param = std::make_shared(val); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamInt256(struct TWEthereumAbiFunction *_Nonnull func_in, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; assert(val != nullptr); - int256_t val2 = ValueEncoder::int256FromUint256(load(*static_cast(val))); - auto param = std::make_shared(val2); + int256_t val2 = EthAbi::ValueEncoder::int256FromUint256(load(*static_cast(val))); + auto param = std::make_shared(val2); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamIntN(struct TWEthereumAbiFunction *_Nonnull func_in, int bits, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; assert(val != nullptr); - int256_t val2 = ValueEncoder::int256FromUint256(load(*static_cast(val))); - auto param = std::make_shared(bits, val2); + int256_t val2 = EthAbi::ValueEncoder::int256FromUint256(load(*static_cast(val))); + auto param = std::make_shared(bits, val2); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamBool(struct TWEthereumAbiFunction *_Nonnull func_in, bool val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(val); + auto param = std::make_shared(val); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamString(struct TWEthereumAbiFunction *_Nonnull func_in, TWString *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; assert(val != nullptr); - auto param = std::make_shared(TWStringUTF8Bytes(val)); + auto param = std::make_shared(TWStringUTF8Bytes(val)); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamAddress(struct TWEthereumAbiFunction *_Nonnull func_in, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; assert(val != nullptr); Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - auto param = std::make_shared(data); + auto param = std::make_shared(data); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamBytes(struct TWEthereumAbiFunction *_Nonnull func_in, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - auto param = std::make_shared(data); + auto param = std::make_shared(data); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamBytesFix(struct TWEthereumAbiFunction *_Nonnull func_in, size_t count, TWData *_Nonnull val, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - auto param = std::make_shared(count, data); + auto param = std::make_shared(count, data); auto idx = function.addParam(param, isOutput); return idx; } int TWEthereumAbiFunctionAddParamArray(struct TWEthereumAbiFunction *_Nonnull func_in, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - auto param = std::make_shared(); + auto param = std::make_shared(); auto idx = function.addParam(param, isOutput); return idx; } @@ -215,13 +214,13 @@ int TWEthereumAbiFunctionAddParamArray(struct TWEthereumAbiFunction *_Nonnull fu uint8_t TWEthereumAbiFunctionGetParamUInt8(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - std::shared_ptr param; + std::shared_ptr param; if (!function.getParam(idx, param, isOutput)) { return 0; } - auto param2 = std::dynamic_pointer_cast(param); + auto param2 = std::dynamic_pointer_cast(param); if (param2 == nullptr) { return 0; } @@ -230,13 +229,13 @@ uint8_t TWEthereumAbiFunctionGetParamUInt8(struct TWEthereumAbiFunction *_Nonnul uint64_t TWEthereumAbiFunctionGetParamUInt64(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - std::shared_ptr param; + std::shared_ptr param; if (!function.getParam(idx, param, isOutput)) { return 0; } - auto param2 = std::dynamic_pointer_cast(param); + auto param2 = std::dynamic_pointer_cast(param); if (param2 == nullptr) { return 0; } @@ -245,15 +244,15 @@ uint64_t TWEthereumAbiFunctionGetParamUInt64(struct TWEthereumAbiFunction *_Nonn TWData *_Nonnull TWEthereumAbiFunctionGetParamUInt256(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; uint256_t val256 = 0; - std::shared_ptr param; + std::shared_ptr param; if (!function.getParam(idx, param, isOutput)) { TW::Data valData = TW::store(val256); return TWDataCreateWithData(&valData); } - auto param2 = std::dynamic_pointer_cast(param); + auto param2 = std::dynamic_pointer_cast(param); if (param2 == nullptr) { TW::Data valData = TW::store(val256); return TWDataCreateWithData(&valData); @@ -265,13 +264,13 @@ TWData *_Nonnull TWEthereumAbiFunctionGetParamUInt256(struct TWEthereumAbiFuncti bool TWEthereumAbiFunctionGetParamBool(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - std::shared_ptr param; + std::shared_ptr param; if (!function.getParam(idx, param, isOutput)) { return false; } - auto param2 = std::dynamic_pointer_cast(param); + auto param2 = std::dynamic_pointer_cast(param); if (param2 == nullptr) { return false; } @@ -280,14 +279,14 @@ bool TWEthereumAbiFunctionGetParamBool(struct TWEthereumAbiFunction *_Nonnull fu TWString *_Nonnull TWEthereumAbiFunctionGetParamString(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; std::string valStr; - std::shared_ptr param; + std::shared_ptr param; if (!function.getParam(idx, param, isOutput)) { return TWStringCreateWithUTF8Bytes(valStr.c_str()); } - auto param2 = std::dynamic_pointer_cast(param); + auto param2 = std::dynamic_pointer_cast(param); if (param2 == nullptr) { return TWStringCreateWithUTF8Bytes(valStr.c_str()); } @@ -297,14 +296,14 @@ TWString *_Nonnull TWEthereumAbiFunctionGetParamString(struct TWEthereumAbiFunct TWData *_Nonnull TWEthereumAbiFunctionGetParamAddress(struct TWEthereumAbiFunction *_Nonnull func_in, int idx, bool isOutput) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; Data valData; - std::shared_ptr param; + std::shared_ptr param; if (!function.getParam(idx, param, isOutput)) { return TWDataCreateWithData(&valData); } - auto param2 = std::dynamic_pointer_cast(param); + auto param2 = std::dynamic_pointer_cast(param); if (param2 == nullptr) { return TWDataCreateWithData(&valData); } @@ -314,12 +313,12 @@ TWData *_Nonnull TWEthereumAbiFunctionGetParamAddress(struct TWEthereumAbiFuncti ///// AddInArrayParam -int addInArrayParam(Function& function, int arrayIdx, const std::shared_ptr& childParam) { - std::shared_ptr param; +int addInArrayParam(EthAbi::Function& function, int arrayIdx, const std::shared_ptr& childParam) { + std::shared_ptr param; if (!function.getInParam(arrayIdx, param)) { return -1; } - std::shared_ptr paramArr = std::dynamic_pointer_cast(param); + std::shared_ptr paramArr = std::dynamic_pointer_cast(param); if (paramArr == nullptr) { return -1; // not an array } @@ -328,130 +327,130 @@ int addInArrayParam(Function& function, int arrayIdx, const std::shared_ptrimpl; + EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + return addInArrayParam(function, arrayIdx, std::make_shared(val)); } int TWEthereumAbiFunctionAddInArrayParamUInt16(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, uint16_t val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + return addInArrayParam(function, arrayIdx, std::make_shared(val)); } int TWEthereumAbiFunctionAddInArrayParamUInt32(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, uint32_t val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + return addInArrayParam(function, arrayIdx, std::make_shared(val)); } int TWEthereumAbiFunctionAddInArrayParamUInt64(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, uint64_t val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + return addInArrayParam(function, arrayIdx, std::make_shared(val)); } int TWEthereumAbiFunctionAddInArrayParamUInt256(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWData *_Nonnull val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; uint256_t val2 = load(*static_cast(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(val2)); + return addInArrayParam(function, arrayIdx, std::make_shared(val2)); } int TWEthereumAbiFunctionAddInArrayParamUIntN(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int bits, TWData *_Nonnull val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; uint256_t val2 = load(*static_cast(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(bits, val2)); + return addInArrayParam(function, arrayIdx, std::make_shared(bits, val2)); } int TWEthereumAbiFunctionAddInArrayParamInt8(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int8_t val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + return addInArrayParam(function, arrayIdx, std::make_shared(val)); } int TWEthereumAbiFunctionAddInArrayParamInt16(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int16_t val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + return addInArrayParam(function, arrayIdx, std::make_shared(val)); } int TWEthereumAbiFunctionAddInArrayParamInt32(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int32_t val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + return addInArrayParam(function, arrayIdx, std::make_shared(val)); } int TWEthereumAbiFunctionAddInArrayParamInt64(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int64_t val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + return addInArrayParam(function, arrayIdx, std::make_shared(val)); } int TWEthereumAbiFunctionAddInArrayParamInt256(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWData *_Nonnull val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; assert(val != nullptr); - int256_t val2 = ValueEncoder::int256FromUint256(load(*static_cast(val))); - return addInArrayParam(function, arrayIdx, std::make_shared(val2)); + int256_t val2 = EthAbi::ValueEncoder::int256FromUint256(load(*static_cast(val))); + return addInArrayParam(function, arrayIdx, std::make_shared(val2)); } int TWEthereumAbiFunctionAddInArrayParamIntN(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, int bits, TWData *_Nonnull val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; assert(val != nullptr); - int256_t val2 = ValueEncoder::int256FromUint256(load(*static_cast(val))); - return addInArrayParam(function, arrayIdx, std::make_shared(bits, val2)); + int256_t val2 = EthAbi::ValueEncoder::int256FromUint256(load(*static_cast(val))); + return addInArrayParam(function, arrayIdx, std::make_shared(bits, val2)); } int TWEthereumAbiFunctionAddInArrayParamBool(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, bool val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; - return addInArrayParam(function, arrayIdx, std::make_shared(val)); + return addInArrayParam(function, arrayIdx, std::make_shared(val)); } int TWEthereumAbiFunctionAddInArrayParamString(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWString *_Nonnull val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; assert(val != nullptr); - return addInArrayParam(function, arrayIdx, std::make_shared(TWStringUTF8Bytes(val))); + return addInArrayParam(function, arrayIdx, std::make_shared(TWStringUTF8Bytes(val))); } int TWEthereumAbiFunctionAddInArrayParamAddress(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWData *_Nonnull val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; assert(val != nullptr); Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(data)); + return addInArrayParam(function, arrayIdx, std::make_shared(data)); } int TWEthereumAbiFunctionAddInArrayParamBytes(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, TWData *_Nonnull val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(data)); + return addInArrayParam(function, arrayIdx, std::make_shared(data)); } int TWEthereumAbiFunctionAddInArrayParamBytesFix(struct TWEthereumAbiFunction *_Nonnull func_in, int arrayIdx, size_t count, TWData *_Nonnull val) { assert(func_in != nullptr); - Function& function = func_in->impl; + EthAbi::Function& function = func_in->impl; Data data = TW::data(TWDataBytes(val), TWDataSize(val)); - return addInArrayParam(function, arrayIdx, std::make_shared(count, data)); + return addInArrayParam(function, arrayIdx, std::make_shared(count, data)); } diff --git a/src/interface/TWEthereumAbiValue.cpp b/src/interface/TWEthereumAbiValue.cpp index 7a6843f23b0..a3dc02612af 100644 --- a/src/interface/TWEthereumAbiValue.cpp +++ b/src/interface/TWEthereumAbiValue.cpp @@ -10,63 +10,63 @@ #include #include -using namespace TW::Ethereum; using namespace TW; +namespace EthAbi = TW::Ethereum::ABI; TWData* _Nonnull TWEthereumAbiValueEncodeBool(bool value) { Data data; - ABI::ValueEncoder::encodeBool(value, data); + EthAbi::ValueEncoder::encodeBool(value, data); return TWDataCreateWithBytes(data.data(), data.size()); } TWData* _Nonnull TWEthereumAbiValueEncodeInt32(int32_t value) { Data data; - ABI::ValueEncoder::encodeInt32(value, data); + EthAbi::ValueEncoder::encodeInt32(value, data); return TWDataCreateWithBytes(data.data(), data.size()); } TWData* _Nonnull TWEthereumAbiValueEncodeUInt32(uint32_t value) { Data data; - ABI::ValueEncoder::encodeUInt32(value, data); + EthAbi::ValueEncoder::encodeUInt32(value, data); return TWDataCreateWithBytes(data.data(), data.size()); } TWData* _Nonnull TWEthereumAbiValueEncodeInt256(TWData* _Nonnull value) { Data data; int256_t value256 = static_cast(TW::load(*reinterpret_cast(value))); - ABI::ValueEncoder::encodeInt256(value256, data); + EthAbi::ValueEncoder::encodeInt256(value256, data); return TWDataCreateWithBytes(data.data(), data.size()); } TWData* _Nonnull TWEthereumAbiValueEncodeUInt256(TWData* _Nonnull value) { Data data; uint256_t value256 = TW::load(*reinterpret_cast(value)); - ABI::ValueEncoder::encodeUInt256(value256, data); + EthAbi::ValueEncoder::encodeUInt256(value256, data); return TWDataCreateWithBytes(data.data(), data.size()); } TWData* _Nonnull TWEthereumAbiValueEncodeAddress(TWData* _Nonnull value) { Data data; - ABI::ValueEncoder::encodeAddress(*reinterpret_cast(value), data); + EthAbi::ValueEncoder::encodeAddress(*reinterpret_cast(value), data); return TWDataCreateWithBytes(data.data(), data.size()); } TWData* _Nonnull TWEthereumAbiValueEncodeString(TWString* _Nonnull value) { Data data; - ABI::ValueEncoder::encodeString(TWStringUTF8Bytes(value), data); + EthAbi::ValueEncoder::encodeString(TWStringUTF8Bytes(value), data); return TWDataCreateWithBytes(data.data(), data.size()); } TWData* _Nonnull TWEthereumAbiValueEncodeBytes(TWData* _Nonnull value) { Data data; - ABI::ValueEncoder::encodeBytes(*reinterpret_cast(value), data); + EthAbi::ValueEncoder::encodeBytes(*reinterpret_cast(value), data); return TWDataCreateWithBytes(data.data(), data.size()); } TWData* _Nonnull TWEthereumAbiValueEncodeBytesDyn(TWData* _Nonnull value) { Data data; - ABI::ValueEncoder::encodeBytesDyn(*reinterpret_cast(value), data); + EthAbi::ValueEncoder::encodeBytesDyn(*reinterpret_cast(value), data); return TWDataCreateWithBytes(data.data(), data.size()); } diff --git a/src/interface/TWEthereumFee.cpp b/src/interface/TWEthereumFee.cpp deleted file mode 100644 index 12ec6f58955..00000000000 --- a/src/interface/TWEthereumFee.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include - -#include - -#include "Ethereum/Fee.h" -#include "Data.h" -#include "HexCoding.h" -#include "uint256.h" - -#include -#include - -using namespace TW; - -TWString* _Nullable TWEthereumFeeSuggest(TWString* _Nonnull feeHistory) { - const auto& json = *reinterpret_cast(feeHistory); - try { - const auto parsed = nlohmann::json::parse(json); - const auto fee = Ethereum::Fee::suggestFee(parsed); - return TWStringCreateWithUTF8Bytes(fee.dump().c_str()); - } - catch(...) { - return nullptr; - } -} diff --git a/src/interface/TWFIOAccount.cpp b/src/interface/TWFIOAccount.cpp index b600d82b493..4cdd419aa71 100644 --- a/src/interface/TWFIOAccount.cpp +++ b/src/interface/TWFIOAccount.cpp @@ -12,7 +12,6 @@ #include using namespace TW; -using namespace TW::FIO; struct TWFIOAccount { std::string description; @@ -20,11 +19,11 @@ struct TWFIOAccount { struct TWFIOAccount *_Nullable TWFIOAccountCreateWithString(TWString *_Nonnull string) { const auto& account = *reinterpret_cast(string); - if (Address::isValid(account)) { - const auto addr = Address(account); - return new TWFIOAccount{Actor::actor(addr)}; + if (FIO::Address::isValid(account)) { + const auto addr = FIO::Address(account); + return new TWFIOAccount{FIO::Actor::actor(addr)}; } - if (Actor::validate(account)) { + if (FIO::Actor::validate(account)) { return new TWFIOAccount{account}; } return nullptr; diff --git a/src/interface/TWGroestlcoinAddress.cpp b/src/interface/TWGroestlcoinAddress.cpp index 11d28b4780a..e825cc591f1 100644 --- a/src/interface/TWGroestlcoinAddress.cpp +++ b/src/interface/TWGroestlcoinAddress.cpp @@ -11,34 +11,32 @@ #include -using namespace TW::Groestlcoin; - -bool TWGroestlcoinAddressEqual(struct TWGroestlcoinAddress *_Nonnull lhs, struct TWGroestlcoinAddress *_Nonnull rhs) { +bool TWGroestlcoinAddressEqual(struct TWGroestlcoinAddress* _Nonnull lhs, struct TWGroestlcoinAddress* _Nonnull rhs) { return lhs->impl.bytes == rhs->impl.bytes; } -bool TWGroestlcoinAddressIsValidString(TWString *_Nonnull string) { +bool TWGroestlcoinAddressIsValidString(TWString* _Nonnull string) { auto& s = *reinterpret_cast(string); - return Address::isValid(s); + return TW::Groestlcoin::Address::isValid(s); } -struct TWGroestlcoinAddress *_Nullable TWGroestlcoinAddressCreateWithString(TWString *_Nonnull string) { +struct TWGroestlcoinAddress* _Nullable TWGroestlcoinAddressCreateWithString(TWString* _Nonnull string) { auto& s = *reinterpret_cast(string); - if (!Address::isValid(s)) { + if (!TW::Groestlcoin::Address::isValid(s)) { return nullptr; } - return new TWGroestlcoinAddress{ Address(s) }; + return new TWGroestlcoinAddress{TW::Groestlcoin::Address(s)}; } -struct TWGroestlcoinAddress *_Nonnull TWGroestlcoinAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey, uint8_t prefix) { - return new TWGroestlcoinAddress{ Address(publicKey->impl, prefix) }; +struct TWGroestlcoinAddress* _Nonnull TWGroestlcoinAddressCreateWithPublicKey(struct TWPublicKey* _Nonnull publicKey, uint8_t prefix) { + return new TWGroestlcoinAddress{TW::Groestlcoin::Address(publicKey->impl, prefix)}; } -void TWGroestlcoinAddressDelete(struct TWGroestlcoinAddress *_Nonnull address) { +void TWGroestlcoinAddressDelete(struct TWGroestlcoinAddress* _Nonnull address) { delete address; } -TWString *_Nonnull TWGroestlcoinAddressDescription(struct TWGroestlcoinAddress *_Nonnull address) { +TWString* _Nonnull TWGroestlcoinAddressDescription(struct TWGroestlcoinAddress* _Nonnull address) { const auto str = address->impl.string(); return TWStringCreateWithUTF8Bytes(str.c_str()); } diff --git a/src/interface/TWHDWallet.cpp b/src/interface/TWHDWallet.cpp index 11859f798d5..18447aa54bd 100644 --- a/src/interface/TWHDWallet.cpp +++ b/src/interface/TWHDWallet.cpp @@ -89,6 +89,12 @@ struct TWPrivateKey *_Nonnull TWHDWalletGetDerivedKey(struct TWHDWallet *_Nonnul return new TWPrivateKey{ wallet->impl.getKey(coin, derivationPath) }; } +struct TWPrivateKey *_Nonnull TWHDWalletGetKeyByCurve(struct TWHDWallet *_Nonnull wallet, enum TWCurve curve, TWString *_Nonnull derivationPath) { + auto& s = *reinterpret_cast(derivationPath); + const auto path = DerivationPath(s); + return new TWPrivateKey{ wallet->impl.getKeyByCurve(curve, path)}; +} + TWString *_Nonnull TWHDWalletGetExtendedPrivateKey(struct TWHDWallet *wallet, TWPurpose purpose, TWCoinType coin, TWHDVersion version) { return new std::string(wallet->impl.getExtendedPrivateKey(purpose, coin, version)); } @@ -97,6 +103,22 @@ TWString *_Nonnull TWHDWalletGetExtendedPublicKey(struct TWHDWallet *wallet, TWP return new std::string(wallet->impl.getExtendedPublicKey(purpose, coin, version)); } +TWString *_Nonnull TWHDWalletGetExtendedPrivateKeyAccount(struct TWHDWallet *wallet, TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) { + return new std::string(wallet->impl.getExtendedPrivateKeyAccount(purpose, coin, derivation, version, account)); +} + +TWString *_Nonnull TWHDWalletGetExtendedPublicKeyAccount(struct TWHDWallet *wallet, TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) { + return new std::string(wallet->impl.getExtendedPublicKeyAccount(purpose, coin, derivation, version, account)); +} + +TWString *_Nonnull TWHDWalletGetExtendedPrivateKeyDerivation(struct TWHDWallet *wallet, TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version) { + return new std::string(wallet->impl.getExtendedPrivateKeyDerivation(purpose, coin, derivation, version)); +} + +TWString *_Nonnull TWHDWalletGetExtendedPublicKeyDerivation(struct TWHDWallet *wallet, TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version) { + return new std::string(wallet->impl.getExtendedPublicKeyDerivation(purpose, coin, derivation, version)); +} + TWPublicKey *TWHDWalletGetPublicKeyFromExtended(TWString *_Nonnull extended, enum TWCoinType coin, TWString *_Nonnull derivationPath) { const auto derivationPathObject = DerivationPath(*reinterpret_cast(derivationPath)); auto publicKey = HDWallet::getPublicKeyFromExtended(*reinterpret_cast(extended), coin, derivationPathObject); diff --git a/src/interface/TWHash.cpp b/src/interface/TWHash.cpp index 5b335f58677..fc442c13fcd 100644 --- a/src/interface/TWHash.cpp +++ b/src/interface/TWHash.cpp @@ -6,10 +6,9 @@ #include #include "../Hash.h" -#include "../Data.h" +#include "Data.h" #include "BinaryCoding.h" -#include "XXHash64.h" #include #include #include @@ -79,16 +78,6 @@ TWData* _Nonnull TWHashGroestl512(TWData* _Nonnull data) { return TWDataCreateWithBytes(result.data(), result.size()); } -TWData* _Nonnull TWHashXXHash64(TWData* _Nonnull data, uint64_t seed) { - const auto result = Hash::xxhash64(reinterpret_cast(TWDataBytes(data)), TWDataSize(data), seed); - return TWDataCreateWithBytes(result.data(), result.size()); -} - -TWData* _Nonnull TWHashTwoXXHash64Concat(TWData* _Nonnull data) { - const auto result = Hash::xxhash64concat(reinterpret_cast(TWDataBytes(data)), TWDataSize(data)); - return TWDataCreateWithBytes(result.data(), result.size()); -} - TWData* _Nonnull TWHashSHA256SHA256(TWData* _Nonnull data) { const auto result = Hash::sha256d(reinterpret_cast(TWDataBytes(data)), TWDataSize(data)); return TWDataCreateWithBytes(result.data(), result.size()); diff --git a/src/interface/TWNEARAccount.cpp b/src/interface/TWNEARAccount.cpp index 959fffcd049..84dca468ef0 100644 --- a/src/interface/TWNEARAccount.cpp +++ b/src/interface/TWNEARAccount.cpp @@ -13,7 +13,6 @@ #include using namespace TW; -using namespace TW::NEAR; struct TWNEARAccount { std::string description; @@ -21,11 +20,11 @@ struct TWNEARAccount { struct TWNEARAccount *_Nullable TWNEARAccountCreateWithString(TWString *_Nonnull string) { const auto& account = *reinterpret_cast(string); - if (Address::isValid(account)) { - const auto addr = Address(account); + if (TW::NEAR::Address::isValid(account)) { + const auto addr = TW::NEAR::Address(account); return new TWNEARAccount{addr.string()}; } - if (Account::isValid(account)) { + if (TW::NEAR::Account::isValid(account)) { return new TWNEARAccount{account}; } return nullptr; diff --git a/src/interface/TWNervosAddress.cpp b/src/interface/TWNervosAddress.cpp new file mode 100644 index 00000000000..963ddf763ff --- /dev/null +++ b/src/interface/TWNervosAddress.cpp @@ -0,0 +1,51 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TrustWalletCore/TWData.h" +#include "../Base58.h" +#include "../Nervos/Address.h" + +#include + +#include + +bool TWNervosAddressEqual(struct TWNervosAddress *_Nonnull lhs, struct TWNervosAddress *_Nonnull rhs) { + return lhs->impl == rhs->impl; +} + +bool TWNervosAddressIsValidString(TWString *_Nonnull string) { + auto& s = *reinterpret_cast(string); + return TW::Nervos::Address::isValid(s); +} + +struct TWNervosAddress *_Nullable TWNervosAddressCreateWithString(TWString *_Nonnull string) { + auto& s = *reinterpret_cast(string); + try { + return new TWNervosAddress{ TW::Nervos::Address(s) }; + } catch (...) { + return nullptr; + } +} + +void TWNervosAddressDelete(struct TWNervosAddress *_Nonnull address) { + delete address; +} + +TWString *_Nonnull TWNervosAddressDescription(struct TWNervosAddress *_Nonnull address) { + return TWStringCreateWithUTF8Bytes(address->impl.string().c_str()); +} + +TWData *_Nonnull TWNervosAddressCodeHash(struct TWNervosAddress *_Nonnull address) { + return TWDataCreateWithBytes(address->impl.codeHash.data(), address->impl.codeHash.size()); +} + +TWString *_Nonnull TWNervosAddressHashType(struct TWNervosAddress *_Nonnull address) { + return TWStringCreateWithUTF8Bytes(address->impl.hashTypeString().c_str()); +} + +TWData *_Nonnull TWNervosAddressArgs(struct TWNervosAddress *_Nonnull address) { + return TWDataCreateWithBytes(address->impl.args.data(), address->impl.args.size()); +} diff --git a/src/interface/TWPBKDF2.cpp b/src/interface/TWPBKDF2.cpp new file mode 100644 index 00000000000..2ea0e4be9ec --- /dev/null +++ b/src/interface/TWPBKDF2.cpp @@ -0,0 +1,48 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include + +#include "Data.h" + +using namespace TW; + +TWData* _Nullable TWPBKDF2HmacSha256(TWData* _Nonnull password, TWData* _Nonnull salt, + uint32_t iterations, uint32_t dkLen) { + + Data key(dkLen); + int passLen = static_cast(TWDataSize(password)); + int saltLen = static_cast(TWDataSize(salt)); + pbkdf2_hmac_sha256( + TWDataBytes(password), + passLen, + TWDataBytes(salt), + saltLen, + iterations, + key.data(), + dkLen + ); + return TWDataCreateWithData(&key); +} + +TWData* _Nullable TWPBKDF2HmacSha512(TWData* _Nonnull password, TWData* _Nonnull salt, + uint32_t iterations, uint32_t dkLen) { + Data key(dkLen); + int passLen = static_cast(TWDataSize(password)); + int saltLen = static_cast(TWDataSize(salt)); + pbkdf2_hmac_sha512( + TWDataBytes(password), + passLen, + TWDataBytes(salt), + saltLen, + iterations, + key.data(), + dkLen + ); + return TWDataCreateWithData(&key); +} diff --git a/src/interface/TWPrivateKey.cpp b/src/interface/TWPrivateKey.cpp index e6d2a846d52..ab91378d9ca 100644 --- a/src/interface/TWPrivateKey.cpp +++ b/src/interface/TWPrivateKey.cpp @@ -17,8 +17,8 @@ using namespace TW; struct TWPrivateKey *TWPrivateKeyCreate() { - Data bytes(PrivateKey::size); - random_buffer(bytes.data(), PrivateKey::size); + Data bytes(PrivateKey::_size); + random_buffer(bytes.data(), PrivateKey::_size); if (!PrivateKey::isValid(bytes)) { // Under no circumstance return an invalid private key. We'd rather // crash. This also captures cases where the random generator fails @@ -81,8 +81,8 @@ struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyEd25519Blake2b(struct TWPri return new TWPublicKey{ pk->impl.getPublicKey(TWPublicKeyTypeED25519Blake2b) }; } -struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyEd25519Extended(struct TWPrivateKey *_Nonnull pk) { - return new TWPublicKey{ pk->impl.getPublicKey(TWPublicKeyTypeED25519Extended) }; +struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyEd25519Cardano(struct TWPrivateKey *_Nonnull pk) { + return new TWPublicKey{ pk->impl.getPublicKey(TWPublicKeyTypeED25519Cardano) }; } struct TWPublicKey *_Nonnull TWPrivateKeyGetPublicKeyCurve25519(struct TWPrivateKey *_Nonnull pk) { @@ -108,9 +108,9 @@ TWData *TWPrivateKeySign(struct TWPrivateKey *_Nonnull pk, TWData *_Nonnull dige } } -TWData *TWPrivateKeySignAsDER(struct TWPrivateKey *_Nonnull pk, TWData *_Nonnull digest, enum TWCurve curve) { +TWData* TWPrivateKeySignAsDER(struct TWPrivateKey* pk, TWData* digest) { auto& d = *reinterpret_cast(digest); - auto result = pk->impl.signAsDER(d, curve); + auto result = pk->impl.signAsDER(d); if (result.empty()) { return nullptr; } else { @@ -118,9 +118,9 @@ TWData *TWPrivateKeySignAsDER(struct TWPrivateKey *_Nonnull pk, TWData *_Nonnull } } -TWData *TWPrivateKeySignSchnorr(struct TWPrivateKey *_Nonnull pk, TWData *_Nonnull message, enum TWCurve curve) { +TWData *TWPrivateKeySignZilliqaSchnorr(struct TWPrivateKey *_Nonnull pk, TWData *_Nonnull message) { const auto& msg = *reinterpret_cast(message); - auto result = pk->impl.signSchnorr(msg, curve); + auto result = pk->impl.signZilliqa(msg); if (result.empty()) { return nullptr; diff --git a/src/interface/TWPublicKey.cpp b/src/interface/TWPublicKey.cpp index 10d68e60a7f..61ada495ab1 100644 --- a/src/interface/TWPublicKey.cpp +++ b/src/interface/TWPublicKey.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -48,16 +48,22 @@ struct TWPublicKey *_Nonnull TWPublicKeyUncompressed(struct TWPublicKey *_Nonnul return new TWPublicKey{ pk->impl.extended() }; } -bool TWPublicKeyVerify(struct TWPublicKey *_Nonnull pk, TWData *signature, TWData *message) { +bool TWPublicKeyVerify(struct TWPublicKey *_Nonnull pk, TWData *_Nonnull signature, TWData *message) { const auto& s = *reinterpret_cast(signature); const auto& m = *reinterpret_cast(message); return pk->impl.verify(s, m); } -bool TWPublicKeyVerifySchnorr(struct TWPublicKey *_Nonnull pk, TWData *_Nonnull signature, TWData *_Nonnull message) { +bool TWPublicKeyVerifyAsDER(struct TWPublicKey *_Nonnull pk, TWData *_Nonnull signature, TWData *message) { const auto& s = *reinterpret_cast(signature); const auto& m = *reinterpret_cast(message); - return pk->impl.verifySchnorr(s, m); + return pk->impl.verifyAsDER(s, m); +} + +bool TWPublicKeyVerifyZilliqaSchnorr(struct TWPublicKey *_Nonnull pk, TWData *_Nonnull signature, TWData *_Nonnull message) { + const auto& s = *reinterpret_cast(signature); + const auto& m = *reinterpret_cast(message); + return pk->impl.verifyZilliqa(s, m); } enum TWPublicKeyType TWPublicKeyKeyType(struct TWPublicKey *_Nonnull publicKey) { diff --git a/src/interface/TWRippleXAddress.cpp b/src/interface/TWRippleXAddress.cpp index 464e88bafa1..f003e5614a0 100644 --- a/src/interface/TWRippleXAddress.cpp +++ b/src/interface/TWRippleXAddress.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../Ripple/XAddress.h" +#include "../XRP/XAddress.h" #include #include @@ -13,7 +13,6 @@ #include using namespace TW; -using namespace TW::Ripple; bool TWRippleXAddressEqual(struct TWRippleXAddress *_Nonnull lhs, struct TWRippleXAddress *_Nonnull rhs) { return lhs->impl == rhs->impl; @@ -21,13 +20,13 @@ bool TWRippleXAddressEqual(struct TWRippleXAddress *_Nonnull lhs, struct TWRippl bool TWRippleXAddressIsValidString(TWString *_Nonnull string) { auto* s = reinterpret_cast(string); - return XAddress::isValid(*s); + return Ripple::XAddress::isValid(*s); } struct TWRippleXAddress *_Nullable TWRippleXAddressCreateWithString(TWString *_Nonnull string) { auto* s = reinterpret_cast(string); try { - const auto address = XAddress(*s); + const auto address = Ripple::XAddress(*s); return new TWRippleXAddress{ std::move(address) }; } catch (...) { return nullptr; @@ -35,7 +34,7 @@ struct TWRippleXAddress *_Nullable TWRippleXAddressCreateWithString(TWString *_N } struct TWRippleXAddress *_Nonnull TWRippleXAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey, const uint32_t tag) { - return new TWRippleXAddress{ XAddress(publicKey->impl, tag) }; + return new TWRippleXAddress{ Ripple::XAddress(publicKey->impl, tag) }; } void TWRippleXAddressDelete(struct TWRippleXAddress *_Nonnull address) { diff --git a/src/interface/TWSegwitAddress.cpp b/src/interface/TWSegwitAddress.cpp index f340d54a97f..59a41db467e 100644 --- a/src/interface/TWSegwitAddress.cpp +++ b/src/interface/TWSegwitAddress.cpp @@ -13,7 +13,6 @@ #include using namespace TW; -using namespace TW::Bitcoin; bool TWSegwitAddressEqual(struct TWSegwitAddress *_Nonnull lhs, struct TWSegwitAddress *_Nonnull rhs) { return lhs->impl == rhs->impl; @@ -21,12 +20,12 @@ bool TWSegwitAddressEqual(struct TWSegwitAddress *_Nonnull lhs, struct TWSegwitA bool TWSegwitAddressIsValidString(TWString *_Nonnull string) { auto* s = reinterpret_cast(string); - return SegwitAddress::isValid(*s); + return Bitcoin::SegwitAddress::isValid(*s); } struct TWSegwitAddress *_Nullable TWSegwitAddressCreateWithString(TWString *_Nonnull string) { auto* s = reinterpret_cast(string); - auto dec = SegwitAddress::decode(*s); + auto dec = Bitcoin::SegwitAddress::decode(*s); if (!std::get<2>(dec)) { return nullptr; } @@ -35,7 +34,7 @@ struct TWSegwitAddress *_Nullable TWSegwitAddressCreateWithString(TWString *_Non } struct TWSegwitAddress *_Nonnull TWSegwitAddressCreateWithPublicKey(enum TWHRP hrp, struct TWPublicKey *_Nonnull publicKey) { - const auto address = SegwitAddress(publicKey->impl, 0, stringForHRP(hrp)); + const auto address = Bitcoin::SegwitAddress(publicKey->impl, stringForHRP(hrp)); return new TWSegwitAddress{ std::move(address) }; } @@ -52,6 +51,10 @@ enum TWHRP TWSegwitAddressHRP(struct TWSegwitAddress *_Nonnull address) { return hrpForString(address->impl.hrp.c_str()); } +int TWSegwitAddressWitnessVersion(struct TWSegwitAddress *_Nonnull address) { + return address->impl.witnessVersion; +} + TWData *_Nonnull TWSegwitAddressWitnessProgram(struct TWSegwitAddress *_Nonnull address) { return TWDataCreateWithBytes(address->impl.witnessProgram.data(), address->impl.witnessProgram.size()); } diff --git a/src/interface/TWSolanaAddress.cpp b/src/interface/TWSolanaAddress.cpp index 023b9033ed8..df311698057 100644 --- a/src/interface/TWSolanaAddress.cpp +++ b/src/interface/TWSolanaAddress.cpp @@ -4,27 +4,26 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include #include "Solana/Address.h" +#include -using namespace TW::Solana; using namespace TW; struct TWSolanaAddress* _Nullable TWSolanaAddressCreateWithString(TWString* _Nonnull string) { auto& str = *reinterpret_cast(string); - return new TWSolanaAddress{Address(str)}; + return new TWSolanaAddress{Solana::Address(str)}; } void TWSolanaAddressDelete(struct TWSolanaAddress* _Nonnull address) { delete address; } -TWString *_Nullable TWSolanaAddressDefaultTokenAddress(struct TWSolanaAddress* _Nonnull address, TWString* _Nonnull tokenMintAddress) { +TWString* _Nullable TWSolanaAddressDefaultTokenAddress(struct TWSolanaAddress* _Nonnull address, TWString* _Nonnull tokenMintAddress) { try { if (address == nullptr || tokenMintAddress == nullptr) { return nullptr; } - Address tokenMint = Address(TWStringUTF8Bytes(tokenMintAddress)); + Solana::Address tokenMint = Solana::Address(TWStringUTF8Bytes(tokenMintAddress)); std::string defaultAddress = address->impl.defaultTokenAddress(tokenMint).string(); return TWStringCreateWithUTF8Bytes(defaultAddress.c_str()); } catch (...) { diff --git a/src/interface/TWStoredKey.cpp b/src/interface/TWStoredKey.cpp index cb4625a40c7..bccd49a9a03 100644 --- a/src/interface/TWStoredKey.cpp +++ b/src/interface/TWStoredKey.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,28 +7,32 @@ #include #include "../Coin.h" -#include "../Data.h" +#include "Data.h" #include "../HDWallet.h" #include "../Keystore/StoredKey.h" #include #include -using namespace TW::Keystore; +namespace KeyStore = TW::Keystore; struct TWStoredKey* _Nullable TWStoredKeyLoad(TWString* _Nonnull path) { try { const auto& pathString = *reinterpret_cast(path); - return new TWStoredKey{ StoredKey::load(pathString) }; + return new TWStoredKey{ KeyStore::StoredKey::load(pathString) }; } catch (...) { return nullptr; } } -struct TWStoredKey* _Nonnull TWStoredKeyCreate(TWString* _Nonnull name, TWData* _Nonnull password) { +struct TWStoredKey* _Nonnull TWStoredKeyCreateLevel(TWString* _Nonnull name, TWData* _Nonnull password, enum TWStoredKeyEncryptionLevel encryptionLevel) { const auto& nameString = *reinterpret_cast(name); const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password)); - return new TWStoredKey{ StoredKey::createWithMnemonicRandom(nameString, passwordData) }; + return new TWStoredKey{ KeyStore::StoredKey::createWithMnemonicRandom(nameString, passwordData, encryptionLevel) }; +} + +struct TWStoredKey* _Nonnull TWStoredKeyCreate(TWString* _Nonnull name, TWData* _Nonnull password) { + return TWStoredKeyCreateLevel(name, password, TWStoredKeyEncryptionLevelDefault); } struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull privateKey, TWString* _Nonnull name, TWData* _Nonnull password, enum TWCoinType coin) { @@ -36,7 +40,7 @@ struct TWStoredKey* _Nullable TWStoredKeyImportPrivateKey(TWData* _Nonnull priva const auto& privateKeyData = *reinterpret_cast(privateKey); const auto& nameString = *reinterpret_cast(name); const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password)); - return new TWStoredKey{ StoredKey::createWithPrivateKeyAddDefaultAddress(nameString, passwordData, coin, privateKeyData) }; + return new TWStoredKey{ KeyStore::StoredKey::createWithPrivateKeyAddDefaultAddress(nameString, passwordData, coin, privateKeyData) }; } catch (...) { return nullptr; } @@ -47,7 +51,7 @@ struct TWStoredKey* _Nullable TWStoredKeyImportHDWallet(TWString* _Nonnull mnemo const auto& mnemonicString = *reinterpret_cast(mnemonic); const auto& nameString = *reinterpret_cast(name); const auto passwordData = TW::data(TWDataBytes(password), TWDataSize(password)); - return new TWStoredKey{ StoredKey::createWithMnemonicAddDefaultAddress(nameString, passwordData, mnemonicString, coin) }; + return new TWStoredKey{ KeyStore::StoredKey::createWithMnemonicAddDefaultAddress(nameString, passwordData, mnemonicString, coin) }; } catch (...) { return nullptr; } @@ -57,7 +61,7 @@ struct TWStoredKey* _Nullable TWStoredKeyImportJSON(TWData* _Nonnull json) { try { const auto& d = *reinterpret_cast(json); const auto parsed = nlohmann::json::parse(d); - return new TWStoredKey{ StoredKey::createWithJson(nlohmann::json::parse(d)) }; + return new TWStoredKey{ KeyStore::StoredKey::createWithJson(nlohmann::json::parse(d)) }; } catch (...) { return nullptr; } @@ -79,7 +83,7 @@ TWString* _Nonnull TWStoredKeyName(struct TWStoredKey* _Nonnull key) { } bool TWStoredKeyIsMnemonic(struct TWStoredKey* _Nonnull key) { - return key->impl.type == StoredKeyType::mnemonicPhrase; + return key->impl.type == KeyStore::StoredKeyType::mnemonicPhrase; } size_t TWStoredKeyAccountCount(struct TWStoredKey* _Nonnull key) { @@ -104,15 +108,41 @@ struct TWAccount* _Nullable TWStoredKeyAccountForCoin(struct TWStoredKey* _Nonnu } } +struct TWAccount* _Nullable TWStoredKeyAccountForCoinDerivation(struct TWStoredKey* _Nonnull key, enum TWCoinType coin, TWDerivation derivation, struct TWHDWallet* _Nullable wallet) { + try { + if (wallet == nullptr) { + return nullptr; + } + const auto account = key->impl.account(coin, derivation, wallet->impl); + return new TWAccount{ account }; + } catch (...) { + return nullptr; + } +} + void TWStoredKeyRemoveAccountForCoin(struct TWStoredKey* _Nonnull key, enum TWCoinType coin) { key->impl.removeAccount(coin); } -void TWStoredKeyAddAccount(struct TWStoredKey* _Nonnull key, TWString* _Nonnull address, enum TWCoinType coin, TWString* _Nonnull derivationPath, TWString* _Nonnull extetndedPublicKey) { +void TWStoredKeyRemoveAccountForCoinDerivation(struct TWStoredKey* _Nonnull key, enum TWCoinType coin, enum TWDerivation derivation) { + key->impl.removeAccount(coin, derivation); +} + +void TWStoredKeyRemoveAccountForCoinDerivationPath(struct TWStoredKey* _Nonnull key, enum TWCoinType coin, TWString* _Nonnull derivationPath) { + const auto dp = TW::DerivationPath(*reinterpret_cast(derivationPath)); + key->impl.removeAccount(coin, dp); +} + +void TWStoredKeyAddAccountDerivation(struct TWStoredKey* _Nonnull key, TWString* _Nonnull address, enum TWCoinType coin, enum TWDerivation derivation, TWString* _Nonnull derivationPath, TWString* _Nonnull publicKey, TWString* _Nonnull extendedPublicKey) { const auto& addressString = *reinterpret_cast(address); - const auto& extetndedPublicKeyString = *reinterpret_cast(extetndedPublicKey); + const auto& publicKeyString = *reinterpret_cast(publicKey); + const auto& extendedPublicKeyString = *reinterpret_cast(extendedPublicKey); const auto dp = TW::DerivationPath(*reinterpret_cast(derivationPath)); - key->impl.addAccount(addressString, coin, dp, extetndedPublicKeyString); + key->impl.addAccount(addressString, coin, derivation, dp, publicKeyString, extendedPublicKeyString); +} + +void TWStoredKeyAddAccount(struct TWStoredKey* _Nonnull key, TWString* _Nonnull address, enum TWCoinType coin, TWString* _Nonnull derivationPath, TWString* _Nonnull publicKey, TWString* _Nonnull extendedPublicKey) { + return TWStoredKeyAddAccountDerivation(key, address, coin, TWDerivationDefault, derivationPath, publicKey, extendedPublicKey); } bool TWStoredKeyStore(struct TWStoredKey* _Nonnull key, TWString* _Nonnull path) { @@ -178,3 +208,11 @@ bool TWStoredKeyFixAddresses(struct TWStoredKey* _Nonnull key, TWData* _Nonnull return false; } } + +TWString* _Nullable TWStoredKeyEncryptionParameters(struct TWStoredKey* _Nonnull key) { + if (!key->impl.id) { + return nullptr; + } + const std::string params = key->impl.payload.json().dump(); + return TWStringCreateWithUTF8Bytes(params.c_str()); +} diff --git a/src/interface/TWTransactionCompiler.cpp b/src/interface/TWTransactionCompiler.cpp new file mode 100644 index 00000000000..bd8b6bcf051 --- /dev/null +++ b/src/interface/TWTransactionCompiler.cpp @@ -0,0 +1,69 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "TransactionCompiler.h" +#include "Data.h" +#include "uint256.h" + +#include + +using namespace TW; + + +TWData *_Nonnull TWTransactionCompilerBuildInput(enum TWCoinType coinType, TWString *_Nonnull from, TWString *_Nonnull to, TWString *_Nonnull amount, TWString *_Nonnull asset, TWString *_Nonnull memo, TWString *_Nonnull chainId) { + Data result; + try { + result = TransactionCompiler::buildInput( + coinType, + std::string(TWStringUTF8Bytes(from)), + std::string(TWStringUTF8Bytes(to)), + std::string(TWStringUTF8Bytes(amount)), + std::string(TWStringUTF8Bytes(asset)), + std::string(TWStringUTF8Bytes(memo)), + std::string(TWStringUTF8Bytes(chainId)) + ); + } catch (...) {} // return empty + return TWDataCreateWithBytes(result.data(), result.size()); +} + +std::vector createFromTWDataVector(const struct TWDataVector* _Nonnull dataVector) { + std::vector ret; + const auto n = TWDataVectorSize(dataVector); + for (auto i = 0ul; i < n; ++i) { + auto elem = TWDataVectorGet(dataVector, i); + ret.push_back(*(static_cast(elem))); + TWDataDelete(elem); + } + return ret; +} + +TWData *_Nonnull TWTransactionCompilerPreImageHashes(enum TWCoinType coinType, TWData *_Nonnull txInputData) { + Data result; + try { + assert(txInputData != nullptr); + const Data inputData = data(TWDataBytes(txInputData), TWDataSize(txInputData)); + + result = TransactionCompiler::preImageHashes(coinType, inputData); + } catch (...) {} // return empty + return TWDataCreateWithBytes(result.data(), result.size()); +} + +TWData *_Nonnull TWTransactionCompilerCompileWithSignatures(enum TWCoinType coinType, TWData *_Nonnull txInputData, const struct TWDataVector *_Nonnull signatures, const struct TWDataVector *_Nonnull publicKeys) { + Data result; + try { + assert(txInputData != nullptr); + const Data inputData = data(TWDataBytes(txInputData), TWDataSize(txInputData)); + assert(signatures != nullptr); + const auto signaturesVec = createFromTWDataVector(signatures); + assert(publicKeys != nullptr); + const auto publicKeysVec = createFromTWDataVector(publicKeys); + + result = TransactionCompiler::compileWithSignatures(coinType, inputData, signaturesVec, publicKeysVec); + } catch (...) {} // return empty + return TWDataCreateWithBytes(result.data(), result.size()); +} diff --git a/src/memory/memzero_wrapper.h b/src/memory/memzero_wrapper.h new file mode 100644 index 00000000000..be8c7859e4d --- /dev/null +++ b/src/memory/memzero_wrapper.h @@ -0,0 +1,23 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include + +#include + +#include + +namespace TW { + +template +static inline void memzero(T* data, std::size_t len = sizeof(T)) noexcept { + static_assert(std::is_trivial_v, "type should be a pod"); + ::memzero(data, len); +} + +} // namespace TW diff --git a/src/operators/equality_comparable.h b/src/operators/equality_comparable.h new file mode 100644 index 00000000000..aa2fd02f34b --- /dev/null +++ b/src/operators/equality_comparable.h @@ -0,0 +1,23 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +namespace TW::operators::details { + +template +class empty_base {}; + +} // namespace TW::operators::details + +namespace TW { + +template > +struct equality_comparable : B { + friend bool operator!=(const T& x, const T& y) { return !(x == y); } +}; + +} // namespace TW diff --git a/src/proto/.clang-tidy b/src/proto/.clang-tidy new file mode 100644 index 00000000000..2c22f7387dd --- /dev/null +++ b/src/proto/.clang-tidy @@ -0,0 +1,6 @@ +--- +InheritParentConfig: false +Checks: '-*,misc-definitions-in-headers' +CheckOptions: + - { key: HeaderFileExtensions, value: "x" } +... diff --git a/src/proto/Aeternity.proto b/src/proto/Aeternity.proto index 8a20764369a..fc501db4859 100644 --- a/src/proto/Aeternity.proto +++ b/src/proto/Aeternity.proto @@ -11,8 +11,10 @@ message SigningInput { // Address of the recipient with "ak_" prefix string to_address = 2; + // Amount (uint256, serialized little endian) bytes amount = 3; + // Fee amount (uint256, serialized little endian) bytes fee = 4; // Message, optional @@ -21,15 +23,18 @@ message SigningInput { // Time to live until block height uint64 ttl = 6; + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 7; + // The secret private key used for signing (32 bytes). bytes private_key = 8; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes, Base64 with checksum string encoded = 1; + // Signature, Base58 with checksum string signature = 2; } diff --git a/src/proto/Aion.proto b/src/proto/Aion.proto index 771e6774171..2b194fc0237 100644 --- a/src/proto/Aion.proto +++ b/src/proto/Aion.proto @@ -5,32 +5,32 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 1; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) bytes gas_price = 2; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 3; // Recipient's address. string to_address = 4; - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) bytes amount = 5; // Optional payload bytes payload = 6; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 7; // Timestamp uint64 timestamp = 8; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Algorand.proto b/src/proto/Algorand.proto index 937493e51a9..167fc0f10ee 100644 --- a/src/proto/Algorand.proto +++ b/src/proto/Algorand.proto @@ -3,18 +3,30 @@ syntax = "proto3"; package TW.Algorand.Proto; option java_package = "wallet.core.jni.proto"; +// Simple transfer message, transfer an amount to an address message Transfer { + // Destination address (string) string to_address = 1; + + // Amount uint64 amount = 2; } +// Asset Transfer message, with assetID message AssetTransfer { + // Destination address (string) string to_address = 1; + + // Amount uint64 amount = 2; + + // ID of the asset being transferred uint64 asset_id = 3; } +// Opt-in message for an asset message AssetOptIn { + // ID of the asset uint64 asset_id = 1; } @@ -22,19 +34,26 @@ message AssetOptIn { message SigningInput { // network / chain id string genesis_id = 1; + // network / chain hash bytes genesis_hash = 2; + // binary note data bytes note = 3; - // private key + + // The secret private key used for signing (32 bytes). bytes private_key = 4; + // network / first round uint64 first_round = 5; + // network / last round uint64 last_round = 6; - // fee + + // fee amount uint64 fee = 7; + // message payload oneof message_oneof { Transfer transfer = 10; AssetTransfer asset_transfer = 11; @@ -42,7 +61,7 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Aptos.proto b/src/proto/Aptos.proto new file mode 100644 index 00000000000..1972c1ea349 --- /dev/null +++ b/src/proto/Aptos.proto @@ -0,0 +1,153 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +syntax = "proto3"; + +package TW.Aptos.Proto; +option java_package = "wallet.core.jni.proto"; + +// Necessary fields to process a TransferMessage +message TransferMessage { + // Destination Account address (string) + string to = 1; + // Amount to be transferred (uint64) + uint64 amount = 2; +} + +// Necessary tag for type function argument +message StructTag { + // Address of the account + string account_address = 1; + // Module name + string module = 2; + // Identifier + string name = 3; +} + +// Necessary fields to process a TokenTransferMessage +message TokenTransferMessage { + // Destination Account address (string) + string to = 1; + // Amount to be transferred (uint64) + uint64 amount = 2; + // token function to call, e.g BTC: 0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC + StructTag function = 3; +} + +// Necessary fields to process a ManagedTokensRegisterMessage +message ManagedTokensRegisterMessage { + // token function to register, e.g BTC: 0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC + StructTag function = 1; +} + +// Necessary fields to process a CreateAccountMessage +message CreateAccountMessage { + // auth account address to create + string auth_key = 1; +} + +// Necessary fields to process an OfferNftMessage +message OfferNftMessage { + // Receiver address + string receiver = 1; + // Creator address + string creator = 2; + // Name of the collection + string collectionName = 3; + // Name of the NFT + string name = 4; + // Property version (should be often 0) + uint64 property_version = 5; + // Amount of NFT's to transfer (should be often 1) + uint64 amount = 6; +} + +// Necessary fields to process an CancelOfferNftMessage +message CancelOfferNftMessage { + // Receiver address + string receiver = 1; + // Creator address + string creator = 2; + // Name of the collection + string collectionName = 3; + // Name of the NFT + string name = 4; + // Property version (should be often 0) + uint64 property_version = 5; +} + +// Necessary fields to process an ClaimNftMessage +message ClaimNftMessage { + // Sender address + string sender = 1; + // Creator address + string creator = 2; + // Name of the collection + string collectionName = 3; + // Name of the NFT + string name = 4; + // Property version (should be often 0) + uint64 property_version = 5; +} + +message NftMessage { + oneof nft_transaction_payload { + OfferNftMessage offer_nft = 1; + CancelOfferNftMessage cancel_offer_nft = 2; + ClaimNftMessage claim_nft = 3; + } +} + +// Input data necessary to create a signed transaction. +message SigningInput { + // Sender Account address (string) + string sender = 1; + // Sequence number, incremented atomically for each tx present on the account, start at 0 (int64) + int64 sequence_number = 2; + // Max gas amount that the user is willing to pay (uint64) + uint64 max_gas_amount = 3; + // Gas unit price - queried through API (uint64) + uint64 gas_unit_price = 4; + // Expiration timestamp for the transaction, can't be in the past (uint64) + uint64 expiration_timestamp_secs = 5; + // Chain id 1 (mainnet) 32(devnet) (uint32 - casted in uint8_t later) + uint32 chain_id = 6; + // Private key to sign the transaction (bytes) + bytes private_key = 7; + // hex encoded function to sign, use it for smart contract approval (string) + string any_encoded = 8; + + oneof transaction_payload { + TransferMessage transfer = 9; + TokenTransferMessage token_transfer = 10; + CreateAccountMessage create_account = 11; + NftMessage nft_message = 12; + ManagedTokensRegisterMessage register_token = 13; + } +} + +// Information related to the signed transaction +message TransactionAuthenticator { + // Signature part of the signed transaction (bytes) + bytes signature = 1; + // Public key of the signer (bytes) + bytes public_key = 2; +} + +// Transaction signing output. +message SigningOutput { + /// The raw transaction (bytes) + bytes raw_txn = 1; + + /// Public key and signature to authenticate + TransactionAuthenticator authenticator = 2; + + /// Signed and encoded transaction bytes. + bytes encoded = 3; + + // Transaction json format for api broadcasting (string) + string json = 4; +} diff --git a/src/proto/Binance.proto b/src/proto/Binance.proto index 846307db7c6..00df9f21fd2 100644 --- a/src/proto/Binance.proto +++ b/src/proto/Binance.proto @@ -3,140 +3,260 @@ syntax = "proto3"; package TW.Binance.Proto; option java_package = "wallet.core.jni.proto"; +import "Common.proto"; + +// Transaction structure, used internally message Transaction { - // int64 SIZE-OF-ENCODED // varint encoded length of the structure after encoding - // 0xF0625DEE // prefix - repeated bytes msgs = 1; // array of size 1, containing the transaction message, which are one of the transaction type below - repeated bytes signatures = 2; // array of size 1, containing the standard signature structure of the transaction sender - string memo = 3; // a short sentence of remark for the transaction, only for `Transfer` transactions. - int64 source = 4; // an identifier for tools triggerring this transaction, set to zero if unwilling to disclose. - bytes data = 5; // reserved for future use + // array of size 1, containing the transaction message, which are one of the transaction type below + repeated bytes msgs = 1; + + // array of size 1, containing the standard signature structure of the transaction sender + repeated bytes signatures = 2; + + // a short sentence of remark for the transaction, only for `Transfer` transactions. + string memo = 3; + + // an identifier for tools triggering this transaction, set to zero if unwilling to disclose. + int64 source = 4; + + // reserved for future use + bytes data = 5; } +// Signature structure, used internally message Signature { - message PubKey { - // 0xEB5AE987 // prefix - // bytes // public key bytes - } - bytes pub_key = 1; // public key bytes of the signer address - bytes signature = 2; // signature bytes, please check chain access section for signature generation - int64 account_number = 3; // another identifier of signer, which can be read from chain by account REST API or RPC - int64 sequence = 4; // sequence number for the next transaction + // public key bytes of the signer address + bytes pub_key = 1; + + // signature bytes, please check chain access section for signature generation + bytes signature = 2; + + // another identifier of signer, which can be read from chain by account REST API or RPC + int64 account_number = 3; + + // sequence number for the next transaction + int64 sequence = 4; } +// Message for Trade order message TradeOrder { - // 0xCE6DC043 // prefix - bytes sender = 1; // originating address - string id = 2; // order id, optional - string symbol = 3; // symbol for trading pair in full name of the tokens - int64 ordertype = 4; // only accept 2 for now, meaning limit order - int64 side = 5; // 1 for buy and 2 fory sell - int64 price = 6; // price of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer - int64 quantity = 7; // quantity of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer - int64 timeinforce = 8; // 1 for Good Till Expire(GTE) order and 3 for Immediate Or Cancel (IOC) + // originating address + bytes sender = 1; + + // order id, optional + string id = 2; + + // symbol for trading pair in full name of the tokens + string symbol = 3; + + // only accept 2 for now, meaning limit order + int64 ordertype = 4; + + // 1 for buy and 2 for sell + int64 side = 5; + + // price of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer + int64 price = 6; + + // quantity of the order, which is the real price multiplied by 1e8 (10^8) and rounded to integer + int64 quantity = 7; + + // 1 for Good Till Expire(GTE) order and 3 for Immediate Or Cancel (IOC) + int64 timeinforce = 8; } +// Message for CancelTrade order message CancelTradeOrder { - // 0x166E681B // prefix - bytes sender = 1; // originating address - string symbol = 2; // symbol for trading pair in full name of the tokens - string refid = 3; // order id to cancel + // originating address + bytes sender = 1; + + // symbol for trading pair in full name of the tokens + string symbol = 2; + + // order id to cancel + string refid = 3; } +// Message for Send order message SendOrder { - // 0x2A2C87FA - // A symbol-amount pair. Could be moved out of SendOrder; kept here for backward compatibility. + // A token amount, symbol-amount pair. Could be moved out of SendOrder; kept here for backward compatibility. message Token { + // Token ID string denom = 1; + + // Amount int64 amount = 2; } + + // Transaction input message Input { + // source address bytes address = 1; + + // input coin amounts repeated Token coins = 2; } + + // Transaction output message Output { + // destination address bytes address = 1; + + // output coin amounts repeated Token coins = 2; } + + // Send inputs repeated Input inputs = 1; + + // Send outputs repeated Output outputs = 2; } +// Message for TokenIssue order message TokenIssueOrder { - // 0x17EFAB80 // prefix - bytes from = 1; // owner address - string name = 2; // token name - string symbol = 3; // token symbol, in full name with "-" suffix - int64 total_supply = 4; // total supply - bool mintable = 5; // mintable + // owner address + bytes from = 1; + + // token name + string name = 2; + + // token symbol, in full name with "-" suffix + string symbol = 3; + + // total supply + int64 total_supply = 4; + + // mintable + bool mintable = 5; } +// Message for TokenMint order message TokenMintOrder { - // 0x467E0829 // prefix - bytes from = 1; // owner address - string symbol = 2; // token symbol, in full name with "-" suffix - int64 amount = 3; // amount to mint + // owner address + bytes from = 1; + + // token symbol, in full name with "-" suffix + string symbol = 2; + + // amount to mint + int64 amount = 3; } +// Message for TokenBurn order message TokenBurnOrder { - // 0x7ED2D2A0 // prefix - bytes from = 1; // owner address - string symbol = 2; // token symbol, in full name with "-" suffix - int64 amount = 3; // amount to burn + // owner address + bytes from = 1; + + // token symbol, in full name with "-" suffix + string symbol = 2; + + // amount to burn + int64 amount = 3; } +// Message for TokenFreeze order message TokenFreezeOrder { - // 0xE774B32D // prefix - bytes from = 1; // owner address - string symbol = 2; // token symbol, in full name with "-" suffix - int64 amount = 3; // amount of token to freeze + // owner address + bytes from = 1; + + // token symbol, in full name with "-" suffix + string symbol = 2; + + // amount of token to freeze + int64 amount = 3; } +// Message for TokenUnfreeze order message TokenUnfreezeOrder { - // 0x6515FF0D // prefix - bytes from = 1; // owner address - string symbol = 2; // token symbol, in full name with "-" suffix - int64 amount = 3; // amount of token to unfreeze + // owner address + bytes from = 1; + + // token symbol, in full name with "-" suffix + string symbol = 2; + + // amount of token to unfreeze + int64 amount = 3; } +// Message for HashTimeLock order message HTLTOrder { - // 0xB33F9A24 // prefix - bytes from = 1; // signer address - bytes to = 2; // recipient address + // signer address + bytes from = 1; + + // recipient address + bytes to = 2; + + // source on other chain, optional string recipient_other_chain = 3; + + // recipient on other chain, optional string sender_other_chain = 4; - bytes random_number_hash = 5; //hash of a random number and timestamp, based on SHA256 + + // hash of a random number and timestamp, based on SHA256 + bytes random_number_hash = 5; + + // timestamp int64 timestamp = 6; + + // amounts repeated SendOrder.Token amount = 7; - string expected_income = 8; // expected gained token on the other chain + + // expected gained token on the other chain + string expected_income = 8; + + // period expressed in block heights int64 height_span = 9; + + // set for cross-chain send bool cross_chain = 10; } +// Message for Deposit HTLT order message DepositHTLTOrder { - // 0xB33F9A24 // prefix - bytes from = 1; // signer address + // signer address + bytes from = 1; + + // amounts repeated SendOrder.Token amount = 2; + + // swap ID bytes swap_id = 3; } +// Message for Claim HTLT order message ClaimHTLOrder { - // 0xC1665300 // prefix - bytes from = 1; // signer address + // signer address + bytes from = 1; + + // swap ID bytes swap_id = 2; + + // random number input bytes random_number = 3; } +// Message for Refund HTLT order message RefundHTLTOrder { - // 0x3454A27C // prefix - bytes from = 1; // signer address + // signer address + bytes from = 1; + + // swap ID bytes swap_id = 2; } +// Transfer message TransferOut { + // source address bytes from = 1; + + // recipient address bytes to = 2; + + // transfer amount SendOrder.Token amount = 3; + + // expiration time int64 expire_time = 4; } @@ -162,37 +282,73 @@ message SideChainUndelegate { string chain_id = 4; } +// Message for TimeLock order message TimeLockOrder { - bytes from_address = 1; // owner address + // owner address + bytes from_address = 1; + + // Description (optional) string description = 2; + // Array of symbol/amount pairs. see SDK https://github.com/binance-chain/javascript-sdk/blob/master/docs/api-docs/classes/tokenmanagement.md#timelock repeated SendOrder.Token amount = 3; + + // lock time int64 lock_time = 4; } +// Message for TimeRelock order message TimeRelockOrder { - bytes from_address = 1; // owner address - int64 id = 2; // order ID + // owner address + bytes from_address = 1; + + // order ID + int64 id = 2; + + // Description (optional) string description = 3; + // Array of symbol/amount pairs. repeated SendOrder.Token amount = 4; + + // lock time int64 lock_time = 5; } +// Message for TimeUnlock order message TimeUnlockOrder { - bytes from_address = 1; // owner address - int64 id = 2; // order ID + // owner address + bytes from_address = 1; + + // order ID + int64 id = 2; } -// Input data necessary to create a signed order. +// Input data necessary to create a signed transaction. message SigningInput { + // Chain ID string chain_id = 1; + + // Source account number int64 account_number = 2; + + // Sequence number (account specific) int64 sequence = 3; + + // Transaction source, see https://github.com/bnb-chain/BEPs/blob/master/BEP10.md + // Some important values: + // 0: Default source value (e.g. for Binance Chain Command Line, or SDKs) + // 1: Binance DEX Web Wallet + // 2: Trust Wallet int64 source = 4; + + // Optional memo string memo = 5; + + // The secret private key used for signing (32 bytes). bytes private_key = 6; + // Payload message oneof order_oneof { TradeOrder trade_order = 8; CancelTradeOrder cancel_trade_order = 9; @@ -216,8 +372,14 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; + + // OK (=0) or other codes in case of error + Common.Proto.SigningError error = 2; + + // error description in case of error + string error_message = 3; } diff --git a/src/proto/Bitcoin.proto b/src/proto/Bitcoin.proto index 50e32f12937..cb321e44a8a 100644 --- a/src/proto/Bitcoin.proto +++ b/src/proto/Bitcoin.proto @@ -5,6 +5,7 @@ option java_package = "wallet.core.jni.proto"; import "Common.proto"; +// A transaction, with its inputs and outputs message Transaction { // Transaction data format version. sint32 version = 1; @@ -15,7 +16,7 @@ message Transaction { // A list of 1 or more transaction inputs or sources for coins. repeated TransactionInput inputs = 3; - // A list of 1 or more transaction outputs or destinations for coins + // A list of 1 or more transaction outputs or destinations for coins. repeated TransactionOutput outputs = 4; } @@ -33,7 +34,7 @@ message TransactionInput { // Bitcoin transaction out-point reference. message OutPoint { - // The hash of the referenced transaction. + // The hash of the referenced transaction (network byte order, usually needs to be reversed). bytes hash = 1; // The index of the specific output in the transaction. @@ -74,31 +75,31 @@ message SigningInput { // If amount is equal or more than the available amount, also max amount will be used. int64 amount = 2; - // Transaction fee per byte. + // Transaction fee rate, satoshis per byte, used to compute required fee (when planning) int64 byte_fee = 3; - // Recipient's address. + // Recipient's address, as string. string to_address = 4; - // Change address. + // Change address, as string. string change_address = 5; - // Available private keys. + // The available secret private key or keys required for signing (32 bytes each). repeated bytes private_key = 6; // Available redeem scripts indexed by script hash. map scripts = 7; - // Available unspent transaction outputs. + // Available input unspent transaction outputs. repeated UnspentTransaction utxo = 8; - // If sending max amount. + // Set if sending max amount is requested. bool use_max_amount = 9; - // Coin type (forks). + // Coin type (used by forks). uint32 coin_type = 10; - // Optional transaction plan + // Optional transaction plan. If missing, plan will be computed. TransactionPlan plan = 11; // Optional lockTime, default value 0 means no time locking. @@ -107,6 +108,9 @@ message SigningInput { // value < 500000000 : Block number at which this transaction is unlocked // value >= 500000000 : UNIX timestamp at which this transaction is unlocked uint32 lock_time = 12; + + // Optional zero-amount, OP_RETURN output + bytes output_op_return = 13; } // Describes a preliminary transaction plan. @@ -114,16 +118,16 @@ message TransactionPlan { // Amount to be received at the other end. int64 amount = 1; - // Maximum available amount. + // Maximum available amount in all the input UTXOs. int64 available_amount = 2; // Estimated transaction fee. int64 fee = 3; - // Change. + // Remaining change int64 change = 4; - // Selected unspent transaction outputs. + // Selected unspent transaction outputs (subset of all input UTXOs) repeated UnspentTransaction utxos = 5; // Zcash branch id @@ -131,19 +135,47 @@ message TransactionPlan { // Optional error Common.Proto.SigningError error = 7; + + // Optional zero-amount, OP_RETURN output + bytes output_op_return = 8; }; -// Transaction signing output. +// Result containing the signed and encoded transaction. +// Note that the amount may be different than the requested amount to account for fees and available funds. message SigningOutput { - // Resulting transaction. Note that the amount may be different than the requested amount to account for fees and available funds. + // Resulting transaction. Transaction transaction = 1; // Signed and encoded transaction bytes. bytes encoded = 2; - // Transaction id + // Transaction ID (hash) string transaction_id = 3; // Optional error Common.Proto.SigningError error = 4; + + // error description + string error_message = 5; +} + +/// Pre-image hash to be used for signing +message HashPublicKey { + /// Pre-image data hash that will be used for signing + bytes data_hash = 1; + + /// public key hash used for signing + bytes public_key_hash = 2; +} + +/// Transaction pre-signing output +message PreSigningOutput { + /// hash, public key list + repeated HashPublicKey hash_public_keys = 1; + + /// error code, 0 is ok, other codes will be treated as errors + Common.Proto.SigningError error = 2; + + /// error description + string error_message = 3; } diff --git a/src/proto/Cardano.proto b/src/proto/Cardano.proto new file mode 100644 index 00000000000..ffeec086591 --- /dev/null +++ b/src/proto/Cardano.proto @@ -0,0 +1,202 @@ +syntax = "proto3"; + +package TW.Cardano.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Common.proto"; + +// A transaction output that can be used as input +message OutPoint { + // The transaction ID + bytes tx_hash = 1; + + // The index of this output within the transaction + uint64 output_index = 2; +} + +// Represents a token and an amount. Token is identified by PolicyID and name. +message TokenAmount { + // Policy ID of the token, as hex string (28x2 digits) + string policy_id = 1; + + // The name of the asset (within the policy) + string asset_name = 2; + + // The amount (uint256, serialized little endian) + bytes amount = 3; +} + +// One input for a transaction +message TxInput { + // The UTXO + OutPoint out_point = 1; + + // The owner address (string) + string address = 2; + + // ADA amount in the UTXO + uint64 amount = 3; + + // optional token amounts in the UTXO + repeated TokenAmount token_amount = 4; +} + +// One output for a transaction +message TxOutput { + // Destination address (string) + string address = 1; + + // ADA amount + uint64 amount = 2; + + // optional token amounts + repeated TokenAmount token_amount = 3; +} + +// Collection of tokens with amount +message TokenBundle { + repeated TokenAmount token = 1; +} + +// Message for simple Transfer tx +message Transfer { + // Destination address as string + string to_address = 1; + + // Change address + string change_address = 2; + + // Requested ADA amount to be transferred. Output can be different only in use_max_amount case. + // Note that Cardano has a minimum amount per UTXO, see TWCardanoMinAdaAmount. + uint64 amount = 3; + + // Optional token(s) to be transferred + // Currently only one token type to be transferred per transaction is supported + TokenBundle token_amount = 4; + + // Request max amount option. If set, tx will send all possible amounts from all inputs, including all tokens; there will be no change; requested amount and token_amount is disregarded in this case. + bool use_max_amount = 5; + + // Optional fee overriding. If left to 0, optimal fee will be calculated. If set, exactly this value will be used as fee. + // Use it with care, it may result in underfunded or wasteful fee. + uint64 force_fee = 6; +} + +// Register a staking key for the account, prerequisite for Staking. +// Note: staking messages are typically used with a 1-output-to-self transaction. +message RegisterStakingKey { + // Staking address (as string) + string staking_address = 1; + + // Amount deposited in this TX. Should be 2 ADA (2000000). If not set correctly, TX will be rejected. See also Delegate.deposit_amount. + uint64 deposit_amount = 2; +} + +// Deregister staking key. can be done when staking is stopped completely. The Staking deposit is returned at this time. +message DeregisterStakingKey { + // Staking address (as string) + string staking_address = 1; + + // Amount undeposited in this TX. Should be 2 ADA (2000000). If not set correctly, TX will be rejected. See also RegisterStakingKey.deposit_amount. + uint64 undeposit_amount = 2; +} + +// Delegate funds in this account to a specified staking pool. +message Delegate { + // Staking address (as string) + string staking_address = 1; + + // PoolID of staking pool + bytes pool_id = 2; + + // Amount deposited in this TX. Should be 0. If not set correctly, TX will be rejected. See also RegisterStakingKey.deposit_amount. + uint64 deposit_amount = 3; +} + +// Withdraw earned staking reward. +message Withdraw { + // Staking address (as string) + string staking_address = 1; + + // Withdrawal amount. Should match the real value of the earned reward. + uint64 withdraw_amount = 2; +} + +// Describes a preliminary transaction plan. +message TransactionPlan { + // total coins in the utxos + uint64 available_amount = 1; + + // coins in the output UTXO + uint64 amount = 2; + + // coin amount deducted as fee + uint64 fee = 3; + + // coins in the change UTXO + uint64 change = 4; + + // coins deposited (going to deposit) in this TX + uint64 deposit = 10; + + // coins undeposited (coming from deposit) in this TX + uint64 undeposit = 11; + + // total tokens in the utxos (optional) + repeated TokenAmount available_tokens = 5; + + // tokens in the output (optional) + repeated TokenAmount output_tokens = 6; + + // tokens in the change (optional) + repeated TokenAmount change_tokens = 7; + + // The selected UTXOs, subset ot the input UTXOs + repeated TxInput utxos = 8; + + // Optional error + Common.Proto.SigningError error = 9; +} + +// Input data necessary to create a signed transaction. +message SigningInput { + // Available input UTXOs + repeated TxInput utxos = 1; + + // Available private keys (double extended keys); every input UTXO address should be covered + // In case of Plan only, keys should be present, in correct number + repeated bytes private_key = 2; + + // Later this can be made oneof if more message types are supported + Transfer transfer_message = 3; + + // Optional, used in case of Staking Key registration (prerequisite for Staking) + RegisterStakingKey register_staking_key = 6; + + // Optional, used in case of (re)delegation + Delegate delegate = 7; + + // Optional, used in case of withdraw + Withdraw withdraw = 8; + + // Optional + DeregisterStakingKey deregister_staking_key = 9; + + // Time-to-live time of the TX + uint64 ttl = 4; + + // Optional plan, if missing it will be computed + TransactionPlan plan = 5; +} + +// Result containing the signed and encoded transaction. +message SigningOutput { + // Encoded transaction bytes + bytes encoded = 1; + + // TxID, derived from transaction data, also needed for submission + bytes tx_id = 2; + + // Optional error + Common.Proto.SigningError error = 3; +} diff --git a/src/proto/Common.proto b/src/proto/Common.proto index 5391a97abc0..cedf89feee4 100644 --- a/src/proto/Common.proto +++ b/src/proto/Common.proto @@ -3,25 +3,68 @@ syntax = "proto3"; package TW.Common.Proto; option java_package = "wallet.core.jni.proto"; +// Error codes, used in multiple blockchains. enum SigningError { - OK = 0; // OK - // chain-generic, generic + // This is the OK case, with value=0 + OK = 0; + + // Chain-generic codes: + // Generic error (used if there is no suitable specific error is adequate) Error_general = 1; + // Internal error, indicates some very unusual, unexpected case Error_internal = 2; - // chain-generic, input + + // Chain-generic codes, input related: + // Low balance: the sender balance is not enough to cover the send and other auxiliary amount such as fee, deposit, or minimal balance. Error_low_balance = 3; - Error_zero_amount_requested = 4; // Requested amount is zero + // Requested amount is zero, send of 0 makes no sense + Error_zero_amount_requested = 4; + // One required key is missing (too few or wrong keys are provided) Error_missing_private_key = 5; - // chain-generic, fee + // A private key provided is invalid (e.g. wrong size, usually should be 32 bytes) + Error_invalid_private_key = 15; + // A provided address (e.g. destination address) is invalid + Error_invalid_address = 16; + // A provided input UTXO is invalid + Error_invalid_utxo = 17; + // The amount of an input UTXO is invalid + Error_invalid_utxo_amount = 18; + + // Chain-generic, fee related: + // Wrong fee is given, probably it is too low to cover minimal fee for the transaction Error_wrong_fee = 6; - // chain-generic, signing + + // Chain-generic, signing related: + // General signing error Error_signing = 7; - Error_tx_too_big = 8; // [NEO] Transaction too big, fee in GAS needed or try send by parts - // UTXO-chain specific, inputs - Error_missing_input_utxos = 9; // No UTXOs provided [BTC] - Error_not_enough_utxos = 10; // Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out) [BTC] - // UTXO-chain specific, script - Error_script_redeem = 11; // [BTC] Missing redeem script - Error_script_output = 12; // [BTC] Invalid output script - Error_script_witness_program = 13; // [BTC] Unrecognized witness program + // Resulting transaction is too large + // [NEO] Transaction too big, fee in GAS needed or try send by parts + Error_tx_too_big = 8; + + // UTXO-chain specific, input related: + // No input UTXOs provided [BTC] + Error_missing_input_utxos = 9; + // Not enough non-dust input UTXOs to cover requested amount (dust UTXOs are filtered out) [BTC] + Error_not_enough_utxos = 10; + + // UTXO-chain specific, script related: + // [BTC] Missing required redeem script + Error_script_redeem = 11; + // [BTC] Invalid required output script + Error_script_output = 12; + // [BTC] Unrecognized witness program + Error_script_witness_program = 13; + + // Invalid memo, e.g. [XRP] Invalid tag + Error_invalid_memo = 14; + // Some input field cannot be parsed + Error_input_parse = 19; + // Multi-input and multi-output transaction not supported + Error_no_support_n2n = 20; + // Incorrect count of signatures passed to compile + Error_signatures_count = 21; + // Incorrect input parameter + Error_invalid_params = 22; + // Invalid input token amount + Error_invalid_requested_token_amount = 23; } diff --git a/src/proto/Cosmos.proto b/src/proto/Cosmos.proto index fc4afa9bc56..97c8bb2e358 100644 --- a/src/proto/Cosmos.proto +++ b/src/proto/Cosmos.proto @@ -3,22 +3,41 @@ syntax = "proto3"; package TW.Cosmos.Proto; option java_package = "wallet.core.jni.proto"; +// A denomination and an amount message Amount { + // name of the denomination string denom = 1; - int64 amount = 2; + + // amount, number as string + string amount = 2; } +// Fee incl. gas message Fee { + // Fee amount(s) repeated Amount amounts = 1; + + // Gas price uint64 gas = 2; } +// Block height, a revision and block height tuple. +// A height can be compared against another Height for the purposes of updating and freezing clients +message Height { + // the revision that the client is currently on + uint64 revision_number = 1; + // the height within the given revision + uint64 revision_height = 2; +} + +// Transaction broadcast mode enum BroadcastMode { BLOCK = 0; // Wait for the tx to pass/fail CheckTx, DeliverTx, and be committed in a block SYNC = 1; // Wait for the tx to pass/fail CheckTx ASYNC = 2; // Don't wait for pass/fail CheckTx; send and return tx immediately } +// A transaction payload message message Message { // cosmos-sdk/MsgSend message Send { @@ -28,6 +47,22 @@ message Message { string type_prefix = 4; } + // cosmos-sdk/MsgTransfer, IBC transfer + message Transfer { + // IBC port, e.g. "transfer" + string source_port = 1; + // IBC connection channel, e.g. "channel-141", see apis /ibc/applications/transfer/v1beta1/denom_traces (connections) or /node_info (own channel) + string source_channel = 2; + Amount token = 3; + string sender = 4; + string receiver = 5; + // Timeout block height. Either timeout height or timestamp should be set. + // Recommendation is to set height, to rev. 1 and block current + 1000 (see api /blocks/latest) + Height timeout_height = 6; + // Timeout timestamp (in nanoseconds) relative to the current block timestamp. Either timeout height or timestamp should be set. + uint64 timeout_timestamp = 7; + } + // cosmos-sdk/MsgDelegate to stake message Delegate { string delegator_address = 1; @@ -60,40 +95,276 @@ message Message { string type_prefix = 3; } + // transfer within wasm/MsgExecuteContract, used by Terra Classic + message WasmTerraExecuteContractTransfer { + // sender address + string sender_address = 1; + + // token contract address + string contract_address = 2; + + // size is uint128, as bigint + bytes amount = 3; + + string recipient_address = 4; + } + + // send within wasm/MsgExecuteContract, used by Terra Classic + message WasmTerraExecuteContractSend { + // sender address + string sender_address = 1; + + // token contract address + string contract_address = 2; + + // size is uint128, as bigint + bytes amount = 3; + + string recipient_contract_address = 4; + + // execute_msg to be executed in the context of recipient contract + string msg = 5; + + // used in case you are sending native tokens along with this message + repeated string coin = 6; + } + + // thorchain/MsgSend + message THORChainSend { + bytes from_address = 1; + bytes to_address = 2; + repeated Amount amounts = 3; + } + + // execute within wasm/MsgExecuteContract, used by Terra Classic + message WasmTerraExecuteContractGeneric { + // sender address + string sender_address = 1; + + // token contract address + string contract_address = 2; + + // execute_msg to be executed in the context of recipient contract + string execute_msg = 3; + + // used in case you are sending native tokens along with this message + // Gap in field numbering is intentional + repeated Amount coins = 5; + } + + // transfer within wasm/MsgExecuteContract + message WasmExecuteContractTransfer { + // sender address + string sender_address = 1; + + // token contract address + string contract_address = 2; + + // size is uint128, as bigint + bytes amount = 3; + + string recipient_address = 4; + } + + // send within wasm/MsgExecuteContract + message WasmExecuteContractSend { + // sender address + string sender_address = 1; + + // token contract address + string contract_address = 2; + + // size is uint128, as bigint + bytes amount = 3; + + string recipient_contract_address = 4; + + // execute_msg to be executed in the context of recipient contract + string msg = 5; + + // used in case you are sending native tokens along with this message + repeated string coin = 6; + } + + // execute within wasm/MsgExecuteContract + message WasmExecuteContractGeneric { + // sender address + string sender_address = 1; + + // token contract address + string contract_address = 2; + + // execute_msg to be executed in the context of recipient contract + string execute_msg = 3; + + // used in case you are sending native tokens along with this message + // Gap in field numbering is intentional + repeated Amount coins = 5; + } + message RawJSON { string type = 1; string value = 2; } + // For signing an already serialized transaction. Account number and chain ID must be set outside. + message SignDirect { + // The prepared serialized TxBody + bytes body_bytes = 1; + // The prepared serialized AuthInfo + bytes auth_info_bytes = 2; + } + + // StakeAuthorization defines authorization for delegate/undelegate/redelegate. + // + // Since: cosmos-sdk 0.43 + message StakeAuthorization { + // max_tokens specifies the maximum amount of tokens can be delegate to a validator. If it is + // empty, there is no spend limit and any amount of coins can be delegated. + Amount max_tokens = 1; + // validators is the oneof that represents either allow_list or deny_list + oneof validators { + // allow_list specifies list of validator addresses to whom grantee can delegate tokens on behalf of granter's + // account. + Validators allow_list = 2; + // deny_list specifies list of validator addresses to whom grantee can not delegate tokens. + Validators deny_list = 3; + } + // Validators defines list of validator addresses. + message Validators { + repeated string address = 1; + } + // authorization_type defines one of AuthorizationType. + AuthorizationType authorization_type = 4; + } + + // AuthorizationType defines the type of staking module authorization type + // + // Since: cosmos-sdk 0.43 + enum AuthorizationType { + // AUTHORIZATION_TYPE_UNSPECIFIED specifies an unknown authorization type + UNSPECIFIED = 0; + // AUTHORIZATION_TYPE_DELEGATE defines an authorization type for Msg/Delegate + DELEGATE = 1; + // AUTHORIZATION_TYPE_UNDELEGATE defines an authorization type for Msg/Undelegate + UNDELEGATE = 2; + // AUTHORIZATION_TYPE_REDELEGATE defines an authorization type for Msg/BeginRedelegate + REDELEGATE = 3; + } + + + // cosmos-sdk/MsgGrant + message AuthGrant { + string granter = 1; + string grantee = 2; + oneof grant_type { + StakeAuthorization grant_stake = 3; + } + int64 expiration = 4; + } + + // cosmos-sdk/MsgRevoke + message AuthRevoke { + string granter = 1; + string grantee = 2; + string msg_type_url = 3; + } + + // VoteOption enumerates the valid vote options for a given governance proposal. + enum VoteOption { + //_UNSPECIFIED defines a no-op vote option. + _UNSPECIFIED = 0; + // YES defines a yes vote option. + YES = 1; + // ABSTAIN defines an abstain vote option. + ABSTAIN = 2; + // NO defines a no vote option. + NO = 3; + // NO_WITH_VETO defines a no with veto vote option. + NO_WITH_VETO = 4; + } + + // cosmos-sdk/MsgVote defines a message to cast a vote. + message MsgVote { + uint64 proposal_id = 1; + string voter = 2; + VoteOption option = 3; + } + + // The payload message oneof message_oneof { Send send_coins_message = 1; - Delegate stake_message = 2; - Undelegate unstake_message = 3; - BeginRedelegate restake_message = 4; - WithdrawDelegationReward withdraw_stake_reward_message = 5; - RawJSON raw_json_message = 6; + Transfer transfer_tokens_message = 2; + Delegate stake_message = 3; + Undelegate unstake_message = 4; + BeginRedelegate restake_message = 5; + WithdrawDelegationReward withdraw_stake_reward_message = 6; + RawJSON raw_json_message = 7; + WasmTerraExecuteContractTransfer wasm_terra_execute_contract_transfer_message = 8; + WasmTerraExecuteContractSend wasm_terra_execute_contract_send_message = 9; + THORChainSend thorchain_send_message = 10; + WasmTerraExecuteContractGeneric wasm_terra_execute_contract_generic = 11; + WasmExecuteContractTransfer wasm_execute_contract_transfer_message = 12; + WasmExecuteContractSend wasm_execute_contract_send_message = 13; + WasmExecuteContractGeneric wasm_execute_contract_generic = 14; + SignDirect sign_direct_message = 15; + AuthGrant auth_grant = 16; + AuthRevoke auth_revoke = 17; + MsgVote msg_vote = 18; } } -// Input data necessary to create a signed order. +// Options for transaction encoding: JSON (Amino, older) or Protobuf. +enum SigningMode { + JSON = 0; // JSON format, Pre-Stargate + Protobuf = 1; // Protobuf-serialized (binary), Stargate +} + +// Input data necessary to create a signed transaction. message SigningInput { - uint64 account_number = 1; - string chain_id = 2; - Fee fee = 3; - string memo = 4; - uint64 sequence = 5; + // Specify if protobuf (a.k.a. Stargate) or earlier JSON serialization is used + SigningMode signing_mode = 1; + + // Source account number + uint64 account_number = 2; - bytes private_key = 6; + // Chain ID (string) + string chain_id = 3; - repeated Message messages = 7; + // Transaction fee + Fee fee = 4; - BroadcastMode mode = 8; + // Optional memo + string memo = 5; + + // Sequence number (account specific) + uint64 sequence = 6; + + // The secret private key used for signing (32 bytes). + bytes private_key = 7; + + // Payload message(s) + repeated Message messages = 8; + + // Broadcast mode (included in output, relevant when broadcasting) + BroadcastMode mode = 9; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signature bytes signature = 1; - // Signed transaction in JSON. + + // Signed transaction in JSON (pre-Stargate case) string json = 2; + + // Signed transaction containing protobuf encoded, Base64-encoded form (Stargate case), + // wrapped in a ready-to-broadcast json. + string serialized = 3; + + // Set in case of error + string error = 4; + + // signatures array json string + string signature_json = 5; } diff --git a/src/proto/Decred.proto b/src/proto/Decred.proto index d8355f5e6b5..fe9a8ff5663 100644 --- a/src/proto/Decred.proto +++ b/src/proto/Decred.proto @@ -6,11 +6,12 @@ option java_package = "wallet.core.jni.proto"; import "Bitcoin.proto"; import "Common.proto"; +// A transfer transaction message Transaction { - /// Serialization format + // Serialization format uint32 serializeType = 1; - /// Transaction data format version + // Transaction data format version uint32 version = 2; // A list of 1 or more transaction inputs or sources for coins. @@ -19,10 +20,10 @@ message Transaction { // A list of 1 or more transaction outputs or destinations for coins repeated TransactionOutput outputs = 4; - /// The time when a transaction can be spent (usually zero, in which case it has no effect). + // The time when a transaction can be spent (usually zero, in which case it has no effect). uint32 lockTime = 5; - /// The block height at which the transaction expires and is no longer valid. + // The block height at which the transaction expires and is no longer valid. uint32 expiry = 6; } @@ -34,8 +35,13 @@ message TransactionInput { // Transaction version as defined by the sender. uint32 sequence = 2; + // The amount of the input int64 valueIn = 3; + + // Creation block height uint32 blockHeight = 4; + + // Index within the block uint32 blockIndex = 5; // Computational script for confirming transaction authorization. @@ -47,14 +53,14 @@ message TransactionOutput { // Transaction amount. int64 value = 1; - /// Transaction output version. + // Transaction output version. uint32 version = 2; // Usually contains the public key as a Decred script setting up conditions to claim this output. bytes script = 3; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Resulting transaction. Note that the amount may be different than the requested amount to account for fees and available funds. Transaction transaction = 1; diff --git a/src/proto/EOS.proto b/src/proto/EOS.proto index d6a09f94f41..7f32c0a75fd 100644 --- a/src/proto/EOS.proto +++ b/src/proto/EOS.proto @@ -13,17 +13,22 @@ enum KeyType { // Values for an Asset object. message Asset { + // Total amount int64 amount = 1; + + // Number of decimals defined uint32 decimals = 2; + + // Asset symbol string symbol = 3; } // Input data necessary to create a signed transaction. message SigningInput { - // Chain id (256-bit number) + // Chain id (uint256, serialized little endian) bytes chain_id = 1; - // Reference Block Id (256-bits) + // Reference Block Id (uint256, serialized little endian) bytes reference_block_id = 2; // Timestamp on the reference block @@ -44,14 +49,14 @@ message SigningInput { // Asset details and amount Asset asset = 8; - // Sender's private key's raw bytes + // Sender's secret private key used for signing (32 bytes). bytes private_key = 9; // Type of the private key KeyType private_key_type = 10; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // JSON of the packed transaction. string json_encoded = 1; diff --git a/src/proto/Elrond.proto b/src/proto/Elrond.proto index 36cb5aaf2c2..9951055fde1 100644 --- a/src/proto/Elrond.proto +++ b/src/proto/Elrond.proto @@ -9,29 +9,104 @@ syntax = "proto3"; package TW.Elrond.Proto; option java_package = "wallet.core.jni.proto"; -// A transaction, typical balance transfer -message TransactionMessage { - uint64 nonce = 1; +// Generic action. Using one of the more specific actions (e.g. transfers, see below) is recommended. +message GenericAction { + // Accounts involved + Accounts accounts = 1; + + // amount string value = 2; - string receiver = 3; - string sender = 4; - uint64 gas_price = 5; - uint64 gas_limit = 6; - string data = 7; - string chain_id = 8; - uint32 version = 9; + + // additional data + string data = 3; + + // transaction version + uint32 version = 4; + + // Currently, the "options" field should be ignored (not set) by applications using TW Core. + // In the future, TW Core will handle specific transaction options + // (such as the "SignedWithHash" flag, as seen in https://github.com/ElrondNetwork/elrond-go-core/blob/main/core/versioning/txVersionChecker.go) + // when building and signing transactions. + uint32 options = 5; +} + +// EGLD transfer (move balance). +message EGLDTransfer { + // Accounts involved + Accounts accounts = 1; + + // Transfer amount (string) + string amount = 2; +} + +// ESDT transfer (transfer regular ESDTs - fungible tokens). +message ESDTTransfer { + // Accounts involved + Accounts accounts = 1; + + // Token ID + string token_identifier = 2; + + // Transfer token amount (string) + string amount = 3; +} + +// ESDTNFT transfer (transfer NFTs, SFTs and Meta ESDTs). +message ESDTNFTTransfer { + // Accounts involved + Accounts accounts = 1; + + // tokens + string token_collection = 2; + + // nonce of the token + uint64 token_nonce = 3; + + // transfer amount + string amount = 4; +} + +// Transaction sender & receiver etc. +message Accounts { + // Nonce of the sender + uint64 sender_nonce = 1; + + // Sender address + string sender = 2; + + // Sender username + string sender_username = 3; + + // Receiver address + string receiver = 4; + + string receiver_username = 5; } // Input data necessary to create a signed transaction. message SigningInput { - bytes private_key = 1; + // The secret private key used for signing (32 bytes). + bytes private_key = 1; + + // Chain identifier, string + string chain_id = 2; + + // Gas price + uint64 gas_price = 3; + + // Limit for the gas used + uint64 gas_limit = 4; + // transfer payload oneof message_oneof { - TransactionMessage transaction = 2; + GenericAction generic_action = 5; + EGLDTransfer egld_transfer = 6; + ESDTTransfer esdt_transfer = 7; + ESDTNFTTransfer esdtnft_transfer = 8; } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { string encoded = 1; string signature = 2; diff --git a/src/proto/Ethereum.proto b/src/proto/Ethereum.proto index 8a91287f89a..ce3ab148556 100644 --- a/src/proto/Ethereum.proto +++ b/src/proto/Ethereum.proto @@ -3,11 +3,13 @@ syntax = "proto3"; package TW.Ethereum.Proto; option java_package = "wallet.core.jni.proto"; +import "Common.proto"; + // Transaction (transfer, smart contract call, ...) message Transaction { // Native coin transfer transaction message Transfer { - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) bytes amount = 1; // Optional payload data @@ -16,40 +18,46 @@ message Transaction { // ERC20 token transfer transaction message ERC20Transfer { + // destination address (string) string to = 1; - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) bytes amount = 2; } // ERC20 approve transaction message ERC20Approve { + // Target of the approval string spender = 1; - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) bytes amount = 2; } // ERC721 NFT transfer transaction message ERC721Transfer { + // Source address string from = 1; + // Destination address string to = 2; - // ID of the token (256-bit number) + // ID of the token (uint256, serialized little endian) bytes token_id = 3; } // ERC1155 NFT transfer transaction message ERC1155Transfer { + // Source address string from = 1; + // Destination address string to = 2; - // ID of the token (256-bit number) + // ID of the token (uint256, serialized little endian) bytes token_id = 3; - // The amount of tokens being transferred + // The amount of tokens being transferred (uint256, serialized little endian) bytes value = 4; bytes data = 5; @@ -57,13 +65,14 @@ message Transaction { // Generic smart contract transaction message ContractGeneric { - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) bytes amount = 1; // Contract call payload data bytes data = 2; } + // Payload transfer oneof transaction_oneof { Transfer transfer = 1; ERC20Transfer erc20_transfer = 2; @@ -74,57 +83,69 @@ message Transaction { } } +// Transaction type enum TransactionMode { - Legacy = 0; // Legacy transaction, pre-EIP2718/EIP1559; for fee gasPrice/gasLimit is used - Enveloped = 1; // Enveloped transaction EIP2718 (with type 0x2), fee is according to EIP1559 (base fee, inclusion fee, ...) + // Legacy transaction, pre-EIP2718/EIP1559; for fee gasPrice/gasLimit is used + Legacy = 0; + + // Enveloped transaction EIP2718 (with type 0x2), fee is according to EIP1559 (base fee, inclusion fee, ...) + Enveloped = 1; } // Input data necessary to create a signed transaction. // Legacy and EIP2718/EIP1559 transactions supported, see TransactionMode. message SigningInput { - // Chain identifier (256-bit number) + // Chain identifier (uint256, serialized little endian) bytes chain_id = 1; - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 2; // Transaction version selector: Legacy or enveloped, has impact on fee structure. // Default is Legacy (value 0) TransactionMode tx_mode = 3; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) // Relevant for legacy transactions only (disregarded for enveloped/EIP1559) bytes gas_price = 4; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 5; - // Maxinmum optional inclusion fee (aka tip) (256-bit number) + // Maximum optional inclusion fee (aka tip) (uint256, serialized little endian) // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) bytes max_inclusion_fee_per_gas = 6; - // Maxinmum fee (256-bit number) + // Maximum fee (uint256, serialized little endian) // Relevant for enveloped/EIP1559 transactions only, tx_mode=Enveloped, (disregarded for legacy) bytes max_fee_per_gas = 7; // Recipient's address. string to_address = 8; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 9; + // The payload transaction Transaction transaction = 10; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; + // The V, R, S components of the resulting signature, (each uint256, serialized little endian) bytes v = 2; bytes r = 3; bytes s = 4; // The payload part, supplied in the input or assembled from input parameters bytes data = 5; + + // error code, 0 is ok, other codes will be treated as errors + Common.Proto.SigningError error = 6; + + // error code description + string error_message = 7; } diff --git a/src/proto/Everscale.proto b/src/proto/Everscale.proto new file mode 100644 index 00000000000..54ed4bb825c --- /dev/null +++ b/src/proto/Everscale.proto @@ -0,0 +1,60 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +syntax = "proto3"; + +package TW.Everscale.Proto; +option java_package = "wallet.core.jni.proto"; + + +// Message option +enum MessageBehavior { + // Sends a message with the specified amount. The sender pays a fee from the account balance + SimpleTransfer = 0; + + // Sends the entire account balance along with the message + SendAllBalance = 1; +} + +// Transfer message +message Transfer { + // If set to true, then the message will be returned if there is an error on the recipient's side. + bool bounce = 1; + + // Affect the attached amount and fees + MessageBehavior behavior = 2; + + // Amount to send in nano EVER + uint64 amount = 3; + + // Expiration UNIX timestamp + uint32 expired_at = 4; + + // Recipient address + string to = 5; + + // Account state if there is any + oneof account_state_oneof { + // Just contract data + string encoded_contract_data = 6; + } +} + +// Input data necessary to create a signed transaction. +message SigningInput { + // The payload transfer + oneof action_oneof { + Transfer transfer = 1; + } + + // The secret private key used for signing (32 bytes). + bytes private_key = 2; +} + +// Result containing the signed and encoded transaction. +message SigningOutput { + string encoded = 1; +} diff --git a/src/proto/FIO.proto b/src/proto/FIO.proto index 5579cde39dd..e946dcf3fa7 100644 --- a/src/proto/FIO.proto +++ b/src/proto/FIO.proto @@ -52,6 +52,7 @@ message Action { } // Acion for adding public chain addresses to a FIO name; add_pub_address + // Note: actor is not needed, computed from private key message AddPubAddress { // The FIO name already registered to the owner. Ex.: "alice@trust" string fio_address = 1; @@ -60,12 +61,11 @@ message Action { repeated PublicAddress public_addresses = 2; // Max fee to spend, can be obtained using get_fee API. - uint64 fee = 3; - - // Note: actor is not needed, computed from private key + uint64 fee = 3; } - // Action for transfering FIO coins; transfer_tokens_pub_key + // Action for transferring FIO coins; transfer_tokens_pub_key + // Note: actor is not needed, computed from private key message Transfer { // FIO address of the payee. Ex.: "FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf" string payee_public_key = 1; @@ -75,11 +75,10 @@ message Action { // Max fee to spend, can be obtained using get_fee API. uint64 fee = 3; - - // Note: actor is not needed, computed from private key } // Action for renewing a FIO name; renew_fio_address + // Note: actor is not needed, computed from private key message RenewFioAddress { // The FIO name to be renewed. Ex.: "alice@trust" string fio_address = 1; @@ -89,11 +88,10 @@ message Action { // Max fee to spend, can be obtained using get_fee API. uint64 fee = 3; - - // Note: actor is not needed, computed from owner_fio_public_key } // Action for creating a new payment request; new_funds_request + // Note: actor is not needed, computed from private key message NewFundsRequest { // The FIO name of the requested payer. Ex.: "alice@trust" string payer_fio_name = 1; @@ -109,10 +107,9 @@ message Action { // Max fee to spend, can be obtained using get_fee API. uint64 fee = 5; - - // Note: actor is not needed, computed from private key } + // Payload message oneof message_oneof { RegisterFioAddress register_fio_address_message = 1; AddPubAddress add_pub_address_message = 2; @@ -134,7 +131,7 @@ message ChainParams { uint64 ref_block_prefix = 3; } -// Transaction signing input +// Input data necessary to create a signed transaction. message SigningInput { // Expiry for this message, in unix time. Can be 0, then it is taken from current time with default expiry uint32 expiry = 1; @@ -142,7 +139,7 @@ message SigningInput { // Current parameters of the FIO blockchain ChainParams chain_params = 2; - // The private key matching the address, needed for signing + // The secret private key matching the address, used for signing (32 bytes). bytes private_key = 3; // The FIO name of the originating wallet (project-wide constant) @@ -152,7 +149,7 @@ message SigningInput { Action action = 5; } -// Transaction signing output +// Result containing the signed and encoded transaction. message SigningOutput { // Signed transaction in JSON string json = 1; diff --git a/src/proto/Filecoin.proto b/src/proto/Filecoin.proto index 0c39da82c72..58ef6a156c0 100644 --- a/src/proto/Filecoin.proto +++ b/src/proto/Filecoin.proto @@ -5,7 +5,7 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Private key of sender account. + // The secret private key of the sender account, used for signing (32 bytes). bytes private_key = 1; // Recipient's address. @@ -14,20 +14,21 @@ message SigningInput { // Transaction nonce. uint64 nonce = 3; - // Transfer value. + // Transfer value (uint256, serialized little endian) bytes value = 4; // Gas limit. int64 gas_limit = 5; - // Gas fee cap. + // Gas fee cap (uint256, serialized little endian) bytes gas_fee_cap = 6; - // Gas premium. + // Gas premium (uint256, serialized little endian) bytes gas_premium = 7; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // Resulting transaction, in JSON. string json = 1; } diff --git a/src/proto/Harmony.proto b/src/proto/Harmony.proto index 72762c24acd..86647fad3bd 100644 --- a/src/proto/Harmony.proto +++ b/src/proto/Harmony.proto @@ -5,54 +5,58 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Chain identifier (256-bit number) + // Chain identifier (uint256, serialized little endian) bytes chain_id = 1; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 2; + // The payload message oneof message_oneof { TransactionMessage transaction_message = 3; StakingMessage staking_message = 4; } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; + // THE V,R,S components of the signature bytes v = 2; bytes r = 3; bytes s = 4; } +// A Transfer message message TransactionMessage { - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 1; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) bytes gas_price = 2; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 3; // Recipient's address. string to_address = 4; - // Amount to send in wei (256-bit number) + // Amount to send in wei (uint256, serialized little endian) bytes amount = 5; // Optional payload bytes payload = 6; - // From shard ID (256-bit number) + // From shard ID (uint256, serialized little endian) bytes from_shard_id = 7; - // To Shard ID (256-bit number) + // To Shard ID (uint256, serialized little endian) bytes to_shard_id = 8; } +// A Staking message. message StakingMessage { // StakeMsg oneof stake_msg { @@ -63,16 +67,17 @@ message StakingMessage { DirectiveCollectRewards collect_rewards = 5; } - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 6; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) bytes gas_price = 7; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 8; } +// Description for a validator message Description { string name = 1; string identity = 2; @@ -81,21 +86,38 @@ message Description { string details = 5; } +// A variable precision number message Decimal { + // The 'raw' value bytes value = 1; + + // The precision (number of decimals) bytes precision = 2; } +// Represents validator commission rule message CommissionRate { + // The rate Decimal rate = 1; + + // Maximum rate Decimal max_rate = 2; + + // Maximum of rate change Decimal max_change_rate = 3; } +// Create Validator directive message DirectiveCreateValidator { + // Address of validator string validator_address = 1; + + // Description, name etc. Description description = 2; + + // Rates CommissionRate commission_rates = 3; + bytes min_self_delegation = 4; bytes max_total_delegation = 5; repeated bytes slot_pub_keys = 6; @@ -103,7 +125,10 @@ message DirectiveCreateValidator { bytes amount = 8; } + +// Edit Validator directive message DirectiveEditValidator { + // Validator address string validator_address = 1; Description description = 2; Decimal commission_rate = 3; @@ -115,18 +140,33 @@ message DirectiveEditValidator { bytes active = 9; } +// Delegate directive message DirectiveDelegate { + // Delegator address string delegator_address = 1; + + // Validator address string validator_address = 2; + + // Delegate amount (uint256, serialized little endian) bytes amount = 3; } +// Undelegate directive message DirectiveUndelegate { + // Delegator address string delegator_address = 1; + + // Validator address string validator_address = 2; + + // Undelegate amount (uint256, serialized little endian) bytes amount = 3; } + +// Collect reward message DirectiveCollectRewards { + // Delegator address string delegator_address = 1; -} \ No newline at end of file +} diff --git a/src/proto/Icon.proto b/src/proto/Icon.proto index 8319b026d7c..fc39efc3ee8 100644 --- a/src/proto/Icon.proto +++ b/src/proto/Icon.proto @@ -11,7 +11,7 @@ message SigningInput { // Recipient address. string to_address = 2; - // Transfer amount. + // Transfer amount (uint256, serialized little endian) bytes value = 3; // The amount of step to send with the transaction. @@ -26,11 +26,11 @@ message SigningInput { // Network identifier bytes network_id = 7; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 8; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // JSON-encoded transaction parameters. string encoded = 1; diff --git a/src/proto/IoTeX.proto b/src/proto/IoTeX.proto index 1df337cf627..275dd40d8ab 100644 --- a/src/proto/IoTeX.proto +++ b/src/proto/IoTeX.proto @@ -3,96 +3,159 @@ syntax = "proto3"; package TW.IoTeX.Proto; option java_package = "wallet.core.jni.proto"; +// A transfer message Transfer { + // Amount (as string) string amount = 1; + + // Destination address string recipient = 2; + + // Payload data bytes payload = 3; } +// A Staking message message Staking { - // create stake - message Create { - string candidateName = 1; - string stakedAmount = 2; - uint32 stakedDuration = 3; - bool autoStake = 4; - bytes payload = 5; - } - - // unstake or withdraw - message Reclaim { - uint64 bucketIndex = 1; - bytes payload = 2; - } - - // add the amount of bucket - message AddDeposit { - uint64 bucketIndex = 1; - string amount = 2; - bytes payload = 3; - } - - // restake the duration and autoStake flag of bucket - message Restake { - uint64 bucketIndex = 1; - uint32 stakedDuration = 2; - bool autoStake = 3; - bytes payload = 4; - } - - // move the bucket to vote for another candidate or transfer the ownership of bucket to another voters - message ChangeCandidate { - uint64 bucketIndex = 1; - string candidateName = 2; - bytes payload = 3; - } - - message TransferOwnership { - uint64 bucketIndex = 1; - string voterAddress = 2; - bytes payload = 3; - } - - message CandidateBasicInfo { - string name = 1; - string operatorAddress = 2; - string rewardAddress = 3; - } - - message CandidateRegister { - CandidateBasicInfo candidate = 1; - string stakedAmount = 2; - uint32 stakedDuration = 3; - bool autoStake = 4; - string ownerAddress = 5; // if ownerAddress is absent, owner of candidate is the sender - bytes payload = 6; - } - oneof message { - Create stakeCreate = 1; - Reclaim stakeUnstake = 2; - Reclaim stakeWithdraw = 3; - AddDeposit stakeAddDeposit = 4; - Restake stakeRestake = 5; - ChangeCandidate stakeChangeCandidate = 6; - TransferOwnership stakeTransferOwnership = 7; - CandidateRegister candidateRegister = 8; - CandidateBasicInfo candidateUpdate = 9; - } + // create stake + message Create { + // validator name + string candidateName = 1; + + // amount to be staked + string stakedAmount = 2; + + // duration + uint32 stakedDuration = 3; + + // auto-restake + bool autoStake = 4; + + // payload data + bytes payload = 5; + } + + // unstake or withdraw + message Reclaim { + // index to claim + uint64 bucketIndex = 1; + + // payload data + bytes payload = 2; + } + + // add the amount of bucket + message AddDeposit { + // index + uint64 bucketIndex = 1; + + // amount to add + string amount = 2; + + // payload data + bytes payload = 3; + } + + // restake the duration and autoStake flag of bucket + message Restake { + // index + uint64 bucketIndex = 1; + + // stake duration + uint32 stakedDuration = 2; + + // auto re-stake + bool autoStake = 3; + + // payload data + bytes payload = 4; + } + + // move the bucket to vote for another candidate or transfer the ownership of bucket to another voters + message ChangeCandidate { + // index + uint64 bucketIndex = 1; + + // validator name + string candidateName = 2; + + // payload data + bytes payload = 3; + } + + // transfer ownserhip of stake + message TransferOwnership { + // index + uint64 bucketIndex = 1; + + // address of voter + string voterAddress = 2; + + // payload data + bytes payload = 3; + } + + // Candidate (validator) info + message CandidateBasicInfo { + string name = 1; + string operatorAddress = 2; + string rewardAddress = 3; + } + + // Register a Candidate + message CandidateRegister { + CandidateBasicInfo candidate = 1; + string stakedAmount = 2; + uint32 stakedDuration = 3; + bool autoStake = 4; + string ownerAddress = 5; // if ownerAddress is absent, owner of candidate is the sender + bytes payload = 6; + } + + // the payload message + oneof message { + Create stakeCreate = 1; + Reclaim stakeUnstake = 2; + Reclaim stakeWithdraw = 3; + AddDeposit stakeAddDeposit = 4; + Restake stakeRestake = 5; + ChangeCandidate stakeChangeCandidate = 6; + TransferOwnership stakeTransferOwnership = 7; + CandidateRegister candidateRegister = 8; + CandidateBasicInfo candidateUpdate = 9; + } } +// Arbitrary contract call message ContractCall { + // amount string amount = 1; + + // contract address string contract = 2; + + // payload data bytes data = 3; } -// transaction signing input +// Input data necessary to create a signed transaction. message SigningInput { + // Transaction version uint32 version = 1; + + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 2; + + // Limit for the gas used uint64 gasLimit = 3; + + // Gas price string gasPrice = 4; + + // The secret private key used for signing (32 bytes). bytes privateKey = 5; + + // Payload transfer oneof action { Transfer transfer = 10; ContractCall call = 12; @@ -106,10 +169,10 @@ message SigningInput { Staking.TransferOwnership stakeTransferOwnership = 46; Staking.CandidateRegister candidateRegister = 47; Staking.CandidateBasicInfo candidateUpdate = 48; - } + } } -// transaction signing output +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded Action bytes bytes encoded = 1; @@ -118,29 +181,47 @@ message SigningOutput { bytes hash = 2; } +// An Action structure +// Used internally message ActionCore { - uint32 version = 1; - uint64 nonce = 2; - uint64 gasLimit = 3; - string gasPrice = 4; - oneof action { - Transfer transfer = 10; - ContractCall execution = 12; - // Native staking - Staking.Create stakeCreate = 40; - Staking.Reclaim stakeUnstake = 41; - Staking.Reclaim stakeWithdraw = 42; - Staking.AddDeposit stakeAddDeposit = 43; - Staking.Restake stakeRestake = 44; - Staking.ChangeCandidate stakeChangeCandidate = 45; - Staking.TransferOwnership stakeTransferOwnership = 46; - Staking.CandidateRegister candidateRegister = 47; - Staking.CandidateBasicInfo candidateUpdate = 48; - } + // version number + uint32 version = 1; + + // Nonce (should be larger than in the last transaction of the account) + uint64 nonce = 2; + + // Gas limit + uint64 gasLimit = 3; + + // Gas price + string gasPrice = 4; + + // action payload + oneof action { + Transfer transfer = 10; + ContractCall execution = 12; + // Native staking + Staking.Create stakeCreate = 40; + Staking.Reclaim stakeUnstake = 41; + Staking.Reclaim stakeWithdraw = 42; + Staking.AddDeposit stakeAddDeposit = 43; + Staking.Restake stakeRestake = 44; + Staking.ChangeCandidate stakeChangeCandidate = 45; + Staking.TransferOwnership stakeTransferOwnership = 46; + Staking.CandidateRegister candidateRegister = 47; + Staking.CandidateBasicInfo candidateUpdate = 48; + } } +// Signed Action +// Used internally message Action { - ActionCore core = 1; - bytes senderPubKey = 2; - bytes signature = 3; -} \ No newline at end of file + // Action details + ActionCore core = 1; + + // public key + bytes senderPubKey = 2; + + // the signature + bytes signature = 3; +} diff --git a/src/proto/NEAR.proto b/src/proto/NEAR.proto index 7bd8b2e2781..a55a9079f94 100644 --- a/src/proto/NEAR.proto +++ b/src/proto/NEAR.proto @@ -3,63 +3,97 @@ syntax = "proto3"; package TW.NEAR.Proto; option java_package = "wallet.core.jni.proto"; +// Public key with type message PublicKey { + // Key type uint32 key_type = 1; + + // The public key data bytes data = 2; } +// Permissions for a function call message FunctionCallPermission { - bytes allowance = 1; // uint128 / little endian byte order + // uint128 / little endian byte order + bytes allowance = 1; + string receiver_id = 2; + + repeated string method_names = 3; } +// Full access message FullAccessPermission { } +// Access key: nonce + permission message AccessKey { + // Nonce uint64 nonce = 1; + + // Permission oneof permission { FunctionCallPermission function_call = 2; FullAccessPermission full_access = 3; } } +// Create Account message CreateAccount { } +// Deploying a contract message DeployContract { bytes code = 1; } +// A method/function call message FunctionCall { - bytes method_name = 1; + // Method/function name + string method_name = 1; + + // input arguments bytes args = 2; + + // gas uint64 gas = 3; - bytes deposit = 4; // uint128 / little endian byte order + + // uint128 / little endian byte order + bytes deposit = 4; } +// Transfer message Transfer { - bytes deposit = 1; // uint128 / little endian byte order + // amount; uint128 / little endian byte order + bytes deposit = 1; } +// Stake message Stake { - bytes stake = 1; // uint128 / little endian byte order - string public_key = 2; + // amount; uint128 / little endian byte order + bytes stake = 1; + + // owner public key + PublicKey public_key = 2; } +// Add a key message AddKey { PublicKey public_key = 1; AccessKey access_key = 2; } +// Delete a key message DeleteKey { PublicKey public_key = 1; } +// Delete account message DeleteAccount { string beneficiary_id = 1; } +// Represents an action message Action { oneof payload { CreateAccount create_account = 1; @@ -75,17 +109,30 @@ message Action { // Input data necessary to create a signed order. message SigningInput { + // ID of the sender string signer_id = 1; + + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 2; + + // ID of the receiver string receiver_id = 3; + + // Recent block hash bytes block_hash = 4; + + // Payload action(s) repeated Action actions = 5; + // The secret private key used for signing (32 bytes). bytes private_key = 6; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed transaction blob bytes signed_transaction = 1; + + // Hash of the transaction + bytes hash = 2; } diff --git a/src/proto/NEO.proto b/src/proto/NEO.proto index 0093145f29f..096327cbc51 100644 --- a/src/proto/NEO.proto +++ b/src/proto/NEO.proto @@ -5,35 +5,61 @@ option java_package = "wallet.core.jni.proto"; import "Common.proto"; +// Input for a transaction (output of a prev tx) message TransactionInput { + // Previous tx hash bytes prev_hash = 1; + + // Output index fixed32 prev_index = 2; // unspent value of UTXO int64 value = 3; + // Asset string asset_id = 4; } +// Output of a transaction message TransactionOutput { + // Asset string asset_id = 1; + + // Amount (as string) sint64 amount = 2; + + // destination address string to_address = 3; + + // change address string change_address = 4; } // Input data necessary to create a signed transaction. message SigningInput { + // Available transaction inputs repeated TransactionInput inputs = 1; + + // Transaction outputs repeated TransactionOutput outputs = 2; + + // The secret private key used for signing (32 bytes). bytes private_key = 3; + + // Fee int64 fee = 4; + + // Asset ID for gas string gas_asset_id = 5; + + // Address for the change string gas_change_address = 6; + + // Optional transaction plan (if missing it will be computed) TransactionPlan plan = 7; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; @@ -50,9 +76,16 @@ message TransactionOutputPlan { // Maximum available amount. int64 available_amount = 2; + // Amount that is left as change int64 change = 3; + + // Asset string asset_id = 4; + + // Destination address string to_address = 5; + + // Address for the change string change_address = 6; }; @@ -69,4 +102,4 @@ message TransactionPlan { // Optional error Common.Proto.SigningError error = 4; -}; \ No newline at end of file +}; diff --git a/src/proto/NULS.proto b/src/proto/NULS.proto index 2bcfd9d7a2e..51ed1806919 100644 --- a/src/proto/NULS.proto +++ b/src/proto/NULS.proto @@ -3,65 +3,122 @@ syntax = "proto3"; package TW.NULS.Proto; option java_package = "wallet.core.jni.proto"; +// Transaction from address message TransactionCoinFrom { + // Source address string from_address = 1; + + // Chain ID uint32 assets_chainid = 2; + + // ID of the asset uint32 assets_id = 3; - //tranaction out amount (256-bit number) + + // transaction out amount (256-bit number) bytes id_amount = 4; - //8 bytes + + // Nonce, 8 bytes bytes nonce = 5; - //lock status: 1 locked; 0 unlocked + + // lock status: 1 locked; 0 unlocked uint32 locked = 6; } +// Transaction to a destination message TransactionCoinTo { + // destination address string to_address = 1; + + // Chain ID uint32 assets_chainid = 2; + + // ID of the asset uint32 assets_id = 3; - // tranaction amount (256-bit number) + + // transaction amount (uint256, serialized little endian) bytes id_amount = 4; + + // lock time uint32 lock_time = 5; } +// A signature message Signature { + // Length of public key data uint32 pkey_len = 1; + + // The public key bytes public_key = 2; + + // The length of the signature uint32 sig_len = 3; + + // The signature data bytes signature = 4; } +// A transaction message Transaction { + // transaction type uint32 type = 1; + + // Timestamp of the transaction uint32 timestamp = 2; + + // Optional string remark string remark = 3; + + // The raw data bytes tx_data = 4; - //CoinFrom + + // CoinFrom TransactionCoinFrom input = 5; - //CoinTo + + // CoinTo TransactionCoinTo output = 6; + // Signature Signature tx_sigs = 7; + + // Tx hash uint32 hash = 8; } // Input data necessary to create a signed order. message SigningInput { + // The secret private key used for signing (32 bytes). bytes private_key = 1; + + // Source address string from = 2; + + // Destination address string to = 3; + + // Transfer amount (uint256, serialized little endian) bytes amount = 4; + + // Chain ID uint32 chain_id = 5; + + // Asset ID uint32 idassets_id = 6; - //The last 8 bytes of latest transaction hash + + // The last 8 bytes of latest transaction hash bytes nonce = 7; + + // Optional memo remark string remark = 8; + // Account balance bytes balance = 9; + // time, accurate to the second uint32 timestamp = 10; } +// Result containing the signed and encoded transaction. message SigningOutput { + // Encoded transaction bytes encoded = 1; -} \ No newline at end of file +} diff --git a/src/proto/Nano.proto b/src/proto/Nano.proto index 954c7930e28..9bd7a7a26bd 100644 --- a/src/proto/Nano.proto +++ b/src/proto/Nano.proto @@ -5,12 +5,13 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - // Private key + // The secret private key used for signing (32 bytes). bytes private_key = 1; // Optional parent block hash bytes parent_block = 2; + // Receive/Send reference oneof link_oneof { // Hash of a block to receive from bytes link_block = 3; @@ -28,12 +29,14 @@ message SigningInput { string work = 7; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signature bytes signature = 1; + // Block hash bytes block_hash = 2; - // Json representation of the block + + // JSON representation of the block string json = 3; } diff --git a/src/proto/Nebulas.proto b/src/proto/Nebulas.proto index fc8e7e9acf9..ea915410d59 100644 --- a/src/proto/Nebulas.proto +++ b/src/proto/Nebulas.proto @@ -8,42 +8,47 @@ message SigningInput { // sender's address. string from_address = 1; - // Chain identifier (256-bit number) + // Chain identifier (uint256, serialized little endian) bytes chain_id = 2; - // Nonce (256-bit number) + // Nonce (uint256, serialized little endian) bytes nonce = 3; - // Gas price (256-bit number) + // Gas price (uint256, serialized little endian) bytes gas_price = 4; - // Gas limit (256-bit number) + // Gas limit (uint256, serialized little endian) bytes gas_limit = 5; // Recipient's address. string to_address = 6; - // Amount to send in wei, 1 NAS = 10^18 Wei (256-bit number) + // Amount to send in wei, 1 NAS = 10^18 Wei (uint256, serialized little endian) bytes amount = 7; - // Timestamp to create transaction (256-bit number) + // Timestamp to create transaction (uint256, serialized little endian) bytes timestamp = 8; // Optional payload string payload = 9; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 10; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // Algorithm code uint32 algorithm = 1; + + // The signature bytes signature = 2; + + // Encoded transaction string raw = 3; } -// +// Generic data message Data { string type = 1; bytes payload = 2; @@ -51,17 +56,39 @@ message Data { // Raw transaction data message RawTransaction { + // tx hash bytes hash = 1; + + // source address bytes from = 2; + + // destination address bytes to = 3; + + // amount (uint256, serialized little endian) bytes value = 4; + + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 5; + + // transaction timestamp int64 timestamp = 6; + + // generic data Data data = 7; + + // chain ID (4 bytes) uint32 chain_id = 8; + + // gas price (uint256, serialized little endian) bytes gas_price = 9; + + // gas limit (uint256, serialized little endian) bytes gas_limit = 10; + // algorithm uint32 alg = 11; + + // signature bytes sign = 12; -} \ No newline at end of file +} diff --git a/src/proto/Nervos.proto b/src/proto/Nervos.proto new file mode 100644 index 00000000000..07cc53060d0 --- /dev/null +++ b/src/proto/Nervos.proto @@ -0,0 +1,208 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +syntax = "proto3"; + +package TW.Nervos.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Common.proto"; + +// Nervos transaction plan +message TransactionPlan { + // A list of cell deps. + repeated CellDep cell_deps = 1; + + // A list of header deps. + repeated bytes header_deps = 2; + + // A list of 1 or more selected cells for this transaction + repeated Cell selected_cells = 3; + + // A list of 1 or more outputs by this transaction + repeated CellOutput outputs = 4; + + // A list of outputs data. + repeated bytes outputs_data = 5; + + // Optional error + Common.Proto.SigningError error = 6; +} + +// Nervos cell dep. +message CellDep { + // Prevents the transaction to be mined before an absolute or relative time + string dep_type = 1; + + // Reference to the previous transaction's output. + OutPoint out_point = 2; +} + +// Nervos transaction out-point reference. +message OutPoint { + // The hash of the referenced transaction. + bytes tx_hash = 1; + + // The index of the specific output in the transaction. + uint32 index = 2; +} + +// Nervos cell output. +message CellOutput { + // Transaction amount. + uint64 capacity = 1; + + // Lock script + Script lock = 2; + + // Type script + Script type = 3; +} + +// Nervos script +message Script { + // Code hash + bytes code_hash = 1; + + // Hash type + string hash_type = 2; + + // args + bytes args = 3; +} + +// Transfer of native asset +message NativeTransfer { + // Recipient's address. + string to_address = 1; + + // Change address. + string change_address = 2; + + // Amount to send. + uint64 amount = 3; + + // If sending max amount. + bool use_max_amount = 4; +} + +// Token transfer (SUDT) +message SudtTransfer { + // Recipient's address. + string to_address = 1; + + // Change address. + string change_address = 2; + + // SUDT (Simple User Defined Token) address + bytes sudt_address = 3; + + // Amount to send. + string amount = 4; + + // If sending max amount. + bool use_max_amount = 5; +} + +// Deposit +message DaoDeposit { + // Recipient's address. + string to_address = 1; + + // Change address. + string change_address = 2; + + // Amount to deposit. + uint64 amount = 3; +} + +message DaoWithdrawPhase1 { + // Deposit cell + Cell deposit_cell = 1; + + // Change address. + string change_address = 2; +} + +message DaoWithdrawPhase2 { + // Deposit cell + Cell deposit_cell = 1; + + // Withdrawing cell + Cell withdrawing_cell = 2; + + // Amount + uint64 amount = 3; +} + +// Input data necessary to create a signed transaction. +message SigningInput { + // Transaction fee per byte. + uint64 byte_fee = 1; + + // The available secret private keys used for signing (32 bytes each). + repeated bytes private_key = 2; + + // Available unspent cell outputs. + repeated Cell cell = 3; + + // Optional transaction plan + TransactionPlan plan = 4; + + // The payload transfer + oneof operation_oneof { + NativeTransfer native_transfer = 5; + SudtTransfer sudt_transfer = 6; + DaoDeposit dao_deposit = 7; + DaoWithdrawPhase1 dao_withdraw_phase1 = 8; + DaoWithdrawPhase2 dao_withdraw_phase2 = 9; + } +} + +// An unspent cell output, that can serve as input to a transaction +message Cell { + // The unspent output + OutPoint out_point = 1; + + // Amount of the cell + uint64 capacity = 2; + + // Lock script + Script lock = 3; + + // Type script + Script type = 4; + + // Data + bytes data = 5; + + // Optional block number + uint64 block_number = 6; + + // Optional block hash + bytes block_hash = 7; + + // Optional since the cell is available to spend + uint64 since = 8; + + // Optional input type data to be included in witness + bytes input_type = 9; + + // Optional output type data to be included in witness + bytes output_type = 10; +} + +// Result containing the signed and encoded transaction. +message SigningOutput { + // Resulting transaction. Note that the amount may be different than the requested amount to account for fees and available funds. + string transaction_json = 1; + + // Transaction id + string transaction_id = 2; + + // Optional error + Common.Proto.SigningError error = 3; +} diff --git a/src/proto/Nimiq.proto b/src/proto/Nimiq.proto index 6c96981278d..76d00dd903e 100644 --- a/src/proto/Nimiq.proto +++ b/src/proto/Nimiq.proto @@ -5,18 +5,24 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { + // The secret private key used for signing (32 bytes). bytes private_key = 1; + // Destination address string destination = 2; + // Amount of the transfer uint64 value = 3; + // Fee amount uint64 fee = 4; + // Validity start, in block height uint32 validity_start_height = 5; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // The encoded transaction bytes encoded = 1; } diff --git a/src/proto/Oasis.proto b/src/proto/Oasis.proto index 6b327044b58..316f6e295df 100644 --- a/src/proto/Oasis.proto +++ b/src/proto/Oasis.proto @@ -9,28 +9,39 @@ syntax = "proto3"; package TW.Oasis.Proto; option java_package = "wallet.core.jni.proto"; +// Transfer message TransferMessage { + // destination address string to = 1; + + // Gas price uint64 gas_price = 2; // Amount values strings prefixed by zero. e.g. "\u000010000000" string gas_amount = 3; + // Amount values strings prefixed by zero string amount = 4; + // Nonce (should be larger than in the last transaction of the account) uint64 nonce = 5; + + // Context, see https://docs.oasis.dev/oasis-core/common-functionality/crypto#domain-separation string context = 6; } +// Input data necessary to create a signed transaction. message SigningInput { + // The secret private key used for signing (32 bytes). bytes private_key = 1; + // Transfer payload oneof message { TransferMessage transfer = 2; } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Ontology.proto b/src/proto/Ontology.proto index 28992aacfcf..9496537dd18 100644 --- a/src/proto/Ontology.proto +++ b/src/proto/Ontology.proto @@ -5,32 +5,39 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed transaction. message SigningInput { - + // Contract ID, e.g. "ONT" string contract = 1; + // Method, e.g. "transfer" string method = 2; + // The secret private key used for signing (32 bytes). bytes owner_private_key = 3; // base58 encode address string (160-bit number) string to_address = 4; + // Transfer amount uint64 amount = 5; + // Private key of the payer bytes payer_private_key = 6; + // Gas price uint64 gas_price = 7; + // Limit for gas used uint64 gas_limit = 8; // base58 encode address string (160-bit number) string query_address = 9; + // Nonce (should be larger than in the last transaction of the account) uint32 nonce = 10; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Polkadot.proto b/src/proto/Polkadot.proto index a0f07d1b0c6..178a26485de 100644 --- a/src/proto/Polkadot.proto +++ b/src/proto/Polkadot.proto @@ -3,17 +3,20 @@ syntax = "proto3"; package TW.Polkadot.Proto; option java_package = "wallet.core.jni.proto"; +// Known networks enum Network { POLKADOT = 0; KUSAMA = 2; } +// Destination options for reward enum RewardDestination { STAKED = 0; STASH = 1; CONTROLLER = 2; } +// An era, a period defined by a starting block and length message Era { // recent block number (called phase in polkadot code), should match block hash uint64 block_number = 1; @@ -22,48 +25,84 @@ message Era { uint64 period = 2; } +// Balance transfer transaction message Balance { + // transfer message Transfer { + // destination address string to_address = 1; - bytes value = 2; // big integer + + // amount (uint256, serialized little endian) + bytes value = 2; } oneof message_oneof { Transfer transfer = 1; } } +// Staking transaction message Staking { + // Bond to a controller message Bond { + // controller ID string controller = 1; + + // amount (uint256, serialized little endian) bytes value = 2; + + // destination for rewards RewardDestination reward_destination = 3; } + // Bond to a controller, with nominators message BondAndNominate { + // controller ID string controller = 1; + + // amount (uint256, serialized little endian) bytes value = 2; + + // destination for rewards RewardDestination reward_destination = 3; + + // list of nominators repeated string nominators = 4; } + // Bond extra amount message BondExtra { + // amount (uint256, serialized little endian) bytes value = 1; } + // Unbond message Unbond { + // amount (uint256, serialized little endian) bytes value = 1; } + // Withdraw unbonded amounts message WithdrawUnbonded { int32 slashing_spans = 1; } + // Nominate message Nominate { + // list of nominators repeated string nominators = 1; } - message Chill {} + // Chill and unbound + message ChillAndUnbond { + // amount (uint256, serialized little endian) + bytes value = 1; + } + + // Chill + message Chill { + } + // Payload messsage oneof message_oneof { Bond bond = 1; BondAndNominate bond_and_nominate = 2; @@ -72,6 +111,7 @@ message Staking { WithdrawUnbonded withdraw_unbonded = 5; Nominate nominate = 6; Chill chill = 7; + ChillAndUnbond chill_and_unbond = 8; } } @@ -80,28 +120,38 @@ message SigningInput { // Recent block hash, or genesis hash if era is not set bytes block_hash = 1; + // Genesis block hash (identifies the chain) bytes genesis_hash = 2; // Current account nonce uint64 nonce = 3; + // Specification version, e.g. 26. uint32 spec_version = 4; + + // Transaction version, e.g. 5. uint32 transaction_version = 5; - bytes tip = 6; // big integer + + // Optional tip to pay, big integer + bytes tip = 6; // Optional time validity limit, recommended, for replay-protection. Empty means Immortal. Era era = 7; + // The secret private key used for signing (32 bytes). bytes private_key = 8; + + // Network type Network network = 9; + // Payload message oneof message_oneof { Balance balance_call = 10; Staking staking_call = 11; } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Ripple.proto b/src/proto/Ripple.proto index 8d0be3c317e..ffbb863741c 100644 --- a/src/proto/Ripple.proto +++ b/src/proto/Ripple.proto @@ -3,28 +3,43 @@ syntax = "proto3"; package TW.Ripple.Proto; option java_package = "wallet.core.jni.proto"; +import "Common.proto"; + // Input data necessary to create a signed transaction. message SigningInput { + // Transfer amount int64 amount = 1; + // Transfer fee int64 fee = 2; + // Account sequence number int32 sequence = 3; + // Ledger sequence number int32 last_ledger_sequence = 4; + // Source account string account = 5; + // Target account string destination = 6; + // A Destination Tag int64 destination_tag = 7; + // Transaction flags, optional int64 flags = 8; + // The secret private key used for signing (32 bytes). bytes private_key = 9; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // Encoded transaction bytes encoded = 1; + + // Optional error + Common.Proto.SigningError error = 2; } diff --git a/src/proto/Solana.proto b/src/proto/Solana.proto index 80eaad6c1ef..0f7280b5da3 100644 --- a/src/proto/Solana.proto +++ b/src/proto/Solana.proto @@ -3,39 +3,62 @@ syntax = "proto3"; package TW.Solana.Proto; option java_package = "wallet.core.jni.proto"; +// Transfer transaction message Transfer { + // destination address string recipient = 1; + + // amount uint64 value = 2; + + // optional memo + string memo = 3; + + // optional referenced public keys + repeated string references = 4; } // Create and initialize a stake account, and delegate amount to it. // Recommendation behavior is to not specify a stake account, and a new unique account will be created each time. // Optionally a stake account pubkey can be specified, but it should not exist on chain. message DelegateStake { + // Validator's public key string validator_pubkey = 1; + + // delegation amount uint64 value = 2; + + // staking account string stake_account = 3; } // Deactivate staking on stake account message DeactivateStake { + // staking account string stake_account = 1; } // Deactivate staking on multiple stake account message DeactivateAllStake { + // staking accounts repeated string stake_accounts = 1; } // Withdraw amount from stake account message WithdrawStake { + // staking account string stake_account = 1; + + // withdrawal amount uint64 value = 2; } -// Techinical structure to group a staking account and an amount +// Technical structure to group a staking account and an amount message StakeAccountValue { + // staking account string stake_account = 1; + + // amount uint64 value = 2; } @@ -48,35 +71,74 @@ message WithdrawAllStake { message CreateTokenAccount { // main account -- can be same as signer, or other main account (if done on some other account's behalf) string main_address = 1; + + // Token minting address string token_mint_address = 2; + + // Token address string token_address = 3; } // Transfer tokens message TokenTransfer { + // Mint address of the token string token_mint_address = 1; + + // Source address string sender_token_address = 2; + + // Destination address string recipient_token_address = 3; + + // Amount uint64 amount = 4; - uint32 decimals = 5; // Note: 8-bit value + + // Note: 8-bit value + uint32 decimals = 5; + + // optional memo§ + string memo = 6; + + // optional referenced public keys + repeated string references = 7; } // CreateTokenAccount and TokenTransfer combined message CreateAndTransferToken { // main account -- can be same as signer, or other main account (if done on some other account's behalf) string recipient_main_address = 1; + + // Mint address of the token string token_mint_address = 2; + // Token address for the recipient, will be created first string recipient_token_address = 3; + + // Sender's token address string sender_token_address = 4; + + // amount uint64 amount = 5; - uint32 decimals = 6; // Note: 8-bit value + + // Note: 8-bit value + uint32 decimals = 6; + + // optional + string memo = 7; + + // optional referenced public keys + repeated string references = 8; } // Input data necessary to create a signed transaction. message SigningInput { + // The secret private key used for signing (32 bytes). bytes private_key = 1; + + // Relatively recent block hash string recent_blockhash = 2; + + // Payload message oneof transaction_type { Transfer transfer_transaction = 3; DelegateStake delegate_stake_transaction = 4; @@ -90,7 +152,8 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // The encoded transaction string encoded = 1; } diff --git a/src/proto/Stellar.proto b/src/proto/Stellar.proto index 1698cf084bd..44a67e3672f 100644 --- a/src/proto/Stellar.proto +++ b/src/proto/Stellar.proto @@ -3,24 +3,28 @@ syntax = "proto3"; package TW.Stellar.Proto; option java_package = "wallet.core.jni.proto"; +// Represents an asset +// Note: alphanum12 currently not supported message Asset { // Optional in case of non-native asset; the asset issuer address string issuer = 1; // Optional in case of non-native asset; the asset alphanum4 code. string alphanum4 = 2; - - // Note: alphanum12 currently not supported } +// Create a new account message OperationCreateAccount { + // address string destination = 1; // Amount (*10^7) int64 amount = 2; } +// Perform payment message OperationPayment { + // Destination address string destination = 1; // Optional, can be left empty for native asset @@ -30,47 +34,95 @@ message OperationPayment { int64 amount = 3; } +// Change trust message OperationChangeTrust { + // The asset Asset asset = 1; // Validity (time bound to), unix time. Set to (now() + 2 * 365 * 86400) for 2 years; set to 0 for missing. int64 valid_before = 2; } +// A predicate (used in claim) +// Rest of predicates not currently supported +// See https://github.com/stellar/stellar-protocol/blob/master/core/cap-0023.md +enum ClaimPredicate { + Predicate_unconditional = 0; +} + +// Claimant: account & predicate +message Claimant { + // Claimant account + string account = 1; + + // predicate + ClaimPredicate predicate = 2; +} + +// Create a claimable balance (2-phase transfer) +message OperationCreateClaimableBalance { + // Optional, can be left empty for native asset + Asset asset = 1; + + // Amount (*10^7) + int64 amount = 2; + + // One or more claimants + repeated Claimant claimants = 3; +} + +// Claim a claimable balance +message OperationClaimClaimableBalance { + // 32-byte balance ID hash + bytes balance_id = 1; +} + +// Empty memo (placeholder) message MemoVoid { } +// Memo with text message MemoText { string text = 1; } +// Memo with an ID message MemoId { int64 id = 1; } +// Memo with a hash message MemoHash { bytes hash = 1; } // Input data necessary to create a signed transaction. message SigningInput { + // Transaction fee int32 fee = 1; + // Account sequence int64 sequence = 2; + // Source account string account = 3; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 4; + // Wellknown passphrase, specific to the chain string passphrase = 5; + // Payload message oneof operation_oneof { OperationCreateAccount op_create_account = 6; OperationPayment op_payment = 7; OperationChangeTrust op_change_trust = 8; + OperationCreateClaimableBalance op_create_claimable_balance = 14; + OperationClaimClaimableBalance op_claim_claimable_balance = 15; } + // Memo oneof memo_type_oneof { MemoVoid memo_void = 9; MemoText memo_text = 10; @@ -80,7 +132,7 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signature. string signature = 1; diff --git a/src/proto/THORChainSwap.proto b/src/proto/THORChainSwap.proto new file mode 100644 index 00000000000..7f3a20396c0 --- /dev/null +++ b/src/proto/THORChainSwap.proto @@ -0,0 +1,97 @@ +syntax = "proto3"; + +package TW.THORChainSwap.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Bitcoin.proto"; +import "Ethereum.proto"; +import "Binance.proto"; + +// Supported blockchains +enum Chain { + THOR = 0; + BTC = 1; + ETH = 2; + BNB = 3; +} + +// Predefined error codes +enum ErrorCode { + // OK + OK = 0; + Error_general = 1; + Error_Input_proto_deserialization = 2; + Error_Unsupported_from_chain = 13; + Error_Unsupported_to_chain = 14; + Error_Invalid_from_address = 15; + Error_Invalid_to_address = 16; + Error_Invalid_vault_address = 21; + Error_Invalid_router_address = 22; +} + +// An error code + a free text +message Error { + // code of the error + ErrorCode code = 1; + + // optional error message + string message = 2; +} + +// Represents an asset. Examples: BNB.BNB, RUNE.RUNE, BNB.RUNE-67C +message Asset { + // Chain ID + Chain chain = 1; + + // Symbol + string symbol = 2; + + // The ID of the token (blockchain-specific format) + string token_id = 3; +} + +// Input for a swap between source and destination chains; for creating a TX on the source chain. +message SwapInput { + // Source chain + Chain from_chain = 1; + + // Source address, on source chain + string from_address = 2; + + // Destination chain+asset, on destination chain + Asset to_asset = 3; + + // Destination address, on destination chain + string to_address = 4; + + // ThorChainSwap vault, on the source chain. Should be queried afresh, as it may change + string vault_address = 5; + + // ThorChain router, only in case of Ethereum source network + string router_address = 6; + + // The source amount, integer as string, in the smallest native unit of the chain + string from_amount = 7; + + // The minimum accepted destination amount. Actual destination amount will depend on current rates, limit amount can be used to prevent using very unfavorable rates. + string to_amount_limit = 8; +} + +// Result of the swap, a SigningInput struct for the specific chain +message SwapOutput { + // Source chain + Chain from_chain = 1; + + // Destination chain + Chain to_chain = 2; + + // Error code, filled in case of error, OK/empty on success + Error error = 3; + + // Prepared unsigned transaction input, on the source chain, to THOR. Some fields must be completed, and it has to be signed. + oneof signing_input_oneof { + Bitcoin.Proto.SigningInput bitcoin = 4; + Ethereum.Proto.SigningInput ethereum = 5; + Binance.Proto.SigningInput binance = 6; + } +} diff --git a/src/proto/Tezos.proto b/src/proto/Tezos.proto index c09f70bd674..7a56c4d2ab5 100644 --- a/src/proto/Tezos.proto +++ b/src/proto/Tezos.proto @@ -6,32 +6,49 @@ option java_package = "wallet.core.jni.proto"; // Input data necessary to create a signed Tezos transaction. // Next field: 3 message SigningInput { + // One or more operations OperationList operation_list = 1; + + // The secret private key used for signing (32 bytes). bytes private_key = 2; } -// Transaction signing output. +// Result containing the signed and encoded transaction. // Next field: 2 message SigningOutput { + // The encoded transaction bytes encoded = 1; } // A list of operations and a branch. // Next field: 3 message OperationList { + // branch string branch = 1; + + // list of operations repeated Operation operations = 2; } // An operation that can be applied to the Tezos blockchain. // Next field: 12 message Operation { + // counter int64 counter = 1; + + // source account string source = 2; + + // fee int64 fee = 3; + + // gas limit int64 gas_limit = 4; + + // storage limit int64 storage_limit = 5; + // Operation types enum OperationKind { // Note: Proto3 semantics require a zero value. ENDORSEMENT = 0; @@ -40,6 +57,7 @@ message Operation { TRANSACTION = 108; DELEGATION = 110; } + // Operation type OperationKind kind = 7; // Operation specific data depending on the type of the operation. @@ -50,11 +68,43 @@ message Operation { } } +message FA12Parameters { + string entrypoint = 1; + string from = 2; + string to = 3; + string value = 4; +} + +message Txs { + string to = 1; + string token_id = 2; + string amount = 3; +} + +message TxObject { + string from = 1; + repeated Txs txs = 2; +} + +message FA2Parameters { + string entrypoint = 1; + repeated TxObject txs_object = 2; +} + +// Generic operation parameters +message OperationParameters { + oneof parameters { + FA12Parameters fa12_parameters = 1; + FA2Parameters fa2_parameters = 2; + } +} + // Transaction operation specific data. // Next field: 3 message TransactionOperationData { string destination = 1; int64 amount = 2; + OperationParameters parameters = 3; } // Reveal operation specific data. @@ -67,4 +117,4 @@ message RevealOperationData { // Next field: 2 message DelegationOperationData { string delegate = 1; -} \ No newline at end of file +} diff --git a/src/proto/Theta.proto b/src/proto/Theta.proto index f779c0b5b0d..4647d89ba83 100644 --- a/src/proto/Theta.proto +++ b/src/proto/Theta.proto @@ -11,23 +11,23 @@ message SigningInput { /// Recipient address string to_address = 2; - /// Theta token amount to send in wei (256-bit number) + /// Theta token amount to send in wei (uint256, serialized little endian) bytes theta_amount = 3; - /// TFuel token amount to send in wei (256-bit number) + /// TFuel token amount to send in wei (uint256, serialized little endian) bytes tfuel_amount = 4; /// Sequence number of the transaction for the sender address uint64 sequence = 5; - /// Fee amount in TFuel wei for the transaction (256-bit number) + /// Fee amount in TFuel wei for the transaction (uint256, serialized little endian) bytes fee = 6; - /// Private key + /// The secret private key used for signing (32 bytes). bytes private_key = 7; } -/// Transaction signing output +// Result containing the signed and encoded transaction. message SigningOutput { /// Signed and encoded transaction bytes bytes encoded = 1; diff --git a/src/proto/TransactionCompiler.proto b/src/proto/TransactionCompiler.proto new file mode 100644 index 00000000000..5afcd7f51ba --- /dev/null +++ b/src/proto/TransactionCompiler.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package TW.TxCompiler.Proto; +option java_package = "wallet.core.jni.proto"; + +import "Common.proto"; + +/// Transaction pre-signing output +message PreSigningOutput { + /// Pre-image data hash that will be used for signing + bytes data_hash = 1; + + /// Pre-image data + bytes data = 2; + + /// error code, 0 is ok, other codes will be treated as errors + Common.Proto.SigningError error = 3; + + /// error code description + string error_message = 4; +} diff --git a/src/proto/Tron.proto b/src/proto/Tron.proto index 3a6b635b169..7ed7ce37723 100644 --- a/src/proto/Tron.proto +++ b/src/proto/Tron.proto @@ -3,6 +3,7 @@ syntax = "proto3"; package TW.Tron.Proto; option java_package = "wallet.core.jni.proto"; +// A transfer transaction message TransferContract { // Sender address. string owner_address = 1; @@ -14,6 +15,7 @@ message TransferContract { int64 amount = 3; } +// Asset transfer message TransferAssetContract { // Asset name. string asset_name = 1; @@ -28,6 +30,7 @@ message TransferAssetContract { int64 amount = 4; } +// TRC20 token transfer message TransferTRC20Contract { // Contract name. string contract_address = 1; @@ -38,79 +41,125 @@ message TransferTRC20Contract { // Recipient address. string to_address = 3; - // Amount to send, uint256, big-endian. + // Amount to send, (uint256, serialized big endian) bytes amount = 4; } +// Freeze balance message FreezeBalanceContract { // Sender address. string owner_address = 1; + // Frozen balance. Minimum 1 int64 frozen_balance = 2; + // Frozen duration int64 frozen_duration = 3; + // Resource type: BANDWIDTH | ENERGY string resource = 10; + // Receiver address string receiver_address = 15; } +// Unfreeze balance message UnfreezeBalanceContract { // Sender address string owner_address = 1; + // Resource type: BANDWIDTH | ENERGY string resource = 10; + // Receiver address string receiver_address = 15; } +// Unfreeze asset message UnfreezeAssetContract { // Sender address string owner_address = 1; } +// Vote asset message VoteAssetContract { // Sender address string owner_address = 1; + // Vote addresses repeated string vote_address = 2; + bool support = 3; + int32 count = 5; } +// Vote witness message VoteWitnessContract { + // A vote message Vote { + // address string vote_address = 1; + + // vote count int64 vote_count = 2; } + + // Owner string owner_address = 1; + + // The votes repeated Vote votes = 2; + bool support = 3; } +// Withdraw balance message WithdrawBalanceContract { // Sender address string owner_address = 1; } +// Smart contract call message TriggerSmartContract { + // Owner string owner_address = 1; + + // Contract address string contract_address = 2; + + // amount int64 call_value = 3; + + // call data bytes data = 4; + + // token value int64 call_token_value = 5; + + // ID of the token int64 token_id = 6; } +// Info from block header message BlockHeader { + // creation timestamp int64 timestamp = 1; + + // root bytes tx_trie_root = 2; + + // hash of the parent bytes parent_hash = 3; + int64 number = 7; + bytes witness_address = 9; + int32 version = 10; } +// Transaction message Transaction { // Transaction timestamp in milliseconds. int64 timestamp = 1; @@ -139,15 +188,16 @@ message Transaction { } } +// Input data necessary to create a signed transaction. message SigningInput { // Transaction. Transaction transaction = 1; - // Private key. + // The secret private key used for signing (32 bytes). bytes private_key = 2; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Transaction identifier. bytes id = 1; @@ -158,5 +208,6 @@ message SigningOutput { bytes ref_block_bytes = 3; bytes ref_block_hash = 4; + // Result in JSON string json = 5; } diff --git a/src/proto/VeChain.proto b/src/proto/VeChain.proto index c89d9f0bfc5..2b2b7e151c2 100644 --- a/src/proto/VeChain.proto +++ b/src/proto/VeChain.proto @@ -3,11 +3,12 @@ syntax = "proto3"; package TW.VeChain.Proto; option java_package = "wallet.core.jni.proto"; +// A clause, between a sender and destination message Clause { /// Recipient address. string to = 1; - /// Transaction amount. + /// Transaction amount (uint256, serialized little endian) bytes value = 2; /// Payload data. @@ -43,11 +44,11 @@ message SigningInput { /// Number set by user. uint64 nonce = 8; - // Private key. + /// The secret private key used for signing (32 bytes). bytes private_key = 9; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed and encoded transaction bytes. bytes encoded = 1; diff --git a/src/proto/Waves.proto b/src/proto/Waves.proto index 011fcf1138b..a2acc2ef599 100644 --- a/src/proto/Waves.proto +++ b/src/proto/Waves.proto @@ -3,29 +3,45 @@ syntax = "proto3"; package TW.Waves.Proto; option java_package = "wallet.core.jni.proto"; -//Transfer transaction +// Transfer transaction message TransferMessage { + // amount int64 amount = 1; + + // asset ID string asset = 2; + // minimum 0.001 Waves (100000 Wavelets) for now int64 fee = 3; + + // asset of the fee string fee_asset = 4; + + // destination address string to = 5; + // any 140 bytes payload, will be displayed to the client as utf-8 string bytes attachment = 6; } -//Lease transaction +// Lease transaction message LeaseMessage { + // amount int64 amount = 1; + + // destination string to = 2; + // minimum 0.001 Waves (100000 Wavelets) for now int64 fee = 3; } -//Lease transaction +// Cancel Lease transaction message CancelLeaseMessage { + // Lease ID to cancel string lease_id = 1; + + // Fee used int64 fee = 2; } @@ -34,7 +50,11 @@ message CancelLeaseMessage { message SigningInput { // in millis int64 timestamp = 1; + + // The secret private key used for signing (32 bytes). bytes private_key = 2; + + // Payload message oneof message_oneof { TransferMessage transfer_message = 3; LeaseMessage lease_message = 4; @@ -42,9 +62,12 @@ message SigningInput { } } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { + // signature data bytes signature = 1; + + // transaction in JSON format string json = 2; } diff --git a/src/proto/Zilliqa.proto b/src/proto/Zilliqa.proto index ea5d3eff4c9..03e5e2167fa 100644 --- a/src/proto/Zilliqa.proto +++ b/src/proto/Zilliqa.proto @@ -3,14 +3,17 @@ syntax = "proto3"; package TW.Zilliqa.Proto; option java_package = "wallet.core.jni.proto"; +// Generic transaction message Transaction { + // Transfer transaction message Transfer { - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) bytes amount = 1; } + // Generic contract call message Raw { - // Amount to send (256-bit number) + // Amount to send (uint256, serialized little endian) bytes amount = 1; // Smart contract code @@ -43,13 +46,14 @@ message SigningInput { // GasLimit uint64 gas_limit = 5; - // Private Key + // The secret private key used for signing (32 bytes). bytes private_key = 6; + // The payload transaction Transaction transaction = 7; } -// Transaction signing output. +// Result containing the signed and encoded transaction. message SigningOutput { // Signed signature bytes. bytes signature = 1; diff --git a/src/uint256.h b/src/uint256.h index 2c61d2edbcc..46159abb2cd 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -55,12 +55,18 @@ inline uint256_t load(const std::string& data) { return result; } -/// Stores a `uint256_t` as a collection of bytes. -inline Data store(const uint256_t& v) { +/// Stores a `uint256_t` as a collection of bytes, with optional padding (typically to 32 bytes). +/// If minLen is given (non-zero), and result is shorter, it is padded (with zeroes, on the left, big endian) +inline Data store(const uint256_t& v, byte minLen = 0) { using boost::multiprecision::cpp_int; Data bytes; bytes.reserve(32); export_bits(v, std::back_inserter(bytes), 8); + if (minLen && bytes.size() < minLen) { + Data padded(minLen - bytes.size()); + append(padded, bytes); + return padded; + } return bytes; } diff --git a/.swiftlint.yml b/swift/.swiftlint.yml similarity index 100% rename from .swiftlint.yml rename to swift/.swiftlint.yml diff --git a/swift/Gemfile b/swift/Gemfile index cdd3a6b3491..b734015f820 100644 --- a/swift/Gemfile +++ b/swift/Gemfile @@ -1,6 +1,10 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + source "https://rubygems.org" -gem "fastlane" +gem 'fastlane' plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/swift/Gemfile.lock b/swift/Gemfile.lock index f4f39eb55c2..6005b99e515 100644 --- a/swift/Gemfile.lock +++ b/swift/Gemfile.lock @@ -1,65 +1,80 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.3) - addressable (2.7.0) + CFPropertyList (3.0.5) + rexml + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) artifactory (3.0.15) atomos (0.1.3) - aws-eventstream (1.1.1) - aws-partitions (1.446.0) - aws-sdk-core (3.114.0) + aws-eventstream (1.2.0) + aws-partitions (1.600.0) + aws-sdk-core (3.131.1) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) + aws-partitions (~> 1, >= 1.525.0) aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.43.0) - aws-sdk-core (~> 3, >= 3.112.0) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.57.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.93.1) - aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-s3 (1.114.0) + aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.3) + aws-sigv4 (~> 1.4) + aws-sigv4 (1.5.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) - claide (1.0.3) + claide (1.1.0) colored (1.2) colored2 (3.1.2) - commander-fastlane (4.4.6) - highline (~> 1.7.2) + commander (4.6.0) + highline (~> 2.0.0) declarative (0.0.20) - digest-crc (0.6.3) + digest-crc (0.6.4) rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) dotenv (2.7.6) - emoji_regex (3.2.2) - excon (0.80.1) - faraday (1.4.1) + emoji_regex (3.2.3) + excon (0.92.3) + faraday (1.10.0) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) - multipart-post (>= 1.2, < 3) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) ruby2_keywords (>= 0.0.4) faraday-cookie_jar (0.0.7) faraday (>= 0.8.0) http-cookie (~> 1.0.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) faraday-net_http (1.0.1) - faraday-net_http_persistent (1.1.0) - faraday_middleware (1.0.0) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) faraday (~> 1.0) - fastimage (2.2.3) - fastlane (2.181.0) + fastimage (2.2.6) + fastlane (2.206.2) CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) + addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) aws-sdk-s3 (~> 1.0) babosa (>= 1.0.3, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) colored - commander-fastlane (>= 4.4.6, < 5.0.0) + commander (~> 4.6) dotenv (>= 2.1.1, < 3.0.0) emoji_regex (>= 0.1, < 4.0) excon (>= 0.71.0, < 1.0.0) @@ -68,19 +83,20 @@ GEM faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.37.0, < 0.39.0) - google-cloud-storage (>= 1.15.0, < 2.0.0) - highline (>= 1.7.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) json (< 3.0.0) jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) multipart-post (~> 2.0.0) naturally (~> 2.2) + optparse (~> 0.1.1) plist (>= 3.1.0, < 4.0.0) rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.3) simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) terminal-notifier (>= 2.0.0, < 3.0.0) terminal-table (>= 1.4.5, < 2.0.0) tty-screen (>= 0.6.3, < 1.0.0) @@ -91,90 +107,85 @@ GEM xcpretty-travis-formatter (>= 0.0.3) fastlane-plugin-create_xcframework (1.1.2) gh_inspector (1.1.3) - google-api-client (0.38.0) - addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.9) - httpclient (>= 2.8.1, < 3.0) - mini_mime (~> 1.0) - representable (~> 3.0) - retriable (>= 2.0, < 4.0) - signet (~> 0.12) - google-apis-core (0.3.0) + google-apis-androidpublisher_v3 (0.22.0) + google-apis-core (>= 0.5, < 2.a) + google-apis-core (0.6.0) addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.14) - httpclient (>= 2.8.1, < 3.0) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) mini_mime (~> 1.0) representable (~> 3.0) - retriable (>= 2.0, < 4.0) + retriable (>= 2.0, < 4.a) rexml - signet (~> 0.14) webrick - google-apis-iamcredentials_v1 (0.3.0) - google-apis-core (~> 0.1) - google-apis-storage_v1 (0.3.0) - google-apis-core (~> 0.1) + google-apis-iamcredentials_v1 (0.11.0) + google-apis-core (>= 0.5, < 2.a) + google-apis-playcustomapp_v1 (0.8.0) + google-apis-core (>= 0.5, < 2.a) + google-apis-storage_v1 (0.15.0) + google-apis-core (>= 0.5, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-env (1.5.0) - faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.1.0) - google-cloud-storage (1.31.0) - addressable (~> 2.5) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.2.0) + google-cloud-storage (1.36.2) + addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) google-apis-storage_v1 (~> 0.1) - google-cloud-core (~> 1.2) - googleauth (~> 0.9) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (0.16.1) - faraday (>= 0.17.3, < 2.0) + googleauth (1.1.3) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.14) - highline (1.7.10) - http-cookie (1.0.3) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - jmespath (1.4.0) - json (2.5.1) - jwt (2.2.3) + jmespath (1.6.1) + json (2.6.2) + jwt (2.4.1) memoist (0.16.2) mini_magick (4.11.0) - mini_mime (1.1.0) + mini_mime (1.1.2) multi_json (1.15.0) multipart-post (2.0.0) nanaimo (0.3.0) naturally (2.2.1) - os (1.1.1) + optparse (0.1.1) + os (1.1.4) plist (3.6.0) - public_suffix (4.0.6) - rake (13.0.3) - representable (3.1.1) + public_suffix (4.0.7) + rake (13.0.6) + representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) rexml (3.2.5) rouge (2.0.7) - ruby2_keywords (0.0.4) - rubyzip (2.3.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) security (0.1.3) - signet (0.15.0) - addressable (~> 2.3) - faraday (>= 0.17.3, < 2.0) + signet (0.16.1) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.0) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) simctl (1.6.8) CFPropertyList naturally - slack-notifier (2.3.2) terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - trailblazer-option (0.1.1) + trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) @@ -182,16 +193,17 @@ GEM uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) + unf_ext (0.0.8.2) + unicode-display_width (1.8.0) webrick (1.7.0) word_wrap (1.0.0) - xcodeproj (1.19.0) + xcodeproj (1.21.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) + rexml (~> 3.2.4) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-travis-formatter (1.0.1) diff --git a/swift/Podfile b/swift/Podfile index 028e65b3612..ab704127b08 100644 --- a/swift/Podfile +++ b/swift/Podfile @@ -10,7 +10,6 @@ target 'WalletCore' do use_frameworks! pod 'SwiftProtobuf' - pod 'SwiftLint' target 'WalletCoreTests' end diff --git a/swift/Podfile.lock b/swift/Podfile.lock index 3448645476f..a8504fac8bd 100644 --- a/swift/Podfile.lock +++ b/swift/Podfile.lock @@ -1,20 +1,16 @@ PODS: - - SwiftLint (0.41.0) - SwiftProtobuf (1.13.0) DEPENDENCIES: - - SwiftLint - SwiftProtobuf SPEC REPOS: trunk: - - SwiftLint - SwiftProtobuf SPEC CHECKSUMS: - SwiftLint: c585ebd615d9520d7fbdbe151f527977b0534f1e SwiftProtobuf: fd4693388a96c8c2df35d3b063272b0e7c499d00 -PODFILE CHECKSUM: 79a99a0b6ec3019db772eb07bcd6c08f70cd2fa3 +PODFILE CHECKSUM: aac2324ba35cdd5631cb37618cd483887bab9cfd -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 diff --git a/swift/Sources/AnySigner.swift b/swift/Sources/AnySigner.swift index 9a6f5862cd6..5abf730b95d 100644 --- a/swift/Sources/AnySigner.swift +++ b/swift/Sources/AnySigner.swift @@ -10,7 +10,15 @@ import SwiftProtobuf public typealias SigningInput = Message public typealias SigningOutput = Message +/// Represents a signer to sign transactions for any blockchain. public final class AnySigner { + + /// Signs a transaction by SigningInput message and coin type + /// + /// - Parameters: + /// - input: The generic SigningInput SwiftProtobuf message + /// - coin: CoinType + /// - Returns: The generic SigningOutput SwiftProtobuf message public static func sign(input: SigningInput, coin: CoinType) -> SigningOutput { do { let outputData = nativeSign(data: try input.serializedData(), coin: coin) @@ -20,6 +28,12 @@ public final class AnySigner { } } + /// Signs a transaction by serialized data of a SigningInput and coin type + /// + /// - Parameters: + /// - data: The serialized data of a SigningInput + /// - coin: CoinType + /// - Returns: The serialized data of a SigningOutput public static func nativeSign(data: Data, coin: CoinType) -> Data { let inputData = TWDataCreateWithNSData(data) defer { @@ -28,10 +42,18 @@ public final class AnySigner { return TWDataNSData(TWAnySignerSign(inputData, TWCoinType(rawValue: coin.rawValue))) } + /// Check if AnySigner supports signing JSON representation of SigningInput for a given coin. public static func supportsJSON(coin: CoinType) -> Bool { return TWAnySignerSupportsJSON(TWCoinType(rawValue: coin.rawValue)) } + /// Signs a transaction specified by the JSON representation of a SigningInput, coin type and a private key + /// + /// - Parameters: + /// - json: JSON representation of a SigningInput + /// - key: The private key data + /// - coin: CoinType + /// - Returns: The JSON representation of a SigningOutput. public static func signJSON(_ json: String, key: Data, coin: CoinType) -> String { let jsonString = TWStringCreateWithNSString(json) let keyData = TWDataCreateWithNSData(key) @@ -41,6 +63,12 @@ public final class AnySigner { return TWStringNSString(TWAnySignerSignJSON(jsonString, keyData, TWCoinType(rawValue: coin.rawValue))) } + /// Plans a transaction (for UTXO chains only). + /// + /// - Parameters: + /// - input: The generic SigningInput SwiftProtobuf message + /// - coin: CoinType + /// - Returns: TransactionPlan SwiftProtobuf message public static func plan(input: SigningInput, coin: CoinType) -> TransactionPlan { do { let outputData = nativePlan(data: try input.serializedData(), coin: coin) @@ -50,6 +78,12 @@ public final class AnySigner { } } + /// Plans a transaction (for UTXO chains only). + /// + /// - Parameters: + /// - input: The serialized data of a SigningInput + /// - coin: CoinType + /// - Returns: The serialized data of a TransactionPlan public static func nativePlan(data: Data, coin: CoinType) -> Data { let inputData = TWDataCreateWithNSData(data) defer { diff --git a/swift/Sources/DerivationPath.Index.swift b/swift/Sources/DerivationPath.Index.swift deleted file mode 100644 index 09a43cc45c3..00000000000 --- a/swift/Sources/DerivationPath.Index.swift +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -import Foundation - -extension DerivationPath { - /// Derivation path index. - public struct Index: Codable, Hashable, CustomStringConvertible { - /// Index value. - public var value: UInt32 - - /// Whether the index is hardened. - public var hardened: Bool - - /// The derivation index. - public var derivationIndex: UInt32 { - if hardened { - return UInt32(value) | 0x80000000 - } else { - return UInt32(value) - } - } - - public init(_ value: UInt32, hardened: Bool = true) { - self.value = value - self.hardened = hardened - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(value) - hasher.combine(hardened) - } - - public static func == (lhs: Index, rhs: Index) -> Bool { - return lhs.value == rhs.value && lhs.hardened == rhs.hardened - } - - public var description: String { - if hardened { - return "\(value)'" - } else { - return value.description - } - } - } -} diff --git a/swift/Sources/DerivationPath.swift b/swift/Sources/DerivationPath.swift deleted file mode 100644 index c9f694bb6b4..00000000000 --- a/swift/Sources/DerivationPath.swift +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -import Foundation - -/// Represents a hierarchical determinisic derivation path. -public struct DerivationPath: Codable, Hashable, CustomStringConvertible { - var indexCount = 5 - - /// List of indices in the derivation path. - public private(set) var indices = [Index]() - - /// Address purpose, each coin will have a different value. - public var purpose: Purpose { - get { - return Purpose(rawValue: indices[0].value)! - } - set { - indices[0] = Index(newValue.rawValue, hardened: true) - } - } - - /// Coin type distinguishes between main net, test net, and forks. - public var coinType: UInt32 { - get { - return indices[1].value - } - set { - indices[1] = Index(newValue, hardened: true) - } - } - - /// Account number. - public var account: UInt32 { - get { - return indices[2].value - } - set { - indices[2] = Index(newValue, hardened: true) - } - } - - /// Change or private addresses will set this to 1. - public var change: UInt32 { - get { - return indices[3].value - } - set { - indices[3] = Index(newValue, hardened: false) - } - } - - /// Address number - public var address: UInt32 { - get { - return indices[4].value - } - set { - indices[4] = Index(newValue, hardened: false) - } - } - - init(indices: [Index]) { - precondition(indices.count == indexCount, "Not enough indices") - self.indices = indices - } - - /// Creates a `DerivationPath` by components. - public init(purpose: Purpose, coin: UInt32, account: UInt32 = 0, change: UInt32 = 0, address: UInt32 = 0) { - self.indices = [Index](repeating: Index(0), count: indexCount) - self.purpose = purpose - self.coinType = coin - self.account = account - self.change = change - self.address = address - } - - /// Creates a derivation path with a string description like `m/10/0/2'/3` - public init?(_ string: String) { - let components = string.split(separator: "/") - for component in components { - if component == "m" { - continue - } - if component.hasSuffix("'") { - guard let index = UInt32(component.dropLast()) else { - return nil - } - indices.append(Index(index, hardened: true)) - } else { - guard let index = UInt32(component) else { - return nil - } - indices.append(Index(index, hardened: false)) - } - } - guard indices.count == indexCount else { - return nil - } - } - - /// String representation. - public var description: String { - return "m/" + indices.map({ $0.description }).joined(separator: "/") - } - - public func hash(into hasher: inout Hasher) { - indices.forEach { hasher.combine($0) } - } - - public static func == (lhs: DerivationPath, rhs: DerivationPath) -> Bool { - return lhs.indices == rhs.indices - } -} diff --git a/swift/Sources/Dummy.cpp b/swift/Sources/Dummy.cpp index 3cb855c4329..e433c2b6009 100644 --- a/swift/Sources/Dummy.cpp +++ b/swift/Sources/Dummy.cpp @@ -4,4 +4,4 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -// Dummy C++ file to foce inclusion of the C++ STL when compiling. +// Dummy C++ file to force inclusion of the C++ STL when compiling. diff --git a/swift/Sources/Extensions/Account+Codable.swift b/swift/Sources/Extensions/Account+Codable.swift new file mode 100644 index 00000000000..39b48c3d0c5 --- /dev/null +++ b/swift/Sources/Extensions/Account+Codable.swift @@ -0,0 +1,69 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import Foundation + +extension Account: Equatable { + public static func == (lhs: Account, rhs: Account) -> Bool { + return lhs.coin == rhs.coin && + lhs.address == rhs.address && + lhs.derivation == rhs.derivation && + lhs.derivationPath == rhs.derivationPath && + lhs.publicKey == rhs.publicKey && + lhs.extendedPublicKey == rhs.extendedPublicKey + } +} + +extension Account: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(coin) + hasher.combine(address) + hasher.combine(derivation) + hasher.combine(derivationPath) + hasher.combine(publicKey) + hasher.combine(extendedPublicKey) + } +} + +extension Account: Codable { + private enum CodingKeys: String, CodingKey { + case coin + case address + case derivation + case derivationPath + case publicKey + case extendedPublicKey + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(coin.rawValue, forKey: .coin) + try container.encode(address, forKey: .address) + try container.encode(derivation.rawValue, forKey: .derivation) + try container.encode(derivationPath, forKey: .derivationPath) + try container.encode(publicKey, forKey: .publicKey) + try container.encode(extendedPublicKey, forKey: .extendedPublicKey) + } + + public convenience init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let rawCoin = try container.decode(UInt32.self, forKey: .coin) + let address = try container.decode(String.self, forKey: .address) + let rawDerivation = try container.decode(UInt32.self, forKey: .derivation) + let derivationPath = try container.decode(String.self, forKey: .derivationPath) + let publicKey = try container.decode(String.self, forKey: .publicKey) + let extendedPublicKey = try container.decode(String.self, forKey: .extendedPublicKey) + + self.init( + address: address, + coin: CoinType(rawValue: rawCoin)!, + derivation: Derivation(rawValue: rawDerivation)!, + derivationPath: derivationPath, + publicKey: publicKey, + extendedPublicKey: extendedPublicKey + ) + } +} diff --git a/swift/Sources/Extensions/AddressProtocol.swift b/swift/Sources/Extensions/AddressProtocol.swift index 273e71cea0f..1660d724252 100644 --- a/swift/Sources/Extensions/AddressProtocol.swift +++ b/swift/Sources/Extensions/AddressProtocol.swift @@ -6,6 +6,7 @@ import Foundation +/// Generic Address protocol for AnyAddress / SegwitAddress / SolanaAddress public protocol Address: CustomStringConvertible {} extension AnyAddress: Equatable {} diff --git a/swift/Sources/Extensions/DerivationPath+Extension.swift b/swift/Sources/Extensions/DerivationPath+Extension.swift new file mode 100644 index 00000000000..09678360e85 --- /dev/null +++ b/swift/Sources/Extensions/DerivationPath+Extension.swift @@ -0,0 +1,119 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import Foundation + +extension DerivationPath: Equatable, Hashable, CustomStringConvertible { + + public typealias Index = DerivationPathIndex + + public static func == (lhs: DerivationPath, rhs: DerivationPath) -> Bool { + return lhs.description == rhs.description + } + + public var coinType: UInt32 { + coin + } + + public var indices: [Index] { + var result = [Index]() + for i in 0.. DerivationPathIndex? { + return self.indexAt(index: UInt32(index)) + } + + public func hash(into hasher: inout Hasher) { + let count = indicesCount() + for i in 0.. Bool { + return lhs.value == rhs.value && lhs.hardened == rhs.hardened + } + + public convenience init(_ value: UInt32, hardened: Bool) { + self.init(value: value, hardened: hardened) + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(value) + hasher.combine(hardened) + } +} + +extension DerivationPathIndex: Codable { + private enum CodingKeys: String, CodingKey { + case value + case hardened + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(value, forKey: .value) + try container.encode(hardened, forKey: .hardened) + } + + public convenience init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let value = try container.decode(UInt32.self, forKey: .value) + let hardened = try container.decode(Bool.self, forKey: .hardened) + self.init(value: value, hardened: hardened) + } +} diff --git a/swift/Sources/TWCardano.swift b/swift/Sources/TWCardano.swift new file mode 100644 index 00000000000..1eb456be7ce --- /dev/null +++ b/swift/Sources/TWCardano.swift @@ -0,0 +1,15 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import Foundation + +public func CardanoMinAdaAmount(tokenBundle: Data) -> UInt64 { + let tokenBundleData = TWDataCreateWithNSData(tokenBundle) + defer { + TWDataDelete(tokenBundleData) + } + return TWCardanoMinAdaAmount(tokenBundleData) +} diff --git a/swift/Sources/Types/UniversalAssetID.swift b/swift/Sources/Types/UniversalAssetID.swift index acd4e1e0082..9cd4f59945d 100644 --- a/swift/Sources/Types/UniversalAssetID.swift +++ b/swift/Sources/Types/UniversalAssetID.swift @@ -23,7 +23,7 @@ public struct UniversalAssetID: CustomStringConvertible, Equatable, Hashable { return [prefix, suffix].joined(separator: "_") } - public init(coin: CoinType, token: String) { + public init(coin: CoinType, token: String = "") { self.coin = coin self.token = token } diff --git a/swift/Sources/Wallet.swift b/swift/Sources/Wallet.swift index 036d2a22d7e..04749b5d564 100644 --- a/swift/Sources/Wallet.swift +++ b/swift/Sources/Wallet.swift @@ -43,6 +43,22 @@ public final class Wallet: Hashable, Equatable { return account } + /// Returns the account for a specific coin and derivation. + /// + /// - Parameters: + /// - password: wallet encryption password + /// - coin: coin type + /// - derivation: derivation, a specific or default + /// - Returns: the account + /// - Throws: `KeyStore.Error.invalidPassword` if the password is incorrect. + public func getAccount(password: String, coin: CoinType, derivation: Derivation) throws -> Account { + let wallet = key.wallet(password: Data(password.utf8)) + guard let account = key.accountForCoinDerivation(coin: coin, derivation: derivation, wallet: wallet) else { + throw KeyStore.Error.invalidPassword + } + return account + } + /// Returns the accounts for a specific coins. /// /// - Parameters: diff --git a/swift/Tests/Addresses/JunoAddressTests.swift b/swift/Tests/Addresses/JunoAddressTests.swift new file mode 100644 index 00000000000..02935c98c3a --- /dev/null +++ b/swift/Tests/Addresses/JunoAddressTests.swift @@ -0,0 +1,25 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import WalletCore +import XCTest + +class JunoAddressTests: XCTestCase { + func testAnyAddressValidation() { + let addr = AnyAddress(string: "juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94", coin: .cosmos, hrp: "juno")!; + XCTAssertTrue(AnyAddress.isValidBech32(string: addr.description, coin: .cosmos, hrp: "juno")); + XCTAssertFalse(AnyAddress.isValidBech32(string: addr.description, coin: .bitcoin, hrp: "juno")); + XCTAssertFalse(AnyAddress.isValid(string: addr.description, coin: .bitcoin)); + XCTAssertFalse(AnyAddress.isValid(string: addr.description, coin: .cosmos)); + } + + func testAnyAddressFromPubkey() { + let data = Data(hexString: "02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc")!; + let pubkey = PublicKey(data: data, type: .secp256k1)!; + let anyAddr = AnyAddress(publicKey: pubkey, coin: .cosmos, hrp: "juno"); + XCTAssertEqual(anyAddr.description, "juno1cj2vfjec3c3luf9fx9vddnglhh9gawmncn4k5n"); + } +} diff --git a/swift/Tests/Base32Tests.swift b/swift/Tests/Base32Tests.swift new file mode 100644 index 00000000000..41be00de149 --- /dev/null +++ b/swift/Tests/Base32Tests.swift @@ -0,0 +1,38 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class Base32Tests: XCTestCase { + func testEncode() { + let encoded = Base32.encode(data: Data.init(bytes: "HelloWorld", count: 10)); + XCTAssertEqual(encoded, "JBSWY3DPK5XXE3DE"); + } + + func testEncodeWithAlphabet() { + let encoded = Base32.encodeWithAlphabet(data: Data.init(bytes: "7uoq6tp427uzv7fztkbsnn64iwotfrristwpryy", count: 39), alphabet: "abcdefghijklmnopqrstuvwxyz234567"); + XCTAssertEqual(encoded, "g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i"); + } + + func testDecode() { + guard let decoded = Base32.decode(string: "JBSWY3DPK5XXE3DE") else { + return XCTFail(); + } + let toCompare = String(data: decoded, encoding:.utf8); + + XCTAssertEqual(toCompare, "HelloWorld"); + } + + func testDecodeWithAlphabet() { + guard let decoded = Base32.decodeWithAlphabet(string: "g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i", alphabet:"abcdefghijklmnopqrstuvwxyz234567") else { + return XCTFail(); + } + let toCompare = String(data: decoded, encoding:.utf8); + + XCTAssertEqual(toCompare, "7uoq6tp427uzv7fztkbsnn64iwotfrristwpryy"); + } +} diff --git a/swift/Tests/Base64Tests.swift b/swift/Tests/Base64Tests.swift new file mode 100644 index 00000000000..b4fdc17c1cf --- /dev/null +++ b/swift/Tests/Base64Tests.swift @@ -0,0 +1,38 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class Base64Tests: XCTestCase { + func testEncode() { + let encoded = Base64.encode(data: Data.init(bytes: "HelloWorld", count: 10)); + XCTAssertEqual(encoded, "SGVsbG9Xb3JsZA=="); + } + + func testDecode() { + guard let decoded = Base64.decode(string: "SGVsbG9Xb3JsZA==") else { + return XCTFail(); + } + let toCompare = String(data: decoded, encoding:.utf8); + + XCTAssertEqual(toCompare, "HelloWorld"); + } + + func testUrlEncode() { + let encoded = Base64.encodeUrl(data: Data.init(bytes: "+\\?ab", count: 5)); + XCTAssertEqual(encoded, "K1w_YWI="); + } + + func testUrlDecode() { + guard let decoded = Base64.decodeUrl(string: "K1w_YWI=") else { + return XCTFail(); + } + let toCompare = String(data: decoded, encoding:.utf8); + + XCTAssertEqual(toCompare, "+\\?ab"); + } +} diff --git a/swift/Tests/Blockchains/AlgorandTests.swift b/swift/Tests/Blockchains/AlgorandTests.swift index e20fa3441ea..0df5b148700 100644 --- a/swift/Tests/Blockchains/AlgorandTests.swift +++ b/swift/Tests/Blockchains/AlgorandTests.swift @@ -20,6 +20,7 @@ class AlgorandTests: XCTestCase { } func testSign() { + let round: UInt64 = 1937767 let transaction = AlgorandTransfer.with { $0.toAddress = "CRLADAHJZEW2GFY2UPEHENLOGCUOU74WYSTUXQLVLJUJFHEUZOHYZNWYR4" $0.amount = 1000000000000 @@ -29,8 +30,8 @@ class AlgorandTests: XCTestCase { $0.genesisHash = Data(base64Encoded: "wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=")! $0.note = "hello".data(using: .utf8)! $0.privateKey = Data(hexString: "d5b43d706ef0cb641081d45a2ec213b5d8281f439f2425d1af54e2afdaabf55b")! - $0.firstRound = 1937767 - $0.lastRound = 1938767 + $0.firstRound = round + $0.lastRound = round + 1000 $0.fee = 263000 $0.transfer = transaction } @@ -39,6 +40,34 @@ class AlgorandTests: XCTestCase { XCTAssertEqual(output.encoded.hexString, "82a3736967c440baa00062adcdcb5875e4435cdc6885d26bfe5308ab17983c0fda790b7103051fcb111554e5badfc0ac7edf7e1223a434342a9eeed5cdb047690827325051560ba374786e8aa3616d74cf000000e8d4a51000a3666565ce00040358a26676ce001d9167a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c76ce001d954fa46e6f7465c40568656c6c6fa3726376c42014560180e9c92da3171aa3c872356e30a8ea7f96c4a74bc1755a68929c94cb8fa3736e64c42061bf060efc02e2887dfffc8ed85268c8c091c013eedf315bc50794d02a8791ada474797065a3706179") } + func testSignVoteTx() { + // manual vote tx is 0 amount + note + + let round: UInt64 = 18426344 + let transaction = AlgorandTransfer.with { + $0.toAddress = "57QZ4S7YHTWPRAM3DQ2MLNSVLAQB7DTK4D7SUNRIEFMRGOU7DMYFGF55BY" + $0.amount = 0 + } + let note = """ + af/gov1:j{"com":1000000} + """ + let input = AlgorandSigningInput.with { + $0.genesisID = "mainnet-v1.0" + $0.genesisHash = Data(base64Encoded: "wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=")! + $0.note = note.data(using: .utf8)! + $0.privateKey = Data(hexString: "d5b43d706ef0cb641081d45a2ec213b5d8281f439f2425d1af54e2afdaabf55b")! + $0.firstRound = round + $0.lastRound = round + 1000 + $0.fee = 1000 + $0.transfer = transaction + } + let output: AlgorandSigningOutput = AnySigner.sign(input: input, coin: .algorand) + + // real key is 1p, posted by: echo '' | xxd -r -p | curl -X POST --data-binary @- + // https://algoexplorer.io/tx/OHYNQA7X5LHUKWEM6ZMUT6RCVOZUELXSYELV7CHQFQBDI3XEM4NQ + XCTAssertEqual(output.encoded.hexString, "82a3736967c440aad1e2d80fbdfc4dc5def13e1dc9f39c9261df9d5c6664478b951d28c3a688c4a261894d8c9bd686f5b2355f2edd54fd611eeaba8a871cc05af728a18598ed04a374786e89a3666565cd03e8a26676ce011929e8a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c76ce01192dd0a46e6f7465c41861662f676f76313a6a7b22636f6d223a313030303030307da3726376c420efe19e4bf83cecf8819b1c34c5b65558201f8e6ae0ff2a36282159133a9f1b30a3736e64c42061bf060efc02e2887dfffc8ed85268c8c091c013eedf315bc50794d02a8791ada474797065a3706179") + } + func testSignJSON() { let json = """ {"genesisId":"mainnet-v1.0","genesisHash":"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=","note":"aGVsbG8=","firstRound":"1937767","lastRound":"1938767","fee":"263000","transfer":{"toAddress":"CRLADAHJZEW2GFY2UPEHENLOGCUOU74WYSTUXQLVLJUJFHEUZOHYZNWYR4","amount":"1000000000000"}} diff --git a/swift/Tests/Blockchains/AptosTests.swift b/swift/Tests/Blockchains/AptosTests.swift new file mode 100644 index 00000000000..3c54581dafe --- /dev/null +++ b/swift/Tests/Blockchains/AptosTests.swift @@ -0,0 +1,48 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import WalletCore +import XCTest + +class AptosTests: XCTestCase { + func testAddress() { + let anyAddress = AnyAddress(string: "0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108", coin: .aptos) + + XCTAssertEqual(anyAddress?.description, "0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108") + XCTAssertEqual(anyAddress?.coin, .aptos) + + let invalid = "MQqpqMQgCBuiPkoXfgZZsJvuzCeI1zc00z6vHJj4" + XCTAssertNil(Data(hexString: invalid)) + XCTAssertNil(AnyAddress(string: invalid, coin: .aptos)) + XCTAssertFalse(AnyAddress.isValid(string: invalid, coin: .aptos)) + } + + func testSign() { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet + let privateKeyData = Data(hexString: "5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")! + let transferMsg = AptosTransferMessage.with { + $0.to = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" + $0.amount = 1000 + } + let input = AptosSigningInput.with { + $0.chainID = 33 + $0.sender = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30" + $0.expirationTimestampSecs = 3664390082 + $0.gasUnitPrice = 100 + $0.maxGasAmount = 3296766 + $0.sequenceNumber = 99 + $0.transfer = transferMsg + $0.privateKey = privateKeyData + } + let output: AptosSigningOutput = AnySigner.sign(input: input, coin: .aptos) + let expectedRawTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021" + let expectedSignature = "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01" + let expectedSignedTx = "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01" + XCTAssertEqual(output.rawTxn.hexString, expectedRawTx) + XCTAssertEqual(output.authenticator.signature.hexString, expectedSignature) + XCTAssertEqual(output.encoded.hexString, expectedSignedTx) + } +} diff --git a/swift/Tests/Blockchains/AvalancheTests.swift b/swift/Tests/Blockchains/AvalancheTests.swift index 5f6d6bb595c..6e7b1a81c1d 100644 --- a/swift/Tests/Blockchains/AvalancheTests.swift +++ b/swift/Tests/Blockchains/AvalancheTests.swift @@ -18,4 +18,20 @@ class AvalancheTests: XCTestCase { XCTAssertEqual(address.description, addressETH.description) XCTAssertEqual(address.data.hexString, addressETH.data.hexString) } + + func testXPub() { + let wallet = HDWallet(mnemonic: "liquid spider narrow follow black west cabbage intact stadium resource gentle raccoon", passphrase: "")! + + let xpub1 = wallet.getExtendedPublicKey(purpose: .bip44, coin: .ethereum, version: .xpub) + let xpub2 = wallet.getExtendedPublicKey(purpose: .bip44, coin: .avalancheCChain, version: .xpub) + + XCTAssertEqual(xpub1, xpub2) + XCTAssertEqual(xpub2, "xpub6Bmo35QfCNvffj8tZsTVRvFxA5ULv2aHDV8dDCa7q6SzkMLJffxWRNE5vSJ4hANoKpmSp3p7Nbcp1vEz5F785HV4Aq2A6jWwHoyWp3EFgYp") + + let xpri1 = wallet.getExtendedPrivateKey(purpose: .bip44, coin: .ethereum, version: .xprv) + let xpri2 = wallet.getExtendedPrivateKey(purpose: .bip44, coin: .smartChain, version: .xprv) + + XCTAssertEqual(xpri1, xpri2) + XCTAssertEqual(xpri2, "xprv9xnSdZsmN1NNTF4RTqvV4nKDc3drWZrRrGD2QpAWGkv1sZ1A88eFsZuc59vKRr4mxJELH7C18ymaHENodYzEbeLq1JmPUAy3CmpA2inVCwo") + } } diff --git a/swift/Tests/Blockchains/BandChainTests.swift b/swift/Tests/Blockchains/BandChainTests.swift index 78e62e3f314..03200907d37 100644 --- a/swift/Tests/Blockchains/BandChainTests.swift +++ b/swift/Tests/Blockchains/BandChainTests.swift @@ -27,7 +27,7 @@ class BandChainTests: XCTestCase { $0.fromAddress = fromAddress $0.toAddress = "band1pnndgfwsrff86263xzpc5cd3t6yfvgjyqc87jh" $0.amounts = [CosmosAmount.with { - $0.amount = 1000000 + $0.amount = "1000000" $0.denom = "uband" }] } @@ -39,7 +39,7 @@ class BandChainTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "uband" }] } @@ -107,7 +107,7 @@ class BandChainTests: XCTestCase { $0.delegatorAddress = "band13nzgys7y9c693u0pq089an4pq6q87hf9kqgkrz" $0.validatorAddress = "bandvaloper13fwr8rmugu2mfuurfx4sfmyv05haw9sujnqzd8" $0.amount = CosmosAmount.with { - $0.amount = 1000000 + $0.amount = "1000000" $0.denom = "uband" } } @@ -119,7 +119,7 @@ class BandChainTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "uband" }] } @@ -191,7 +191,7 @@ class BandChainTests: XCTestCase { let fee = CosmosFee.with { $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "uband" }] $0.gas = 200000 @@ -253,7 +253,7 @@ class BandChainTests: XCTestCase { $0.delegatorAddress = "band13tug898kgtwprg7fevzzqgh45draa3cyffw3kp" $0.validatorAddress = "bandvaloper1jp633fleakzv4uxxvl707j9u2jj6j5x2rg7glv" $0.amount = CosmosAmount.with { - $0.amount = 500000 + $0.amount = "500000" $0.denom = "uband" } } @@ -265,7 +265,7 @@ class BandChainTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "uband" }] } @@ -331,7 +331,7 @@ class BandChainTests: XCTestCase { $0.validatorSrcAddress = "bandvaloper1hln9scsl9yqup8nxyum06rmggql5m5zqwxmt3p" $0.validatorDstAddress = "bandvaloper1hydxm5h8v6tty2x623az65x3r39tl3paxyxtr0" $0.amount = CosmosAmount.with { - $0.amount = 500000 + $0.amount = "500000" $0.denom = "uband" } } @@ -343,7 +343,7 @@ class BandChainTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "uband" }] } diff --git a/swift/Tests/Blockchains/BitcoinTests.swift b/swift/Tests/Blockchains/BitcoinTests.swift index e4b7255fc76..4ace92e08e5 100644 --- a/swift/Tests/Blockchains/BitcoinTests.swift +++ b/swift/Tests/Blockchains/BitcoinTests.swift @@ -174,4 +174,13 @@ class BitcoinTransactionSignerTests: XCTestCase { XCTAssertEqual(output.error, .ok) XCTAssertEqual(output.encoded.hexString, "01000000026c90312e53a3411347a197bfd637c2583d617dd2317262a70e1b5245d2f1e36a000000008a47304402201a631068ea5ddea19467ef7c932a0f3b04f366ca2beaf70e18958e47456124980220614816c449e39cf6acc6625e1cf3100db1db7c0b755bdbb6804d4fa3c4b735d10141041b3937fac1f14074447cde9d3a324ed292d2865ed0d7a7da26cb43558ce4db4ef33c47e820e53031ae16bb0c39205def059a5ca8e1d617650eabc72c5206a81dffffffff13bf27945c669cf3c1d70cf3048f4ab14f1ab6acf06d10d425e8288217a81efd000000008a473044022051d381d8f48a9a4866ca4109f12647922514604a4733e8da8aac046e19275f700220797c3ebf20df7d2a9fed283f9d0ad14cbd656cafb5ec70a2b1c85646ea7485190141041b3937fac1f14074447cde9d3a324ed292d2865ed0d7a7da26cb43558ce4db4ef33c47e820e53031ae16bb0c39205def059a5ca8e1d617650eabc72c5206a81dffffffff0194590000000000001976a914a0c0a50f986924e65ae9bd18eafae448f83117ed88ac00000000") } + + func testBitcoinMessageSigner() { + let verifyResult = BitcoinMessageSigner.verifyMessage( + address: "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", + message: "test signature", + signature: "H+3L5IbSVcejp4S2VwLXCxLEMQAWDvKbE8lQyq0ocdvyM1aoEudkzN/S/qLI3vnNOFY6V13BXWSFrPr3OjGa5Dk=" + ) + XCTAssertTrue(verifyResult) + } } diff --git a/swift/Tests/Blockchains/BluzelleTests.swift b/swift/Tests/Blockchains/BluzelleTests.swift index 02891cc58f0..fde2d9cc4a4 100644 --- a/swift/Tests/Blockchains/BluzelleTests.swift +++ b/swift/Tests/Blockchains/BluzelleTests.swift @@ -94,7 +94,7 @@ class BluzelleSignerTests: XCTestCase { $0.fromAddress = myAddress $0.toAddress = "bluzelle1xccvees6ev4wm2r49rc6ptulsdxa8x8jfpmund" $0.amounts = [CosmosAmount.with { - $0.amount = 1 + $0.amount = "1" $0.denom = "ubnt" }] } @@ -106,7 +106,7 @@ class BluzelleSignerTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 500000 $0.amounts = [CosmosAmount.with { - $0.amount = 1000 + $0.amount = "1000" $0.denom = "ubnt" }] } diff --git a/swift/Tests/Blockchains/CardanoTests.swift b/swift/Tests/Blockchains/CardanoTests.swift index 8e7d5ed1b86..32561ab157d 100644 --- a/swift/Tests/Blockchains/CardanoTests.swift +++ b/swift/Tests/Blockchains/CardanoTests.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,12 +9,216 @@ import XCTest class CardanoTests: XCTestCase { func testAddress() { - let key = PrivateKey(data: Data(hexString: "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4")!)! - let pubkey = key.getPublicKeyEd25519Extended() + let key = PrivateKey(data: Data(hexString: "e8c8c5b2df13f3abed4e6b1609c808e08ff959d7e6fc3d849e3f2880550b574437aa559095324d78459b9bb2da069da32337e1cc5da78f48e1bd084670107f3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fae0d152bb611cb9ff34e945e4ff627e6fba81da687a601a879759cd76530b5744424db69a75edd4780a5fbc05d1a3c84ac4166ff8e424808481dd8e77627ce5f5bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276")!)! + let pubkey = key.getPublicKeyEd25519Cardano() let address = AnyAddress(publicKey: pubkey, coin: .cardano) - let addressFromString = AnyAddress(string: "addr1s3tl64970vuthz2j0qkz7kd2ya5j3fxuhdnv333vu38e6c37e4dq80ek4raf7hs3adag2tzpuxz7895a2x8xde5f8jqa8lrjyuqfj5k50pm668", coin: .cardano)! + let addressFromString = AnyAddress(string: "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq", coin: .cardano)! - XCTAssertEqual(pubkey.data.hexString, "57fd54be7b38bb8952782c2f59aa276928a4dcbb66c8c62ce44f9d623ecd5a03bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4") + XCTAssertEqual(pubkey.data.hexString, "fafa7eb4146220db67156a03a5f7a79c666df83eb31abbfbe77c85e06d40da3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26faf4b8d5201961e68f2e177ba594101f513ee70fe70a41324e8ea8eb787ffda6f4bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276") XCTAssertEqual(address.description, addressFromString.description) } + + func testDeriveAddressWallet() { + let wallet = HDWallet(mnemonic: "cost dash dress stove morning robust group affair stomach vacant route volume yellow salute laugh", passphrase: "")! + let privateKey = wallet.getKeyForCoin(coin: .cardano) + XCTAssertEqual(privateKey.data.hexString, "e8c8c5b2df13f3abed4e6b1609c808e08ff959d7e6fc3d849e3f2880550b574437aa559095324d78459b9bb2da069da32337e1cc5da78f48e1bd084670107f3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fae0d152bb611cb9ff34e945e4ff627e6fba81da687a601a879759cd76530b5744424db69a75edd4780a5fbc05d1a3c84ac4166ff8e424808481dd8e77627ce5f5bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276") + let address = CoinType.cardano.deriveAddress(privateKey: privateKey) + XCTAssertEqual(address, "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq") + } + + func testSignTransfer1() { + var input = CardanoSigningInput.with { + $0.transferMessage.toAddress = "addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5" + $0.transferMessage.changeAddress = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23" + $0.transferMessage.amount = 7000000 + $0.ttl = 53333333 + } + input.privateKey.append(Data(hexString: "089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e")!) + + let utxo1 = CardanoTxInput.with { + $0.outPoint.txHash = Data(hexString: "f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767")! + $0.outPoint.outputIndex = 1 + $0.address = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23" + $0.amount = 1500000 + } + input.utxos.append(utxo1) + + let utxo2 = CardanoTxInput.with { + $0.outPoint.txHash = Data(hexString: "554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0")! + $0.outPoint.outputIndex = 0 + $0.address = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23" + $0.amount = 6500000 + } + input.utxos.append(utxo2) + + // Sign + let output: CardanoSigningOutput = AnySigner.sign(input: input, coin: .cardano) + XCTAssertEqual(output.error, TW_Common_Proto_SigningError.ok) + + let encoded = output.encoded + XCTAssertEqual(encoded.hexString, + "83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a006acfc082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a000ca96c021a000298d4031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058407cf591599852b5f5e007fdc241062405c47e519266c0d884b0767c1d4f5eacce00db035998e53ed10ca4ba5ce4aac8693798089717ce6cf4415f345cc764200ef6") + + let txid = output.txID + XCTAssertEqual(txid.hexString, "9b5b15e133cd73ccaa85307d2986aebc846505118a2eb4e6111e6b4b67d1f389") + } + + func testSignTransferToken1() throws { + let toToken = CardanoTokenAmount.with { + $0.policyID = "9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + $0.assetName = "SUNDAE" + $0.amount = Data(hexString: "01312d00")! // 20000000 + } + var toTokenBundle = CardanoTokenBundle(); + toTokenBundle.token.append(toToken) + + // check min ADA amount, set it + let inputTokenAmountSerialized = try toTokenBundle.serializedData() + let minAmount = CardanoMinAdaAmount(tokenBundle: inputTokenAmountSerialized) + XCTAssertEqual(minAmount, 1444443) + + var input = CardanoSigningInput.with { + $0.transferMessage.toAddress = "addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5" + $0.transferMessage.changeAddress = "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq" + $0.transferMessage.amount = minAmount + $0.transferMessage.useMaxAmount = false + $0.transferMessage.tokenAmount = toTokenBundle + $0.ttl = 53333333 + } + input.privateKey.append(Data(hexString: "089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e")!) + + var utxo1 = CardanoTxInput.with { + $0.outPoint.txHash = Data(hexString: "f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767")! + $0.outPoint.outputIndex = 1 + $0.address = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23" + $0.amount = 8051373 + } + let token3 = CardanoTokenAmount.with { + $0.policyID = "9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + $0.assetName = "CUBY" + $0.amount = Data(hexString: "2dc6c0")! // 3000000 + } + utxo1.tokenAmount.append(token3) + input.utxos.append(utxo1) + + var utxo2 = CardanoTxInput.with { + $0.outPoint.txHash = Data(hexString: "f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767")! + $0.outPoint.outputIndex = 2 + $0.address = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23" + $0.amount = 2000000 + } + let token1 = CardanoTokenAmount.with { + $0.policyID = "9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + $0.assetName = "SUNDAE" + $0.amount = Data(hexString: "04d3e8d9")! // 80996569 + } + utxo2.tokenAmount.append(token1) + let token2 = CardanoTokenAmount.with { + $0.policyID = "9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77" + $0.assetName = "CUBY" + $0.amount = Data(hexString: "1e8480")! // 2000000 + } + utxo2.tokenAmount.append(token2) + input.utxos.append(utxo2) + + // Sign + let output: CardanoSigningOutput = AnySigner.sign(input: input, coin: .cardano) + XCTAssertEqual(output.error, TW_Common_Proto_SigningError.ok) + + let encoded = output.encoded + XCTAssertEqual(encoded.hexString, + "83a40082825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76702018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd5821a00160a5ba1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a01312d00825839018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468821a0080aac9a1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a244435542591a004c4b404653554e4441451a03a2bbd9021a0002aa09031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840d90dcfbd190cbe59c42094e59eeb49b3de9d80a85b786cc311f932c5c9302d1c8c6c577b22aa70ff7955c139c700ea918f8cb425c3ba43a27980e1d238e4e908f6") + + let txid = output.txID + XCTAssertEqual(txid.hexString, "201c537693b005b64a0f0528e366ec67a84be0119ed4363b547f141f2a7770c2") + } + + func testGetStakingAddress() throws { + let stakingAddress = Cardano.getStakingAddress(baseAddress: "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23") + XCTAssertEqual(stakingAddress, "stake1u80jysjtdzqt88jt4jx93h5lumfr67d273r4vwyasfa2pxcwxllmx") + } + + func testSignStakingRegisterAndDelegate() throws { + let ownAddress = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23" + let stakingAddress = Cardano.getStakingAddress(baseAddress: ownAddress) + let poolIdNufi = "7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6" + + var input = CardanoSigningInput.with { + $0.transferMessage.toAddress = ownAddress + $0.transferMessage.changeAddress = ownAddress + $0.transferMessage.amount = 4000000 // not relevant as we use MaxAmount + $0.transferMessage.useMaxAmount = true + $0.ttl = 69885081 + // Register staking key, 2 ADA desposit + $0.registerStakingKey.stakingAddress = stakingAddress + $0.registerStakingKey.depositAmount = 2000000 + // Delegate + $0.delegate.stakingAddress = stakingAddress + $0.delegate.poolID = Data(hexString: poolIdNufi)! + $0.delegate.depositAmount = 0 + } + input.privateKey.append(Data(hexString: "089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e")!) + + let utxo1 = CardanoTxInput.with { + $0.outPoint.txHash = Data(hexString: "9b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e")! + $0.outPoint.outputIndex = 0 + $0.address = ownAddress + $0.amount = 4000000 + } + let utxo2 = CardanoTxInput.with { + $0.outPoint.txHash = Data(hexString: "9b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e")! + $0.outPoint.outputIndex = 1 + $0.address = ownAddress + $0.amount = 26651312 + } + input.utxos.append(utxo1) + input.utxos.append(utxo2) + + // Sign + let output: CardanoSigningOutput = AnySigner.sign(input: input, coin: .cardano) + XCTAssertEqual(output.error, TW_Common_Proto_SigningError.ok) + + let encoded = output.encoded + XCTAssertEqual(encoded.hexString, + "83a500828258209b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e008258209b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e01018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a01b27ef5021a0002b03b031a042a5c99048282008200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b83028200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b581c7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6a100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840677c901704be027d9a1734e8aa06f0700009476fa252baaae0de280331746a320a61456d842d948ea5c0e204fc36f3bd04c88ca7ee3d657d5a38014243c37c07825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e0693258401fa21bdc62b85ca217bf08cbacdeba2fadaf33dc09ee3af9cc25b40f24822a1a42cfbc03585cc31a370ef75aaec4d25db6edcf329e40a4e725ec8718c94f220af6") + + let txid = output.txID + XCTAssertEqual(txid.hexString, "96a781fd6481b6a7fd3926da110265e8c44b53947b81daa84da5b148825d02aa") + } + + func testSignStakingWithdraw() throws { + let ownAddress = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23" + let stakingAddress = Cardano.getStakingAddress(baseAddress: "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23") + + var input = CardanoSigningInput.with { + $0.transferMessage.toAddress = ownAddress + $0.transferMessage.changeAddress = ownAddress + $0.transferMessage.amount = 6000000 // not relevant as we use MaxAmount + $0.transferMessage.useMaxAmount = true + $0.ttl = 71678326 + // Withdraw available amount + $0.withdraw.stakingAddress = stakingAddress + $0.withdraw.withdrawAmount = 3468 + } + input.privateKey.append(Data(hexString: "089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e")!) + + let utxo1 = CardanoTxInput.with { + $0.outPoint.txHash = Data(hexString: "7dfd2c579794314b1f84efc9db932a098e440ccefb874945591f1d4e85a9152a")! + $0.outPoint.outputIndex = 0 + $0.address = ownAddress + $0.amount = 6305913 + } + input.utxos.append(utxo1) + + // Sign + let output: CardanoSigningOutput = AnySigner.sign(input: input, coin: .cardano) + XCTAssertEqual(output.error, TW_Common_Proto_SigningError.ok) + + let encoded = output.encoded + XCTAssertEqual(encoded.hexString, + "83a500818258207dfd2c579794314b1f84efc9db932a098e440ccefb874945591f1d4e85a9152a00018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a005da6ff021a00029f06031a0445b97605a1581de1df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b190d8ca100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058401ebaca2876fd17122404912a2558a98109cdf0f990a938d2917fa2c3b8c4e55e18a2cbabfa82fff03fa0d7ab8b88ca01ed18e42af3bfc4cda7f423a3aa30c00b825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e069325840777f04fa8f083fe562aecf78898aaaaac36e2cc6ca962f6ffb01e84a421cae1860496db79b2c5fb2879524c3d5121060b9ea1e693336230c6e5338e14c4c3303f6") + + let txid = output.txID + XCTAssertEqual(txid.hexString, "6dcf3956232953fc25b8355fb1ded1e912b5802090fd21434d789087d6329683") + } } diff --git a/swift/Tests/Blockchains/CosmosTests.swift b/swift/Tests/Blockchains/CosmosTests.swift index 21a7ab0dd4c..3d940fe09d5 100644 --- a/swift/Tests/Blockchains/CosmosTests.swift +++ b/swift/Tests/Blockchains/CosmosTests.swift @@ -34,7 +34,7 @@ class CosmosSignerTests: XCTestCase { $0.fromAddress = fromAddress.description $0.toAddress = "cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573" $0.amounts = [CosmosAmount.with { - $0.amount = 1 + $0.amount = "1" $0.denom = "muon" }] } @@ -46,12 +46,13 @@ class CosmosSignerTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 200 + $0.amount = "200" $0.denom = "muon" }] } let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; $0.accountNumber = 1037 $0.chainID = "gaia-13003" $0.memo = "" @@ -63,50 +64,80 @@ class CosmosSignerTests: XCTestCase { let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .cosmos) - let expectedJSON: String = -""" -{ - "mode": "block", - "tx": { - "fee": { - "amount": [ - { - "amount": "200", - "denom": "muon" - } - ], - "gas": "200000" - }, - "memo": "", - "msg": [ - { - "type": "cosmos-sdk/MsgSend", - "value": { - "amount": [ - { - "amount": "1", - "denom": "muon" + XCTAssertJSONEqual(output.serialized, "{\"tx_bytes\": \"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATESZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAgSEQoLCgRtdW9uEgMyMDAQwJoMGkD54fQAFlekIAnE62hZYl0uQelh/HLv0oQpCciY5Dn8H1SZFuTsrGdu41PH1Uxa4woptCELi/8Ov9yzdeEFAC9H\", \"mode\": \"BROADCAST_MODE_BLOCK\"}") + XCTAssertEqual(output.error, "") + } + + func testAuthCompounding() { + let authMessage = CosmosMessage.AuthGrant.with { + $0.granter = "cosmos13k0q0l7lg2kr32kvt7ly236ppldy8v9dzwh3gd" + $0.grantee = "cosmos1fs7lu28hx5m9akm7rp0c2422cn8r2f7gurujhf" + $0.grantStake = CosmosMessage.StakeAuthorization.with { + $0.allowList.address = ["cosmosvaloper1gjtvly9lel6zskvwtvlg5vhwpu9c9waw7sxzwx"] + $0.authorizationType = CosmosMessage.AuthorizationType.delegate } - ], - "from_address": "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", - "to_address": "cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573" + $0.expiration = 1692309600 } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F" - }, - "signature": "/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg==" - } - ] - } -} -""" + let message = CosmosMessage.with { + $0.authGrant = authMessage + } + let fee = CosmosFee.with { + $0.gas = 96681 + $0.amounts = [CosmosAmount.with { + $0.amount = "2418" + $0.denom = "uatom" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 1290826 + $0.chainID = "cosmoshub-4" + $0.memo = "" + $0.sequence = 5 + $0.messages = [message] + $0.fee = fee + $0.privateKey = PrivateKey(data: Data(hexString: "c7764249cdf77f8f1d840fa8af431579e5e41cf1af937e1e23afa22f3f4f0ccc")!)!.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .cosmos) - XCTAssertJSONEqual(expectedJSON, output.json) + XCTAssertJSONEqual(output.serialized, "{\"tx_bytes\": \"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjoSNgo0Y29zbW9zdmFsb3BlcjFnanR2bHk5bGVsNnpza3Z3dHZsZzV2aHdwdTljOXdhdzdzeHp3eCABEgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQIFyfuijGKf87Hz61ZqxasfLI1PZnNge4RDq/tRyB/tZI6p80iGRqHecoV6+84EQkc9GTlNRQOSlApRCsivT9XI=\", \"mode\": \"BROADCAST_MODE_BLOCK\"}") + XCTAssertEqual(output.error, "") + } + + func testRevokeAuthCompounding() { + let revokeAuthMessage = CosmosMessage.AuthRevoke.with { + $0.granter = "cosmos13k0q0l7lg2kr32kvt7ly236ppldy8v9dzwh3gd" + $0.grantee = "cosmos1fs7lu28hx5m9akm7rp0c2422cn8r2f7gurujhf" + $0.msgTypeURL = "/cosmos.staking.v1beta1.MsgDelegate" + } + let message = CosmosMessage.with { + $0.authRevoke = revokeAuthMessage + } + let fee = CosmosFee.with { + $0.gas = 87735 + $0.amounts = [CosmosAmount.with { + $0.amount = "2194" + $0.denom = "uatom" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 1290826 + $0.chainID = "cosmoshub-4" + $0.memo = "" + $0.sequence = 4 + $0.messages = [message] + $0.fee = fee + $0.privateKey = PrivateKey(data: Data(hexString: "c7764249cdf77f8f1d840fa8af431579e5e41cf1af937e1e23afa22f3f4f0ccc")!)!.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .cosmos) + + XCTAssertJSONEqual(output.serialized, "{\"tx_bytes\": \"CqoBCqcBCh8vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnUmV2b2tlEoMBCi1jb3Ntb3MxM2swcTBsN2xnMmtyMzJrdnQ3bHkyMzZwcGxkeTh2OWR6d2gzZ2QSLWNvc21vczFmczdsdTI4aHg1bTlha203cnAwYzI0MjJjbjhyMmY3Z3VydWpoZhojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAQSEwoNCgV1YXRvbRIEMjE5NBC3rQUaQI7K+W7MMBoD6FbFZxRBqs9VTjErztjWTy57+fvrLaTCIZ+eBs7CuaKqfUZdSN8otjubSHVTQID3k9DpPAX0yDo=\", \"mode\": \"BROADCAST_MODE_BLOCK\"}") + XCTAssertEqual(output.error, "") } func testStaking() { @@ -114,7 +145,7 @@ class CosmosSignerTests: XCTestCase { $0.delegatorAddress = "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02" $0.validatorAddress = "cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp" $0.amount = CosmosAmount.with { - $0.amount = 10 + $0.amount = "10" $0.denom = "muon" } } @@ -126,12 +157,13 @@ class CosmosSignerTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 101721 $0.amounts = [CosmosAmount.with { - $0.amount = 1018 + $0.amount = "1018" $0.denom = "muon" }] } let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; $0.accountNumber = 1037 $0.chainID = "gaia-13003" $0.memo = "" @@ -143,47 +175,8 @@ class CosmosSignerTests: XCTestCase { let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .cosmos) - let expectedJSON = """ -{ - "mode": "block", - "tx": { - "fee": { - "amount": [ - { - "amount": "1018", - "denom": "muon" - } - ], - "gas": "101721" - }, - "memo": "", - "msg": [ - { - "type": "cosmos-sdk/MsgDelegate", - "value": { - "amount": { - "amount": "10", - "denom": "muon" - }, - "delegator_address": "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02", - "validator_address": "cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp" - } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F" - }, - "signature": "wIvfbCsLRCjzeXXoXTKfHLGXRbAAmUp0O134HVfVc6pfdVNJvvzISMHRUHgYcjsSiFlLyR32heia/yLgMDtIYQ==" - } - ] - } -} - -""" - XCTAssertJSONEqual(expectedJSON, output.json) + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CpsBCpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCi1jb3Ntb3MxaHNrNmpyeXlxamZocDVkaGM1NXRjOWp0Y2t5Z3gwZXBoNmRkMDISNGNvc21vc3ZhbG9wZXIxemt1cHI4M2hyemtuM3VwNWVsa3R6Y3EzdHVmdDhueHNtd2RxZ3AaCgoEbXVvbhICMTASZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAcSEgoMCgRtdW9uEgQxMDE4ENmaBhpA8O9Jm/kL6Za2I3poDs5vpMowYJgNvYCJBRU/vxAjs0lNZYsq40qpTbwOTbORjJA5UjQ6auc40v6uCFT4q4z+uA==\"}") + XCTAssertEqual(output.error, "") } func testWithdraw() { @@ -209,12 +202,13 @@ class CosmosSignerTests: XCTestCase { let fee = CosmosFee.with { $0.amounts = [CosmosAmount.with { $0.denom = "uatom" - $0.amount = 1 + $0.amount = "1" }] $0.gas = 220000 } let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; $0.fee = fee $0.accountNumber = 8698 $0.chainID = "cosmoshub-2" @@ -226,54 +220,56 @@ class CosmosSignerTests: XCTestCase { let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .cosmos) - let expectedJSON = """ - { - "mode": "block", - "tx": { - "fee": { - "amount": [ - { - "amount": "1", - "denom": "uatom" - } - ], - "gas": "220000" - }, - "memo": "", - "msg": [ - { - "type": "cosmos-sdk/MsgWithdrawDelegationReward", - "value": { - "delegator_address": "cosmos100rhxclqasy6vnrcervgh99alx5xw7lkfp4u54", - "validator_address": "cosmosvaloper1ey69r37gfxvxg62sh4r0ktpuc46pzjrm873ae8" - } - },{ - "type": "cosmos-sdk/MsgWithdrawDelegationReward", - "value": { - "delegator_address": "cosmos100rhxclqasy6vnrcervgh99alx5xw7lkfp4u54", - "validator_address": "cosmosvaloper1sjllsnramtg3ewxqwwrwjxfgc4n4ef9u2lcnj0" - } - },{ - "type": "cosmos-sdk/MsgWithdrawDelegationReward", - "value": { - "delegator_address": "cosmos100rhxclqasy6vnrcervgh99alx5xw7lkfp4u54", - "validator_address": "cosmosvaloper1648ynlpdw7fqa2axt0w2yp3fk542junl7rsvq6" - } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F" - }, - "signature": "2k5bSnfWxaauXHBNJTKmf4CpLiCWLg7UAC/q2SVhZNkU+n0DdLBSTdmYhKYmmtpl/Njm4YrcxE0WLb/hVccQ+g==" - } - ] - } + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CukDCqABCjcvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1dpdGhkcmF3RGVsZWdhdG9yUmV3YXJkEmUKLWNvc21vczEwMHJoeGNscWFzeTZ2bnJjZXJ2Z2g5OWFseDV4dzdsa2ZwNHU1NBI0Y29zbW9zdmFsb3BlcjFleTY5cjM3Z2Z4dnhnNjJzaDRyMGt0cHVjNDZwempybTg3M2FlOAqgAQo3L2Nvc21vcy5kaXN0cmlidXRpb24udjFiZXRhMS5Nc2dXaXRoZHJhd0RlbGVnYXRvclJld2FyZBJlCi1jb3Ntb3MxMDByaHhjbHFhc3k2dm5yY2VydmdoOTlhbHg1eHc3bGtmcDR1NTQSNGNvc21vc3ZhbG9wZXIxc2psbHNucmFtdGczZXd4cXd3cndqeGZnYzRuNGVmOXUybGNuajAKoAEKNy9jb3Ntb3MuZGlzdHJpYnV0aW9uLnYxYmV0YTEuTXNnV2l0aGRyYXdEZWxlZ2F0b3JSZXdhcmQSZQotY29zbW9zMTAwcmh4Y2xxYXN5NnZucmNlcnZnaDk5YWx4NXh3N2xrZnA0dTU0EjRjb3Ntb3N2YWxvcGVyMTY0OHlubHBkdzdmcWEyYXh0MHcyeXAzZms1NDJqdW5sN3JzdnE2EmUKUQpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQJXKG7D830zVXu7qgALJ3RKyQI6qZZ8rnWhgdH/kfqdxRIECgIIARi+AhIQCgoKBXVhdG9tEgExEOC2DRpAXLgJ+8xEMUn7nkFj3ukg2V65Vh5ob7HKeCaNpMM6OPQrpW2r6askfssIFcOd8ThiBEz65bJz81Fmb5MtDTGv4g==\"}") + XCTAssertEqual(output.error, "") + } + + func testIbcTransfer() { + let privateKey = PrivateKey(data: Data(hexString: "8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af")!)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .cosmos) + + let transferMessage = CosmosMessage.Transfer.with { + $0.sourcePort = "transfer" + $0.sourceChannel = "channel-141" + $0.sender = fromAddress.description + $0.receiver = "osmo18s0hdnsllgcclweu9aymw4ngktr2k0rkvn7jmn" + $0.token = CosmosAmount.with { + $0.amount = "100000" + $0.denom = "uatom" + } + $0.timeoutHeight = CosmosHeight.with { + $0.revisionNumber = 1 + $0.revisionHeight = 8800000 + } + } + + let message = CosmosMessage.with { + $0.transferTokensMessage = transferMessage + } + + let fee = CosmosFee.with { + $0.gas = 500000 + $0.amounts = [CosmosAmount.with { + $0.amount = "12500" + $0.denom = "uatom" + }] } - """ - XCTAssertJSONEqual(expectedJSON, output.json) + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 546179 + $0.chainID = "cosmoshub-4" + $0.sequence = 2 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .cosmos) + + // https://www.mintscan.io/cosmos/txs/817101F3D96314AD028733248B28BAFAD535024D7D2C8875D3FE31DC159F096B + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"Cr4BCrsBCikvaWJjLmFwcGxpY2F0aW9ucy50cmFuc2Zlci52MS5Nc2dUcmFuc2ZlchKNAQoIdHJhbnNmZXISC2NoYW5uZWwtMTQxGg8KBXVhdG9tEgYxMDAwMDAiLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseCorb3NtbzE4czBoZG5zbGxnY2Nsd2V1OWF5bXc0bmdrdHIyazBya3ZuN2ptbjIHCAEQgI6ZBBJoClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEC7O9c5DejAsZ/lUaN5LMfNukR9GfX5qUrQcHhPh1WNkkSBAoCCAEYAhIUCg4KBXVhdG9tEgUxMjUwMBCgwh4aQK0HIWdFMk+C6Gi1KG/vELe1ffcc1aEWUIqz2t/ZhwqNNHxUUSp27wteiugHEMVTEIOBhs84t2gIcT/nD/1yKOU=\"}") + XCTAssertEqual(output.error, "") } } diff --git a/swift/Tests/Blockchains/CronosTests.swift b/swift/Tests/Blockchains/CronosTests.swift new file mode 100644 index 00000000000..b28a89849bb --- /dev/null +++ b/swift/Tests/Blockchains/CronosTests.swift @@ -0,0 +1,17 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class CronosTests: XCTestCase { + + func testAddress() { + let address = AnyAddress(string: "0xEC49280228b0D05Aa8e8b756503254e1eE7835ab", coin: .cronosChain)! + + XCTAssertEqual(address.data.hexString, "ec49280228b0d05aa8e8b756503254e1ee7835ab") + } +} diff --git a/swift/Tests/Blockchains/CryptoorgTests.swift b/swift/Tests/Blockchains/CryptoorgTests.swift index 04ffd6443f6..7926940b6b5 100644 --- a/swift/Tests/Blockchains/CryptoorgTests.swift +++ b/swift/Tests/Blockchains/CryptoorgTests.swift @@ -28,7 +28,7 @@ class CryptoorgTests: XCTestCase { $0.fromAddress = fromAddress.description $0.toAddress = "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus" $0.amounts = [CosmosAmount.with { - $0.amount = 100000000 + $0.amount = "50000000" $0.denom = "basecro" }] } @@ -40,16 +40,17 @@ class CryptoorgTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 5000 + $0.amount = "5000" $0.denom = "basecro" }] } let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; $0.accountNumber = 125798 $0.chainID = "crypto-org-chain-mainnet-1" $0.memo = "" - $0.sequence = 0 + $0.sequence = 2 $0.messages = [message] $0.fee = fee $0.privateKey = privateKey.data @@ -57,49 +58,8 @@ class CryptoorgTests: XCTestCase { let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .cryptoOrg) - let expectedJSON: String = -""" -{ - "mode": "block", - "tx": { - "fee": { - "amount": [ - { - "amount": "5000", - "denom": "basecro" - } - ], - "gas": "200000" - }, - "memo": "", - "msg": [ - { - "type": "cosmos-sdk/MsgSend", - "value": { - "amount": [ - { - "amount": "100000000", - "denom": "basecro" - } - ], - "from_address": "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", - "to_address": "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus" - } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A4gxsGFiPn6L5Z2IjHEISkXI0IkwfL9exV3GLB171Wvj" - }, - "signature": "5+5rSFFg0FE9cTklQWQHNktBDJsz7UCnMSgF0t0+gYcrIhEWUyTtibXaHZQbKAAaciJ1BkHXYREjU55VswByVg==" - } - ] - } -} -""" - - XCTAssertJSONEqual(expectedJSON, output.json) + // https://crypto.org/explorer/tx/BCB213B0A121F0CF11BECCF52475F1C8328D6070F3CFDA9E14C42E6DB30E847E + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"CpABCo0BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm0KKmNybzFjdHd0Y3dwZ2tza3k5ODhkaHRoNmpzbHh2ZXVtZ3UwZDQ1emdmMBIqY3JvMXhwYWh5NmM3d2xkeGFjdjZsZDk5aDQzNW1odmZuc3VwMjR2Y3VzGhMKB2Jhc2Vjcm8SCDUwMDAwMDAwEmkKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOIMbBhYj5+i+WdiIxxCEpFyNCJMHy/XsVdxiwde9Vr4xIECgIIARgCEhUKDwoHYmFzZWNybxIENTAwMBDAmgwaQAcxK9xk6r69gmz+1UWaCnYxNuXPXZdp59YcqKPJE5d6fp+IICTBOwd2rs8MiApcf8kNSrbZ6oECxcGQAdxF0SI=\"}") + XCTAssertEqual(output.error, "") } } diff --git a/swift/Tests/Blockchains/Data b/swift/Tests/Blockchains/Data index f50c5d874d3..154e08de1f2 120000 --- a/swift/Tests/Blockchains/Data +++ b/swift/Tests/Blockchains/Data @@ -1 +1 @@ -../../../tests/Ethereum/Data \ No newline at end of file +../../../tests/chains/Ethereum/Data \ No newline at end of file diff --git a/swift/Tests/Blockchains/ECashTests.swift b/swift/Tests/Blockchains/ECashTests.swift new file mode 100644 index 00000000000..7826b92e44f --- /dev/null +++ b/swift/Tests/Blockchains/ECashTests.swift @@ -0,0 +1,91 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import WalletCore +import XCTest + +class ECashTests: XCTestCase { + + func testExtendedKeys() { + let wallet = HDWallet.test + + let xprv = wallet.getExtendedPrivateKey(purpose: .bip44, coin: .ecash, version: .xprv) + let xpub = wallet.getExtendedPublicKey(purpose: .bip44, coin: .ecash, version: .xpub) + + XCTAssertEqual(xprv, "xprv9xjBcTizebJaV61xMkuMJ89vis7saMmwFgTYeF83KwinEksJ4frk7wB4mDiKiwXDCbJmgmh6Bp1FkF8SopNZhbF3B5wyX32cuDVFZtuUDvB") + XCTAssertEqual(xpub, "xpub6BiY1yFtUxrsha6RTnSMfG6fGtxMypVncuP9SdXetHFm7ZCScDAzfjVYcW32bkNCGJ5DTqawAHSTbJdTBL8wVxqUDGpxnRtukrhhBoS7Wy7") + } + + func testDeriveFromXPub() { + let xpub = "xpub6BiY1yFtUxrsha6RTnSMfG6fGtxMypVncuP9SdXetHFm7ZCScDAzfjVYcW32bkNCGJ5DTqawAHSTbJdTBL8wVxqUDGpxnRtukrhhBoS7Wy7" + + let coin = CoinType.ecash + let xpubAddr2 = HDWallet.getPublicKeyFromExtended( + extended: xpub, + coin: coin, + derivationPath: DerivationPath(purpose: .bip44, coin: coin.slip44Id, account: 0, change: 0, address: 2).description + )! + + let xpubAddr9 = HDWallet.getPublicKeyFromExtended( + extended: xpub, + coin: coin, + derivationPath: DerivationPath(purpose: .bip44, coin: coin.slip44Id, account: 0, change: 0, address: 9).description + )! + + XCTAssertEqual(coin.deriveAddressFromPublicKey(publicKey: xpubAddr2), "ecash:qpttymfhuq3v8tasfv7drlglhq6ne6zxquqltu3dcj") + XCTAssertEqual(coin.deriveAddressFromPublicKey(publicKey: xpubAddr9), "ecash:qqjraw2s5pwqwzql4znjpvp4vtvy3c9gmugq62r2j7") + } + + func testAddress() { + XCTAssertEqual( + "ecash:prm3srpqu4kmx00370m4wt5qr3cp7sekmc0adf8na6", + AnyAddress(string: "ecash:prm3srpqu4kmx00370m4wt5qr3cp7sekmc0adf8na6", coin: .ecash)?.description + ) + XCTAssertEqual( + "ecash:prm3srpqu4kmx00370m4wt5qr3cp7sekmc0adf8na6", + AnyAddress(string: "prm3srpqu4kmx00370m4wt5qr3cp7sekmc0adf8na6", coin: .ecash)?.description + ) + } + + func testLockScript() { + let address = AnyAddress(string: "pzukqjmcyzrkh3gsqzdcy3e3d39cqxhl3gkypy0vjg", coin: .ecash)! + let script = BitcoinScript.lockScriptForAddress(address: address.description, coin: .ecash) + XCTAssertEqual(script.data.hexString, "a914b9604b7820876bc510009b8247316c4b801aff8a87") + + let address2 = AnyAddress(string: "qphr8l8ns8wd99a8653ctfe5qcrxaumz5qck55dsl3", coin: .ecash)! + let script2 = BitcoinScript.lockScriptForAddress(address: address2.description, coin: .ecash) + XCTAssertEqual(script2.data.hexString, "76a9146e33fcf381dcd297a7d52385a73406066ef362a088ac") + } + + func testSign() throws { + let utxoTxId = "050d00e2e18ef13969606f1ceee290d3f49bd940684ce39898159352952b8ce2" + let privateKey = PrivateKey(data: Data(hexString: "7fdafb9db5bc501f2096e7d13d331dc7a75d9594af3d251313ba8b6200f4e384")!)! + let address = CoinType.ecash.deriveAddress(privateKey: privateKey) + let utxo = BitcoinUnspentTransaction.with { + $0.outPoint.hash = Data.reverse(hexString: utxoTxId) // reverse of UTXO tx id, Bitcoin internal expects network byte order + $0.outPoint.index = 2 // outpoint index of this this UTXO + $0.outPoint.sequence = UINT32_MAX + $0.amount = 5151 // value of this UTXO + $0.script = BitcoinScript.lockScriptForAddress(address: address, coin: .ecash).data // Build lock script from address or public key hash + } + + let input = BitcoinSigningInput.with { + $0.hashType = BitcoinScript.hashTypeForCoin(coinType: .ecash) + $0.amount = 600 + $0.byteFee = 1 + $0.toAddress = "1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx" + $0.changeAddress = "1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU" + $0.utxo = [utxo] + $0.privateKey = [privateKey.data] + } + + let output: BitcoinSigningOutput = AnySigner.sign(input: input, coin: .ecash) + XCTAssertEqual(output.error, TW_Common_Proto_SigningError.ok) + + XCTAssertEqual(output.transactionID, "96ee20002b34e468f9d3c5ee54f6a8ddaa61c118889c4f35395c2cd93ba5bbb4") + XCTAssertEqual(output.encoded.hexString, "0100000001e28c2b955293159898e34c6840d99bf4d390e2ee1c6f606939f18ee1e2000d05020000006b483045022100b70d158b43cbcded60e6977e93f9a84966bc0cec6f2dfd1463d1223a90563f0d02207548d081069de570a494d0967ba388ff02641d91cadb060587ead95a98d4e3534121038eab72ec78e639d02758e7860cdec018b49498c307791f785aa3019622f4ea5bffffffff0258020000000000001976a914769bdff96a02f9135a1d19b749db6a78fe07dc9088ace5100000000000001976a9149e089b6889e032d46e3b915a3392edfd616fb1c488ac00000000") + } +} diff --git a/swift/Tests/Blockchains/ElrondTests.swift b/swift/Tests/Blockchains/ElrondTests.swift index d9d5b1d746a..06718ed42a3 100644 --- a/swift/Tests/Blockchains/ElrondTests.swift +++ b/swift/Tests/Blockchains/ElrondTests.swift @@ -9,10 +9,10 @@ import XCTest class ElrondTests: XCTestCase { - let aliceBech32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" - let aliceSeedHex = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf" - let alicePubKeyHex = "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293" - let bobBech32 = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" + let aliceBech32 = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + let alicePubKeyHex = "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1" + let aliceSeedHex = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9" + let bobBech32 = "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx" func testAddress() { let key = PrivateKey(data: Data(hexString: aliceSeedHex)!)! @@ -24,28 +24,106 @@ class ElrondTests: XCTestCase { XCTAssertEqual(address.description, addressFromString.description) } - func testSign() { + func testSignGenericAction() { let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! let input = ElrondSigningInput.with { - $0.transaction = ElrondTransactionMessage.with { - $0.nonce = 0 + $0.genericAction = ElrondGenericAction.with { + $0.accounts = ElrondAccounts.with { + $0.senderNonce = 7 + $0.sender = aliceBech32 + $0.receiver = bobBech32 + } $0.value = "0" - $0.sender = aliceBech32 - $0.receiver = bobBech32 - $0.gasPrice = 1000000000 - $0.gasLimit = 50000 $0.data = "foo" - $0.chainID = "1" $0.version = 1 } + $0.gasPrice = 1000000000 + $0.gasLimit = 50000 + $0.chainID = "1" + $0.privateKey = privateKey.data + } + + let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let expectedSignature = "e8647dae8b16e034d518a1a860c6a6c38d16192d0f1362833e62424f424e5da660770dff45f4b951d9cc58bfb9d14559c977d443449bfc4b8783ff9c84065700" + let expectedEncoded = #"{"nonce":7,"value":"0","receiver":"\#(bobBech32)","sender":"\#(aliceBech32)","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# + + XCTAssertEqual(output.signature, expectedSignature) + XCTAssertEqual(output.encoded, expectedEncoded) + } + + func testSignEGLDTransfer() { + let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! + + let input = ElrondSigningInput.with { + $0.egldTransfer = ElrondEGLDTransfer.with { + $0.accounts = ElrondAccounts.with { + $0.senderNonce = 7 + $0.sender = aliceBech32 + $0.receiver = bobBech32 + } + $0.amount = "1000000000000000000" + } + $0.chainID = "1" + $0.privateKey = privateKey.data + } + + let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let expectedSignature = "7e1c4c63b88ea72dcf7855a54463b1a424eb357ac3feb4345221e512ce07c7a50afb6d7aec6f480b554e32cf2037082f3bc17263d1394af1f3ef240be53c930b" + let expectedEncoded = #"{"nonce":7,"value":"1000000000000000000","receiver":"\#(bobBech32)","sender":"\#(aliceBech32)","gasPrice":1000000000,"gasLimit":50000,"chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# + + XCTAssertEqual(output.signature, expectedSignature) + XCTAssertEqual(output.encoded, expectedEncoded) + } + + func testSignESDTTransfer() { + let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! + + let input = ElrondSigningInput.with { + $0.esdtTransfer = ElrondESDTTransfer.with { + $0.accounts = ElrondAccounts.with { + $0.senderNonce = 7 + $0.sender = aliceBech32 + $0.receiver = bobBech32 + } + $0.amount = "10000000000000" + $0.tokenIdentifier = "MYTOKEN-1234" + } + + $0.privateKey = privateKey.data + } + + let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let expectedSignature = "9add6d9ac3f1a1fddb07b934e8a73cad3b8c232bdf29d723c1b38ad619905f03e864299d06eb3fe3bbb48a9f1d9b7f14e21dc5eaffe0c87f5718ad0c4198bb0c" + let expectedData = "RVNEVFRyYW5zZmVyQDRkNTk1NDRmNGI0NTRlMmQzMTMyMzMzNEAwOTE4NGU3MmEwMDA=" + let expectedEncoded = #"{"nonce":7,"value":"0","receiver":"\#(bobBech32)","sender":"\#(aliceBech32)","gasPrice":1000000000,"gasLimit":425000,"data":"\#(expectedData)","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# + + XCTAssertEqual(output.signature, expectedSignature) + XCTAssertEqual(output.encoded, expectedEncoded) + } + + func testSignESDTNFTTransfer() { + let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! + + let input = ElrondSigningInput.with { + $0.esdtnftTransfer = ElrondESDTNFTTransfer.with { + $0.accounts = ElrondAccounts.with { + $0.senderNonce = 7 + $0.sender = aliceBech32 + $0.receiver = bobBech32 + } + $0.tokenCollection = "LKMEX-aab910" + $0.tokenNonce = 4 + $0.amount = "184300000000000000" + } $0.privateKey = privateKey.data } let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) - let expectedSignature = "b5fddb8c16fa7f6123cb32edc854f1e760a3eb62c6dc420b5a4c0473c58befd45b621b31a448c5b59e21428f2bc128c80d0ee1caa4f2bf05a12be857ad451b00" - let expectedEncoded = #"{"nonce":0,"value":"0","receiver":"\#(bobBech32)","sender":"\#(aliceBech32)","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# + let expectedSignature = "cc935685d5b31525e059a16a832cba98dee751983a5a93de4198f6553a2c55f5f1e0b4300fe9077376fa754546da0b0f6697e66462101a209aafd0fc775ab60a" + let expectedData = "RVNEVE5GVFRyYW5zZmVyQDRjNGI0ZDQ1NTgyZDYxNjE2MjM5MzEzMEAwNEAwMjhlYzNkZmEwMWFjMDAwQDgwNDlkNjM5ZTVhNjk4MGQxY2QyMzkyYWJjY2U0MTAyOWNkYTc0YTE1NjM1MjNhMjAyZjA5NjQxY2MyNjE4Zjg=" + let expectedEncoded = #"{"nonce":7,"value":"0","receiver":"\#(aliceBech32)","sender":"\#(aliceBech32)","gasPrice":1000000000,"gasLimit":937500,"data":"\#(expectedData)","chainID":"1","version":1,"signature":"\#(expectedSignature)"}"# XCTAssertEqual(output.signature, expectedSignature) XCTAssertEqual(output.encoded, expectedEncoded) diff --git a/swift/Tests/Blockchains/EthereumAbiTests.swift b/swift/Tests/Blockchains/EthereumAbiTests.swift index 3edc89d55c2..a6a18430e5b 100644 --- a/swift/Tests/Blockchains/EthereumAbiTests.swift +++ b/swift/Tests/Blockchains/EthereumAbiTests.swift @@ -213,4 +213,12 @@ class EthereumAbiTests: XCTestCase { let hash = EthereumAbi.encodeTyped(messageJson: message) XCTAssertEqual(hash.hexString, "a85c2e2b118698e88db68a8105b794a8cc7cec074e89ef991cb4f5f533819cc2") } + + func testEncodeSeaportMessage() throws { + let url = Bundle(for: EthereumAbiTests.self).url(forResource: "seaport_712", withExtension: "json")! + let json = try String(contentsOf: url) + let hash = EthereumAbi.encodeTyped(messageJson: json) + + XCTAssertEqual(hash.hexString, "54140d99a864932cbc40fd8a2d1d1706c3923a79c183a3b151e929ac468064db") + } } diff --git a/swift/Tests/Blockchains/EthereumFeeTests.swift b/swift/Tests/Blockchains/EthereumFeeTests.swift deleted file mode 100644 index 09e5a830931..00000000000 --- a/swift/Tests/Blockchains/EthereumFeeTests.swift +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -import XCTest -import WalletCore - -class EthereumFeeTests: XCTestCase { - - func testBaseFeeOnly() throws { - let url = Bundle(for: EthereumFeeTests.self).url(forResource: "eth_feeHistory", withExtension: "json")! - let data = try String(contentsOf: url) - - guard let result = EthereumFee.suggest(feeHistory: data) else { - XCTFail("fail to suggest fee") - return - } - - let expected = """ - { - "baseFee": "80885125076", - "maxPriorityFee": "2000000000" - } - """ - - XCTAssertJSONEqual(result, expected) - } - - func testBaseFeeAndPriority() throws { - let url = Bundle(for: EthereumFeeTests.self).url(forResource: "eth_feeHistory2", withExtension: "json")! - let data = try String(contentsOf: url) - - guard let result = EthereumFee.suggest(feeHistory: data) else { - XCTFail("fail to suggest fee") - return - } - - let expected = """ - { - "baseFee": "87408740685", - "maxPriorityFee": "1500000000" - } - """ - - XCTAssertJSONEqual(result, expected) - } - - func testHexPadding() throws { - let url = Bundle(for: EthereumFeeTests.self).url(forResource: "eth_feeHistory3", withExtension: "json")! - let data = try String(contentsOf: url) - - guard let result = EthereumFee.suggest(feeHistory: data) else { - XCTFail("fail to suggest fee") - return - } - - let expected = """ - { - "baseFee": "44208904215", - "maxPriorityFee": "1500000000" - } - """ - - XCTAssertJSONEqual(result, expected) - } -} diff --git a/swift/Tests/Blockchains/EthereumTests.swift b/swift/Tests/Blockchains/EthereumTests.swift index ae65e1ce74e..b16ba0bf7db 100644 --- a/swift/Tests/Blockchains/EthereumTests.swift +++ b/swift/Tests/Blockchains/EthereumTests.swift @@ -21,7 +21,7 @@ class EthereumTests: XCTestCase { XCTAssertFalse(AnyAddress.isValid(string: invalid, coin: .ethereum)) } - func testSigner() { + func testSigner() throws { let input = EthereumSigningInput.with { $0.chainID = Data(hexString: "01")! $0.nonce = Data(hexString: "09")! @@ -39,6 +39,7 @@ class EthereumTests: XCTestCase { let output: EthereumSigningOutput = AnySigner.sign(input: input, coin: .ethereum) + XCTAssertEqual(try input.serializedData().hexString, "0a0101120109220504a817c8002a025208422a3078333533353335333533353335333533353335333533353335333533353335333533353335333533354a204646464646464646464646464646464646464646464646464646464646464646520c0a0a0a080de0b6b3a7640000") XCTAssertEqual(output.encoded.hexString, "f86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83") } diff --git a/swift/Tests/Blockchains/EverscaleTests.swift b/swift/Tests/Blockchains/EverscaleTests.swift new file mode 100644 index 00000000000..d3ce3f32293 --- /dev/null +++ b/swift/Tests/Blockchains/EverscaleTests.swift @@ -0,0 +1,54 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import WalletCore +import XCTest + +class EverscaleTests: XCTestCase { + func testAddressFromPrivateKey() { + let privateKey = PrivateKey(data: Data(hexString: "15d126cb1a84acdbcd1d9c3f6975968c2beb18cc43c95849d4b0226e1c8552aa")!)! + let publicKey = privateKey.getPublicKeyEd25519() + let address = AnyAddress(publicKey: publicKey, coin: .everscale) + XCTAssertEqual(address.description, "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04") + } + + func testAddressFromPublicKey() { + let publicKey = PublicKey(data: Data(hexString: "a0303f8fc89a3c2124f5dc6f3ab9a9cb246b7d1e24897eaf5e63eeee20085db0")!, type: PublicKeyType.ed25519)! + let address = AnyAddress(publicKey: publicKey, coin: .everscale) + XCTAssertEqual(address.description, "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04") + } + + func testAddressFromString() { + let addressString = "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04" + let address = AnyAddress(string: addressString, coin: .everscale) + XCTAssertEqual(address!.description, "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04") + } + + func testSign() throws { + let privateKeyData = Data(hexString: "542bd4288352f1c6b270046f153d406aec054a0a06000ab9b36b5c6dd3050ad4")! + + let transfer = EverscaleTransfer.with { + $0.bounce = false + $0.behavior = EverscaleMessageBehavior.simpleTransfer + $0.amount = 100000000 + $0.expiredAt = 1680770631 + $0.to = "0:db18a67f4626f15ac0537a18445937f685f9b30184f0d7b28be4bdeb92d2fd90" + $0.encodedContractData = "te6ccgEBAQEAKgAAUAAAAAFLqS2KOWKN+7Y5OSiKhKisiw6t/h2ovvR3WbapyAtrdctwupw=" + } + + let input = EverscaleSigningInput.with { + $0.transfer = transfer + $0.privateKey = privateKeyData + } + + let output: EverscaleSigningOutput = AnySigner.sign(input: input, coin: .everscale) + + // Link to the message: https://everscan.io/messages/73807b0a3ca2d8564c023dccd5b9da222a270f68338c6fc2c064dda376a2c59d + let expectedString = "te6ccgICAAIAAQAAAKoAAAHfiAG+Ilaz1wTyTEauoymMGl6o+NGqhszIlHS8BXAmXniYrAImASIQKH2jIwoA65IGC6aua4gAA4fFo/Nuxgb3sIRELhZnSXIS7IsE2E4D+8hk3EWGVZX+ICqlN/ka9DvXduhaXUlsUyF0MjgAAAAIHAABAGhCAG2MUz+jE3itYCm9DCIsm/tC/NmAwnhr2UXyXvXJaX7IIC+vCAAAAAAAAAAAAAAAAAAA" + + XCTAssertEqual(output.encoded, expectedString) + } +} diff --git a/swift/Tests/Blockchains/EvmosTests.swift b/swift/Tests/Blockchains/EvmosTests.swift new file mode 100644 index 00000000000..ae10bdc08aa --- /dev/null +++ b/swift/Tests/Blockchains/EvmosTests.swift @@ -0,0 +1,76 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class EvmosTests: XCTestCase { + + func testAddressData() throws { + let wallet = HDWallet(strength: 256, passphrase: "")! + let nativeEvmos = wallet.getAddressForCoin(coin: .nativeEvmos) + let evmos = wallet.getAddressForCoin(coin: .evmos) + + let addr1 = AnyAddress(string: nativeEvmos, coin: .nativeEvmos) + let addr2 = AnyAddress(string: evmos, coin: .evmos) + + XCTAssertEqual(addr1?.data.hexString, addr2?.data.hexString) + } + + func testSigningNativeTransfer() { + + let wallet = HDWallet(mnemonic: "glue blanket noodle name bring castle degree vibrant great joy usual mother pyramid cat balance swear diagram green split goat token day arm shoe", passphrase: "")! + let privateKey = wallet.getKeyForCoin(coin: .nativeEvmos) + let publicKey = privateKey.getPublicKeySecp256k1(compressed: false) + + XCTAssertEqual(publicKey.data.hexString, "049475c9fa23ec693667baa76c4da69b49cccfdf058c4dcb27ba67cfbc9082d9ed9074786560aa698b19bb9729526b1c75934f3d4a78f7be719e4386b749b36310") + + let fromAddress = AnyAddress(publicKey: publicKey, coin: .nativeEvmos) + + print(publicKey.compressed.data.hexString) + + XCTAssertEqual(fromAddress.description, "evmos1rk39dk3wff5nps7emuhv3ntkn3nsz6z2erqfr0") + + let sendCoinsMessage = CosmosMessage.Send.with { + $0.fromAddress = fromAddress.description + $0.toAddress = "evmos10k9lrrruap9nu96mxwwye2f6a5wazeh33kq67z" // 1p + $0.amounts = [CosmosAmount.with { + $0.amount = "200000000000000" // 0.0002 Evmos + $0.denom = "aevmos" + }] + } + + let message = CosmosMessage.with { + $0.sendCoinsMessage = sendCoinsMessage + } + + let fee = CosmosFee.with { + $0.gas = 140000 + $0.amounts = [CosmosAmount.with { + $0.amount = "1400000000000000" + $0.denom = "aevmos" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 1982243 + $0.chainID = "evmos_9001-2" + $0.sequence = 0 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .nativeEvmos) + // https://www.mintscan.io/evmos/txs/B05D2047086B158665EC552879270AEF40AEAAFEE7D275B63E9674E3CC4C4E55 + let expected = """ + {"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpoBCpcBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEncKLGV2bW9zMXJrMzlkazN3ZmY1bnBzN2VtdWh2M250a24zbnN6NnoyZXJxZnIwEixldm1vczEwazlscnJydWFwOW51OTZteHd3eWUyZjZhNXdhemVoMzNrcTY3ehoZCgZhZXZtb3MSDzIwMDAwMDAwMDAwMDAwMBJ7ClcKTwooL2V0aGVybWludC5jcnlwdG8udjEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiEClHXJ+iPsaTZnuqdsTaabSczP3wWMTcsnumfPvJCC2e0SBAoCCAESIAoaCgZhZXZtb3MSEDE0MDAwMDAwMDAwMDAwMDAQ4MUIGkAz9vh1EutbLrLZmRA4eK72bA6bhfMX0YnhtRl5jeaL3AYmk0qdrwG9XzzleBsZ++IokJIk47cgOOyvEjl92Jhj"} + """ + XCTAssertJSONEqual(output.serialized, expected) + XCTAssertEqual(output.error, "") + } +} diff --git a/swift/Tests/Blockchains/KavaTests.swift b/swift/Tests/Blockchains/KavaTests.swift index b9ed2095a30..f9a5f8957a5 100644 --- a/swift/Tests/Blockchains/KavaTests.swift +++ b/swift/Tests/Blockchains/KavaTests.swift @@ -29,7 +29,7 @@ class KavaTests: XCTestCase { $0.fromAddress = fromAddress $0.toAddress = "kava1hdp298kaz0eezpgl6scsykxljrje3667hmlv0h" $0.amounts = [CosmosAmount.with { - $0.amount = 1000000 + $0.amount = "1000000" $0.denom = "ukava" }] } @@ -41,7 +41,7 @@ class KavaTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "ukava" }] } @@ -101,7 +101,7 @@ class KavaTests: XCTestCase { $0.delegatorAddress = "kava1jf9aaj9myrzsnmpdr7twecnaftzmku2mdpy2a7" $0.validatorAddress = "kavavaloper17498ffqdj49zca4jm7mdf3eevq7uhcsgjvm0uk" $0.amount = CosmosAmount.with { - $0.amount = 1000000 + $0.amount = "1000000" $0.denom = "ukava" } } @@ -113,7 +113,7 @@ class KavaTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "ukava" }] } @@ -179,7 +179,7 @@ class KavaTests: XCTestCase { let fee = CosmosFee.with { $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "ukava" }] $0.gas = 200000 @@ -235,7 +235,7 @@ class KavaTests: XCTestCase { $0.delegatorAddress = "kava1jf9aaj9myrzsnmpdr7twecnaftzmku2mdpy2a7" $0.validatorAddress = "kavavaloper17498ffqdj49zca4jm7mdf3eevq7uhcsgjvm0uk" $0.amount = CosmosAmount.with { - $0.amount = 500000 + $0.amount = "500000" $0.denom = "ukava" } } @@ -247,7 +247,7 @@ class KavaTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "ukava" }] } @@ -307,7 +307,7 @@ class KavaTests: XCTestCase { $0.validatorSrcAddress = "kavavaloper17498ffqdj49zca4jm7mdf3eevq7uhcsgjvm0uk" $0.validatorDstAddress = "kavavaloper14fkp35j5nkvtztmxmsxh88jks6p3w8u7p76zs9" $0.amount = CosmosAmount.with { - $0.amount = 500000 + $0.amount = "500000" $0.denom = "ukava" } } @@ -319,7 +319,7 @@ class KavaTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 100 + $0.amount = "100" $0.denom = "ukava" }] } diff --git a/swift/Tests/Blockchains/KuCoinCommunityChainTests.swift b/swift/Tests/Blockchains/KuCoinCommunityChainTests.swift new file mode 100644 index 00000000000..df42dfc52fb --- /dev/null +++ b/swift/Tests/Blockchains/KuCoinCommunityChainTests.swift @@ -0,0 +1,22 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import WalletCore +import XCTest + +class KuCoinCommunityChainTests: XCTestCase { + + func testAddress() { + let key = PrivateKey(data: Data(hexString: "33b85056aabab539bcb68540735ecf054e38bc58b29b751530e2b54ecb4ca564")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: false) + let address = AnyAddress(publicKey: pubkey, coin: .kuCoinCommunityChain) + let addressFromString = AnyAddress(string: "0xE5cA667d795685E9915E5F4b4254ca832eEB398B", coin: .kuCoinCommunityChain)! + + XCTAssertEqual(pubkey.data.hexString, "0413bde18e3329af54d51a24f424fe09a8d7d42c324c07e10e53a6e139cbee80e6288142dec2ed46f7b81dccbb28d6168cdc7b208928730cbeeb911f8db6a707bb") + XCTAssertEqual(address.description, addressFromString.description) + } + +} diff --git a/swift/Tests/Blockchains/NervosTests.swift b/swift/Tests/Blockchains/NervosTests.swift new file mode 100644 index 00000000000..e250869c9ad --- /dev/null +++ b/swift/Tests/Blockchains/NervosTests.swift @@ -0,0 +1,123 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class NervosTests: XCTestCase { + + func testDerive() throws { + let wallet = HDWallet(mnemonic: "disorder wolf eager ladder fence renew dynamic idea metal camera bread obscure", passphrase: "")! + let address = wallet.getAddressForCoin(coin: .nervos) + + XCTAssertEqual(address, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqga4k4agxexsd3zdq0wvrlyumfz7n5r7fsjxtnw8") + } + + func testAddress() throws { + + let key = PrivateKey(data: Data(hexString: "8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: true) + let address = AnyAddress(publicKey: pubkey, coin: .nervos) + let addressFromString = AnyAddress(string: "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras980hksatlslfaktks7epf25", coin: .nervos)! + + XCTAssertEqual(address.description, addressFromString.description) + } + + func testSign() throws { + + let string = "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras980hksatlslfaktks7epf25" + let address = NervosAddress(string: string)! + let lockScript = NervosScript.with { + $0.codeHash = address.codeHash + $0.hashType = address.hashType + $0.args = address.args + } + + let input = NervosSigningInput.with { + $0.nativeTransfer = NervosNativeTransfer.with { + $0.toAddress = "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3" + $0.changeAddress = "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78yze6eyfyvd537z66ur22c9mmrgz82ama" + $0.amount = 10000000000 + } + $0.byteFee = 1 + $0.cell = [ + NervosCell.with { + $0.capacity = 100000000000 + $0.outPoint = NervosOutPoint.with { + $0.txHash = Data(hexString: "71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3")! + $0.index = 1 + } + $0.lock = lockScript + }, + NervosCell.with { + $0.capacity = 20000000000 + $0.outPoint = NervosOutPoint.with { + $0.txHash = Data(hexString: "71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3")! + $0.index = 0 + } + $0.lock = lockScript + } + ] + $0.privateKey = [ + Data(hexString: "8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb")! + ] + } + + let output: NervosSigningOutput = AnySigner.sign(input: input, coin: .nervos) + let json = """ + { + "cell_deps": [ + { + "dep_type": "dep_group", + "out_point": { + "index": "0x0", + "tx_hash": "0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c" + } + } + ], + "header_deps": [], + "inputs": [ + { + "previous_output": { + "index": "0x0", + "tx_hash": "0x71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3" + }, + "since": "0x0" + } + ], + "outputs": [ + { + "capacity": "0x2540be400", + "lock": { + "args": "0xab201f55b02f53b385f79b34dfad548e549b48fc", + "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hash_type": "type" + }, + "type": null + }, + { + "capacity": "0x2540be230", + "lock": { + "args": "0xb0d65be39059d6489231b48f85ad706a560bbd8d", + "code_hash": "0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8", + "hash_type": "type" + }, + "type": null + } + ], + "outputs_data": ["0x", "0x"], + "version": "0x0", + "witnesses": [ + "0x55000000100000005500000055000000410000002a9ef2ad7829e5ea0c7a32735d29a0cb2ec20434f6fd5bf6e29cda56b28e08140156191cbbf80313d3c9cae4b74607acce7b28eb21d52ef058ed8491cdde70b700" + ] + } + """ + + XCTAssertEqual(output.transactionID, "0xf2c32afde7e72011985583873bc16c0a3c01fc01fc161eb4b914fcf84c53cdf8") + XCTAssertEqual(output.error, CommonSigningError.ok) + XCTAssertJSONEqual(output.transactionJson, json) + } +} diff --git a/swift/Tests/Blockchains/OasisTests.swift b/swift/Tests/Blockchains/OasisTests.swift index fc9f4df1bd7..03ad91930f7 100644 --- a/swift/Tests/Blockchains/OasisTests.swift +++ b/swift/Tests/Blockchains/OasisTests.swift @@ -36,6 +36,6 @@ class OasisTests: XCTestCase { let output: OasisSigningOutput = AnySigner.sign(input: input, coin: .oasis) - XCTAssertEqual(output.encoded.hexString, "a273756e747275737465645f7261775f76616c7565585ea4656e6f6e636500666d6574686f64707374616b696e672e5472616e7366657263666565a2636761730066616d6f756e74410064626f6479a262746f5500c73cc001463434915ba3f39751beb7c0905b45eb66616d6f756e744400989680697369676e6174757265a26a7075626c69635f6b6579582093d8f8a455f50527976a8aa87ebde38d5606efa86cb985d3fb466aff37000e3b697369676e61747572655840e331ce731ed819106586152b13cd98ecf3248a880bdc71174ee3d83f6d5f3f8ee8fc34c19b22032f2f1e3e06d382720125d7a517fba9295c813228cc2b63170b") + XCTAssertEqual(output.encoded.hexString, "a2697369676e6174757265a2697369676e617475726558406e51c18c9b2015c9b49414b3307336597f51ff331873d214ce2db81c9651a34d99529ccaa294a39ccd01c6b0bc2c2239d87c624e5ba4840cf99ac8f9283e240c6a7075626c69635f6b6579582093d8f8a455f50527976a8aa87ebde38d5606efa86cb985d3fb466aff37000e3b73756e747275737465645f7261775f76616c7565585ea463666565a2636761730066616d6f756e74410064626f6479a262746f5500c73cc001463434915ba3f39751beb7c0905b45eb66616d6f756e744400989680656e6f6e636500666d6574686f64707374616b696e672e5472616e73666572") } } diff --git a/swift/Tests/Blockchains/OsmosisTests.swift b/swift/Tests/Blockchains/OsmosisTests.swift new file mode 100644 index 00000000000..bd7303dbe51 --- /dev/null +++ b/swift/Tests/Blockchains/OsmosisTests.swift @@ -0,0 +1,63 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import WalletCore +import XCTest + +class OsmosisTests: XCTestCase { + func testAddress() { + let key = PrivateKey(data: Data(hexString: "8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: true) + let address = AnyAddress(publicKey: pubkey, coin: .osmosis) + let addressFromString = AnyAddress(string: "osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5", coin: .osmosis)! + + XCTAssertEqual(pubkey.data.hexString, "02ecef5ce437a302c67f95468de4b31f36e911f467d7e6a52b41c1e13e1d563649") + XCTAssertEqual(address.description, addressFromString.description) + } + + func testSigningTransaction() { + let privateKey = PrivateKey(data: Data(hexString: "8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af")!)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .osmosis) + + let sendCoinsMessage = CosmosMessage.Send.with { + $0.fromAddress = fromAddress.description + $0.toAddress = "osmo18s0hdnsllgcclweu9aymw4ngktr2k0rkvn7jmn" + $0.amounts = [CosmosAmount.with { + $0.amount = "99800" + $0.denom = "uosmo" + }] + } + + let message = CosmosMessage.with { + $0.sendCoinsMessage = sendCoinsMessage + } + + let fee = CosmosFee.with { + $0.gas = 200000 + $0.amounts = [CosmosAmount.with { + $0.amount = "200" + $0.denom = "uosmo" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 124703 + $0.chainID = "osmosis-1" + $0.memo = "" + $0.sequence = 0 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .osmosis) + + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_BLOCK\",\"tx_bytes\":\"Co0BCooBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmoKK29zbW8xbWt5Njljbjhla3R3eTA4NDV2ZWM5dXBzZHBoa3R4dDBlbjk3ZjUSK29zbW8xOHMwaGRuc2xsZ2NjbHdldTlheW13NG5na3RyMmswcmt2bjdqbW4aDgoFdW9zbW8SBTk5ODAwEmQKTgpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQLs71zkN6MCxn+VRo3ksx826RH0Z9fmpStBweE+HVY2SRIECgIIARISCgwKBXVvc21vEgMyMDAQwJoMGkAMY//Md5GRUR4lVZhk558hFS3kii9QZYoYKfg4+ac/xgNeyoiEweVDhcmEvlH1orVwjLUOnYs4ly2a/yIurYVj\"}") + XCTAssertEqual(output.error, "") + } +} diff --git a/swift/Tests/Blockchains/PolkadotTests.swift b/swift/Tests/Blockchains/PolkadotTests.swift index f13dce7b122..aae9c432a70 100644 --- a/swift/Tests/Blockchains/PolkadotTests.swift +++ b/swift/Tests/Blockchains/PolkadotTests.swift @@ -137,4 +137,30 @@ class PolkadotTests: XCTestCase { // https://polkadot.subscan.io/extrinsic/4999416-1 XCTAssertEqual("0x" + output.encoded.hexString, "0xb501840036092fac541e0e5feda19e537c679b487566d7101141c203ac8322c27e5f076a00c8268c2dfd4074f41d225e12e62e5975ff8debf0f828d31ddbfed6f7593e067fb860298eb12f50294f7ba0f82795809c84fc5cce6fcb36cde4cb1c07edbbb60900140007010300943577") } + + func testChillAndUnbond() { + // real key in 1p test + let key = PrivateKey(data: Data(hexString: "298fcced2b497ed48367261d8340f647b3fca2d9415d57c2e3c5ef90482a2266")!)! + + let input = PolkadotSigningInput.with { + $0.genesisHash = genesisHash + $0.blockHash = Data(hexString: "0x35ba668bb19453e8da6334cadcef2a27c8d4141bfc8b49e78e853c3d73e1ecd0")! + $0.era = PolkadotEra.with { + $0.blockNumber = 10541373 + $0.period = 64 + } + $0.nonce = 6 + $0.specVersion = 9200 + $0.network = .polkadot + $0.transactionVersion = 12 + $0.privateKey = key.data + $0.stakingCall.chillAndUnbond = PolkadotStaking.ChillAndUnbond.with { + $0.value = Data(hexString: "0x1766444D00")! // 10.05 DOT + } + } + let output: PolkadotSigningOutput = AnySigner.sign(input: input, coin: .polkadot) + + // https://polkadot.subscan.io/extrinsic/10541383-2 + XCTAssertEqual("0x" + output.encoded.hexString, "0xd10184008361bd08ddca5fda28b5e2aa84dc2621de566e23e089e555a42194c3eaf2da7900c891ba102db672e378945d74cf7f399226a76b43cab502436971599255451597fc2599902e4b62c7ce85ecc3f653c693fef3232be620984b5bb5bcecbbd7b209d50318001a02080706070207004d446617") + } } diff --git a/swift/Tests/Blockchains/RoninTests.swift b/swift/Tests/Blockchains/RoninTests.swift index b5bf4e35a6c..2cb54acb389 100644 --- a/swift/Tests/Blockchains/RoninTests.swift +++ b/swift/Tests/Blockchains/RoninTests.swift @@ -22,11 +22,11 @@ class RoninTests: XCTestCase { $0.nonce = Data(hexString: "0x02")! $0.gasPrice = Data() $0.gasLimit = Data(hexString: "0xca98")! // 51,864 - $0.toAddress = "0x97a9107c1793bc407d6f527b77e7fff4d812bece" // AXS + $0.toAddress = "ronin:97a9107c1793bc407d6f527b77e7fff4d812bece" // AXS $0.privateKey = keyData $0.transaction = EthereumTransaction.with { $0.erc20Transfer = EthereumTransaction.ERC20Transfer.with { - $0.to = "0x47331175b23C2f067204B506CA1501c26731C990" + $0.to = "ronin:47331175b23c2f067204b506ca1501c26731c990" $0.amount = Data(hexString: "0x016345785d8a0000")! // 0.1 AXS } } diff --git a/swift/Tests/Blockchains/SmartBitcoinCashTests.swift b/swift/Tests/Blockchains/SmartBitcoinCashTests.swift new file mode 100644 index 00000000000..131f13d14b6 --- /dev/null +++ b/swift/Tests/Blockchains/SmartBitcoinCashTests.swift @@ -0,0 +1,22 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import WalletCore +import XCTest + +class SmartBitcoinCashTests: XCTestCase { + + func testAddress() { + let key = PrivateKey(data: Data(hexString: "ab4accc9310d90a61fc354d8f353bca4a2b3c0590685d3eb82d0216af3badddc")!)! + let pubkey = key.getPublicKeySecp256k1(compressed: false) + let address = AnyAddress(publicKey: pubkey, coin: .smartBitcoinCash) + let addressFromString = AnyAddress(string: "0xA3Dcd899C0f3832DFDFed9479a9d828c6A4EB2A7", coin: .smartBitcoinCash)! + + XCTAssertEqual(pubkey.data.hexString, "0448a9ffac8022f1c7eb5253746e24d11d9b6b2737c0aecd48335feabb95a179916b1f3a97bed6740a85a2d11c663d38566acfb08af48a47ce0c835c65c9b23d0d") + XCTAssertEqual(address.description, addressFromString.description) + } + +} diff --git a/swift/Tests/Blockchains/THORChainSwapTests.swift b/swift/Tests/Blockchains/THORChainSwapTests.swift new file mode 100644 index 00000000000..cb51dd73711 --- /dev/null +++ b/swift/Tests/Blockchains/THORChainSwapTests.swift @@ -0,0 +1,99 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class THORSwapTests: XCTestCase { + + func testSignerEthBnb() throws { + // prepare swap input + let input = THORChainSwapSwapInput.with { + $0.fromChain = .eth + $0.fromAddress = "0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7" + $0.toAsset = THORChainSwapAsset.with { + $0.chain = .bnb + $0.symbol = "BNB" + $0.tokenID = "" + } + $0.toAddress = "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx" + $0.vaultAddress = "0x1091c4De6a3cF09CdA00AbDAeD42c7c3B69C83EC" + $0.routerAddress = "0x42A5Ed456650a09Dc10EBc6361A7480fDd61f27B" + $0.fromAmount = "50000000000000000" + $0.toAmountLimit = "600003" + } + + // serialize input + let inputSerialized = try input.serializedData() + XCTAssertEqual(inputSerialized.hexString, "0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a1135303030303030303030303030303030304206363030303033") + + // invoke swap + let outputData = THORChainSwap.buildSwap(input: inputSerialized) + XCTAssertEqual(outputData.count, 311) + + // parse result in proto + let outputProto = try THORChainSwapSwapOutput(serializedData: outputData) + XCTAssertEqual(outputProto.fromChain, TW_THORChainSwap_Proto_Chain.eth) + XCTAssertEqual(outputProto.toChain, TW_THORChainSwap_Proto_Chain.bnb) + XCTAssertEqual(outputProto.error.code, TW_THORChainSwap_Proto_ErrorCode.ok) + var txInput = outputProto.ethereum + + // set few fields before signing + txInput.chainID = Data(hexString: "01")! + txInput.nonce = Data(hexString: "03")! + txInput.gasPrice = Data(hexString: "06FC23AC00")! + txInput.gasLimit = Data(hexString: "013880")! + txInput.privateKey = Data(hexString: "4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")! + + // sign and encode resulting input + let output: EthereumSigningOutput = AnySigner.sign(input: txInput, coin: .ethereum) + + XCTAssertEqual(output.encoded.hexString, "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000025a06ae104be3201baca38315352f81fac70ca4dd47339981914e64e91149813e780a066a3f0b2c44ddf5a96a38481274f623f552a593d723237d6742185f4885c0064") + } + + func testSignerBnbBtc() throws { + // prepare swap input + let input = THORChainSwapSwapInput.with { + $0.fromChain = .bnb + $0.fromAddress = "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx" + $0.toAsset = THORChainSwapAsset.with { + $0.chain = .btc + $0.symbol = "BTC" + $0.tokenID = "" + } + $0.toAddress = "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8" + $0.vaultAddress = "bnb1n9esxuw8ca7ts8l6w66kdh800s09msvul6vlse" + $0.routerAddress = "" + $0.fromAmount = "10000000" + $0.toAmountLimit = "10000000" + } + + // serialize input + let inputSerialized = try input.serializedData() + XCTAssertEqual(inputSerialized.hexString, "0803122a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372781a0708011203425443222a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070382a2a626e62316e396573787577386361377473386c367736366b64683830307330396d7376756c36766c73653a08313030303030303042083130303030303030") + + // invoke swap + let outputData = THORChainSwap.buildSwap(input: inputSerialized) + XCTAssertEqual(outputData.count, 149) + + // parse result in proto + let outputProto = try THORChainSwapSwapOutput(serializedData: outputData) + XCTAssertEqual(outputProto.fromChain, TW_THORChainSwap_Proto_Chain.bnb) + XCTAssertEqual(outputProto.toChain, TW_THORChainSwap_Proto_Chain.btc) + XCTAssertEqual(outputProto.error.code, TW_THORChainSwap_Proto_ErrorCode.ok) + var txInput = outputProto.binance + + // set few fields before signing + + txInput.chainID = "Binance-Chain-Nile" + txInput.privateKey = Data(hexString: "bcf8b072560dda05122c99390def2c385ec400e1a93df0657a85cf6b57a715da")! + + // sign and encode resulting input + let output: BinanceSigningOutput = AnySigner.sign(input: txInput, coin: .binance) + + XCTAssertEqual(output.encoded.hexString, "8002f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204126a0a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e1912404836ee8659caa86771281d3f104424d95977bdedf644ec8585f1674796fde525669a6d446f72da89ee90fb0e064473b0a2159a79630e081592c52948d03d67071a40535741503a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a3130303030303030") + } +} diff --git a/swift/Tests/Blockchains/THORChainTests.swift b/swift/Tests/Blockchains/THORChainTests.swift index 75aeb4511b6..45d9cb3ac80 100644 --- a/swift/Tests/Blockchains/THORChainTests.swift +++ b/swift/Tests/Blockchains/THORChainTests.swift @@ -24,7 +24,7 @@ class THORChainSignerTests: XCTestCase { let privateKey = PrivateKey(data: Data(hexString: "7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e")!)! - func testSigningTransaction() { + func testJsonModeSigning() { let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) let fromAddress = AnyAddress(publicKey: publicKey, coin: .thorchain) @@ -32,7 +32,7 @@ class THORChainSignerTests: XCTestCase { $0.fromAddress = fromAddress.description $0.toAddress = "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn" $0.amounts = [CosmosAmount.with { - $0.amount = 10000000 + $0.amount = "10000000" $0.denom = "rune" }] } @@ -44,7 +44,7 @@ class THORChainSignerTests: XCTestCase { let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 2000000 + $0.amount = "2000000" $0.denom = "rune" }] } @@ -61,49 +61,94 @@ class THORChainSignerTests: XCTestCase { let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .thorchain) - let expectedJSON: String = -""" -{ - "mode": "block", - "tx": { - "fee": { - "amount": [ - { - "amount": "2000000", - "denom": "rune" - } - ], - "gas": "200000" - }, - "memo": "", - "msg": [ - { - "type": "thorchain/MsgSend", - "value": { + let expectedJSON: String = """ + { + "mode": "block", + "tx": { + "fee": { "amount": [ { - "amount": "10000000", + "amount": "2000000", "denom": "rune" } ], - "from_address": "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", - "to_address": "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn" - } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3" + "gas": "200000" }, - "signature": "ZPhcYubhAd6iz/pBrtLfSJaK04ISnEo+jBFvFFzoToMJA9NGhhCFmsmXMQ1AtoJ6C1aylvUnck93A7ork8ZzEQ==" + "memo": "", + "msg": [ + { + "type": "thorchain/MsgSend", + "value": { + "amount": [ + { + "amount": "10000000", + "denom": "rune" + } + ], + "from_address": "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", + "to_address": "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn" + } + } + ], + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3" + }, + "signature": "ZPhcYubhAd6iz/pBrtLfSJaK04ISnEo+jBFvFFzoToMJA9NGhhCFmsmXMQ1AtoJ6C1aylvUnck93A7ork8ZzEQ==" + } + ] } - ] - } -} -""" + } + """ XCTAssertJSONEqual(expectedJSON, output.json) } + + func testProtobufModeSigning() { + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .thorchain) + + let sendCoinsMessage = CosmosMessage.THORChainSend.with { + $0.fromAddress = fromAddress.data + $0.toAddress = AnyAddress(string: "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn", coin: .thorchain)!.data + $0.amounts = [CosmosAmount.with { + $0.amount = "38000000" + $0.denom = "rune" + }] + } + + let message = CosmosMessage.with { + $0.thorchainSendMessage = sendCoinsMessage + } + + let fee = CosmosFee.with { + $0.gas = 2500000 + $0.amounts = [CosmosAmount.with { + $0.amount = "200" + $0.denom = "rune" + }] + } + + let input = CosmosSigningInput.with { + $0.accountNumber = 593 + $0.chainID = "thorchain-mainnet-v1" + $0.sequence = 21 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + $0.signingMode = .protobuf + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .thorchain) + let expectedJSON = """ + { + "mode": "BROADCAST_MODE_BLOCK", + "tx_bytes": "ClIKUAoOL3R5cGVzLk1zZ1NlbmQSPgoUFSLnZ9tusZcIsAOAKb+9YHvJvQ4SFMqGRZ+wBVHH30JUDF54aRksgzrbGhAKBHJ1bmUSCDM4MDAwMDAwEmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQPtmX45bPQpL1/OWkK7pBWZzNXZbjExVKfJ6nBJ3jF8dxIECgIIARgVEhIKCwoEcnVuZRIDMjAwEKDLmAEaQKZtS3ATa26OOGvqdKm14ZbHeNfkPtIajXi5MkZ5XaX2SWOeX+YnCPZ9TxF9Jj5cVIo71m55xq4hVL3yDbRe89g=" + } + """ + + XCTAssertJSONEqual(expectedJSON, output.serialized) + } } diff --git a/swift/Tests/Blockchains/TerraClassicTests.swift b/swift/Tests/Blockchains/TerraClassicTests.swift new file mode 100644 index 00000000000..bb1e8c81e21 --- /dev/null +++ b/swift/Tests/Blockchains/TerraClassicTests.swift @@ -0,0 +1,605 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class TerraClassicTests: XCTestCase { + + let privateKey = PrivateKey(data: Data(hexString: "1037f828ca313f4c9e120316e8e9ff25e17f07fe66ba557d5bc5e2eeb7cba8f6")!)! + + func testAddress() { + let address = CoinType.terra.deriveAddress(privateKey: privateKey) + + XCTAssertEqual(address, "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe") + XCTAssertTrue(CoinType.terra.validate(address: "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms")) + XCTAssertTrue(CoinType.terra.validate(address: "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk")) + XCTAssertFalse(CoinType.terra.validate(address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")) + } + + func testRawJSON() { + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .terra).description + + let message = CosmosMessage.with { + $0.rawJsonMessage = CosmosMessage.RawJSON.with { + $0.type = "bank/MsgSend" + $0.value = """ + { + "amount": [{ + "amount": "1000000", + "denom": "uluna" + }], + "from_address": "\(fromAddress)", + "to_address": "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms" + } + """ + } + } + + let fee = CosmosFee.with { + $0.gas = 200000 + $0.amounts = [CosmosAmount.with { + $0.amount = "3000" + $0.denom = "uluna" + }] + } + + let input = CosmosSigningInput.with { + $0.accountNumber = 158 + $0.chainID = "soju-0013" + $0.memo = "" + $0.sequence = 0 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + + let expectedJSON: String = +""" +{ + "mode": "block", + "tx": { + "msg": [{ + "type": "bank/MsgSend", + "value": { + "from_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", + "to_address": "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms", + "amount": [{ + "denom": "uluna", + "amount": "1000000" + }] + } + }], + "fee": { + "amount": [{ + "denom": "uluna", + "amount": "3000" + }], + "gas": "200000" + }, + "signatures": [{ + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" + }, + "signature": "KPdiVsKpY12JG/VKEJVa/FpMKclxlS0qNNG6VOAypj10R5vY5UX5IgRJET1zNYnH0wvcXxfNXV+s8jtwN2UXiQ==" + }], + "memo": "" + } +} +""" + XCTAssertJSONEqual(expectedJSON, output.json) + } + + func testSigningTransaction() { + // https://finder.terra.money/soju-0013/tx/1403B07F2D218BCE961CB92D83377A924FEDB54C1F0B62E25C8B93B63470EBF7 + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .terra).description + + let message = CosmosMessage.with { + $0.sendCoinsMessage = CosmosMessage.Send.with { + $0.fromAddress = fromAddress + $0.toAddress = "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms" + $0.amounts = [CosmosAmount.with { + $0.amount = "1000000" + $0.denom = "uluna" + }] + $0.typePrefix = "bank/MsgSend" + } + } + + let fee = CosmosFee.with { + $0.gas = 200000 + $0.amounts = [CosmosAmount.with { + $0.amount = "3000" + $0.denom = "uluna" + }] + } + + let input = CosmosSigningInput.with { + $0.accountNumber = 158 + $0.chainID = "soju-0013" + $0.memo = "" + $0.sequence = 0 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + + let expectedJSON: String = +""" +{ + "mode": "block", + "tx": { + "msg": [{ + "type": "bank/MsgSend", + "value": { + "from_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", + "to_address": "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms", + "amount": [{ + "denom": "uluna", + "amount": "1000000" + }] + } + }], + "fee": { + "amount": [{ + "denom": "uluna", + "amount": "3000" + }], + "gas": "200000" + }, + "signatures": [{ + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" + }, + "signature": "KPdiVsKpY12JG/VKEJVa/FpMKclxlS0qNNG6VOAypj10R5vY5UX5IgRJET1zNYnH0wvcXxfNXV+s8jtwN2UXiQ==" + }], + "memo": "" + } +} +""" + XCTAssertJSONEqual(expectedJSON, output.json) + } + + func testStaking() { + // https://finder.terra.money/soju-0013/tx/4C0A6690ECB601ACB42D3ECAF4C24C0555B5E32E45B09C3B1607B144CD191F87 + let stakeMessage = CosmosMessage.Delegate.with { + $0.delegatorAddress = "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe" + $0.validatorAddress = "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + $0.amount = CosmosAmount.with { + $0.amount = "1000000" + $0.denom = "uluna" + } + $0.typePrefix = "staking/MsgDelegate" + } + + let message = CosmosMessage.with { + $0.stakeMessage = stakeMessage + } + + let fee = CosmosFee.with { + $0.gas = 200000 + $0.amounts = [CosmosAmount.with { + $0.amount = "3000" + $0.denom = "uluna" + }] + } + + let input = CosmosSigningInput.with { + $0.accountNumber = 158 + $0.chainID = "soju-0013" + $0.memo = "" + $0.sequence = 1 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + + let expectedJSON = """ +{ + "mode": "block", + "tx": { + "fee": { + "amount": [{ + "amount": "3000", + "denom": "uluna" + }], + "gas": "200000" + }, + "memo": "", + "msg": [{ + "type": "staking/MsgDelegate", + "value": { + "amount": { + "amount": "1000000", + "denom": "uluna" + }, + "delegator_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", + "validator_address": "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + } + }], + "signatures": [{ + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" + }, + "signature": "F8UJxbkqa0j6dYTk8PymrudBKI3WYhZImRxMFCw0ecFCmPGgNTg7yfpKZo6K6JtnoJaP7bQ4db5e4wnhMCJyAQ==" + }] + } +} +""" + XCTAssertJSONEqual(expectedJSON, output.json) + } + + func testWithdraw() { + // https://finder.terra.money/soju-0013/tx/AE0E4F2B254449950A3A7F41FABCE0B3C846D70F809380313CE3BB323E490BBD + let withdrawMessage = CosmosMessage.WithdrawDelegationReward.with { + $0.delegatorAddress = "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe" + $0.validatorAddress = "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + $0.typePrefix = "distribution/MsgWithdrawDelegationReward" + } + + let message = CosmosMessage.with { + $0.withdrawStakeRewardMessage = withdrawMessage + } + + let fee = CosmosFee.with { + $0.amounts = [CosmosAmount.with { + $0.amount = "3000" + $0.denom = "uluna" + }] + $0.gas = 200000 + } + + let input = CosmosSigningInput.with { + $0.accountNumber = 158 + $0.chainID = "soju-0013" + $0.memo = "" + $0.sequence = 2 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + + let expectedJSON = """ +{ + "mode": "block", + "tx": { + "fee": { + "amount": [{ + "amount": "3000", + "denom": "uluna" + }], + "gas": "200000" + }, + "memo": "", + "msg": [{ + "type": "distribution/MsgWithdrawDelegationReward", + "value": { + "delegator_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", + "validator_address": "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + } + }], + "signatures": [{ + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" + }, + "signature": "Kfwi1uJplzLucXDyQZsJI9v8lMFJFUBLD46+MpwBwYwPJgqPRzSOfyjRpmNou0G/Qe1hbsGEgqb85FQpsgLz+g==" + }] + } +} +""" + XCTAssertJSONEqual(expectedJSON, output.json) + } + + func testUndelegate() { + // https://finder.terra.money/soju-0013/tx/FCF50C180303AECA97F916D0CE0E0937BA4C4D2F6777FFF2AA0D52A9DAF9CCBA + let unstakeMessage = CosmosMessage.Undelegate.with { + $0.delegatorAddress = "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe" + $0.validatorAddress = "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + $0.amount = CosmosAmount.with { + $0.amount = "500000" + $0.denom = "uluna" + } + $0.typePrefix = "staking/MsgUndelegate" + } + + let message = CosmosMessage.with { + $0.unstakeMessage = unstakeMessage + } + + let fee = CosmosFee.with { + $0.gas = 200000 + $0.amounts = [CosmosAmount.with { + $0.amount = "3000" + $0.denom = "uluna" + }] + } + + let input = CosmosSigningInput.with { + $0.accountNumber = 158 + $0.chainID = "soju-0013" + $0.memo = "" + $0.sequence = 3 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + + let expectedJSON = """ +{ + "mode": "block", + "tx": { + "fee": { + "amount": [{ + "amount": "3000", + "denom": "uluna" + }], + "gas": "200000" + }, + "memo": "", + "msg": [{ + "type": "staking/MsgUndelegate", + "value": { + "amount": { + "amount": "500000", + "denom": "uluna" + }, + "delegator_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", + "validator_address": "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + } + }], + "signatures": [{ + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" + }, + "signature": "THf/RxsBr2EhHE2OMHLXfv+qSP9ORbvHgo4OSOS2P95xxGH73wW+t1zIl9cGlIVvcoChwaCg5/iEuvbgXUWpNw==" + }] + } +} +""" + XCTAssertJSONEqual(expectedJSON, output.json) + } + + func testRedlegate() { + // https://finder.terra.money/soju-0013/tx/36CE381BDF72AD7407EEE3859E3349F83B723BE9AD407E9D8C38DEE0C4434D29 + let restakeMessage = CosmosMessage.BeginRedelegate.with { + $0.delegatorAddress = "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe" + $0.validatorSrcAddress = "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + $0.validatorDstAddress = "terravaloper1rhrptnx87ufpv62c7ngt9yqlz2hr77xr9nkcr9" + $0.amount = CosmosAmount.with { + $0.amount = "500000" + $0.denom = "uluna" + } + $0.typePrefix = "staking/MsgBeginRedelegate" + } + + let message = CosmosMessage.with { + $0.restakeMessage = restakeMessage + } + + let fee = CosmosFee.with { + $0.gas = 200000 + $0.amounts = [CosmosAmount.with { + $0.amount = "3000" + $0.denom = "uluna" + }] + } + + let input = CosmosSigningInput.with { + $0.accountNumber = 158 + $0.chainID = "soju-0013" + $0.memo = "" + $0.sequence = 4 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + + let expectedJSON = """ +{ + "mode": "block", + "tx": { + "fee": { + "amount": [{ + "amount": "3000", + "denom": "uluna" + }], + "gas": "200000" + }, + "memo": "", + "msg": [{ + "type": "staking/MsgBeginRedelegate", + "value": { + "amount": { + "amount": "500000", + "denom": "uluna" + }, + "delegator_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", + "validator_dst_address": "terravaloper1rhrptnx87ufpv62c7ngt9yqlz2hr77xr9nkcr9", + "validator_src_address": "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + } + }], + "signatures": [{ + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" + }, + "signature": "HyEpSz48dkebmBFvwh5xDiiZD0jUdOvzTD3ACMw0rOQ9F3JhK2cPaEx6/ZmYNIrdsPqMNkUnHcDYD1o4IztoEg==" + }] + } +} +""" + XCTAssertJSONEqual(expectedJSON, output.json) + } + + func testSigningWasmTerraTransferTxProtobuf() { + let privateKey = PrivateKey(data: Data(hexString: "cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616")!)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .terra) + + let wasmTransferMessage = CosmosMessage.WasmTerraExecuteContractTransfer.with { + $0.senderAddress = fromAddress.description + $0.contractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76" // ANC + $0.amount = Data(hexString: "03D090")! // 250000 + $0.recipientAddress = "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp" + } + + let message = CosmosMessage.with { + $0.wasmTerraExecuteContractTransferMessage = wasmTransferMessage + } + + let fee = CosmosFee.with { + $0.gas = 200000 + $0.amounts = [CosmosAmount.with { + $0.amount = "3000" + $0.denom = "uluna" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 3407705 + $0.chainID = "columbus-5" + $0.memo = "" + $0.sequence = 3 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + + XCTAssertJSONEqual(output.serialized, +""" +{ + "tx_bytes": "CucBCuQBCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBK5AQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2Glt7InRyYW5zZmVyIjp7ImFtb3VudCI6IjI1MDAwMCIsInJlY2lwaWVudCI6InRlcnJhMWpsZ2FxeTludm4yaGY1dDJzcmE5eWN6OHM3N3duZjlsMGttZ2NwIn19EmcKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQNwZjrHsPmJKW/rXOWfukpQ1+lOHOJW3/IlFFnKLNmsABIECgIIARgDEhMKDQoFdWx1bmESBDMwMDAQwJoMGkAaprIEMLPH2HmFdwFGoaipb2GIyhXt6ombz+WMnG2mORBI6gFt0M+IymYgzZz6w1SW52R922yafDnn7yXfutRw", + "mode": "BROADCAST_MODE_BLOCK" +} +""" + ) + XCTAssertEqual(output.error, "") + } + + func testSigningWasmTerraGenericProtobuf() { + let privateKey = PrivateKey(data: Data(hexString: "cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616")!)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .terra) + + let wasmGenericMessage = CosmosMessage.WasmTerraExecuteContractGeneric.with { + $0.senderAddress = fromAddress.description + $0.contractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76" // ANC + $0.executeMsg = """ + {"transfer": { "amount": "250000", "recipient": "terra1d7048csap4wzcv5zm7z6tdqem2agyp9647vdyj" } } + """ + } + + let message = CosmosMessage.with { + $0.wasmTerraExecuteContractGeneric = wasmGenericMessage + } + + let fee = CosmosFee.with { + $0.gas = 200000 + $0.amounts = [CosmosAmount.with { + $0.amount = "3000" + $0.denom = "uluna" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 3407705 + $0.chainID = "columbus-5" + $0.memo = "" + $0.sequence = 7 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + + XCTAssertJSONEqual(output.serialized, + """ + { + "tx_bytes": "Cu4BCusBCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBLAAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2GmJ7InRyYW5zZmVyIjogeyAiYW1vdW50IjogIjI1MDAwMCIsICJyZWNpcGllbnQiOiAidGVycmExZDcwNDhjc2FwNHd6Y3Y1em03ejZ0ZHFlbTJhZ3lwOTY0N3ZkeWoiIH0gfRJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYBxITCg0KBXVsdW5hEgQzMDAwEMCaDBpAkPsS7xlSng2LMc9KiD1soN5NLaDcUh8I9okPmsdJN3le1B7yxRGNB4aQfhaRl/8Z0r5vitRT0AWuxDasd8wcFw==", + "mode": "BROADCAST_MODE_BLOCK" + } + """ + ) + XCTAssertEqual(output.error, "") + } + + func testSigningWasmTerraGenericWithCoins() { + let privateKey = PrivateKey(data: Data(hexString: "cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616")!)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .terra) + + let wasmGenericMessage = CosmosMessage.WasmTerraExecuteContractGeneric.with { + $0.senderAddress = fromAddress.description + $0.contractAddress = "terra1sepfj7s0aeg5967uxnfk4thzlerrsktkpelm5s" // ANC Market + $0.executeMsg = """ + { "deposit_stable": {} } + """ + $0.coins = [CosmosAmount.with { + $0.amount = "1000" + $0.denom = "uusd" + }] + } + + let message = CosmosMessage.with { + $0.wasmTerraExecuteContractGeneric = wasmGenericMessage + } + + let fee = CosmosFee.with { + $0.gas = 600000 + $0.amounts = [CosmosAmount.with { + $0.amount = "7000" + $0.denom = "uluna" + }] + } + + let input = CosmosSigningInput.with { + $0.signingMode = .protobuf; + $0.accountNumber = 3407705 + $0.chainID = "columbus-5" + $0.memo = "" + $0.sequence = 9 + $0.messages = [message] + $0.fee = fee + $0.privateKey = privateKey.data + } + + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + + XCTAssertJSONEqual(output.serialized, + """ + { + "tx_bytes": "CrIBCq8BCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBKEAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMXNlcGZqN3MwYWVnNTk2N3V4bmZrNHRoemxlcnJza3RrcGVsbTVzGhh7ICJkZXBvc2l0X3N0YWJsZSI6IHt9IH0qDAoEdXVzZBIEMTAwMBJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYCRITCg0KBXVsdW5hEgQ3MDAwEMDPJBpAGyi7f1ioY8XV6pjFq1s86Om4++CIUnd3rLHif2iopCcYvX0mLkTlQ6NUERg8nWTYgXcj6fOTO/ptgPuAtv0NWg==", + "mode": "BROADCAST_MODE_BLOCK" + } + """ + ) + XCTAssertEqual(output.error, "") + } + } diff --git a/swift/Tests/Blockchains/TerraTests.swift b/swift/Tests/Blockchains/TerraTests.swift index daa53853b13..02a264beadf 100644 --- a/swift/Tests/Blockchains/TerraTests.swift +++ b/swift/Tests/Blockchains/TerraTests.swift @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,178 +9,124 @@ import WalletCore class TerraTests: XCTestCase { - let privateKey = PrivateKey(data: Data(hexString: "1037f828ca313f4c9e120316e8e9ff25e17f07fe66ba557d5bc5e2eeb7cba8f6")!)! + let privateKey80e8 = PrivateKey(data: Data(hexString: "80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005")!)! // terra1hsk6jryyqjfhp5dhc55tc9jtckygx0ep37hdd2 + let privateKeycf08 = PrivateKey(data: Data(hexString: "cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616")!)! // terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf + let privateKey1037 = PrivateKey(data: Data(hexString: "1037f828ca313f4c9e120316e8e9ff25e17f07fe66ba557d5bc5e2eeb7cba8f6")!)! func testAddress() { - let address = CoinType.terra.deriveAddress(privateKey: privateKey) + let address = CoinType.terraV2.deriveAddress(privateKey: privateKey1037) XCTAssertEqual(address, "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe") - XCTAssertTrue(CoinType.terra.validate(address: "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms")) - XCTAssertTrue(CoinType.terra.validate(address: "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk")) - XCTAssertFalse(CoinType.terra.validate(address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")) + XCTAssertTrue(CoinType.terraV2.validate(address: "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms")) + XCTAssertTrue(CoinType.terraV2.validate(address: "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk")) + XCTAssertFalse(CoinType.terraV2.validate(address: "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")) } - func testRawJSON() { - let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) - let fromAddress = AnyAddress(publicKey: publicKey, coin: .terra).description + func testSignSendTx() { + let publicKey = privateKey80e8.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .terraV2).description + XCTAssertEqual(fromAddress, "terra1hsk6jryyqjfhp5dhc55tc9jtckygx0ep37hdd2") let message = CosmosMessage.with { - $0.rawJsonMessage = CosmosMessage.RawJSON.with { - $0.type = "bank/MsgSend" - $0.value = """ - { - "amount": [{ - "amount": "1000000", - "denom": "uluna" - }], - "from_address": "\(fromAddress)", - "to_address": "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms" - } - """ + $0.sendCoinsMessage = CosmosMessage.Send.with { + $0.fromAddress = fromAddress + $0.toAddress = "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp" + $0.amounts = [CosmosAmount.with { + $0.amount = "1000000" + $0.denom = "uluna" + }] } } let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 3000 + $0.amount = "30000" $0.denom = "uluna" }] } let input = CosmosSigningInput.with { - $0.accountNumber = 158 - $0.chainID = "soju-0013" + $0.signingMode = .protobuf; + $0.accountNumber = 1037 + $0.chainID = "phoenix-1" $0.memo = "" - $0.sequence = 0 + $0.sequence = 1 $0.messages = [message] $0.fee = fee - $0.privateKey = privateKey.data + $0.privateKey = privateKey80e8.data } - let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terraV2) let expectedJSON: String = -""" -{ - "mode": "block", - "tx": { - "msg": [{ - "type": "bank/MsgSend", - "value": { - "from_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", - "to_address": "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms", - "amount": [{ - "denom": "uluna", - "amount": "1000000" - }] - } - }], - "fee": { - "amount": [{ - "denom": "uluna", - "amount": "3000" - }], - "gas": "200000" - }, - "signatures": [{ - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" - }, - "signature": "KPdiVsKpY12JG/VKEJVa/FpMKclxlS0qNNG6VOAypj10R5vY5UX5IgRJET1zNYnH0wvcXxfNXV+s8jtwN2UXiQ==" - }], - "memo": "" - } -} -""" - XCTAssertJSONEqual(expectedJSON, output.json) + """ + { + "tx_bytes": "CpEBCo4BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm4KLHRlcnJhMWhzazZqcnl5cWpmaHA1ZGhjNTV0YzlqdGNreWd4MGVwMzdoZGQyEix0ZXJyYTFqbGdhcXk5bnZuMmhmNXQyc3JhOXljejhzNzd3bmY5bDBrbWdjcBoQCgV1bHVuYRIHMTAwMDAwMBJoClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYARIUCg4KBXVsdW5hEgUzMDAwMBDAmgwaQPh0C3rjzdixIUiyPx3FlWAxzbKILNAcSRVeQnaTl1vsI5DEfYa2oYlUBLqyilcMCcU/iaJLhex30No2ak0Zn1Q=", + "mode": "BROADCAST_MODE_BLOCK" + } + """ + XCTAssertJSONEqual(expectedJSON, output.serialized) + XCTAssertEqual(output.error, "") } - func testSigningTransaction() { - // https://finder.terra.money/soju-0013/tx/1403B07F2D218BCE961CB92D83377A924FEDB54C1F0B62E25C8B93B63470EBF7 - let publicKey = privateKey.getPublicKeySecp256k1(compressed: true) - let fromAddress = AnyAddress(publicKey: publicKey, coin: .terra).description + func testSignWasmTransferTx() { + let publicKey = privateKeycf08.getPublicKeySecp256k1(compressed: true) + let fromAddress = AnyAddress(publicKey: publicKey, coin: .terraV2).description + XCTAssertEqual(fromAddress, "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf") + + let wasmTransferMessage = CosmosMessage.WasmExecuteContractTransfer.with { + $0.senderAddress = fromAddress.description + $0.contractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76" + $0.amount = Data(hexString: "03D090")! // 250000 + $0.recipientAddress = "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp" + } let message = CosmosMessage.with { - $0.sendCoinsMessage = CosmosMessage.Send.with { - $0.fromAddress = fromAddress - $0.toAddress = "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms" - $0.amounts = [CosmosAmount.with { - $0.amount = 1000000 - $0.denom = "uluna" - }] - $0.typePrefix = "bank/MsgSend" - } + $0.wasmExecuteContractTransferMessage = wasmTransferMessage } let fee = CosmosFee.with { $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 3000 + $0.amount = "3000" $0.denom = "uluna" }] } let input = CosmosSigningInput.with { - $0.accountNumber = 158 - $0.chainID = "soju-0013" + $0.signingMode = .protobuf; + $0.accountNumber = 3407705 + $0.chainID = "phoenix-1" $0.memo = "" - $0.sequence = 0 + $0.sequence = 3 $0.messages = [message] $0.fee = fee - $0.privateKey = privateKey.data + $0.privateKey = privateKeycf08.data } - let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terraV2) - let expectedJSON: String = -""" -{ - "mode": "block", - "tx": { - "msg": [{ - "type": "bank/MsgSend", - "value": { - "from_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", - "to_address": "terra1hdp298kaz0eezpgl6scsykxljrje3667d233ms", - "amount": [{ - "denom": "uluna", - "amount": "1000000" - }] - } - }], - "fee": { - "amount": [{ - "denom": "uluna", - "amount": "3000" - }], - "gas": "200000" - }, - "signatures": [{ - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" - }, - "signature": "KPdiVsKpY12JG/VKEJVa/FpMKclxlS0qNNG6VOAypj10R5vY5UX5IgRJET1zNYnH0wvcXxfNXV+s8jtwN2UXiQ==" - }], - "memo": "" - } -} -""" - XCTAssertJSONEqual(expectedJSON, output.json) + XCTAssertJSONEqual(output.serialized, + """ + { + "tx_bytes": "CuUBCuIBCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QSuQEKLHRlcnJhMTh3dWtwODRkcTIyN3d1NG1naDBqbTZuOW5sbmo2cnM4MnBwOXdmEix0ZXJyYTE0ejU2bDBmcDJsc2Y4Nnp5M2h0eTJ6NDdlemtobnRodHI5eXE3NhpbeyJ0cmFuc2ZlciI6eyJhbW91bnQiOiIyNTAwMDAiLCJyZWNpcGllbnQiOiJ0ZXJyYTFqbGdhcXk5bnZuMmhmNXQyc3JhOXljejhzNzd3bmY5bDBrbWdjcCJ9fRJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYAxITCg0KBXVsdW5hEgQzMDAwEMCaDBpAiBGbQaj+jsXE6/FssD3fC77QOxpli9GqsPea+KoNyMIEgVj89Hii+oU1bAEQS4qV0SaE2V6RNy24uCcFTIRbcQ==", + "mode": "BROADCAST_MODE_BLOCK" + } + """ + ) + XCTAssertEqual(output.error, "") } - func testStaking() { - // https://finder.terra.money/soju-0013/tx/4C0A6690ECB601ACB42D3ECAF4C24C0555B5E32E45B09C3B1607B144CD191F87 + func testSignStakeTx() throws { + let stakeMessage = CosmosMessage.Delegate.with { - $0.delegatorAddress = "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe" - $0.validatorAddress = "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + $0.delegatorAddress = "terra1ncfyexz3nrrdru37ahqpp4wen48v7p5nany478" + $0.validatorAddress = "terravaloper1ekq8xuypdxtf3nfmffmydnhny59pjuy0p8wpn7" $0.amount = CosmosAmount.with { - $0.amount = 1000000 + $0.amount = "1000000" // 5 Luna $0.denom = "uluna" } - $0.typePrefix = "staking/MsgDelegate" } let message = CosmosMessage.with { @@ -188,268 +134,84 @@ class TerraTests: XCTestCase { } let fee = CosmosFee.with { - $0.gas = 200000 + $0.gas = 254682 $0.amounts = [CosmosAmount.with { - $0.amount = 3000 + $0.amount = "38203" $0.denom = "uluna" }] } let input = CosmosSigningInput.with { - $0.accountNumber = 158 - $0.chainID = "soju-0013" - $0.memo = "" - $0.sequence = 1 + $0.signingMode = .protobuf; + $0.accountNumber = 127185 + $0.chainID = "phoenix-1" + $0.sequence = 6 $0.messages = [message] $0.fee = fee - $0.privateKey = privateKey.data - } - - let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) - - let expectedJSON = """ -{ - "mode": "block", - "tx": { - "fee": { - "amount": [{ - "amount": "3000", - "denom": "uluna" - }], - "gas": "200000" - }, - "memo": "", - "msg": [{ - "type": "staking/MsgDelegate", - "value": { - "amount": { - "amount": "1000000", - "denom": "uluna" - }, - "delegator_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", - "validator_address": "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" - } - }], - "signatures": [{ - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" - }, - "signature": "F8UJxbkqa0j6dYTk8PymrudBKI3WYhZImRxMFCw0ecFCmPGgNTg7yfpKZo6K6JtnoJaP7bQ4db5e4wnhMCJyAQ==" - }] - } -} -""" - XCTAssertJSONEqual(expectedJSON, output.json) - } - - func testWithdraw() { - // https://finder.terra.money/soju-0013/tx/AE0E4F2B254449950A3A7F41FABCE0B3C846D70F809380313CE3BB323E490BBD - let withdrawMessage = CosmosMessage.WithdrawDelegationReward.with { - $0.delegatorAddress = "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe" - $0.validatorAddress = "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" - $0.typePrefix = "distribution/MsgWithdrawDelegationReward" + $0.privateKey = privateKey1037.data // real key is terra: define... } - let message = CosmosMessage.with { - $0.withdrawStakeRewardMessage = withdrawMessage + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terraV2) + let expected = """ + { + "mode": "BROADCAST_MODE_BLOCK", + "tx_bytes": "Cp8BCpwBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJ1Cix0ZXJyYTFuY2Z5ZXh6M25ycmRydTM3YWhxcHA0d2VuNDh2N3A1bmFueTQ3OBIzdGVycmF2YWxvcGVyMWVrcTh4dXlwZHh0ZjNuZm1mZm15ZG5obnk1OXBqdXkwcDh3cG43GhAKBXVsdW5hEgcxMDAwMDAwEmgKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQNd8YVWZSHWp4AjGe4G4aKOl7d3Lftf3RPKbwV1UYlo5BIECgIIARgGEhQKDgoFdWx1bmESBTM4MjAzENrFDxpAamKyAvWhWCv0nUKz7yiYETpkZETflDvfe1vmuFIy31g+s0u1cgLNo+7jBRXRuzYJXukigtwoLUrxY/C8rowiJw==" } + """ - let fee = CosmosFee.with { - $0.amounts = [CosmosAmount.with { - $0.amount = 3000 - $0.denom = "uluna" - }] - $0.gas = 200000 - } + // https://finder.terra.money/mainnet/tx/4441c65f24783b8f59b20b1b80ee43f1f4f6ff827597d87b6bbc94982b45be0c + XCTAssertJSONEqual(output.serialized, expected) + XCTAssertEqual(output.error, "") + } - let input = CosmosSigningInput.with { - $0.accountNumber = 158 - $0.chainID = "soju-0013" - $0.memo = "" - $0.sequence = 2 - $0.messages = [message] - $0.fee = fee - $0.privateKey = privateKey.data - } + func testSignClaimRewards() throws { + let delegator = "terra1ncfyexz3nrrdru37ahqpp4wen48v7p5nany478" + let validators = [ + "terravaloper1ekq8xuypdxtf3nfmffmydnhny59pjuy0p8wpn7" + ] - let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) - - let expectedJSON = """ -{ - "mode": "block", - "tx": { - "fee": { - "amount": [{ - "amount": "3000", - "denom": "uluna" - }], - "gas": "200000" - }, - "memo": "", - "msg": [{ - "type": "distribution/MsgWithdrawDelegationReward", - "value": { - "delegator_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", - "validator_address": "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" + let withdrawals = validators.map { validator in + CosmosMessage.WithdrawDelegationReward.with { + $0.delegatorAddress = delegator + $0.validatorAddress = validator } - }], - "signatures": [{ - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" - }, - "signature": "Kfwi1uJplzLucXDyQZsJI9v8lMFJFUBLD46+MpwBwYwPJgqPRzSOfyjRpmNou0G/Qe1hbsGEgqb85FQpsgLz+g==" - }] - } -} -""" - XCTAssertJSONEqual(expectedJSON, output.json) - } - - func testUndelegate() { - // https://finder.terra.money/soju-0013/tx/FCF50C180303AECA97F916D0CE0E0937BA4C4D2F6777FFF2AA0D52A9DAF9CCBA - let unstakeMessage = CosmosMessage.Undelegate.with { - $0.delegatorAddress = "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe" - $0.validatorAddress = "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" - $0.amount = CosmosAmount.with { - $0.amount = 500000 - $0.denom = "uluna" + }.map { withdraw in + CosmosMessage.with { + $0.withdrawStakeRewardMessage = withdraw } - $0.typePrefix = "staking/MsgUndelegate" - } - - let message = CosmosMessage.with { - $0.unstakeMessage = unstakeMessage } let fee = CosmosFee.with { - $0.gas = 200000 $0.amounts = [CosmosAmount.with { - $0.amount = 3000 + $0.amount = "29513" $0.denom = "uluna" }] + $0.gas = 196749 } let input = CosmosSigningInput.with { - $0.accountNumber = 158 - $0.chainID = "soju-0013" - $0.memo = "" - $0.sequence = 3 - $0.messages = [message] + $0.signingMode = .protobuf; $0.fee = fee - $0.privateKey = privateKey.data - } - - let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) - - let expectedJSON = """ -{ - "mode": "block", - "tx": { - "fee": { - "amount": [{ - "amount": "3000", - "denom": "uluna" - }], - "gas": "200000" - }, - "memo": "", - "msg": [{ - "type": "staking/MsgUndelegate", - "value": { - "amount": { - "amount": "500000", - "denom": "uluna" - }, - "delegator_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", - "validator_address": "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" - } - }], - "signatures": [{ - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" - }, - "signature": "THf/RxsBr2EhHE2OMHLXfv+qSP9ORbvHgo4OSOS2P95xxGH73wW+t1zIl9cGlIVvcoChwaCg5/iEuvbgXUWpNw==" - }] - } -} -""" - XCTAssertJSONEqual(expectedJSON, output.json) - } - - func testRedlegate() { - // https://finder.terra.money/soju-0013/tx/36CE381BDF72AD7407EEE3859E3349F83B723BE9AD407E9D8C38DEE0C4434D29 - let restakeMessage = CosmosMessage.BeginRedelegate.with { - $0.delegatorAddress = "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe" - $0.validatorSrcAddress = "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" - $0.validatorDstAddress = "terravaloper1rhrptnx87ufpv62c7ngt9yqlz2hr77xr9nkcr9" - $0.amount = CosmosAmount.with { - $0.amount = 500000 - $0.denom = "uluna" - } - $0.typePrefix = "staking/MsgBeginRedelegate" - } - - let message = CosmosMessage.with { - $0.restakeMessage = restakeMessage + $0.accountNumber = 127185 + $0.chainID = "phoenix-1" + $0.sequence = 5 + $0.messages = withdrawals + $0.fee = fee + $0.privateKey = privateKey1037.data // real key is terra: define... } - let fee = CosmosFee.with { - $0.gas = 200000 - $0.amounts = [CosmosAmount.with { - $0.amount = 3000 - $0.denom = "uluna" - }] - } + let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terraV2) - let input = CosmosSigningInput.with { - $0.accountNumber = 158 - $0.chainID = "soju-0013" - $0.memo = "" - $0.sequence = 4 - $0.messages = [message] - $0.fee = fee - $0.privateKey = privateKey.data + let expected = """ + { + "mode": "BROADCAST_MODE_BLOCK", + "tx_bytes": "CqEBCp4BCjcvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1dpdGhkcmF3RGVsZWdhdG9yUmV3YXJkEmMKLHRlcnJhMW5jZnlleHozbnJyZHJ1MzdhaHFwcDR3ZW40OHY3cDVuYW55NDc4EjN0ZXJyYXZhbG9wZXIxZWtxOHh1eXBkeHRmM25mbWZmbXlkbmhueTU5cGp1eTBwOHdwbjcSaApQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjkEgQKAggBGAUSFAoOCgV1bHVuYRIFMjk1MTMQjYEMGkA/bh2va6RRZvkSLnej84dJgSSvbgcHgYDbkRt8wDge03W747BZcuBcg/U5EuE7zBqSJrKUTZl7oUCp//rYlJKV" } + """ - let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .terra) - - let expectedJSON = """ -{ - "mode": "block", - "tx": { - "fee": { - "amount": [{ - "amount": "3000", - "denom": "uluna" - }], - "gas": "200000" - }, - "memo": "", - "msg": [{ - "type": "staking/MsgBeginRedelegate", - "value": { - "amount": { - "amount": "500000", - "denom": "uluna" - }, - "delegator_address": "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe", - "validator_dst_address": "terravaloper1rhrptnx87ufpv62c7ngt9yqlz2hr77xr9nkcr9", - "validator_src_address": "terravaloper1pdx498r0hrc2fj36sjhs8vuhrz9hd2cw0yhqtk" - } - }], - "signatures": [{ - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A13xhVZlIdangCMZ7gbhoo6Xt3ct+1/dE8pvBXVRiWjk" - }, - "signature": "HyEpSz48dkebmBFvwh5xDiiZD0jUdOvzTD3ACMw0rOQ9F3JhK2cPaEx6/ZmYNIrdsPqMNkUnHcDYD1o4IztoEg==" - }] - } -} -""" - XCTAssertJSONEqual(expectedJSON, output.json) + // https://finder.terra.money/mainnet/tx/0e62170ed5407992251d7e161f23c3467e1bea54c7f601953953bdabc7f0c30c + XCTAssertJSONEqual(output.serialized, expected) + XCTAssertEqual(output.error, "") } + } diff --git a/swift/Tests/Blockchains/TezosTests.swift b/swift/Tests/Blockchains/TezosTests.swift index a9416b97322..67a26a6c971 100644 --- a/swift/Tests/Blockchains/TezosTests.swift +++ b/swift/Tests/Blockchains/TezosTests.swift @@ -26,6 +26,93 @@ class TezosTests: XCTestCase { XCTAssertEqual(address.description, "tz1cG2jx3W4bZFeVGBjsTxUAG8tdpTXtE8PT") } + + public func testSigningFA12() { + let privateKeyData = Data(hexString: "363265a0b3f06661001cab8b4f3ca8fd97ae70608184979cf7300836f57ec2d6")! + + let branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp" + var operationList = TezosOperationList() + operationList.branch = branch + + let transactionOperationData = TezosTransactionOperationData.with { + $0.amount = 0 + $0.destination = "KT1EwXFWoG9bYebmF4pYw72aGjwEnBWefgW5" + $0.parameters.fa12Parameters.entrypoint = "transfer"; + $0.parameters.fa12Parameters.from = "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"; + $0.parameters.fa12Parameters.to = "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"; + $0.parameters.fa12Parameters.value = "123"; + } + + let transactionOperation = TezosOperation.with { + $0.source = "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP" + $0.fee = 100000 + $0.counter = 2993172 + $0.gasLimit = 100000 + $0.storageLimit = 0 + $0.kind = .transaction + $0.transactionOperationData = transactionOperationData + } + + operationList.operations = [ transactionOperation ] + + let input = TezosSigningInput.with { + $0.operationList = operationList + $0.privateKey = privateKeyData + } + + let output: TezosSigningOutput = AnySigner.sign(input: input, coin: .tezos) + let expected = "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0694d8b601a08d0600000145bd8a65cc48159d8ea60a55df735b7c5ad45f0e00ffff087472616e736665720000005907070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb012914d768155fba2df319a81136e8e3e573b9cadb1676834490c90212615d271da029b6b0531e290e9063bcdb40bea43627af048b18e036f02be2b6b22fc8b307" + + XCTAssertEqual(output.encoded.hexString, expected) + } + + public func testSigningFA2() { + let privateKeyData = Data(hexString: "363265a0b3f06661001cab8b4f3ca8fd97ae70608184979cf7300836f57ec2d6")! + + let branch = "BKvEAX9HXfJZWYfTQbR1C7B3ADoKY6a1aKVRF7qQqvc9hS8Rr3m" + var operationList = TezosOperationList() + operationList.branch = branch + + let transferInfos = TezosTxs.with{ + $0.to = "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP" + $0.tokenID = "0" + $0.amount = "10" + } + + let transactionOperationData = TezosTransactionOperationData.with { + $0.amount = 0 + $0.destination = "KT1DYk1XDzHredJq1EyNkDindiWDqZyekXGj" + $0.parameters.fa2Parameters.entrypoint = "transfer"; + $0.parameters.fa2Parameters.txsObject = [TezosTxObject.with{ + $0.from = "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP" + $0.txs = [transferInfos] + }] + } + + + + let transactionOperation = TezosOperation.with { + $0.source = "tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP" + $0.fee = 100000 + $0.counter = 2993173 + $0.gasLimit = 100000 + $0.storageLimit = 0 + $0.kind = .transaction + $0.transactionOperationData = transactionOperationData + } + + operationList.operations = [ transactionOperation ] + + let input = TezosSigningInput.with { + $0.operationList = operationList + $0.privateKey = privateKeyData + } + + let output: TezosSigningOutput = AnySigner.sign(input: input, coin: .tezos) + let expected = "1b1f9345dc9f77bd24b09034d1d2f9a28f02ac837f49db54b8d68341f53dc4b76c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0695d8b601a08d0600000136767f88850bae28bfb9f46b73c5e87ede4de12700ffff087472616e7366657200000066020000006107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b5550020000003107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070000000a552d24710d6c59383286700c6c2917b25a6c1fa8b587e593c289dd47704278796792f1e522c1623845ec991e292b0935445e6994850bd03f035a006c5ed93806" + + XCTAssertEqual(output.encoded.hexString, expected) + } public func testSigning() { let privateKeyData = Data(hexString: "c6377a4cc490dc913fc3f0d9cf67d293a32df4547c46cb7e9e33c3b7b97c64d8")! diff --git a/swift/Tests/Blockchains/ZcoinTests.swift b/swift/Tests/Blockchains/ZcoinTests.swift index 0aabb50b9a2..7014d4e274d 100644 --- a/swift/Tests/Blockchains/ZcoinTests.swift +++ b/swift/Tests/Blockchains/ZcoinTests.swift @@ -8,11 +8,11 @@ import XCTest import WalletCore class ZcoinTests: XCTestCase { - let zcoin = CoinType.zcoin + let coin = CoinType.firo func testValidAddresses() { - XCTAssertTrue(zcoin.validate(address: "a4YtT82mWWxHZhLmdx7e5aroW92dqJoRs3")) - XCTAssertTrue(zcoin.validate(address: "4CFa4fnAQvFz4VpikGNzQ9XfCDXMmdk6sh")) + XCTAssertTrue(coin.validate(address: "a4YtT82mWWxHZhLmdx7e5aroW92dqJoRs3")) + XCTAssertTrue(coin.validate(address: "4CFa4fnAQvFz4VpikGNzQ9XfCDXMmdk6sh")) } func testInvalidAddresses() { @@ -23,7 +23,7 @@ class ZcoinTests: XCTestCase { "Xm1iDLBP5tdxTxc6t7uJBCVjC4L2A5vB2J", "TKjdnbJxP4yHeLTHZ86DGnFFY6QhTjuBv2", ] { - XCTAssertFalse(zcoin.validate(address: addr)) + XCTAssertFalse(coin.validate(address: addr)) } } } diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index de4c865949b..5dd2b6d4ef4 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -47,7 +47,7 @@ class CoinAddressDerivationTests: XCTestCase { let expectedResult = "0x3E6FFC80745E6669135a76F4A7ce6BCF02436e04" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .cardano: - let expectedResult = "addr1snpa4z7ntyfszv7ckquprdw75w4qjqh0qmya9jtkpxxlzxghlqyvv7l0yjamh8fxraw06p3ua8sj2g2gv98v4849s43t9g2999kquuu5egnprk" + let expectedResult = "addr1qyr8jjfnypp95eq74aqzn7ss687ehxclgj7mu6gratmg3mul2040vt35dypp042awzsjk5xm3zr3zm5qh7454uwdv08s84ray2" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .cosmos: let expectedResult = "cosmos142j9u5eaduzd7faumygud6ruhdwme98qsy2ekn" @@ -74,14 +74,31 @@ class CoinAddressDerivationTests: XCTestCase { .smartChain, .polygon, .optimism, + .zksync, .arbitrum, .ecochain, .avalancheCChain, .xdai, .fantom, - .ronin: + .celo, + .cronosChain, + .smartBitcoinCash, + .kuCoinCommunityChain, + .boba, + .metis, + .aurora, + .evmos, + .moonriver, + .moonbeam, + .kavaEvm, + .klaytn, + .meter, + .okxchain: let expectedResult = "0x8f348F300873Fd5DA36950B2aC75a26584584feE" assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .ronin: + let expectedResult = "ronin:8f348F300873Fd5DA36950B2aC75a26584584feE" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ethereumClassic: let expectedResult = "0x078bA3228F3E6C08bEEac9A005de0b7e7089aD1c" assertCoinDerivation(coin, expectedResult, derivedAddress, address) @@ -124,6 +141,9 @@ class CoinAddressDerivationTests: XCTestCase { case .neo: let expectedResult = "AT6w7PJvwPcSqHvtbNBY2aHPDv12eW5Uuf" assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .nervos: + let expectedResult = "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02wectaumxn0664yw2jd53lqk4mxg3"; + assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .nimiq: let expectedResult = "NQ76 7AVR EHDA N05U X7SY XB14 XJU7 8ERV GM6H" assertCoinDerivation(coin, expectedResult, derivedAddress, address) @@ -160,7 +180,8 @@ class CoinAddressDerivationTests: XCTestCase { case .stellar: let expectedResult = "GA3H6I4C5XUBYGVB66KXR27JV5KS3APSTKRUWOIXZ5MVWZKVTLXWKZ2P" assertCoinDerivation(coin, expectedResult, derivedAddress, address) - case .terra: + case .terra, + .terraV2: let expectedResult = "terra1rh402g98t7sly8trzqw5cyracntlep6qe3smug" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .tezos: @@ -196,7 +217,7 @@ class CoinAddressDerivationTests: XCTestCase { case .zcash: let expectedResult = "t1YYnByMzdGhQv3W3rnjHMrJs6HH4Y231gy" assertCoinDerivation(coin, expectedResult, derivedAddress, address) - case .zcoin: + case .firo: let expectedResult = "aEd5XFChyXobvEics2ppAqgK3Bgusjxtik" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .zelcash: @@ -220,8 +241,20 @@ class CoinAddressDerivationTests: XCTestCase { case .cryptoOrg: let expectedResult = "cro16fdf785ejm00jf9a24d23pzqzjh2h05klxjwu8" assertCoinDerivation(coin, expectedResult, derivedAddress, address) - case .celo: - let expectedResult = "0xea1ac53e7Ccb5b47cdE341C118615Ef1862e3CF5" + case .osmosis: + let expectedResult = "osmo142j9u5eaduzd7faumygud6ruhdwme98qclefqp" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .ecash: + let expectedResult = "ecash:qpelrdn7a0hcucjlf9ascz3lkxv7r3rffgzn6x5377" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .nativeEvmos: + let expectedResult = "evmos13u6g7vqgw074mgmf2ze2cadzvkz9snlwstd20d" + assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .everscale: + let expectedResult = "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04"; + assertCoinDerivation(coin, expectedResult, derivedAddress, address) + case .aptos: + let expectedResult = "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"; assertCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() diff --git a/swift/Tests/CoinTypeTests.swift b/swift/Tests/CoinTypeTests.swift index 5b49e9325d5..353b85cf729 100644 --- a/swift/Tests/CoinTypeTests.swift +++ b/swift/Tests/CoinTypeTests.swift @@ -30,4 +30,10 @@ class CoinTypeTests: XCTestCase { XCTAssertEqual(CoinType.avalancheCChain.rawValue, 10009000) XCTAssertEqual(CoinType.xdai.rawValue, 10000100) } + + func testCoinDerivation() { + XCTAssertEqual(CoinType.bitcoin.derivationPath(), "m/84'/0'/0'/0/0") + XCTAssertEqual(CoinType.bitcoin.derivationPathWithDerivation(derivation: Derivation.bitcoinLegacy), "m/44'/0'/0'/0/0") + XCTAssertEqual(CoinType.solana.derivationPathWithDerivation(derivation: Derivation.solanaSolana), "m/44'/501'/0'/0'") + } } diff --git a/swift/Tests/DerivationPathTests.swift b/swift/Tests/DerivationPathTests.swift index e24ab38190a..3867d434847 100644 --- a/swift/Tests/DerivationPathTests.swift +++ b/swift/Tests/DerivationPathTests.swift @@ -10,6 +10,7 @@ import XCTest class DerivationPathTests: XCTestCase { func testInitWithIndices() { let path = DerivationPath(purpose: .bip44, coin: CoinType.ethereum.slip44Id, account: 0, change: 0, address: 0) + XCTAssertEqual(path.indices[0], DerivationPath.Index(44, hardened: true)) XCTAssertEqual(path.indices[1], DerivationPath.Index(60, hardened: true)) XCTAssertEqual(path.indices[2], DerivationPath.Index(0, hardened: true)) @@ -18,20 +19,20 @@ class DerivationPathTests: XCTestCase { } func testInitWithString() { - let path = DerivationPath("m/44'/60'/0'/0/0") - - XCTAssertNotNil(path) - XCTAssertEqual(path?.indices[0], DerivationPath.Index(44, hardened: true)) - XCTAssertEqual(path?.indices[1], DerivationPath.Index(60, hardened: true)) - XCTAssertEqual(path?.indices[2], DerivationPath.Index(0, hardened: true)) - XCTAssertEqual(path?.indices[3], DerivationPath.Index(0, hardened: false)) - XCTAssertEqual(path?.indices[4], DerivationPath.Index(0, hardened: false)) - - XCTAssertEqual(path?.purpose, Purpose(rawValue: 44)!) - XCTAssertEqual(path?.coinType, 60) - XCTAssertEqual(path?.account, 0) - XCTAssertEqual(path?.change, 0) - XCTAssertEqual(path?.address, 0) + let path = DerivationPath(string: "m/44'/60'/0'/0/0")! + let indices = path.indices + + XCTAssertEqual(indices[0], DerivationPath.Index(value: 44, hardened: true)) + XCTAssertEqual(indices[1], DerivationPath.Index(value: 60, hardened: true)) + XCTAssertEqual(indices[2], DerivationPath.Index(value: 0, hardened: true)) + XCTAssertEqual(indices[3], DerivationPath.Index(value: 0, hardened: false)) + XCTAssertEqual(indices[4], DerivationPath.Index(value: 0, hardened: false)) + + XCTAssertEqual(path.purpose, Purpose(rawValue: 44)!) + XCTAssertEqual(path.coinType, 60) + XCTAssertEqual(path.account, 0) + XCTAssertEqual(path.change, 0) + XCTAssertEqual(path.address, 0) } func testInitInvalid() { @@ -41,12 +42,14 @@ class DerivationPathTests: XCTestCase { func testDescription() { let path = DerivationPath("m/44'/60'/0'/0/0") + XCTAssertEqual(path?.description, "m/44'/60'/0'/0/0") } func testEqual() { let path1 = DerivationPath("m/44'/60'/0'/0/0") let path2 = DerivationPath("44'/60'/0'/0/0") + XCTAssertNotNil(path1) XCTAssertNotNil(path2) XCTAssertEqual(path1, path2) diff --git a/swift/Tests/HDWalletTests.swift b/swift/Tests/HDWalletTests.swift index 37d268ec84c..e6e5b8b6b46 100644 --- a/swift/Tests/HDWalletTests.swift +++ b/swift/Tests/HDWalletTests.swift @@ -152,7 +152,7 @@ class HDWalletTests: XCTestCase { } func testDeriveZcoin() { - let zcoin = CoinType.zcoin + let zcoin = CoinType.firo let wallet = HDWallet.test let key = wallet.getKeyForCoin(coin: zcoin) let address = zcoin.deriveAddress(privateKey: key) @@ -386,10 +386,10 @@ class HDWalletTests: XCTestCase { XCTAssertEqual("RHQmrg7nNFnRUwg2mH7GafhRY3ZaF6FB2x", address) } - func testDeriveTerra() { - let coin = CoinType.terra + func testDeriveTerraV2() { + let coin = CoinType.terraV2 let key = HDWallet.test.getKeyForCoin(coin: coin) - let address = CoinType.terra.deriveAddress(privateKey: key) + let address = CoinType.terraV2.deriveAddress(privateKey: key) XCTAssertEqual(address, "terra1jf9aaj9myrzsnmpdr7twecnaftzmku2mhs2hfe") } diff --git a/swift/Tests/HashTests.swift b/swift/Tests/HashTests.swift index b85f3325d8c..fc192dceebe 100644 --- a/swift/Tests/HashTests.swift +++ b/swift/Tests/HashTests.swift @@ -15,11 +15,4 @@ class HashTests: XCTestCase { XCTAssertEqual(hashed.hexString, "e30d87cfa2a75db545eac4d61baf970366a8357c7f72fa95b52d0accb698f13a") } - - func testTwoXXhash64() { - let message = "ReservedBalance".data(using: .utf8)! - let hashed = Hash.twoXXHash64Concat(data: message) - - XCTAssertEqual(hashed.hexString, "3c22813def93ef32c365b55cb92f10f9") - } } diff --git a/swift/Tests/Keystore/KeyStoreTests.swift b/swift/Tests/Keystore/KeyStoreTests.swift index 7c205b766cd..3ae5529e79e 100755 --- a/swift/Tests/Keystore/KeyStoreTests.swift +++ b/swift/Tests/Keystore/KeyStoreTests.swift @@ -217,27 +217,33 @@ class KeyStoreTests: XCTestCase { "address": "bc1q4zehq85jqx9zzgzvzn9t64yjy66nunn3vehuv6", "coin": 0, "derivationPath": "m/84'/0'/0'/0/0", - "extendedPublicKey": "zpub6qMRMrwcEYaqjf8wSpNqtBfUee6MqpQjrZNKfj5a48EUFUx2yUmfkDJMdHwWvkg8SjdS3ua6dy9ofMrzrytTfdyy2pXg344yFwm2Ta9cm6Q" + "extendedPublicKey": "zpub6qMRMrwcEYaqjf8wSpNqtBfUee6MqpQjrZNKfj5a48EUFUx2yUmfkDJMdHwWvkg8SjdS3ua6dy9ofMrzrytTfdyy2pXg344yFwm2Ta9cm6Q", + "publicKey": "0334c47fa4eafef196f62eb53192a39bc36c5823ad4bd23db503170b9d3dbe80fd" }, { "address": "0x33F44330cc4253cCd4ce4224186DB9baCe2190ea", "coin": 60, - "derivationPath": "m/44'/60'/0'/0/0" + "derivationPath": "m/44'/60'/0'/0/0", + "publicKey": "04906ab3a756b952c1f2ad41daf0c82cc12fb155cd73919b904ffb2630866abfe3feae7169c3e322465d119f4b20465b2a98f8bcb9e19bf22d84ba04e277c1c6ee" }, { "address": "bnb1njuczq3hgvupu2vnczrjz7rc8x4uxlmhjyq95z", "coin": 714, - "derivationPath": "m/44'/714'/0'/0/0" + "derivationPath": "m/44'/714'/0'/0/0", + "publicKey": "03397cf6ee9ddfee746dc750e9b1abd9824ff8fec3e29bb09b3b2c330a88b605b8" }, { "address": "0x5dEc7A9299360aEb44c83B8F730F2BF5Dd1688bC", "coin": 10000714, - "derivationPath": "m/44'/714'/0'/0/0" + "derivationPath": "m/44'/714'/0'/0/0", + "publicKey": "04397cf6ee9ddfee746dc750e9b1abd9824ff8fec3e29bb09b3b2c330a88b605b81e46b99afe5dd84a5420b9e54f04b26aeb034f12849145a8163255875af1aef7" }, { "address": "0x33F44330cc4253cCd4ce4224186DB9baCe2190ea", "coin": 20000714, - "derivationPath": "m/44'/60'/0'/0/0" + "derivationPath": "m/44'/60'/0'/0/0", + "publicKey": "04906ab3a756b952c1f2ad41daf0c82cc12fb155cd73919b904ffb2630866abfe3feae7169c3e322465d119f4b20465b2a98f8bcb9e19bf22d84ba04e277c1c6ee" }, { "address": "838f8aeba6bb083b5b6e22030fb051eaf1a8b6cd692d4ad533cba60c77e6b8f2", "coin": 397, - "derivationPath": "m/44'/397'/0'" + "derivationPath": "m/44'/397'/0'", + "publicKey": "838f8aeba6bb083b5b6e22030fb051eaf1a8b6cd692d4ad533cba60c77e6b8f2" }], "crypto": { "cipher": "aes-128-ctr", @@ -264,6 +270,13 @@ class KeyStoreTests: XCTestCase { let password = "e28ddf66cec05c1fc09939a00628b230459202b2493fccac288038ef37815723" let keyStore = try KeyStore(keyDirectory: keyDirectory) + + // Fill public key if needed + let btcAccount = try keyStore.bnbWallet.getAccount(password: password, coin: .bitcoin) + XCTAssertEqual(btcAccount.publicKey, "0334c47fa4eafef196f62eb53192a39bc36c5823ad4bd23db503170b9d3dbe80fd") + + // Fix all empty + _ = keyStore.bnbWallet.key.fixAddresses(password: Data(password.utf8)) _ = try keyStore.addAccounts(wallet: keyStore.bnbWallet, coins: [.smartChainLegacy, .smartChain], password: password) // simulate migration code @@ -339,6 +352,7 @@ class KeyStoreTests: XCTestCase { for account in accounts { XCTAssertFalse(account.address.isEmpty) + XCTAssertFalse(account.publicKey.isEmpty) } XCTAssertEqual(coins.count, wallet.accounts.count) @@ -385,6 +399,31 @@ class KeyStoreTests: XCTestCase { } } + func testCreateMultiAccount() throws { + let mnemonic = "team engine square letter hero song dizzy scrub tornado fabric divert saddle" + let password = "password" + let keyStore = try KeyStore(keyDirectory: keyDirectory) + let wallet = try keyStore.import(mnemonic: mnemonic, name: "name", encryptPassword: password, coins: [.bitcoin, .solana]) + + _ = try keyStore.addAccounts(wallet: wallet, coins: [.bitcoin, .solana], password: password) + + let btc1 = try wallet.getAccount(password: password, coin: .bitcoin, derivation: .default) + XCTAssertEqual(btc1.address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny") + XCTAssertEqual(btc1.extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn") + + let btc2 = try wallet.getAccount(password: password, coin: .bitcoin, derivation: .bitcoinLegacy) + XCTAssertEqual(btc2.address, "1NyRyFewhZcWMa9XCj3bBxSXPXyoSg8dKz") + XCTAssertEqual(btc2.extendedPublicKey, "xpub6CR52eaUuVb4kXAVyHC2i5ZuqJ37oWNPZFtjXaazFPXZD45DwWBYEBLdrF7fmCR9pgBuCA9Q57zZfyJjDUBDNtWkhWuGHNYKLgDHpqrHsxV") + + let solana1 = try wallet.getAccount(password: password, coin: .solana, derivation: .default) + XCTAssertEqual(solana1.address, "HiipoCKL8hX2RVmJTz3vaLy34hS2zLhWWMkUWtw85TmZ") + XCTAssertEqual(solana1.derivationPath, "m/44'/501'/0'") + + let solana2 = try wallet.getAccount(password: password, coin: .solana, derivation: .solanaSolana) + XCTAssertEqual(solana2.address, "CgWJeEWkiYqosy1ba7a3wn9HAQuHyK48xs3LM4SSDc1C") + XCTAssertEqual(solana2.derivationPath, "m/44'/501'/0'/0'") + } + func createTempDirURL() throws -> URL { let dir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("keystore") try? fileManager.removeItem(at: dir) diff --git a/swift/Tests/Keystore/KeystoreKeyTests.swift b/swift/Tests/Keystore/KeystoreKeyTests.swift index dafa208495b..b33659ceaef 100755 --- a/swift/Tests/Keystore/KeystoreKeyTests.swift +++ b/swift/Tests/Keystore/KeystoreKeyTests.swift @@ -119,4 +119,25 @@ class KeystoreKeyTests: XCTestCase { let data = keystore.decryptPrivateKey(password: password) XCTAssertEqual(data?.hexString, "4357b2f9a6150ba969bc52f01c98cce5313595fe49f2d08303759c73e5c7a46c") } + + struct KdfParams: Decodable { + let dklen: Int + let n: Int + } + + struct EncryptionParameters: Decodable { + let kdf: String + let kdfparams: KdfParams + } + + func testEncryptionParameters() { + let url = Bundle(for: type(of: self)).url(forResource: "key", withExtension: "json")! + let key = StoredKey.load(path: url.path)! + + let paramsData = key.encryptionParameters!.data(using: .utf8)! + let params = try! JSONDecoder().decode(EncryptionParameters.self, from: paramsData) + + XCTAssertEqual(params.kdf, "scrypt"); + XCTAssertEqual(params.kdfparams.n, 262144); + } } diff --git a/swift/Tests/PBKDF2Tests.swift b/swift/Tests/PBKDF2Tests.swift new file mode 100644 index 00000000000..c5ccbadc1af --- /dev/null +++ b/swift/Tests/PBKDF2Tests.swift @@ -0,0 +1,42 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class PBKDF2Tests: XCTestCase { + + let password = "password".data(using: .utf8)! + let salt = "salt".data(using: .utf8)! + let salt2 = Data(base64Encoded: "kNHS+Mx//slRsmLF9396HQ==")! + + func testSha256Hmac() { + + let key = PBKDF2.hmacSha256(password: password, salt: salt, iterations: 1, dkLen: 20)! + + XCTAssertEqual(key.hexString, "120fb6cffcf8b32c43e7225256c4f837a86548c9") + + let key2 = PBKDF2.hmacSha256(password: password, salt: salt, iterations: 4096, dkLen: 20)! + + XCTAssertEqual(key2.hexString, "c5e478d59288c841aa530db6845c4c8d962893a0") + + let key3 = PBKDF2.hmacSha256(password: password, salt: salt2, iterations: 100, dkLen: 32)! + XCTAssertEqual(key3.hexString, "9cf33ebd3542c691fac6f61609a8d13355a0adf4d15eed77cc9d13f792b77c3a") + } + + func testSha512Hmac() { + let key = PBKDF2.hmacSha512(password: password, salt: salt, iterations: 1, dkLen: 20)! + + XCTAssertEqual(key.hexString, "867f70cf1ade02cff3752599a3a53dc4af34c7a6") + + let key2 = PBKDF2.hmacSha512(password: password, salt: salt, iterations: 4096, dkLen: 20)! + + XCTAssertEqual(key2.hexString, "d197b1b33db0143e018b12f3d1d1479e6cdebdcc") + + let key3 = PBKDF2.hmacSha512(password: password, salt: salt2, iterations: 100, dkLen: 32)! + XCTAssertEqual(key3.hexString, "6a9a209f35be9212118ea055e11b545451b53b686608a6362d59ddf31a2b3ce0") + } +} diff --git a/swift/Tests/PrivateKeyTests.swift b/swift/Tests/PrivateKeyTests.swift index 6ecdf044344..ae363969f17 100644 --- a/swift/Tests/PrivateKeyTests.swift +++ b/swift/Tests/PrivateKeyTests.swift @@ -79,8 +79,8 @@ class PrivateKeyTests: XCTestCase { let message = "hello schnorr".data(using: .utf8)! - let sig = privateKey.signSchnorr(message: message, curve: .secp256k1)! - let verified = publicKey.verifySchnorr(signature: sig, message: message) + let sig = privateKey.signZilliqaSchnorr(message: message)! + let verified = publicKey.verifyZilliqaSchnorr(signature: sig, message: message) XCTAssertEqual(sig.hexString, "d166b1ae7892c5ef541461dc12a50214d0681b63d8037cda29a3fe6af8bb973e4ea94624d85bc0010bdc1b38d05198328fae21254adc2bf5feaf2804d54dba55") XCTAssertTrue(verified) diff --git a/swift/Tests/TransactionCompilerTests.swift b/swift/Tests/TransactionCompilerTests.swift new file mode 100644 index 00000000000..b5e147a65f5 --- /dev/null +++ b/swift/Tests/TransactionCompilerTests.swift @@ -0,0 +1,176 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import XCTest +import WalletCore + +class TransactionCompilerTests: XCTestCase { + override func setUp() { + continueAfterFailure = false + } + + func testBitcoinCompileWithSignatures() throws { + // Test external signining with a Bitcoin transaction with 3 input UTXOs, all used, but only using 2 public keys. + // Three signatures are neeeded. This illustrates that order of UTXOs/hashes is not always the same. + + let revUtxoHash0 = Data(hexString: "07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa8")! + let revUtxoHash1 = Data(hexString: "d6892a5aa54e3b8fe430efd23f49a8950733aaa9d7c915d9989179f48dd1905e")! + let revUtxoHash2 = Data(hexString: "6021efcf7555f90627364339fc921139dd40a06ccb2cb2a2a4f8f4ea7a2dc74d")! + let inPubKey0 = Data(hexString: "024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382")! + let inPubKey1 = Data(hexString: "0217142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc")! + let inPubKeyHash0 = Data(hexString: "bd92088bb7e82d611a9b94fbb74a0908152b784f")! + let inPubKeyHash1 = Data(hexString: "6641abedacf9483b793afe1718689cc9420bbb1c")! + + // Test data: Input UTXO infos + struct UtxoInfo { + let revUtxoHash: Data + let publicKey: Data + let amount: Int64 + let index: UInt32 + } + let utxoInfos: [UtxoInfo] = [ + // first + UtxoInfo(revUtxoHash: revUtxoHash0, publicKey: inPubKey0, amount: 600000, index: 0), + // second UTXO, with same pubkey + UtxoInfo(revUtxoHash: revUtxoHash1, publicKey: inPubKey0, amount: 500000, index: 1), + // third UTXO, with different pubkey + UtxoInfo(revUtxoHash: revUtxoHash2, publicKey: inPubKey1, amount: 400000, index: 0), + ] + + // Signature infos, indexed by pubkeyhash+hash + struct SignatureInfo { + let signature: Data + let publicKey: Data + } + let signatureInfos: [String: SignatureInfo] = [ + inPubKeyHash0.hexString + "+" + "a296bead4172007be69b21971a790e076388666c162a9505698415f1b003ebd7": + SignatureInfo(signature: Data(hexString: "304402201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f2a40")!, publicKey: inPubKey0), + inPubKeyHash1.hexString + "+" + "505f527f00e15fcc5a2d2416c9970beb57dfdfaca99e572a01f143b24dd8fab6": + SignatureInfo(signature: Data(hexString: "3044022041294880caa09bb1b653775310fcdd1458da6b8e7d7fae34e37966414fe115820220646397c9d2513edc5974ecc336e9b287de0cdf071c366f3b3dc3ff309213e4e4")!, publicKey: inPubKey1), + inPubKeyHash0.hexString + "+" + "60ed6e9371e5ddc72fd88e46a12cb2f68516ebd307c0fd31b1b55cf767272101": + SignatureInfo(signature: Data(hexString: "30440220764e3d5b3971c4b3e70b23fb700a7462a6fe519d9830e863a1f8388c402ad0b102207e777f7972c636961f92375a2774af3b7a2a04190251bbcb31d19c70927952dc")!, publicKey: inPubKey0), + ] + + let coin = CoinType.bitcoin + let ownAddress = "bc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z00ppggv" + + // Setup input for Plan + var signingInput = BitcoinSigningInput.with { + $0.coinType = coin.rawValue + $0.hashType = BitcoinSigHashType.all.rawValue + $0.amount = 1200000 + $0.useMaxAmount = false + $0.byteFee = 1 + $0.toAddress = "bc1q2dsdlq3343vk29runkgv4yc292hmq53jedfjmp" + $0.changeAddress = ownAddress + } + + // process UTXOs + var count: UInt32 = 0 + for u in utxoInfos { + let publicKey = PublicKey(data: u.publicKey, type: PublicKeyType.secp256k1) + let address = SegwitAddress(hrp: .bitcoin, publicKey: publicKey!) + if (count == 0) { XCTAssertEqual(address.description, ownAddress) } + if (count == 1) { XCTAssertEqual(address.description, ownAddress) } + if (count == 2) { XCTAssertEqual(address.description, "bc1qveq6hmdvl9yrk7f6lct3s6yue9pqhwcuxedggg") } + + let utxoScript = BitcoinScript.lockScriptForAddress(address: address.description, coin: coin) + if (count == 0) { XCTAssertEqual(utxoScript.data.hexString, "0014bd92088bb7e82d611a9b94fbb74a0908152b784f") } + if (count == 1) { XCTAssertEqual(utxoScript.data.hexString, "0014bd92088bb7e82d611a9b94fbb74a0908152b784f") } + if (count == 2) { XCTAssertEqual(utxoScript.data.hexString, "00146641abedacf9483b793afe1718689cc9420bbb1c") } + + let keyHash: Data = utxoScript.matchPayToWitnessPublicKeyHash()! + if (count == 0) { XCTAssertEqual(keyHash.hexString, inPubKeyHash0.hexString) } + if (count == 1) { XCTAssertEqual(keyHash.hexString, inPubKeyHash0.hexString) } + if (count == 2) { XCTAssertEqual(keyHash.hexString, inPubKeyHash1.hexString) } + + let redeemScript = BitcoinScript.buildPayToPublicKeyHash(hash: keyHash) + if (count == 0) { XCTAssertEqual(redeemScript.data.hexString, "76a914bd92088bb7e82d611a9b94fbb74a0908152b784f88ac") } + if (count == 1) { XCTAssertEqual(redeemScript.data.hexString, "76a914bd92088bb7e82d611a9b94fbb74a0908152b784f88ac") } + if (count == 2) { XCTAssertEqual(redeemScript.data.hexString, "76a9146641abedacf9483b793afe1718689cc9420bbb1c88ac") } + signingInput.scripts[keyHash.hexString] = redeemScript.data + + let outPoint = BitcoinOutPoint.with { + $0.hash = u.revUtxoHash + $0.index = u.index + $0.sequence = UInt32.max + } + let utxo = BitcoinUnspentTransaction.with { + $0.script = utxoScript.data + $0.amount = u.amount + $0.outPoint = outPoint + } + signingInput.utxo.append(utxo) + + count += 1 + } + XCTAssertEqual(count, 3) + XCTAssertEqual(signingInput.utxo.count, 3) + + // Plan + let plan: BitcoinTransactionPlan = AnySigner.plan(input: signingInput, coin: coin) + + // At this point plan can be checked, assume it is accepted unmodified + XCTAssertEqual(plan.amount, 1200000) + XCTAssertEqual(plan.fee, 277) + XCTAssertEqual(plan.change, 299723) + XCTAssertEqual(plan.utxos.count, 3) + // Note that UTXOs happen to be in reverse order compared to the input + XCTAssertEqual(plan.utxos[0].outPoint.hash.hexString, revUtxoHash2.hexString) + XCTAssertEqual(plan.utxos[1].outPoint.hash.hexString, revUtxoHash1.hexString) + XCTAssertEqual(plan.utxos[2].outPoint.hash.hexString, revUtxoHash0.hexString) + + // Extend input with accepted plan + signingInput.plan = plan + + // Serialize input + let txInputData = try signingInput.serializedData() + XCTAssertEqual(txInputData.count, 692) + + /// Step 2: Obtain preimage hashes + let preImageHashes = TransactionCompiler.preImageHashes(coinType: coin, txInputData: txInputData) + let preSigningOutput: BitcoinPreSigningOutput = try BitcoinPreSigningOutput(serializedData: preImageHashes) + + XCTAssertEqual(preSigningOutput.error, CommonSigningError.ok) + XCTAssertEqual(preSigningOutput.hashPublicKeys[0].dataHash.hexString, "505f527f00e15fcc5a2d2416c9970beb57dfdfaca99e572a01f143b24dd8fab6") + XCTAssertEqual(preSigningOutput.hashPublicKeys[1].dataHash.hexString, "a296bead4172007be69b21971a790e076388666c162a9505698415f1b003ebd7") + XCTAssertEqual(preSigningOutput.hashPublicKeys[2].dataHash.hexString, "60ed6e9371e5ddc72fd88e46a12cb2f68516ebd307c0fd31b1b55cf767272101") + XCTAssertEqual(preSigningOutput.hashPublicKeys[0].publicKeyHash.hexString, inPubKeyHash1.hexString) + XCTAssertEqual(preSigningOutput.hashPublicKeys[1].publicKeyHash.hexString, inPubKeyHash0.hexString) + XCTAssertEqual(preSigningOutput.hashPublicKeys[2].publicKeyHash.hexString, inPubKeyHash0.hexString) + + // Simulate signatures, normally they are obtained from external source, e.g. a signature server. + var signatureVec = DataVector() + var pubkeyVec = DataVector() + for h in preSigningOutput.hashPublicKeys { + let preImageHash = h.dataHash + let pubkeyHash = h.publicKeyHash + + let key: String = pubkeyHash.hexString + "+" + preImageHash.hexString + XCTAssertTrue(signatureInfos.contains { $0.key == key }) + let sigInfo: SignatureInfo = signatureInfos[key]! + let publicKeyData = sigInfo.publicKey + let publicKey = PublicKey(data: publicKeyData, type: PublicKeyType.secp256k1) + let signature = sigInfo.signature + + signatureVec.add(data: signature) + pubkeyVec.add(data: publicKeyData) + + // Verify signature (pubkey & hash & signature) + publicKey?.verifyAsDER(signature: signature, message: preImageHash) + } + + /// Step 3: Compile transaction info + let compileWithSignatures = TransactionCompiler.compileWithSignatures(coinType: coin, txInputData: txInputData, signatures: signatureVec, publicKeys: pubkeyVec) + + let ExpectedTx = "010000000001036021efcf7555f90627364339fc921139dd40a06ccb2cb2a2a4f8f4ea7a2dc74d0000000000ffffffffd6892a5aa54e3b8fe430efd23f49a8950733aaa9d7c915d9989179f48dd1905e0100000000ffffffff07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa80000000000ffffffff02804f1200000000001600145360df8231ac5965147c9d90ca930a2aafb05232cb92040000000000160014bd92088bb7e82d611a9b94fbb74a0908152b784f02473044022041294880caa09bb1b653775310fcdd1458da6b8e7d7fae34e37966414fe115820220646397c9d2513edc5974ecc336e9b287de0cdf071c366f3b3dc3ff309213e4e401210217142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc0247304402201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f2a400121024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382024730440220764e3d5b3971c4b3e70b23fb700a7462a6fe519d9830e863a1f8388c402ad0b102207e777f7972c636961f92375a2774af3b7a2a04190251bbcb31d19c70927952dc0121024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb49338200000000" + + XCTAssertEqual(compileWithSignatures.count, 786) + let output: BitcoinSigningOutput = try BitcoinSigningOutput(serializedData: compileWithSignatures) + XCTAssertEqual(output.encoded.count, 518) + XCTAssertEqual(output.encoded.hexString, ExpectedTx) + } +} diff --git a/swift/Tests/UniversalAssetIDTests.swift b/swift/Tests/UniversalAssetIDTests.swift index 51498a08c2c..73ad5ac8358 100644 --- a/swift/Tests/UniversalAssetIDTests.swift +++ b/swift/Tests/UniversalAssetIDTests.swift @@ -29,7 +29,7 @@ class UniversalAssetIDTests: XCTestCase { XCTAssertEqual(busd.description, "c714_tBUSD-BD1") } - func testEqutable() { + func testEquatable() { XCTAssertEqual( UniversalAssetID(coin: .ethereum), diff --git a/swift/common-xcframework.yml b/swift/common-xcframework.yml index c9531eb76a0..fead1284c2c 100644 --- a/swift/common-xcframework.yml +++ b/swift/common-xcframework.yml @@ -11,7 +11,7 @@ settings: ENABLE_BITCODE: YES HEADER_SEARCH_PATHS: $(SRCROOT)/wallet-core ${SRCROOT}/trezor-crypto/crypto SYSTEM_HEADER_SEARCH_PATHS: ${SRCROOT}/include ${SRCROOT}/../build/local/include ${SRCROOT}/trezor-crypto/include $(SRCROOT)/protobuf /usr/local/include /opt/homebrew/include - CLANG_CXX_LANGUAGE_STANDARD: c++17 + CLANG_CXX_LANGUAGE_STANDARD: c++20 GCC_WARN_64_TO_32_BIT_CONVERSION: NO targets: @@ -109,7 +109,8 @@ targets: - trezor-crypto/crypto/groestl.c - trezor-crypto/crypto/hmac_drbg.c - trezor-crypto/crypto/rfc6979.c - - trezor-crypto/crypto/schnorr.c + - trezor-crypto/crypto/zilliqa.c + - trezor-crypto/crypto/cardano.c - trezor-crypto/crypto/shamir.c - trezor-crypto/crypto/sodium/private/fe_25_5/fe.c - trezor-crypto/crypto/sodium/private/ed25519_ref10.c @@ -141,11 +142,15 @@ targets: - protobuf/google/protobuf/extension_set_heavy.cc - protobuf/google/protobuf/field_mask.pb.cc - protobuf/google/protobuf/generated_enum_util.cc + - protobuf/google/protobuf/generated_message_bases.cc - protobuf/google/protobuf/generated_message_reflection.cc - protobuf/google/protobuf/generated_message_table_driven.cc - protobuf/google/protobuf/generated_message_table_driven_lite.cc + - protobuf/google/protobuf/generated_message_tctable_full.cc + - protobuf/google/protobuf/generated_message_tctable_lite.cc - protobuf/google/protobuf/generated_message_util.cc - protobuf/google/protobuf/implicit_weak_message.cc + - protobuf/google/protobuf/inlined_string_field.cc - protobuf/google/protobuf/io/coded_stream.cc - protobuf/google/protobuf/io/gzip_stream.cc - protobuf/google/protobuf/io/io_win32.cc @@ -162,6 +167,7 @@ targets: - protobuf/google/protobuf/parse_context.cc - protobuf/google/protobuf/reflection_ops.cc - protobuf/google/protobuf/repeated_field.cc + - protobuf/google/protobuf/repeated_ptr_field.cc - protobuf/google/protobuf/service.cc - protobuf/google/protobuf/source_context.pb.cc - protobuf/google/protobuf/struct.pb.cc @@ -195,7 +201,6 @@ targets: - protobuf/google/protobuf/util/internal/protostream_objectsource.cc - protobuf/google/protobuf/util/internal/protostream_objectwriter.cc - protobuf/google/protobuf/util/internal/type_info.cc - - protobuf/google/protobuf/util/internal/type_info_test_helper.cc - protobuf/google/protobuf/util/internal/utility.cc - protobuf/google/protobuf/util/json_util.cc - protobuf/google/protobuf/util/message_differencer.cc diff --git a/swift/cpp.xcconfig.in b/swift/cpp.xcconfig.in index 36b166aceb6..41c80610459 100644 --- a/swift/cpp.xcconfig.in +++ b/swift/cpp.xcconfig.in @@ -5,4 +5,4 @@ // file LICENSE at the root of the source code distribution tree. HEADER_SEARCH_PATHS = $(SRCROOT)/../src @Boost_INCLUDE_DIRS@ -SYSTEM_HEADER_SEARCH_PATHS = $(SRCROOT)/../src/build/local/include $(SRCROOT)/../build/local/src/protobuf/protobuf-3.14.0/src +SYSTEM_HEADER_SEARCH_PATHS = $(SRCROOT)/../src/build/local/include $(SRCROOT)/../build/local/src/protobuf/protobuf-3.19.2/src diff --git a/swift/project.yml b/swift/project.yml index 9c2ce918cb2..3d17eb60784 100644 --- a/swift/project.yml +++ b/swift/project.yml @@ -6,7 +6,7 @@ settings: base: HEADER_SEARCH_PATHS: $(SRCROOT)/wallet-core ${SRCROOT}/trezor-crypto/crypto SYSTEM_HEADER_SEARCH_PATHS: ${SRCROOT}/include ${SRCROOT}/../build/local/include ${SRCROOT}/trezor-crypto/include $(SRCROOT)/protobuf /usr/local/include /opt/homebrew/include - CLANG_CXX_LANGUAGE_STANDARD: c++17 + CLANG_CXX_LANGUAGE_STANDARD: c++20 SWIFT_VERSION: 5.1 IPHONEOS_DEPLOYMENT_TARGET: 13.0 configs: @@ -27,6 +27,7 @@ targets: excludes: - ".vscode" - "proto/*.proto" + - "Cosmos/Protobuf/*.proto" - "Tron/Protobuf/*.proto" - "Zilliqa/Protobuf/*.proto" - Sources @@ -43,14 +44,22 @@ targets: settings: SKIP_INSTALL: false SUPPORTS_MACCATALYST: true + DEBUG_INFORMATION_FORMAT: dwarf-with-dsym BUILD_LIBRARY_FOR_DISTRIBUTION: true INFOPLIST_FILE: 'Info.plist' CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION: YES_ERROR CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER: $(inherited) false OTHER_CFLAGS: $(inherited) -Wno-comma -DNDEBUG postCompileScripts: - - script: ${PODS_ROOT}/SwiftLint/swiftlint --config ../.swiftlint.yml + - script: | + export PATH=/opt/homebrew/bin:$PATH; + if which swiftlint >/dev/null; then + swiftlint + else + echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint" + fi name: SwiftLint + shell: /bin/bash WalletCoreTests: type: bundle.unit-test @@ -117,7 +126,8 @@ targets: - trezor-crypto/crypto/groestl.c - trezor-crypto/crypto/hmac_drbg.c - trezor-crypto/crypto/rfc6979.c - - trezor-crypto/crypto/schnorr.c + - trezor-crypto/crypto/zilliqa.c + - trezor-crypto/crypto/cardano.c - trezor-crypto/crypto/shamir.c - trezor-crypto/crypto/sodium/private/fe_25_5/fe.c - trezor-crypto/crypto/sodium/private/ed25519_ref10.c @@ -149,11 +159,15 @@ targets: - protobuf/google/protobuf/extension_set_heavy.cc - protobuf/google/protobuf/field_mask.pb.cc - protobuf/google/protobuf/generated_enum_util.cc + - protobuf/google/protobuf/generated_message_bases.cc - protobuf/google/protobuf/generated_message_reflection.cc - protobuf/google/protobuf/generated_message_table_driven.cc - protobuf/google/protobuf/generated_message_table_driven_lite.cc + - protobuf/google/protobuf/generated_message_tctable_full.cc + - protobuf/google/protobuf/generated_message_tctable_lite.cc - protobuf/google/protobuf/generated_message_util.cc - protobuf/google/protobuf/implicit_weak_message.cc + - protobuf/google/protobuf/inlined_string_field.cc - protobuf/google/protobuf/io/coded_stream.cc - protobuf/google/protobuf/io/gzip_stream.cc - protobuf/google/protobuf/io/io_win32.cc @@ -170,6 +184,7 @@ targets: - protobuf/google/protobuf/parse_context.cc - protobuf/google/protobuf/reflection_ops.cc - protobuf/google/protobuf/repeated_field.cc + - protobuf/google/protobuf/repeated_ptr_field.cc - protobuf/google/protobuf/service.cc - protobuf/google/protobuf/source_context.pb.cc - protobuf/google/protobuf/struct.pb.cc @@ -203,7 +218,6 @@ targets: - protobuf/google/protobuf/util/internal/protostream_objectsource.cc - protobuf/google/protobuf/util/internal/protostream_objectwriter.cc - protobuf/google/protobuf/util/internal/type_info.cc - - protobuf/google/protobuf/util/internal/type_info_test_helper.cc - protobuf/google/protobuf/util/internal/utility.cc - protobuf/google/protobuf/util/json_util.cc - protobuf/google/protobuf/util/message_differencer.cc diff --git a/swift/protobuf b/swift/protobuf index 3d4e7cf4303..a96b2043a74 120000 --- a/swift/protobuf +++ b/swift/protobuf @@ -1 +1 @@ -../build/local/src/protobuf/protobuf-3.14.0/src \ No newline at end of file +../build/local/src/protobuf/protobuf-3.19.2/src \ No newline at end of file diff --git a/swift/protobuf.patch b/swift/protobuf.patch deleted file mode 100644 index 05c920be396..00000000000 --- a/swift/protobuf.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- common.cc 2021-04-23 10:54:11.000000000 +0800 -+++ common.patch.cc 2021-04-23 11:03:01.000000000 +0800 -@@ -38,17 +38,17 @@ - #include - #include - --#ifdef _WIN32 --#ifndef WIN32_LEAN_AND_MEAN --#define WIN32_LEAN_AND_MEAN // We only need minimal includes --#endif --#include --#define snprintf _snprintf // see comment in strutil.cc --#elif defined(HAVE_PTHREAD) -+// #ifdef _WIN32 -+// #ifndef WIN32_LEAN_AND_MEAN -+// #define WIN32_LEAN_AND_MEAN // We only need minimal includes -+// #endif -+// #include -+// #define snprintf _snprintf // see comment in strutil.cc -+// #elif defined(HAVE_PTHREAD) - #include --#else --#error "No suitable threading library available." --#endif -+// #else -+// #error "No suitable threading library available." -+// #endif - #if defined(__ANDROID__) - #include - #endif diff --git a/tests/Aeternity/AddressTests.cpp b/tests/Aeternity/AddressTests.cpp deleted file mode 100644 index 40c99814889..00000000000 --- a/tests/Aeternity/AddressTests.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include -#include -#include - -using namespace TW; -using namespace TW::Aeternity; - -TEST(AeternityAddress, FromPublicKey) { - auto publicKey = PublicKey(parse_hex("ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148"),TWPublicKeyTypeED25519); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); -} - -TEST(AeternityAddress, FromString) { - auto address = Address("ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); - ASSERT_EQ(address.string(), "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); -} \ No newline at end of file diff --git a/tests/Aeternity/SignerTests.cpp b/tests/Aeternity/SignerTests.cpp deleted file mode 100644 index aab6d85abfb..00000000000 --- a/tests/Aeternity/SignerTests.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Aeternity/Signer.h" -#include "Aeternity/Transaction.h" -#include "HexCoding.h" - -#include "Aeternity/Address.h" -#include -#include "uint256.h" - -using namespace TW; -using namespace TW::Aeternity; - -TEST(AeternitySigner, Sign) { - std::string sender_id = "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"; - std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; - uint256_t amount = 10; - uint256_t fee = 20000000000000; - std::string payload = "Hello World"; - uint64_t ttl = 82757; - uint64_t nonce = 49; - - auto transaction = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); - auto privateKey = PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); - - auto result = Signer::sign(privateKey, transaction); - EXPECT_EQ(result.signature(), "sg_VW42qDPP3MMNFAStYaumjZz7mC7BZYpbNa15E57ejqUe7JdQFWCiX65eLNUpGMpt8tSpfgCfkYzcaFppqx7W75CrcWdC8"); - EXPECT_EQ(result.encoded(), "tx_+KkLAfhCuEDZ2XDV5OuHv1iuLn66sFLBUwnzp1K8JW1Zz+fEgmuEh6HEvNu0R112M3IYkVzvTSnT0pJ3TWhVOumgJ+IWwW8HuGH4XwwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKMKhhIwnOVAAIMBQ0Uxi0hlbGxvIFdvcmxkDZqNSg=="); -} - -TEST(AeternitySigner, SignTxWithZeroTtl) { - std::string sender_id = "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"; - std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; - uint256_t amount = 10; - uint256_t fee = 20000000000000; - std::string payload = "Hello World"; - uint64_t ttl = 0; - uint64_t nonce = 49; - - auto transaction = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); - auto privateKey = PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); - - auto result = Signer::sign(privateKey, transaction); - EXPECT_EQ(result.signature(), "sg_7qJK868bqEZ5ciC2P3WCKYfhayvKTHvPsz3bdPgpfF3Ky7yNg9f8k22A3gxjjSm9afa6JmP8TJpF4GJkFh2k7gGaog9KS"); - EXPECT_EQ(result.encoded(), "tx_+KYLAfhCuEA0OgWhpq/VfS6ksMS+Df4ewZxIITEhjaaMOiyT0aRuAEe6b5+d2cQtzoyz58NNr+N4MFowctrGXrCrrkhNIywLuF74XAwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKMKhhIwnOVAAAAxi0hlbGxvIFdvcmxkjoDNvQ=="); -} - -TEST(AeternitySigner, SignTxWithZeroAmount) { - std::string sender_id = "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"; - std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; - uint256_t amount = 0; - uint256_t fee = 20000000000000; - std::string payload = "Zero amount test"; - uint64_t ttl = 113579; - uint64_t nonce = 7; - - auto transaction = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); - auto privateKey = PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); - - auto result = Signer::sign(privateKey, transaction); - EXPECT_EQ(result.signature(), "sg_ShWvujPnyKBT1Ng2X5k6XSchVK8Bq7LYEisPMH11DUoPkXZcooBzqw81j9j5JewoFFpT9xEhUptj1azcLA21ogURYh4Lz"); - EXPECT_EQ(result.encoded(), "tx_+K4LAfhCuEDEbeoiVYmJCXm91KNfZXOvZMoT9x/sZja09EXZmErFBxm52b1IVoM4806Zr+TsliAYzUyKfUUFo3jGfXEPdZ8PuGb4ZAwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKMAhhIwnOVAAIMBu6sHkFplcm8gYW1vdW50IHRlc3S5L3Vn"); -} - -TEST(AeternitySigner, SignTxWithZeroNonce) { - std::string sender_id = "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"; - std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; - uint256_t amount = 3369980000000000000; - uint256_t fee = 20000000000000; - std::string payload = "Zero nonce test"; - uint64_t ttl = 113579; - uint64_t nonce = 0; - - auto transaction = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); - auto privateKey = PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); - - auto result = Signer::sign(privateKey, transaction); - EXPECT_EQ(result.signature(), "sg_MaJc4ptSUhq5kH6mArszDAvu4f7PejyuhmgM6U8GEr8bRUTaSFbdFPx4C6FEYA5v5Lgwu9EToaWnHgR2xkqZ9JjHnaBpA"); - EXPECT_EQ(result.encoded(), "tx_+LULAfhCuECdQsgcE8bp+9CANdasxkt5gxfjBSI1ztyPl1aNJbm+MwUvE7Lu/qvAkHijfe+Eui2zrqhZRYc5mblRa+oLOIIEuG34awwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKOILsSS9IArwACGEjCc5UAAgwG7qwCPWmVybyBub25jZSB0ZXN0piWfFA=="); -} diff --git a/tests/Aeternity/TWAnySignerTests.cpp b/tests/Aeternity/TWAnySignerTests.cpp deleted file mode 100644 index 01b4fcf80be..00000000000 --- a/tests/Aeternity/TWAnySignerTests.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "uint256.h" -#include "proto/Aeternity.pb.h" -#include - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; -using namespace TW::Aeternity; - -TEST(TWAnySignerAeternity, Sign) { - auto privateKey = parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); - - Proto::SigningInput input; - input.set_from_address("ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); - input.set_to_address("ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"); - auto amount = store(uint256_t(10)); - input.set_amount(amount.data(), amount.size()); - auto fee = store(uint256_t(20000000000000)); - input.set_fee(fee.data(), fee.size()); - input.set_payload("Hello World"); - input.set_ttl(82757); - input.set_nonce(49); - input.set_private_key(privateKey.data(), privateKey.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeAeternity); - - ASSERT_EQ(output.encoded(), "tx_+KkLAfhCuEDZ2XDV5OuHv1iuLn66sFLBUwnzp1K8JW1Zz+fEgmuEh6HEvNu0R112M3IYkVzvTSnT0pJ3TWhVOumgJ+IWwW8HuGH4XwwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKMKhhIwnOVAAIMBQ0Uxi0hlbGxvIFdvcmxkDZqNSg=="); -} diff --git a/tests/Aeternity/TWCoinTypeTests.cpp b/tests/Aeternity/TWCoinTypeTests.cpp deleted file mode 100644 index 7db96db7589..00000000000 --- a/tests/Aeternity/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWAeternityCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeAeternity)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeAeternity, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeAeternity, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeAeternity)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeAeternity)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeAeternity), 18); - ASSERT_EQ(TWBlockchainAeternity, TWCoinTypeBlockchain(TWCoinTypeAeternity)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAeternity)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAeternity)); - assertStringsEqual(symbol, "AE"); - assertStringsEqual(txUrl, "https://explorer.aepps.com/transactions/t123"); - assertStringsEqual(accUrl, "https://explorer.aepps.com/account/transactions/a12"); - assertStringsEqual(id, "aeternity"); - assertStringsEqual(name, "Aeternity"); -} diff --git a/tests/Aeternity/TransactionTests.cpp b/tests/Aeternity/TransactionTests.cpp deleted file mode 100644 index 025eb2dc056..00000000000 --- a/tests/Aeternity/TransactionTests.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Aeternity/Address.cpp" -#include "Aeternity/Transaction.cpp" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "../interface/TWTestUtilities.h" - -#include "HexCoding.h" -#include -#include - -TEST(AeternityTransaction, EncodeRlp) { - std::string sender_id = "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi"; - std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; - uint64_t amount = 10; - uint64_t fee = 2e13; - std::string payload = "Hello World"; - uint64_t ttl = 82757; - uint64_t nonce = 49; - - auto tx = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); - auto encodedTx = tx.encode(); - auto encodedTxHex = hex(encodedTx); - - ASSERT_EQ(encodedTxHex, "f85f0c01a101cea7ade470c9f99d9d4e400880a86f1d49bb444b62f11a9ebb64bbcfeb73fef3a1011f13a3b08bf001400662a68b69d875f7803cec4c08647f6ed5d84c7897bd50a30a8612309ce5400083014345318b48656c6c6f20576f726c64"); -} - -TEST(AeternityTransaction, EncodeRlpWithZeroAmount) { - std::string sender_id = "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi"; - std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; - uint64_t amount = 0; - uint64_t fee = 2e13; - std::string payload = "Hello World"; - uint64_t ttl = 82757; - uint64_t nonce = 49; - - auto tx = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); - auto encodedTx = tx.encode(); - auto encodedTxHex = hex(encodedTx); - - ASSERT_EQ(encodedTxHex, "f85f0c01a101cea7ade470c9f99d9d4e400880a86f1d49bb444b62f11a9ebb64bbcfeb73fef3a1011f13a3b08bf001400662a68b69d875f7803cec4c08647f6ed5d84c7897bd50a3008612309ce5400083014345318b48656c6c6f20576f726c64"); -} - -TEST(AeternityTransaction, EncodeRlpWithZeroTtl) { - std::string sender_id = "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi"; - std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; - uint64_t amount = 10; - uint64_t fee = 2e13; - std::string payload = "Hello World"; - uint64_t ttl = 0; - uint64_t nonce = 49; - - auto tx = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); - auto encodedTx = tx.encode(); - auto encodedTxHex = hex(encodedTx); - - ASSERT_EQ(encodedTxHex, "f85c0c01a101cea7ade470c9f99d9d4e400880a86f1d49bb444b62f11a9ebb64bbcfeb73fef3a1011f13a3b08bf001400662a68b69d875f7803cec4c08647f6ed5d84c7897bd50a30a8612309ce5400000318b48656c6c6f20576f726c64"); -} - diff --git a/tests/Aion/AddressTests.cpp b/tests/Aion/AddressTests.cpp deleted file mode 100644 index 5942e0ce0b6..00000000000 --- a/tests/Aion/AddressTests.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Aion/Address.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Aion; - -TEST(AionAddress, FromPublicKey) { - auto publicKey = PublicKey(parse_hex("01a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7"), TWPublicKeyTypeED25519); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "0xa0d2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"); -} - -TEST(AionAddress, FromString) { - std::string aionAddress = "0xa0d2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"; - const auto address = Address(aionAddress); - ASSERT_EQ(address.string(), aionAddress); -} - -TEST(AionAddress, isValid) { - std::string validAddress = "0xa0d2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"; - std::string invalidAddress = "0xzzd2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"; - - ASSERT_TRUE(Address::isValid(validAddress)); - ASSERT_FALSE(Address::isValid(invalidAddress)); -} diff --git a/tests/Aion/RLPTests.cpp b/tests/Aion/RLPTests.cpp deleted file mode 100644 index 977fbdbda65..00000000000 --- a/tests/Aion/RLPTests.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Aion/RLP.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Aion; -using boost::multiprecision::uint128_t; - -TEST(AionRLP, EncodeLong) { - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(1))), "01"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(21000))), "825208"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(1000000))), "830f4240"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(20000000000))), "8800000004a817c800"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(9007199254740991))), "88001fffffffffffff"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(9007199254740990))), "88001ffffffffffffe"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(4294967296L))), "880000000100000000"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(4295000060L))), "880000000100007ffc"); - EXPECT_EQ(hex(RLP::encodeLong(uint128_t(72057594037927935L))), "8800ffffffffffffff"); -} diff --git a/tests/Aion/SignerTests.cpp b/tests/Aion/SignerTests.cpp deleted file mode 100644 index b8a7970f248..00000000000 --- a/tests/Aion/SignerTests.cpp +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Aion/Signer.h" -#include "Aion/Transaction.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Aion; - -TEST(AionSigner, Sign) { - auto address = Aion::Address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); - auto transaction = Transaction(9, 20000000000, 21000, address, 10000, 155157377101, {}); - - auto privateKey = PrivateKey(parse_hex("db33ffdf82c7ba903daf68d961d3c23c20471a8ce6b408e52d579fd8add80cc9")); - Signer::sign(privateKey, transaction); - - EXPECT_EQ(hex(transaction.signature), "a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); - - // Raw transaction - EXPECT_EQ(hex(transaction.encode()), "f89b09a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108085242019b04d8252088800000004a817c80001b860a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); -} - -TEST(AionSigner, SignWithData) { - auto address = Aion::Address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); - auto transaction = Transaction(9, 20000000000, 21000, address, 10000, 155157377101, parse_hex("41494f4e0000")); - - auto privateKey = PrivateKey(parse_hex("db33ffdf82c7ba903daf68d961d3c23c20471a8ce6b408e52d579fd8add80cc9")); - Signer::sign(privateKey, transaction); - - EXPECT_EQ(hex(transaction.signature), "a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a736fc2642c2d62900204779aa274dba3b8712eff7a8464aa78ea52b09ece20679fe3f5edf94c84a7e0c5f93213be891bc279af927086f455167f5bc73d3046c0d"); - - // Raw transaction - EXPECT_EQ(hex(transaction.encode()), "f8a109a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108641494f4e000085242019b04d8252088800000004a817c80001b860a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a736fc2642c2d62900204779aa274dba3b8712eff7a8464aa78ea52b09ece20679fe3f5edf94c84a7e0c5f93213be891bc279af927086f455167f5bc73d3046c0d"); -} diff --git a/tests/Aion/TWAnySignerTests.cpp b/tests/Aion/TWAnySignerTests.cpp deleted file mode 100644 index 1f30f761d03..00000000000 --- a/tests/Aion/TWAnySignerTests.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "uint256.h" -#include "proto/Aion.pb.h" -#include - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; -using namespace TW::Aion; - -TEST(TWAnySignerAion, Sign) { - auto privateKey = parse_hex("db33ffdf82c7ba903daf68d961d3c23c20471a8ce6b408e52d579fd8add80cc9"); - - Proto::SigningInput input; - input.set_to_address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); - auto amount = store(uint256_t(10000)); - input.set_amount(amount.data(), amount.size()); - auto gasPrice = store(uint256_t(20000000000)); - input.set_gas_price(gasPrice.data(), gasPrice.size()); - auto gasLimit = store(uint256_t(21000)); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - auto nonce = store(uint256_t(9)); - input.set_nonce(nonce.data(), nonce.size()); - input.set_timestamp(155157377101); - input.set_private_key(privateKey.data(), privateKey.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeAion); - - ASSERT_EQ(hex(output.encoded()), "f89b09a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108085242019b04d8252088800000004a817c80001b860a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); -} diff --git a/tests/Aion/TWCoinTypeTests.cpp b/tests/Aion/TWCoinTypeTests.cpp deleted file mode 100644 index e87d983d8d8..00000000000 --- a/tests/Aion/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWAionCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeAion)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeAion, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeAion, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeAion)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeAion)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeAion), 18); - ASSERT_EQ(TWBlockchainAion, TWCoinTypeBlockchain(TWCoinTypeAion)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAion)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAion)); - assertStringsEqual(symbol, "AION"); - assertStringsEqual(txUrl, "https://mainnet.aion.network/#/transaction/t123"); - assertStringsEqual(accUrl, "https://mainnet.aion.network/#/account/a12"); - assertStringsEqual(id, "aion"); - assertStringsEqual(name, "Aion"); -} diff --git a/tests/Aion/TransactionTests.cpp b/tests/Aion/TransactionTests.cpp deleted file mode 100644 index ad6b8546c2b..00000000000 --- a/tests/Aion/TransactionTests.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Aion/Transaction.h" - -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Aion; - -TEST(AionTransaction, Encode) { - auto address = Aion::Address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); - auto transaction = Transaction(9, 20000000000, 21000, address, 10000, 155157377101, {}); - ASSERT_EQ(hex(transaction.encode()), "f83909a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108085242019b04d8252088800000004a817c80001"); -} - -TEST(AionTransaction, EncodeWithSignature) { - auto address = Aion::Address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); - auto transaction = Transaction(9, 20000000000, 21000, address, 10000, 155157377101, {}); - transaction.signature = parse_hex("a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); - ASSERT_EQ(hex(transaction.encode()), "f89b09a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108085242019b04d8252088800000004a817c80001b860a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); -} diff --git a/tests/Algorand/AddressTests.cpp b/tests/Algorand/AddressTests.cpp deleted file mode 100644 index 0ed495d060e..00000000000 --- a/tests/Algorand/AddressTests.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Algorand/Address.h" -#include "PublicKey.h" -#include "PrivateKey.h" -#include -#include - -using namespace TW; -using namespace TW::Algorand; - -TEST(AlgorandAddress, Validation) { - // empty address - ASSERT_FALSE(Address::isValid("")); - // invalid checksum - ASSERT_FALSE(Address::isValid("JBCQYJ2FREG667NAN7BFKH4RFIKPT7CYDQJNW3SNN5Z7F7ILFLKQ346TS3")); - // wrong length - ASSERT_FALSE(Address::isValid("JBCQYJ2FREG667NAN7BFKH4RFIKPT7CYDQJNW3SNN5Z7F7ILFLKQ346TSU ")); - // Stellar address - ASSERT_FALSE(Address::isValid("GABQHYQOY22KHGTCTAK24AWAUE4TXERF4O4JBSXELNM7IL5CTPUWM3SC")); - - ASSERT_TRUE(Address::isValid("HXIWBVQGOO6ZWE5NYJO22XMYRUGZ6TGNX2K2EERPT3ZIWPHE5CLJGB2GEA")); -} - -TEST(AlgorandAddress, FromPrivateKey) { - auto privateKey = PrivateKey(parse_hex("526d96fffdbfe787b2f00586298538f9a019e97f6587964dc61aae9ad1d7fa23")); - auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(address.string(), "JBCQYJ2FREG667NAN7BFKH4RFIKPT7CYDQJNW3SNN5Z7F7ILFLKQ346TSU"); -} - -TEST(AlgorandAddress, FromPublicKey) { - auto publicKey = PublicKey(parse_hex("c2b423afa8b0095e5ae105668b91b2132db4dadbf38acfc64908d3476a00191f"), TWPublicKeyTypeED25519); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "YK2CHL5IWAEV4WXBAVTIXENSCMW3JWW36OFM7RSJBDJUO2QADEP5QYVO5I"); -} - -TEST(AlgorandAddress, FromString) { - auto address = Address("PITDOF57RHOVLT37KM7DCXDCETLDL3OA5CBAN7LQ44Z36LGFC27IJ2IQ64"); - ASSERT_EQ(address.string(), "PITDOF57RHOVLT37KM7DCXDCETLDL3OA5CBAN7LQ44Z36LGFC27IJ2IQ64"); -} diff --git a/tests/Algorand/SignerTests.cpp b/tests/Algorand/SignerTests.cpp deleted file mode 100644 index f9cfcecb2bf..00000000000 --- a/tests/Algorand/SignerTests.cpp +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Algorand/Address.h" -#include "Algorand/Signer.h" -#include "Algorand/BinaryCoding.h" -#include "HexCoding.h" -#include "Base64.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include -#include - -using namespace TW; -using namespace TW::Algorand; - -TEST(AlgorandSigner, EncodeNumbers) { - auto tests = { - std::make_tuple(100ull, "64"), - std::make_tuple(200ull, "ccc8"), - std::make_tuple(55536ull, "cdd8f0"), - std::make_tuple(3294967296ull, "cec4653600"), - std::make_tuple(14294967296ull, "cf00000003540be400"), - }; - - for (auto& test : tests) { - Data data; - encodeNumber(std::get<0>(test), data); - ASSERT_EQ(hex(data), std::get<1>(test)); - } -} - -TEST(AlgorandSigner, EncodeStrings) { - auto tests = { - std::make_tuple("algo", "a4616c676f"), - std::make_tuple("It's like JSON. but fast and small.", "d92349742773206c696b65204a534f4e2e20627574206661737420616e6420736d616c6c2e"), - std::make_tuple( - "MessagePack is an efficient binary serialization format. It lets you exchange data among multiple languages like JSON. But it's faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one extra byte in addition to the strings themselves.", - "da011f4d6573736167655061636b20697320616e20656666696369656e742062696e6172792073657269616c697a6174696f6e20666f726d61742e204974206c65747320796f752065786368616e6765206461746120616d6f6e67206d756c7469706c65206c616e677561676573206c696b65204a534f4e2e2042757420697427732066617374657220616e6420736d616c6c65722e20536d616c6c20696e7465676572732061726520656e636f64656420696e746f20612073696e676c6520627974652c20616e64207479706963616c2073686f727420737472696e67732072657175697265206f6e6c79206f6e65206578747261206279746520696e206164646974696f6e20746f2074686520737472696e6773207468656d73656c7665732e" - ) - }; - - for (auto& test : tests) { - Data data; - Algorand::encodeString(std::get<0>(test), data); - ASSERT_EQ(hex(data), std::get<1>(test)); - } -} - -TEST(AlgorandSigner, EncodeBytes) { - auto rawtx = "010000000001029294c2b3bd4d25483c4c12432df01a856a38cc0cb48da1a7dd590b7d893392a90000000000ffffffffded892ea55bf1c6ccc495d3493767d7c24497f612b9edc9ab8d30eb671ea76750000000000ffffffff021027000000000000160014b96bacd6f729ef8ac1dd30d159433c0917ba8d3db00f00000000000016001476cd9d430de6db162fc3db509920255ff6d2bdb002483045022100eb8675ff6775e9c399dddba9f178002b745872e541617d690cbce7c933adb87602205de8074c173696de65d4c644a84ea1337c9e9928c7052fddcf9d99e35815e2f20121032858d3a5f9825408ea3959800c5daf22e7a91e459ef168df45071266501d28e102473044022025f1cf362a9c09bd351769f1918ab9f0a6c3f6c4682f29fdbfc08354554ea37b02203f62345b3da4d7a29f58c7c741682be4108a0fb2013980332cc3e081aad7423f01210237d83670da2d3947a58752dab95d59b592c78f2e734d1c14dbf75b29bbe4116100000000"; - Data data; - encodeBytes(parse_hex(rawtx), data); - ASSERT_EQ(hex(data), "c50173010000000001029294c2b3bd4d25483c4c12432df01a856a38cc0cb48da1a7dd590b7d893392a90000000000ffffffffded892ea55bf1c6ccc495d3493767d7c24497f612b9edc9ab8d30eb671ea76750000000000ffffffff021027000000000000160014b96bacd6f729ef8ac1dd30d159433c0917ba8d3db00f00000000000016001476cd9d430de6db162fc3db509920255ff6d2bdb002483045022100eb8675ff6775e9c399dddba9f178002b745872e541617d690cbce7c933adb87602205de8074c173696de65d4c644a84ea1337c9e9928c7052fddcf9d99e35815e2f20121032858d3a5f9825408ea3959800c5daf22e7a91e459ef168df45071266501d28e102473044022025f1cf362a9c09bd351769f1918ab9f0a6c3f6c4682f29fdbfc08354554ea37b02203f62345b3da4d7a29f58c7c741682be4108a0fb2013980332cc3e081aad7423f01210237d83670da2d3947a58752dab95d59b592c78f2e734d1c14dbf75b29bbe4116100000000"); -} - -TEST(AlgorandSigner, Sign) { - auto key = PrivateKey(parse_hex("c9d3cc16fecabe2747eab86b81528c6ed8b65efc1d6906d86aabc27187a1fe7c")); - auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); - auto from = Address(publicKey); - auto to = Address("UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM"); - Data note; - std::string genesisId = "mainnet-v1.0"; - auto genesisHash = Base64::decode("wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="); - auto transaction = Transfer( - /* from */ from, - /* to */ to, - /* fee */ 488931, - /* amount */ 847, - /* first round */ 51, - /* last round */ 61, - /* note */ note, - /* type */ "pay", - /* genesis id*/ genesisId, - /* genesis hash*/ genesisHash - ); - - auto serialized = transaction.serialize(); - auto signature = Signer::sign(key, transaction); - auto result = transaction.BaseTransaction::serialize(signature); - - ASSERT_EQ(hex(serialized), "89a3616d74cd034fa3666565ce000775e3a2667633a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c763da3726376c420a089aa6922e3b998fadff6cd4808ddf9e021e4944e389ea3d5c638786689197ea3736e64c42074b000b6368551a6066d713e2866002e8dab34b69ede09a72e85a39bbb1f7928a474797065a3706179"); - ASSERT_EQ(hex(signature), "de73363dbdeda0682adca06f6268a16a6ec47253c94d5692dc1c49a84a05847812cf66d7c4cf07c7e2f50f143ec365d405e30b35117b264a994626054d2af604"); - ASSERT_EQ(hex(result), "82a3736967c440de73363dbdeda0682adca06f6268a16a6ec47253c94d5692dc1c49a84a05847812cf66d7c4cf07c7e2f50f143ec365d405e30b35117b264a994626054d2af604a374786e89a3616d74cd034fa3666565ce000775e3a2667633a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c763da3726376c420a089aa6922e3b998fadff6cd4808ddf9e021e4944e389ea3d5c638786689197ea3736e64c42074b000b6368551a6066d713e2866002e8dab34b69ede09a72e85a39bbb1f7928a474797065a3706179"); -} - -TEST(AlgorandSigner, SignAsset) { - // https://testnet.algoexplorer.io/tx/NJ62HYO2LC222AVLIN2GW5LKIWKLGC7NZLIQ3DUL2RDVRYO2UW7A - auto key = PrivateKey(parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04")); - auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); - auto from = Address(publicKey); - auto to = Address("GJIWJSX2EU5RC32LKTDDXWLA2YICBHKE35RV2ZPASXZYKWUWXFLKNFSS4U"); - Data note; - std::string genesisId = "testnet-v1.0"; - auto genesisHash = Base64::decode("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="); - auto transaction = AssetTransfer( - /* from */ from, - /* to */ to, - /* fee */ 2340, - /* amount */ 1000000, - /* asset id */13379146, - /* first round */ 15775683, - /* last round */ 15776683, - /* note */ note, - /* type */ "axfer", - /* genesis id*/ genesisId, - /* genesis hash*/ genesisHash - ); - - auto serialized = transaction.serialize(); - auto signature = Signer::sign(key, transaction); - auto result = transaction.BaseTransaction::serialize(signature); - - ASSERT_EQ(hex(serialized), "8aa461616d74ce000f4240a461726376c420325164cafa253b116f4b54c63bd960d610209d44df635d65e095f3855a96b956a3666565cd0924a26676ce00f0b7c3a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bbaba3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); - ASSERT_EQ(hex(signature), "412720eff99a17280a437bdb8eeba7404b855d6433fffd5dde7f7966c1f9ae531a1af39e18b8a58b4a6c6acb709cca92f8a18c36d8328be9520c915311027005"); - ASSERT_EQ(hex(result), "82a3736967c440412720eff99a17280a437bdb8eeba7404b855d6433fffd5dde7f7966c1f9ae531a1af39e18b8a58b4a6c6acb709cca92f8a18c36d8328be9520c915311027005a374786e8aa461616d74ce000f4240a461726376c420325164cafa253b116f4b54c63bd960d610209d44df635d65e095f3855a96b956a3666565cd0924a26676ce00f0b7c3a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bbaba3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); -} - -TEST(AlgorandSigner, SignAssetOptIn) { - // https://testnet.algoexplorer.io/tx/47LE2QS4B5N6IFHXOUN2MJUTCOQCHNY6AB3AJYECK4IM2VYKJDKQ - auto key = PrivateKey(parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04")); - auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); - auto address = Address(publicKey); - Data note; - std::string genesisId = "testnet-v1.0"; - auto genesisHash = Base64::decode("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="); - auto transaction = OptInAssetTransaction( - /* from */ address, - /* fee */ 2340, - /* asset id */13379146, - /* first round */ 15775553, - /* last round */ 15776553, - /* note */ note, - /* type */ "axfer", - /* genesis id*/ genesisId, - /* genesis hash*/ genesisHash - ); - - auto serialized = transaction.serialize(); - auto signature = Signer::sign(key, transaction); - auto result = transaction.BaseTransaction::serialize(signature); - - ASSERT_EQ(hex(serialized), "89a461726376c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a3666565cd0924a26676ce00f0b741a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bb29a3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); - ASSERT_EQ(hex(signature), "f3a29d9a40271c00b542b38ab2ccb4967015ae6609368d4b8eb2f5e2b5348577cf9e0f62b0777ccb2d8d9b943b15c24c0cf1db312cb01a3c198d9d9c6c5bb00b"); - ASSERT_EQ(hex(result), "82a3736967c440f3a29d9a40271c00b542b38ab2ccb4967015ae6609368d4b8eb2f5e2b5348577cf9e0f62b0777ccb2d8d9b943b15c24c0cf1db312cb01a3c198d9d9c6c5bb00ba374786e89a461726376c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a3666565cd0924a26676ce00f0b741a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bb29a3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); -} - -TEST(AlgorandSigner, ProtoSignerOptIn) { - // https://testnet.algoexplorer.io/tx/47LE2QS4B5N6IFHXOUN2MJUTCOQCHNY6AB3AJYECK4IM2VYKJDKQ - auto optIn = new Proto::AssetOptIn(); - optIn -> set_asset_id(13379146); - - auto privateKey = parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04"); - - auto input = Proto::SigningInput(); - auto genesisHash = Base64::decode("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="); - std::string str(genesisHash.begin(), genesisHash.end()); - input.set_allocated_asset_opt_in(optIn); - input.set_genesis_hash(str); - input.set_genesis_id("testnet-v1.0"); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_first_round(15775553); - input.set_last_round(15776553); - input.set_fee(2340); - - auto result = Signer::sign(input); - auto encoded = result.encoded(); - - ASSERT_EQ(hex(encoded), "82a3736967c440f3a29d9a40271c00b542b38ab2ccb4967015ae6609368d4b8eb2f5e2b5348577cf9e0f62b0777ccb2d8d9b943b15c24c0cf1db312cb01a3c198d9d9c6c5bb00ba374786e89a461726376c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a3666565cd0924a26676ce00f0b741a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bb29a3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); -} - -TEST(AlgorandSigner, ProtoSignerAssetTransaction) { - // https://testnet.algoexplorer.io/tx/NJ62HYO2LC222AVLIN2GW5LKIWKLGC7NZLIQ3DUL2RDVRYO2UW7A - auto transaction = new Proto::AssetTransfer(); - transaction -> set_asset_id(13379146); - transaction -> set_amount(1000000); - transaction -> set_to_address("GJIWJSX2EU5RC32LKTDDXWLA2YICBHKE35RV2ZPASXZYKWUWXFLKNFSS4U"); - - auto privateKey = parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04"); - - auto input = Proto::SigningInput(); - auto genesisHash = Base64::decode("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="); - std::string str(genesisHash.begin(), genesisHash.end()); - input.set_allocated_asset_transfer(transaction); - input.set_genesis_hash(str); - input.set_genesis_id("testnet-v1.0"); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_first_round(15775683); - input.set_last_round(15776683); - input.set_fee(2340); - - auto result = Signer::sign(input); - auto encoded = result.encoded(); - - ASSERT_EQ(hex(encoded), "82a3736967c440412720eff99a17280a437bdb8eeba7404b855d6433fffd5dde7f7966c1f9ae531a1af39e18b8a58b4a6c6acb709cca92f8a18c36d8328be9520c915311027005a374786e8aa461616d74ce000f4240a461726376c420325164cafa253b116f4b54c63bd960d610209d44df635d65e095f3855a96b956a3666565cd0924a26676ce00f0b7c3a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bbaba3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); -} diff --git a/tests/Algorand/TWAnySignerTests.cpp b/tests/Algorand/TWAnySignerTests.cpp deleted file mode 100644 index 63723876d9e..00000000000 --- a/tests/Algorand/TWAnySignerTests.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Base64.h" -#include "proto/Algorand.pb.h" -#include - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; -using namespace TW::Algorand; - -TEST(TWAnySignerAlgorand, Sign) { - auto privateKey = parse_hex("d5b43d706ef0cb641081d45a2ec213b5d8281f439f2425d1af54e2afdaabf55b"); - auto note = parse_hex("68656c6c6f"); - auto genesisHash = Base64::decode("wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="); - - Proto::SigningInput input; - auto& transaction = *input.mutable_transfer(); - transaction.set_to_address("CRLADAHJZEW2GFY2UPEHENLOGCUOU74WYSTUXQLVLJUJFHEUZOHYZNWYR4"); - transaction.set_amount(1000000000000ull); - input.set_first_round(1937767ull); - input.set_last_round(1938767ull); - input.set_fee(263000ull); - input.set_genesis_id("mainnet-v1.0"); - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - input.set_note(note.data(), note.size()); - input.set_private_key(privateKey.data(), privateKey.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeAlgorand); - - ASSERT_EQ(hex(output.encoded()), "82a3736967c440baa00062adcdcb5875e4435cdc6885d26bfe5308ab17983c0fda790b7103051fcb111554e5badfc0ac7edf7e1223a434342a9eeed5cdb047690827325051560ba374786e8aa3616d74cf000000e8d4a51000a3666565ce00040358a26676ce001d9167a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c76ce001d954fa46e6f7465c40568656c6c6fa3726376c42014560180e9c92da3171aa3c872356e30a8ea7f96c4a74bc1755a68929c94cb8fa3736e64c42061bf060efc02e2887dfffc8ed85268c8c091c013eedf315bc50794d02a8791ada474797065a3706179"); -} - -TEST(TWAnySignerAlgorand, SignJSON) { - auto json = STRING(R"({"genesisId":"mainnet-v1.0","genesisHash":"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=","note":"aGVsbG8=","firstRound":"1937767","lastRound":"1938767","fee":"263000","transfer":{"toAddress":"CRLADAHJZEW2GFY2UPEHENLOGCUOU74WYSTUXQLVLJUJFHEUZOHYZNWYR4","amount":"1000000000000"}})"); - auto key = DATA("d5b43d706ef0cb641081d45a2ec213b5d8281f439f2425d1af54e2afdaabf55b"); - - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeAlgorand)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeAlgorand)); - assertStringsEqual(result, "82a3736967c440baa00062adcdcb5875e4435cdc6885d26bfe5308ab17983c0fda790b7103051fcb111554e5badfc0ac7edf7e1223a434342a9eeed5cdb047690827325051560ba374786e8aa3616d74cf000000e8d4a51000a3666565ce00040358a26676ce001d9167a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c76ce001d954fa46e6f7465c40568656c6c6fa3726376c42014560180e9c92da3171aa3c872356e30a8ea7f96c4a74bc1755a68929c94cb8fa3736e64c42061bf060efc02e2887dfffc8ed85268c8c091c013eedf315bc50794d02a8791ada474797065a3706179"); -} diff --git a/tests/Algorand/TWCoinTypeTests.cpp b/tests/Algorand/TWCoinTypeTests.cpp deleted file mode 100644 index f95721fa727..00000000000 --- a/tests/Algorand/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWAlgorandCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeAlgorand)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("CR7POXFTYDLC7TV3IXHA7AZKWABUJC52BACLHJQNXAKZJGRPQY3A")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeAlgorand, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("J4AEINCSSLDA7LNBNWM4ZXFCTLTOZT5LG3F5BLMFPJYGFWVCMU37EZI2AM")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeAlgorand, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeAlgorand)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeAlgorand)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeAlgorand), 6); - ASSERT_EQ(TWBlockchainAlgorand, TWCoinTypeBlockchain(TWCoinTypeAlgorand)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAlgorand)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAlgorand)); - assertStringsEqual(symbol, "ALGO"); - assertStringsEqual(txUrl, "https://algoexplorer.io/tx/CR7POXFTYDLC7TV3IXHA7AZKWABUJC52BACLHJQNXAKZJGRPQY3A"); - assertStringsEqual(accUrl, "https://algoexplorer.io/address/J4AEINCSSLDA7LNBNWM4ZXFCTLTOZT5LG3F5BLMFPJYGFWVCMU37EZI2AM"); - assertStringsEqual(id, "algorand"); - assertStringsEqual(name, "Algorand"); -} diff --git a/tests/Arbitrum/TWCoinTypeTests.cpp b/tests/Arbitrum/TWCoinTypeTests.cpp deleted file mode 100644 index d0220106ec0..00000000000 --- a/tests/Arbitrum/TWCoinTypeTests.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWArbitrumCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeArbitrum)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xa1e319be22c08155e5904aa211fb87df5f4ade48de79c6578b1cf3dfda9e498c")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeArbitrum, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xecf9ffa7f51e1194f89c25ad8c484f6bfd04e1ac")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeArbitrum, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeArbitrum)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeArbitrum)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeArbitrum), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeArbitrum)); - - assertStringsEqual(symbol, "ARETH"); - assertStringsEqual(txUrl, "https://arbiscan.io/tx/0xa1e319be22c08155e5904aa211fb87df5f4ade48de79c6578b1cf3dfda9e498c"); - assertStringsEqual(accUrl, "https://arbiscan.io/address/0xecf9ffa7f51e1194f89c25ad8c484f6bfd04e1ac"); - assertStringsEqual(id, "arbitrum"); - assertStringsEqual(name, "Arbitrum"); -} diff --git a/tests/Avalanche/TWCoinTypeTests.cpp b/tests/Avalanche/TWCoinTypeTests.cpp deleted file mode 100644 index 49220a3a60b..00000000000 --- a/tests/Avalanche/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWAvalancheCoinType, TWCoinTypeCChain) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeAvalancheCChain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x9243890b844219accefd8798271052f5a056453ec18984a56e81c92921330d54")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeAvalancheCChain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xa664325f36Ec33E66323fe2620AF3f2294b2Ef3A")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeAvalancheCChain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeAvalancheCChain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeAvalancheCChain)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeAvalancheCChain), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeAvalancheCChain)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAvalancheCChain)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAvalancheCChain)); - assertStringsEqual(symbol, "AVAX"); - assertStringsEqual(txUrl, "https://cchain.explorer.avax.network/tx/0x9243890b844219accefd8798271052f5a056453ec18984a56e81c92921330d54"); - assertStringsEqual(accUrl, "https://cchain.explorer.avax.network/address/0xa664325f36Ec33E66323fe2620AF3f2294b2Ef3A"); - assertStringsEqual(id, "avalanchec"); - assertStringsEqual(name, "Avalanche C-Chain"); -} diff --git a/tests/BandChain/TWCoinTypeTests.cpp b/tests/BandChain/TWCoinTypeTests.cpp deleted file mode 100644 index 4fdd7e8f90b..00000000000 --- a/tests/BandChain/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWBandChainCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBandChain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("473264551D3063A9EC64EC251C61BE92DDDFCF6CC46D026D1E574D83D5447173")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBandChain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("band12nmsm9khdsv0tywge43q3zwj8kkj3hvup9rltp")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBandChain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBandChain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBandChain)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBandChain), 6); - ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeBandChain)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeBandChain)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBandChain)); - assertStringsEqual(symbol, "BAND"); - assertStringsEqual(txUrl, "https://scan-wenchang-testnet2.bandchain.org//tx/473264551D3063A9EC64EC251C61BE92DDDFCF6CC46D026D1E574D83D5447173"); - assertStringsEqual(accUrl, "https://scan-wenchang-testnet2.bandchain.org//account/band12nmsm9khdsv0tywge43q3zwj8kkj3hvup9rltp"); - assertStringsEqual(id, "band"); - assertStringsEqual(name, "BandChain"); -} diff --git a/tests/Binance/TWAnySignerTests.cpp b/tests/Binance/TWAnySignerTests.cpp deleted file mode 100644 index 8cb091000c9..00000000000 --- a/tests/Binance/TWAnySignerTests.cpp +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Binance/Address.h" -#include "proto/Binance.pb.h" -#include - -#include "../interface/TWTestUtilities.h" -#include -#include - -using namespace TW; -using namespace TW::Binance; - -Proto::SigningOutput SignTest() { - auto input = Proto::SigningInput(); - input.set_chain_id("Binance-Chain-Nile"); - input.set_account_number(0); - input.set_sequence(0); - input.set_source(0); - - auto key = parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"); - input.set_private_key(key.data(), key.size()); - - auto& order = *input.mutable_send_order(); - - // bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2 - auto fromKeyhash = parse_hex("40c2979694bbc961023d1d27be6fc4d21a9febe6"); - // bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5 - auto toKeyhash = parse_hex("bffe47abfaede50419c577f1074fee6dd1535cd1"); - - { - auto input = order.add_inputs(); - input->set_address(fromKeyhash.data(), fromKeyhash.size()); - auto inputCoin = input->add_coins(); - inputCoin->set_denom("BNB"); - inputCoin->set_amount(1); - } - - { - auto output = order.add_outputs(); - output->set_address(toKeyhash.data(), toKeyhash.size()); - auto outputCoin = output->add_coins(); - outputCoin->set_denom("BNB"); - outputCoin->set_amount(1); - } - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeBinance); - return output; -} - -TEST(TWAnySignerBinance, Sign) { - Proto::SigningOutput output = SignTest(); - ASSERT_EQ(hex(output.encoded()), "b801f0625dee0a462a2c87fa0a1f0a1440c2979694bbc961023d1d27be6fc4d21a9febe612070a03424e421001121f0a14bffe47abfaede50419c577f1074fee6dd1535cd112070a03424e421001126a0a26eb5ae98721026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e50212401b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679"); -} - -TEST(TWAnySignerBinance, SignJSON) { - auto json = STRING(R"({"chainId":"Binance-Chain-Tigris","accountNumber":"13186","source":"2","memo":"Testing","sendOrder":{"inputs":[{"address":"EuZU7e+eUIuDNzaph9Bp2lqJrts=","coins":[{"denom":"BNB","amount":"1345227"}]}],"outputs":[{"address":"M7vzB7mBRvE9IGk8+UbC13pMryg=","coins":[{"denom":"BNB","amount":"1345227"}]}]}})"); - auto key = DATA("f947b3554a1c2fa70e1caa9de53fbda353348d1e856c593848f3a29737d31f11"); - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeBinance)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeBinance)); - assertStringsEqual(result, "ca01f0625dee0a4a2a2c87fa0a210a1412e654edef9e508b833736a987d069da5a89aedb12090a03424e4210cb8d5212210a1433bbf307b98146f13d20693cf946c2d77a4caf2812090a03424e4210cb8d52126d0a26eb5ae9872102e58176f271a9796b4288908be85094a2ac978e25535fd59a37b58626e3a84d9e1240015b4beb3d6ef366a7a92fd283f66b8f0d8cdb6b152a9189146b27f84f507f047e248517cf691a36ebc2b7f3b7f64e27585ce1c40f1928d119c31af428efcf3e1882671a0754657374696e672002"); -} - -TEST(TWAnySignerBinance, MultithreadedSigning) { - auto f = [](int n) { - for (int i = 0; i < n; i++) { - Proto::SigningOutput output = SignTest(); - ASSERT_EQ(hex(output.encoded()), "b801f0625dee0a462a2c87fa0a1f0a1440c2979694bbc961023d1d27be6fc4d21a9febe612070a03424e421001121f0a14bffe47abfaede50419c577f1074fee6dd1535cd112070a03424e421001126a0a26eb5ae98721026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e50212401b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679"); - } - }; - - // Ensure multiple threads cause no asserts - std::thread th1(f, 1000); - std::thread th2(f, 1000); - std::thread th3(f, 1000); - - th1.join(); - th2.join(); - th3.join(); -} diff --git a/tests/Binance/TWCoinTypeTests.cpp b/tests/Binance/TWCoinTypeTests.cpp deleted file mode 100644 index aaa9c3f0b9e..00000000000 --- a/tests/Binance/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWBinanceCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBinance)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("A93625C9F9ABEA1A8E31585B30BBB16C34FAE0D172EB5B6B2F834AF077BF06BB")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBinance, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("bnb1u7jm0cll5h3224y0tapwn6gf6pr49ytewx4gsz")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBinance, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBinance)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBinance)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBinance), 8); - ASSERT_EQ(TWBlockchainBinance, TWCoinTypeBlockchain(TWCoinTypeBinance)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeBinance)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBinance)); - assertStringsEqual(symbol, "BNB"); - assertStringsEqual(txUrl, "https://explorer.binance.org/tx/A93625C9F9ABEA1A8E31585B30BBB16C34FAE0D172EB5B6B2F834AF077BF06BB"); - assertStringsEqual(accUrl, "https://explorer.binance.org/address/bnb1u7jm0cll5h3224y0tapwn6gf6pr49ytewx4gsz"); - assertStringsEqual(id, "binance"); - assertStringsEqual(name, "BNB"); -} diff --git a/tests/BinanceSmartChain/SignerTests.cpp b/tests/BinanceSmartChain/SignerTests.cpp deleted file mode 100644 index b3721eb7eec..00000000000 --- a/tests/BinanceSmartChain/SignerTests.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include -#include "Ethereum/Signer.h" -#include "Ethereum/Transaction.h" -#include "Ethereum/Address.h" -#include "Ethereum/RLP.h" -#include "Ethereum/ABI.h" -#include "proto/Ethereum.pb.h" -#include "HexCoding.h" -#include "uint256.h" -#include "../interface/TWTestUtilities.h" - -#include - -namespace TW::Binance { - -using namespace TW::Ethereum; -using namespace TW::Ethereum::ABI; - - -TEST(BinanceSmartChain, SignNativeTransfer) { - // https://explorer.binance.org/smart-testnet/tx/0x6da28164f7b3bc255d749c3ae562e2a742be54c12bf1858b014cc2fe5700684e - - auto toAddress = parse_hex("0x31BE00EB1fc8e14A696DBC72f746ec3e95f49683"); - auto transaction = TransactionNonTyped::buildNativeTransfer( - /* nonce: */ 0, - /* gasPrice: */ 20000000000, - /* gasLimit: */ 21000, - /* to: */ toAddress, - /* amount: */ 10000000000000000 // 0.01 - ); - - // addr: 0xB9F5771C27664bF2282D98E09D7F50cEc7cB01a7 mnemonic: isolate dismiss ... cruel note - auto privateKey = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); - uint256_t chainID = 97; - auto signature = Signer::sign(privateKey, chainID, transaction); - - auto encoded = transaction->encoded(signature, chainID); - ASSERT_EQ(hex(encoded), "f86c808504a817c8008252089431be00eb1fc8e14a696dbc72f746ec3e95f49683872386f26fc100008081e5a057806b486844c5d0b7b5ce34b289f4e8776aa1fe24a3311cef5053995c51050ca07697aa0695de27da817625df0e7e4c64b0ab22d9df30aec92299a7b380be8db7"); -} - -TEST(BinanceSmartChain, SignTokenTransfer) { - auto toAddress = parse_hex("0x31BE00EB1fc8e14A696DBC72f746ec3e95f49683"); - auto func = Function("transfer", std::vector>{ - std::make_shared(toAddress), - std::make_shared(uint256_t(10000000000000000)) - }); - Data payloadFunction; - func.encode(payloadFunction); - EXPECT_EQ(hex(payloadFunction), "a9059cbb00000000000000000000000031be00eb1fc8e14a696dbc72f746ec3e95f49683000000000000000000000000000000000000000000000000002386f26fc10000"); - - auto input = Proto::SigningInput(); - auto chainId = store(uint256_t(97)); - auto nonce = store(uint256_t(30)); - auto gasPrice = store(uint256_t(20000000000)); - auto gasLimit = store(uint256_t(1000000)); - auto tokenContractAddress = "0xed24fc36d5ee211ea25a80239fb8c4cfd80f12ee"; - auto dummyAmount = store(uint256_t(0)); - // addr: 0xB9F5771C27664bF2282D98E09D7F50cEc7cB01a7 mnemonic: isolate dismiss ... cruel note - auto privateKey = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); - - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_to_address(tokenContractAddress); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); - transfer.set_data(payloadFunction.data(), payloadFunction.size()); - - const std::string expected = "f8ab1e8504a817c800830f424094ed24fc36d5ee211ea25a80239fb8c4cfd80f12ee80b844a9059cbb00000000000000000000000031be00eb1fc8e14a696dbc72f746ec3e95f49683000000000000000000000000000000000000000000000000002386f26fc1000081e6a0aa9d5e9a947e96f728fe5d3e6467000cd31a693c00270c33ec64b4abddc29516a00bf1d5646139b2bcca1ad64e6e79f45b7d1255de603b5a3765cbd9544ae148d0"; - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSmartChain); - - EXPECT_EQ(hex(output.encoded()), expected); -} - -} // namespace TW::Binance diff --git a/tests/BinanceSmartChain/TWAnyAddressTests.cpp b/tests/BinanceSmartChain/TWAnyAddressTests.cpp deleted file mode 100644 index e50537d7b8c..00000000000 --- a/tests/BinanceSmartChain/TWAnyAddressTests.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include "HexCoding.h" - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; - -TEST(TWBinanceSmartChain, Address) { - - auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA("727f677b390c151caf9c206fd77f77918f56904b5504243db9b21e51182c4c06").get())); - auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), false)); - auto string = "0xf3d468DBb386aaD46E92FF222adDdf872C8CC064"; - - auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeSmartChain)); - auto expected = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(STRING(string).get(), TWCoinTypeSmartChain)); - - auto addressString = WRAPS(TWAnyAddressDescription(address.get())); - auto expectedString = WRAPS(TWAnyAddressDescription(expected.get())); - - assertStringsEqual(addressString, string); - assertStringsEqual(expectedString, string); -} diff --git a/tests/BinanceSmartChain/TWCoinTypeTests.cpp b/tests/BinanceSmartChain/TWCoinTypeTests.cpp deleted file mode 100644 index 8cca95a5cf4..00000000000 --- a/tests/BinanceSmartChain/TWCoinTypeTests.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWBinanceSmartChainCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeSmartChain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb9ae2e808fe8e57171f303ad8f6e3fd17d949b0bfc7b4db6e8e30a71cc517d7e")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeSmartChain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x35552c16704d214347f29Fa77f77DA6d75d7C752")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeSmartChain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeSmartChain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeSmartChain)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeSmartChain), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeSmartChain)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeSmartChain)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeSmartChain)); - ASSERT_EQ(20000714, TWCoinTypeSmartChain); - assertStringsEqual(symbol, "BNB"); - assertStringsEqual(txUrl, "https://bscscan.com/tx/0xb9ae2e808fe8e57171f303ad8f6e3fd17d949b0bfc7b4db6e8e30a71cc517d7e"); - assertStringsEqual(accUrl, "https://bscscan.com/address/0x35552c16704d214347f29Fa77f77DA6d75d7C752"); - assertStringsEqual(id, "smartchain"); - assertStringsEqual(name, "Smart Chain"); -} - -TEST(TWBinanceSmartChainLegacyCoinType, TWCoinType) { - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeSmartChainLegacy)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeSmartChainLegacy)); - - ASSERT_EQ(10000714, TWCoinTypeSmartChainLegacy); - assertStringsEqual(id, "bsc"); - assertStringsEqual(name, "Smart Chain Legacy"); -} diff --git a/tests/Bitcoin/SegwitAddressTests.cpp b/tests/Bitcoin/SegwitAddressTests.cpp deleted file mode 100644 index 256f5b59981..00000000000 --- a/tests/Bitcoin/SegwitAddressTests.cpp +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Bech32.h" -#include "Bitcoin/SegwitAddress.h" -#include "HexCoding.h" - -#include -#include -#include -#include - -using namespace TW; -using namespace TW::Bitcoin; - -static const std::string valid_checksum[] = { - "A12UEL5L", - "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", - "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", - "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", - "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", -}; - -static const std::string invalid_checksum[] = { - " 1nwldj5", - "\x7f""1axkwrx", - "an124characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx0123456789012345678901234567890123456789", - "pzry9x0s0muk", - "1pzry9x0s0muk", - "x1b4n0q5v", - "li1dgmt3", - "de1lg7wt\xff", -}; - -struct valid_address_data { - std::string address; - std::string scriptPubKeyHex; -}; - -struct invalid_address_data { - std::string hrp; - int version; - size_t program_length; -}; - -static const std::vector valid_address = { - /// test vectors from BIP173 - {"BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", "0014751e76e8199196d454941c45d1b3a323f1433bd6"}, - {"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"}, - //{"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"}, - //{"BC1SW50QA3JX3S", "6002751e"}, - //{"bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", "5210751e76e8199196d454941c45d1b3a323"}, - {"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"}, - - /// test vectors from BIP350 - {"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"}, - {"BC1SW50QGDZ25J", "6002751e"}, - {"bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", "5210751e76e8199196d454941c45d1b3a323"}, - {"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"}, - {"tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", "5120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"}, - {"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", "512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"}, -}; - -static const std::vector invalid_address = { - /// test vectors from BIP73 - //"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", // Invalid human-readable part - "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", // Invalid checksum - "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", // Invalid witness version - "bc1rw5uspcuh", // Invalid program length - "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", // Invalid program length - "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", // Invalid program length for witness version 0 (per BIP141) - "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", // Mixed case - "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", // zero padding of more than 4 bits - "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", // Non-zero padding in 8-to-5 conversion - "bc1gmk9yu", // Empty data section - - /// test vectors from BIP350 - //"tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut", // Invalid human-readable part - "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd", // Invalid checksum (Bech32 instead of Bech32m) - "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf", // Invalid checksum (Bech32 instead of Bech32m) - "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL", // Invalid checksum (Bech32 instead of Bech32m) - "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh", // Invalid checksum (Bech32m instead of Bech32) - "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47", // Invalid checksum (Bech32m instead of Bech32) - "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4", // Invalid character in checksum - "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R", // Invalid witness version - "bc1pw5dgrnzv", // Invalid program length (1 byte) - "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav", // Invalid program length (41 bytes) - "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", // Invalid program length for witness version 0 (per BIP141) - "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq", // Mixed case - "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf", // zero padding of more than 4 bits - "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j", // Non-zero padding in 8-to-5 conversion - "bc1gmk9yu", // Empty data section - - "an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", // overall max length exceeded - "bc1q9zpgru", // 1 byte data (only version byte) - "BC1SW50QA3JX3S", // version = 16 - "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", // version = 2 -}; - -static const invalid_address_data invalid_address_enc[] = { - {"BC", 0, 20}, - {"bc", 0, 21}, - {"bc", 17, 32}, - {"bc", 1, 1}, - {"bc", 16, 41}, -}; - -static Data segwit_scriptpubkey(int witver, const Data& witprog) { - Data ret; - ret.push_back(witver ? (0x50 + (witver & 0x1f)) : 0); - ret.push_back(witprog.size()); - ret.insert(ret.end(), witprog.begin(), witprog.end()); - return ret; -} - -bool case_insensitive_equal(const std::string& s1, const std::string& s2) { - std::string s1l = s1; - std::transform(s1l.begin(), s1l.end(), s1l.begin(), [](unsigned char c){ return std::tolower(c); }); - std::string s2l = s2; - std::transform(s2l.begin(), s2l.end(), s2l.begin(), [](unsigned char c){ return std::tolower(c); }); - return s1l == s2l; -} - -TEST(SegwitAddress, ValidChecksum) { - for (auto i = 0; i < sizeof(valid_checksum) / sizeof(valid_checksum[0]); ++i) { - auto dec = Bech32::decode(valid_checksum[i]); - ASSERT_FALSE(std::get<0>(dec).empty()); - - auto recode = Bech32::encode(std::get<0>(dec), std::get<1>(dec), Bech32::ChecksumVariant::Bech32); - ASSERT_FALSE(recode.empty()); - - ASSERT_TRUE(case_insensitive_equal(recode, valid_checksum[i])); - } -} - -TEST(SegwitAddress, InvalidChecksum) { - for (auto i = 0; i < sizeof(invalid_checksum) / sizeof(invalid_checksum[0]); ++i) { - auto dec = Bech32::decode(invalid_checksum[i]); - EXPECT_TRUE(std::get<0>(dec).empty() && std::get<1>(dec).empty()); - } -} - -TEST(SegwitAddress, ValidAddress) { - for (auto& td: valid_address) { - auto dec = SegwitAddress::decode(td.address); - EXPECT_TRUE(std::get<2>(dec)) << "Valid address could not be decoded " << td.address; - EXPECT_TRUE(std::get<0>(dec).witnessProgram.size() > 0) << "Empty decoded address data for " << td.address; - EXPECT_EQ(std::get<1>(dec).length(), 2); // hrp - - // recode - std::string recode = std::get<0>(dec).string(); - EXPECT_FALSE(recode.empty()) << "Recode failed for " << td.address; - EXPECT_TRUE(case_insensitive_equal(td.address, recode)); - - Data spk = segwit_scriptpubkey(std::get<0>(dec).witnessVersion, std::get<0>(dec).witnessProgram); - EXPECT_EQ(hex(spk), td.scriptPubKeyHex); - } -} - -TEST(SegwitAddress, InvalidAddress) { - for (auto i = 0; i < invalid_address.size(); ++i) { - auto dec = SegwitAddress::decode(invalid_address[i]); - EXPECT_FALSE(std::get<2>(dec)) << "Invalid address reported as valid: " << invalid_address[i]; - } -} - -TEST(SegwitAddress, InvalidAddressEncoding) { - for (auto i = 0; i < sizeof(invalid_address_enc) / sizeof(invalid_address_enc[0]); ++i) { - auto address = SegwitAddress(invalid_address_enc[i].hrp, invalid_address_enc[i].version, Data(invalid_address_enc[i].program_length, 0)); - std::string code = address.string(); - EXPECT_TRUE(code.empty()); - } -} - -TEST(SegwitAddress, LegacyAddress) { - auto result = SegwitAddress::decode("TLWEciM1CjP5fJqM2r9wymAidkkYtTU5k3"); - EXPECT_FALSE(std::get<2>(result)); -} - -TEST(SegwitAddress, fromRaw) { - { - auto addr = SegwitAddress::fromRaw("bc", parse_hex("000e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e16")); - EXPECT_TRUE(addr.second); - EXPECT_EQ(addr.first.string(), "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"); - } - { - // empty data - auto addr = SegwitAddress::fromRaw("bc", Data()); - EXPECT_FALSE(addr.second); - } -} diff --git a/tests/Bitcoin/TWBitcoinTransactionTests.cpp b/tests/Bitcoin/TWBitcoinTransactionTests.cpp deleted file mode 100644 index 4086289a438..00000000000 --- a/tests/Bitcoin/TWBitcoinTransactionTests.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Bitcoin/Transaction.h" -#include "HexCoding.h" -#include "../interface/TWTestUtilities.h" - -#include - -#include - -using namespace TW; -using namespace TW::Bitcoin; - -TEST(BitcoinTransaction, Encode) { - auto transaction = Transaction(2, 0); - - auto po0 = OutPoint(parse_hex("5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f"), 0); - transaction.inputs.emplace_back(po0, Script(), 4294967295); - - auto po1 = OutPoint(parse_hex("bf829c6bcf84579331337659d31f89dfd138f7f7785802d5501c92333145ca7c"), 18); - transaction.inputs.emplace_back(po1, Script(), 4294967295); - - auto po2 = OutPoint(parse_hex("22a6f904655d53ae2ff70e701a0bbd90aa3975c0f40bfc6cc996a9049e31cdfc"), 1); - transaction.inputs.emplace_back(po2, Script(), 4294967295); - - auto oscript0 = Script(parse_hex("76a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac")); - transaction.outputs.emplace_back(18000000, oscript0); - - auto oscript1 = Script(parse_hex("0x76a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac")); - transaction.outputs.emplace_back(400000000, oscript1); - - Data unsignedData; - transaction.encode(unsignedData, Transaction::SegwitFormatMode::NonSegwit); - ASSERT_EQ(unsignedData.size(), 201); - ASSERT_EQ(hex(unsignedData), - "02000000035897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f0000000000ffffffffbf829c6bcf84579331337659d31f89dfd138f7f7785802d5501c92333145ca7c1200000000ffffffff22a6f904655d53ae2ff70e701a0bbd90aa3975c0f40bfc6cc996a9049e31cdfc0100000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d717000000001976a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac00000000"); -} diff --git a/tests/Bitcoin/TWCoinTypeTests.cpp b/tests/Bitcoin/TWCoinTypeTests.cpp deleted file mode 100644 index 0cba7d8cb7e..00000000000 --- a/tests/Bitcoin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWBitcoinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBitcoin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0607f62530b68cfcc91c57a1702841dd399a899d0eecda8e31ecca3f52f01df2")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBitcoin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("17A16QmavnUfCW11DAApiJxp7ARnxN5pGX")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBitcoin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBitcoin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBitcoin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBitcoin), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeBitcoin)); - ASSERT_EQ(0x5, TWCoinTypeP2shPrefix(TWCoinTypeBitcoin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBitcoin)); - assertStringsEqual(symbol, "BTC"); - assertStringsEqual(txUrl, "https://blockchair.com/bitcoin/transaction/0607f62530b68cfcc91c57a1702841dd399a899d0eecda8e31ecca3f52f01df2"); - assertStringsEqual(accUrl, "https://blockchair.com/bitcoin/address/17A16QmavnUfCW11DAApiJxp7ARnxN5pGX"); - assertStringsEqual(id, "bitcoin"); - assertStringsEqual(name, "Bitcoin"); -} diff --git a/tests/Bitcoin/TWSegwitAddressTests.cpp b/tests/Bitcoin/TWSegwitAddressTests.cpp deleted file mode 100644 index cf426d39ff2..00000000000 --- a/tests/Bitcoin/TWSegwitAddressTests.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" - -#include -#include - -#include - -const char *address1 = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"; -const char *address2 = "bc1qr583w2swedy2acd7rung055k8t3n7udp7vyzyg"; - -TEST(TWSegwitAddress, PublicKeyToAddress) { - auto pkData = DATA("0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"); - auto publicKey = WRAP(TWPublicKey, TWPublicKeyCreateWithData(pkData.get(), TWPublicKeyTypeSECP256k1)); - - auto address = WRAP(TWSegwitAddress, TWSegwitAddressCreateWithPublicKey(TWHRPBitcoin, publicKey.get())); - auto string = WRAPS(TWSegwitAddressDescription(address.get())); - - ASSERT_STREQ(address1, TWStringUTF8Bytes(string.get())); -} - -TEST(TWSegwitAddress, InitWithAddress) { - auto string = STRING(address1); - auto address = WRAP(TWSegwitAddress, TWSegwitAddressCreateWithString(string.get())); - auto description = WRAPS(TWSegwitAddressDescription(address.get())); - - ASSERT_TRUE(address.get() != nullptr); - ASSERT_STREQ(address1, TWStringUTF8Bytes(description.get())); -} - -TEST(TWSegwitAddress, InvalidAddress) { - std::vector> strings = { - STRING("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"), - STRING("bc1rw5uspcuh"), - STRING("bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"), - STRING("BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"), - STRING("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"), - STRING("bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"), - STRING("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"), - STRING("bc1gmk9yu"), - }; - for (auto& string : strings) { - ASSERT_TRUE(TWSegwitAddressCreateWithString(string.get()) == nullptr) << "Invalid address '" << TWStringUTF8Bytes(string.get()) << "' reported as valid."; - } -} diff --git a/tests/BitcoinCash/TWCoinTypeTests.cpp b/tests/BitcoinCash/TWCoinTypeTests.cpp deleted file mode 100644 index 9b56e10a7af..00000000000 --- a/tests/BitcoinCash/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWBitcoinCashCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBitcoinCash)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBitcoinCash, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBitcoinCash, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBitcoinCash)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBitcoinCash)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBitcoinCash), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeBitcoinCash)); - ASSERT_EQ(0x5, TWCoinTypeP2shPrefix(TWCoinTypeBitcoinCash)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBitcoinCash)); - assertStringsEqual(symbol, "BCH"); - assertStringsEqual(txUrl, "https://blockchair.com/bitcoin-cash/transaction/t123"); - assertStringsEqual(accUrl, "https://blockchair.com/bitcoin-cash/address/a12"); - assertStringsEqual(id, "bitcoincash"); - assertStringsEqual(name, "Bitcoin Cash"); -} diff --git a/tests/BitcoinGold/TWCoinTypeTests.cpp b/tests/BitcoinGold/TWCoinTypeTests.cpp deleted file mode 100644 index 55b11cdfa52..00000000000 --- a/tests/BitcoinGold/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWBitcoinGoldCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBitcoinGold)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("2f807d7734de35d2236a1b3d8704eb12954f5f82ea66987949b10e94d9999b23")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBitcoinGold, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("GJjz2Du9BoJQ3CPcoyVTHUJZSj62i1693U")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBitcoinGold, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBitcoinGold)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBitcoinGold)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBitcoinGold), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeBitcoinGold)); - ASSERT_EQ(23, TWCoinTypeP2shPrefix(TWCoinTypeBitcoinGold)); - ASSERT_EQ(0, TWCoinTypeStaticPrefix(TWCoinTypeBitcoinGold)); - assertStringsEqual(symbol, "BTG"); - assertStringsEqual(txUrl, "https://explorer.bitcoingold.org/insight/tx/2f807d7734de35d2236a1b3d8704eb12954f5f82ea66987949b10e94d9999b23"); - assertStringsEqual(accUrl, "https://explorer.bitcoingold.org/insight/address/GJjz2Du9BoJQ3CPcoyVTHUJZSj62i1693U"); - assertStringsEqual(id, "bitcoingold"); - assertStringsEqual(name, "Bitcoin Gold"); -} diff --git a/tests/BitcoinGold/TWSegwitAddressTests.cpp b/tests/BitcoinGold/TWSegwitAddressTests.cpp deleted file mode 100644 index c624b0a90de..00000000000 --- a/tests/BitcoinGold/TWSegwitAddressTests.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// - -#include "../interface/TWTestUtilities.h" -#include "Bitcoin/SegwitAddress.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "HexCoding.h" -#include -#include - -using namespace TW; - -TEST(TWBitcoinGoldSegwitAddress, Valid) { - ASSERT_TRUE(Bitcoin::SegwitAddress::isValid("btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk")); - ASSERT_FALSE(Bitcoin::SegwitAddress::isValid("btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sl")); -} - -/// Initializes a Bech32 address with a human-readable part, a witness -/// version, and a witness program. -TEST(TWBitcoinGoldSegwitAddress, WitnessProgramToAddress) { - auto address = Bitcoin::SegwitAddress("btg", 0, parse_hex("5e6132a9ad21f7423081441ab4ae229501f6c8a8")); - - ASSERT_TRUE(Bitcoin::SegwitAddress::isValid(address.string())); - ASSERT_EQ(address.string(), "btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk"); -} - -/// Initializes a Bech32 address with a public key and a HRP prefix. -TEST(TWBitcoinGoldSegwitAddress, PubkeyToAddress) { - const auto publicKey = PublicKey(parse_hex("02f74712b5d765a73b52a14c1e113f2ef3f9502d09d5987ee40f53828cfe68b9a6"), TWPublicKeyTypeSECP256k1); - - /// construct with public key - auto address = Bitcoin::SegwitAddress(publicKey, 0, "btg"); - - ASSERT_TRUE(Bitcoin::SegwitAddress::isValid(address.string())); - ASSERT_EQ(address.string(), "btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk"); -} - -/// Decodes a SegWit address. -TEST(TWBitcoinGoldSegwitAddress, Decode) { - auto result = Bitcoin::SegwitAddress::decode("btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk"); - - ASSERT_TRUE(std::get<2>(result)); - ASSERT_EQ(std::get<0>(result).string(), "btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk"); - ASSERT_EQ(std::get<1>(result), "btg"); -} diff --git a/tests/Bluzelle/TWCoinTypeTests.cpp b/tests/Bluzelle/TWCoinTypeTests.cpp deleted file mode 100644 index 33acfa33f7b..00000000000 --- a/tests/Bluzelle/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWCoinTypeBluzelle, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBluzelle)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("AC026E0EC6E33A77D5EA6B9CEF9810699BC2AD8C5582E007E7857457C6D3B819")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBluzelle, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("bluzelle1q9cryfal7u3jvnq6er5ufety20xtzw6ycx2te9")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBluzelle, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBluzelle)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBluzelle)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBluzelle), 6); - ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeBluzelle)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeBluzelle)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBluzelle)); - assertStringsEqual(symbol, "BLZ"); - assertStringsEqual(txUrl, "https://bigdipper.net.bluzelle.com/transactions/AC026E0EC6E33A77D5EA6B9CEF9810699BC2AD8C5582E007E7857457C6D3B819"); - assertStringsEqual(accUrl, "https://bigdipper.net.bluzelle.com/account/bluzelle1q9cryfal7u3jvnq6er5ufety20xtzw6ycx2te9"); - assertStringsEqual(id, "bluzelle"); - assertStringsEqual(name, "Bluzelle"); -} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 28a0132cf92..fbeed4ace8a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,9 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + enable_testing() # Prevent overriding the parent project's compiler/linker @@ -9,7 +15,7 @@ if(WIN32) else() # Add googletest directly to our build. This defines # the gtest and gtest_main targets. - add_subdirectory(${CMAKE_SOURCE_DIR}/build/local/src/gtest/googletest-release-1.10.0 + add_subdirectory(${CMAKE_SOURCE_DIR}/build/local/src/gtest/googletest-release-1.11.0 ${CMAKE_CURRENT_BINARY_DIR}/googletest-build EXCLUDE_FROM_ALL) set(GTEST_LIBRARIES gtest) @@ -21,17 +27,27 @@ endif() ##include_directories(${Protobuf_INCLUDE_DIRS}) # Test executable -file(GLOB_RECURSE test_sources *.cpp **/*.cpp) +file(GLOB_RECURSE test_sources *.cpp **/*.cpp **/*.cc) +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") + list(FILTER test_sources EXCLUDE REGEX ".*CMakeCXXCompilerId\\.cpp$") +endif() + add_executable(tests ${test_sources}) target_link_libraries(tests ${GTEST_MAIN_LIBRARIES} TrezorCrypto TrustWalletCore walletconsolelib ${Protobuf_LIBRARIES} Boost::boost) target_include_directories(tests PRIVATE ${CMAKE_SOURCE_DIR}/src) +target_include_directories(tests PRIVATE ${CMAKE_SOURCE_DIR}/tests/common) + if(NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) target_compile_options(tests PRIVATE "-Wall") endif() +if (NOT ANDROID AND TW_UNITY_BUILD) + set_target_properties(tests PROPERTIES UNITY_BUILD ON) +endif() + set_target_properties(tests PROPERTIES - CXX_STANDARD 17 + CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON ) diff --git a/tests/Callisto/TWCoinTypeTests.cpp b/tests/Callisto/TWCoinTypeTests.cpp deleted file mode 100644 index e08e4a46cc9..00000000000 --- a/tests/Callisto/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWCallistoCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCallisto)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCallisto, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCallisto, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCallisto)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCallisto)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCallisto), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeCallisto)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeCallisto)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeCallisto)); - assertStringsEqual(symbol, "CLO"); - assertStringsEqual(txUrl, "https://explorer2.callisto.network/tx/t123"); - assertStringsEqual(accUrl, "https://explorer2.callisto.network/addr/a12"); - assertStringsEqual(id, "callisto"); - assertStringsEqual(name, "Callisto"); -} diff --git a/tests/Cardano/AddressTests.cpp b/tests/Cardano/AddressTests.cpp deleted file mode 100644 index d798fb66f90..00000000000 --- a/tests/Cardano/AddressTests.cpp +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Cardano/AddressV3.h" - -#include "HDWallet.h" -#include "HexCoding.h" -#include "PrivateKey.h" - -#include - -#include - -using namespace TW; -using namespace TW::Cardano; -using namespace std; - - -TEST(CardanoAddress, Validation) { - // valid V3 address - ASSERT_TRUE(AddressV3::isValid("addr1s3hdtrqgs47l7ue5srga8wmk9dzw279x9e7lxadalt6z0fk64nnn2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59j5lempe")); - ASSERT_TRUE(AddressV3::isValid("addr1s3xuxwfetyfe7q9u3rfn6je9stlvcgmj8rezd87qjjegdtxm3y3f2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59jz29g8j")); - ASSERT_TRUE(AddressV3::isValid("ca1qvqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jqxuzx4s")); - ASSERT_TRUE(AddressV3::isValid("ca1qsqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jq2f29vkz6t30xqcnyve5x5mrwwpe8ganc0f78aqyzsjrg3z5v36gguhxny")); - - // valid V2 address - ASSERT_TRUE(AddressV3::isValid("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx")); - ASSERT_TRUE(AddressV3::isValid("Ae2tdPwUPEZ6RUCnjGHFqi59k5WZLiv3HoCCNGCW8SYc5H9srdTzn1bec4W")); - - // valid V1 address - ASSERT_TRUE(AddressV3::isValid("DdzFFzCqrhssmYoG5Eca1bKZFdGS8d6iag1mU4wbLeYcSPVvBNF2wRG8yhjzQqErbg63N6KJA4DHqha113tjKDpGEwS5x1dT2KfLSbSJ")); - ASSERT_TRUE(AddressV3::isValid("DdzFFzCqrht7HGoJ87gznLktJGywK1LbAJT2sbd4txmgS7FcYLMQFhawb18ojS9Hx55mrbsHPr7PTraKh14TSQbGBPJHbDZ9QVh6Z6Di")); - - // invalid checksum V3 - ASSERT_FALSE(AddressV3::isValid("PREFIX1qvqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jqxuzx4s")); - // invalid checksum V2 - ASSERT_FALSE(AddressV3::isValid("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvm")); - // random - ASSERT_FALSE(AddressV3::isValid("hasoiusaodiuhsaijnnsajnsaiussai")); - // empty - ASSERT_FALSE(AddressV3::isValid("")); -} - -TEST(CardanoAddress, FromStringV2) { - { - auto address = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); - ASSERT_EQ(address.string(), "Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); - } - { - auto address = AddressV3("DdzFFzCqrhssmYoG5Eca1bKZFdGS8d6iag1mU4wbLeYcSPVvBNF2wRG8yhjzQqErbg63N6KJA4DHqha113tjKDpGEwS5x1dT2KfLSbSJ"); - ASSERT_EQ(address.string(), "DdzFFzCqrhssmYoG5Eca1bKZFdGS8d6iag1mU4wbLeYcSPVvBNF2wRG8yhjzQqErbg63N6KJA4DHqha113tjKDpGEwS5x1dT2KfLSbSJ"); - } -} - -TEST(CardanoAddress, FromStringV3) { - { - // single addr - auto address = AddressV3("ca1qvqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jqxuzx4s"); - EXPECT_EQ(address.string("ca"), "ca1qvqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jqxuzx4s"); - EXPECT_EQ(address.string("ca"), "ca1qvqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jqxuzx4s"); - EXPECT_EQ(AddressV3::Discrim_Production, address.discrimination); - EXPECT_EQ(AddressV3::Kind_Single, address.kind); - EXPECT_EQ("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", hex(address.key1)); - EXPECT_EQ("", hex(address.groupKey)); - } - { - // group addr - auto address = AddressV3("addr1s3xuxwfetyfe7q9u3rfn6je9stlvcgmj8rezd87qjjegdtxm3y3f2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59jz29g8j"); - EXPECT_EQ(address.string(), "addr1s3xuxwfetyfe7q9u3rfn6je9stlvcgmj8rezd87qjjegdtxm3y3f2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59jz29g8j"); - EXPECT_EQ(AddressV3::Discrim_Test, address.discrimination); - EXPECT_EQ(AddressV3::Kind_Group, address.kind); - EXPECT_EQ("4dc3393959139f00bc88d33d4b2582fecc237238f2269fc094b286acdb892295", hex(address.key1)); - EXPECT_EQ("6d0b99fdb1cc851fbc8dbcb7d6710fde28df46360c259d482db679c24c4e50b2", hex(address.groupKey)); - } -} - -TEST(CardanoAddress, MnemonicToAddressV2) { - { - // Test from cardano-crypto.js; Test wallet - auto mnemonic = "cost dash dress stove morning robust group affair stomach vacant route volume yellow salute laugh"; - auto wallet = HDWallet(mnemonic, ""); - - PrivateKey masterPrivKey = wallet.getMasterKey(TWCurve::TWCurveED25519Extended); - PrivateKey masterPrivKeyExt = wallet.getMasterKeyExtension(TWCurve::TWCurveED25519Extended); - ASSERT_EQ("a018cd746e128a0be0782b228c275473205445c33b9000a33dd5668b430b5744", hex(masterPrivKey.bytes)); - ASSERT_EQ("26877cfe435fddda02409b839b7386f3738f10a30b95a225f4b720ee71d2505b", hex(masterPrivKeyExt.bytes)); - - { - string addr = wallet.deriveAddress(TWCoinType::TWCoinTypeCardano); - EXPECT_EQ("addr1sna05l45z33zpkm8z44q8f0h57wxvm0c86e34wlmua7gtcrdgrdrzy8ny3walyfjanhe33nsyuh088qr5gepqaen6jsa9r94xvvd7fh6jc3e6x", addr); - } - { - PrivateKey privKey0 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/44'/1815'/0'/0/0")); - PublicKey pubKey0 = privKey0.getPublicKey(TWPublicKeyTypeED25519Extended); - auto addr0 = AddressV2(pubKey0); - EXPECT_EQ("Ae2tdPwUPEZ6RUCnjGHFqi59k5WZLiv3HoCCNGCW8SYc5H9srdTzn1bec4W", addr0.string()); - } - { - PrivateKey privKey1 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/44'/1815'/0'/0/1")); - PublicKey pubKey1 = privKey1.getPublicKey(TWPublicKeyTypeED25519Extended); - auto addr1 = AddressV2(pubKey1); - EXPECT_EQ("Ae2tdPwUPEZ7dnds6ZyhQdmgkrDFFPSDh8jG9RAhswcXt1bRauNw5jczjpV", addr1.string()); - } - { - PrivateKey privKey1 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/44'/1815'/0'/0/2")); - PublicKey pubKey1 = privKey1.getPublicKey(TWPublicKeyTypeED25519Extended); - auto addr1 = AddressV2(pubKey1); - EXPECT_EQ("Ae2tdPwUPEZ8LAVy21zj4BF97iWxKCmPv12W6a18zLX3V7rZDFFVgqUBkKw", addr1.string()); - } - { - PrivateKey privKey0 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/1852'/1815'/0'/0/0")); - PublicKey pubKey0 = privKey0.getPublicKey(TWPublicKeyTypeED25519Extended); - auto addr0 = AddressV3(pubKey0); - EXPECT_EQ("addr1sna05l45z33zpkm8z44q8f0h57wxvm0c86e34wlmua7gtcrdgrdrzy8ny3walyfjanhe33nsyuh088qr5gepqaen6jsa9r94xvvd7fh6jc3e6x", addr0.string()); - } - { - PrivateKey privKey1 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/1852'/1815'/0'/0/1")); - PublicKey pubKey1 = privKey1.getPublicKey(TWPublicKeyTypeED25519Extended); - auto addr1 = AddressV3(pubKey1); - EXPECT_EQ("addr1sjkw630aatyg273m9cpgezvs2unf6xrtw0z7udhguh7ednkhf9p0jduldrg5qsnaz99e3sl4f8t56w0hs0zhql9lacr63mx693ppjw2r5nwehs", addr1.string()); - } - { - PrivateKey privKey1 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/1852'/1815'/0'/0/2")); - PublicKey pubKey1 = privKey1.getPublicKey(TWPublicKeyTypeED25519Extended); - auto addr1 = AddressV3(pubKey1); - EXPECT_EQ("addr1sng939f9el5mnsj4l30kk2f02ea63rwhny5pa69masam4xtsmp5naq6lks0p7pzkn35z7juyd7hhk3zc8p9dc736pu4nzhyy6fusxapa9v5h5c", addr1.string()); - } - } - { - auto mnemonicPlay1 = "youth away raise north opinion slice dash bus soldier dizzy bitter increase saddle live champion"; - auto wallet = HDWallet(mnemonicPlay1, ""); - string addr = wallet.deriveAddress(TWCoinType::TWCoinTypeCardano); - EXPECT_EQ("addr1ssf3e4w2g8gpqlewnt0a4t9kwvdwhxyaaqu7tmru20zgakwf2mdu3jamu779gr3085lykk7r0q8t6lf6p2vfj7u9ma2s7a748vn0se2gxym6da", addr); - } - { - auto mnemonicPlay2 = "return custom two home gain guilt kangaroo supply market current curtain tomorrow heavy blue robot"; - auto wallet = HDWallet(mnemonicPlay2, ""); - string addr = wallet.deriveAddress(TWCoinType::TWCoinTypeCardano); - EXPECT_EQ("addr1sn8hmvmhxw6926mgz3fn5qp205wmu42adg8uehnce3nfr4umecm3mfqmxy4jyl5xewag3kq52vulqpgt386atv3v5upz9j0rl4cc42m707gewk", addr); - } - { - auto mnemonicALDemo = "civil void tool perfect avocado sweet immense fluid arrow aerobic boil flash"; - auto wallet = HDWallet(mnemonicALDemo, ""); - string addr = wallet.deriveAddress(TWCoinType::TWCoinTypeCardano); - EXPECT_EQ("addr1sn32yvavqtnzpqggxf9aa0yypng80gr3anfpwppz8dhztx4cevzp5v6nf40c6d8v6z70fcd76634sdlyfpfpk6d3ya84czk83jlze676vmpf37", addr); - } - { - // V2 Tested against AdaLite - auto mnemonicPlay1 = "youth away raise north opinion slice dash bus soldier dizzy bitter increase saddle live champion"; - auto wallet = HDWallet(mnemonicPlay1, ""); - PrivateKey privateKey = wallet.getKey(TWCoinTypeCardano, DerivationPath(TWPurposeBIP44, TWCoinTypeCardano, DerivationPathIndex(0, true).derivationIndex(), 0, 0)); - PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Extended); - string addr = AddressV2(publicKey).string(); - EXPECT_EQ("Ae2tdPwUPEZJYT9g1JgQWtLveUHavyRxQGi6hVzoQjct7yyCLGgk3pCyx7h", addr); - } - { - // V2 Tested against AdaLite - auto mnemonicPlay2 = "return custom two home gain guilt kangaroo supply market current curtain tomorrow heavy blue robot"; - auto wallet = HDWallet(mnemonicPlay2, ""); - PrivateKey privateKey = wallet.getKey(TWCoinTypeCardano, DerivationPath(TWPurposeBIP44, TWCoinTypeCardano, DerivationPathIndex(0, true).derivationIndex(), 0, 0)); - PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Extended); - string addr = AddressV2(publicKey).string(); - EXPECT_EQ("Ae2tdPwUPEZLtJx7LA2XZ3zzwonH9x9ieX3dMzaTBD3TfXuKczjMSjTecr1", addr); - } - { - // V2 AdaLite Demo phrase, 12-word. AdaLite uses V1 for it, in V2 it produces different addresses. - // In AdaLite V1 addr0 is DdzFFzCqrht7HGoJ87gznLktJGywK1LbAJT2sbd4txmgS7FcYLMQFhawb18ojS9Hx55mrbsHPr7PTraKh14TSQbGBPJHbDZ9QVh6Z6Di - auto mnemonicALDemo = "civil void tool perfect avocado sweet immense fluid arrow aerobic boil flash"; - auto wallet = HDWallet(mnemonicALDemo, ""); - PrivateKey privateKey = wallet.getKey(TWCoinTypeCardano, DerivationPath(TWPurposeBIP44, TWCoinTypeCardano, DerivationPathIndex(0, true).derivationIndex(), 0, 0)); - PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Extended); - string addr = AddressV2(publicKey).string(); - EXPECT_EQ("Ae2tdPwUPEZJbLcD8iLgN7hVGvq66WdR4zocntRekSP97Ds3MvCfmEDjJYu", addr); - } -} - -TEST(CardanoAddress, KeyHashV2) { - auto xpub = parse_hex("e6f04522f875c1563682ca876ddb04c2e2e3ae718e3ff9f11c03dd9f9dccf69869272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000"); - auto hash = AddressV2::keyHash(xpub); - ASSERT_EQ("a1eda96a9952a56c983d9f49117f935af325e8a6c9d38496e945faa8", hex(hash)); -} - -TEST(CardanoAddress, FromPublicKeyInternalV3) { - // tests from chain-lib - { - auto address = AddressV3::createSingle(AddressV3::Discrim_Production, parse_hex("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20")); - auto bech = address.string("ca"); - EXPECT_EQ("ca1qvqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jqxuzx4s", bech); - EXPECT_EQ("amaqeayeaudaocajbifqydiob4ibceqtcqkrmfyydenbwha5dypsa", address.stringBase32()); - } - { - auto address = AddressV3::createGroup(AddressV3::Discrim_Production, - parse_hex("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"), - parse_hex("292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748") - ); - auto bech = address.string("ca"); - EXPECT_EQ("ca1qsqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jq2f29vkz6t30xqcnyve5x5mrwwpe8ganc0f78aqyzsjrg3z5v36gguhxny", bech); - EXPECT_EQ("aqaqeayeaudaocajbifqydiob4ibceqtcqkrmfyydenbwha5dypsakjkfmwc2lrpgaytemzugu3doobzhi5typj6h5aecqsdircumr2i", address.stringBase32()); - } - { - auto address = AddressV3::createGroup(AddressV3::Discrim_Test, - parse_hex("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"), - parse_hex("292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748") - ); - auto bech = address.string("ca"); - EXPECT_EQ("ca1ssqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jq2f29vkz6t30xqcnyve5x5mrwwpe8ganc0f78aqyzsjrg3z5v36gjdetkp", bech); - EXPECT_EQ("qqaqeayeaudaocajbifqydiob4ibceqtcqkrmfyydenbwha5dypsakjkfmwc2lrpgaytemzugu3doobzhi5typj6h5aecqsdircumr2i", address.stringBase32()); - } - { - auto address = AddressV3::createAccount(AddressV3::Discrim_Test, parse_hex("292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748")); - auto bech = address.string("ca"); - EXPECT_EQ("ca1s55j52ev95hz7vp3xgengdfkxuurjw3m8s7nu06qg9pyx3z9ger5samu4rv", bech); - EXPECT_EQ("quusukzmfuxc6mbrgiztinjwg44dsor3hq6t4p2aifbegrcfizduq", address.stringBase32()); - } - // end of chain-lib - { - auto address = AddressV3::createGroup(AddressV3::Discrim_Test, - parse_hex("4dc3393959139f00bc88d33d4b2582fecc237238f2269fc094b286acdb892295"), - parse_hex("6d0b99fdb1cc851fbc8dbcb7d6710fde28df46360c259d482db679c24c4e50b2") - ); - auto bech = address.string("addr"); - EXPECT_EQ("addr1s3xuxwfetyfe7q9u3rfn6je9stlvcgmj8rezd87qjjegdtxm3y3f2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59jz29g8j", bech); - } -} - -TEST(CardanoAddress, FromPublicKeyV2) { - { - // caradano-crypto.js test - auto publicKey = PublicKey(parse_hex("e6f04522f875c1563682ca876ddb04c2e2e3ae718e3ff9f11c03dd9f9dccf69869272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000"), TWPublicKeyTypeED25519Extended); - auto address = AddressV2(publicKey); - ASSERT_EQ(address.string(), "Ae2tdPwUPEZCxt4UV1Uj2AMMRvg5pYPypqZowVptz3GYpK4pkcvn3EjkuNH"); - } - { - // Adalite test account addr0 - auto publicKey = PublicKey(parse_hex("57fd54be7b38bb8952782c2f59aa276928a4dcbb66c8c62ce44f9d623ecd5a03bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4"), TWPublicKeyTypeED25519Extended); - auto address = AddressV2(publicKey); - ASSERT_EQ(address.string(), "Ae2tdPwUPEZ6RUCnjGHFqi59k5WZLiv3HoCCNGCW8SYc5H9srdTzn1bec4W"); - } - { - // Adalite test account addr1 - auto publicKey = PublicKey(parse_hex("25af99056d600f7956312406bdd1cd791975bb1ae91c9d034fc65f326195fcdb247ee97ec351c0820dd12de4ca500232f73a35fe6f86778745bcd57f34d1048d"), TWPublicKeyTypeED25519Extended); - auto address = AddressV2(publicKey); - ASSERT_EQ(address.string(), "Ae2tdPwUPEZ7dnds6ZyhQdmgkrDFFPSDh8jG9RAhswcXt1bRauNw5jczjpV"); - } - { - // Play1 addr0 - auto publicKey = PublicKey(parse_hex("7cee0f30b9d536a786547dd77b35679b6830e945ffde768eb4f2a061b9dba016e513fa1290da1d22e83a41f17eed72d4489483b561fff36b9555ffdb91c430e2"), TWPublicKeyTypeED25519Extended); - auto address = AddressV2(publicKey); - ASSERT_EQ(address.string(), "Ae2tdPwUPEZJYT9g1JgQWtLveUHavyRxQGi6hVzoQjct7yyCLGgk3pCyx7h"); - } -} - -TEST(CardanoAddress, FromPrivateKeyV2) { - { - // mnemonic Test, addr0 - auto privateKey = PrivateKey( - parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), - parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), - parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4") - ); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Extended); - ASSERT_EQ(hex(publicKey.bytes), "57fd54be7b38bb8952782c2f59aa276928a4dcbb66c8c62ce44f9d623ecd5a03bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4"); - auto address = AddressV2(publicKey); - ASSERT_EQ(address.string(), "Ae2tdPwUPEZ6RUCnjGHFqi59k5WZLiv3HoCCNGCW8SYc5H9srdTzn1bec4W"); - } - { - // mnemonic Play1, addr0 - auto privateKey = PrivateKey( - parse_hex("a089c9423100960440ccd5b7adbd202d1ab1993a7bb30fc88b287d94016df247"), - parse_hex("da86a87f08fb15de1431a6c0ccd5ebf51c3bee81f7eaf714801bbbe4d903154a"), - parse_hex("e513fa1290da1d22e83a41f17eed72d4489483b561fff36b9555ffdb91c430e2") - ); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Extended); - ASSERT_EQ(hex(publicKey.bytes), "7cee0f30b9d536a786547dd77b35679b6830e945ffde768eb4f2a061b9dba016e513fa1290da1d22e83a41f17eed72d4489483b561fff36b9555ffdb91c430e2"); - auto address = AddressV2(publicKey); - ASSERT_EQ(address.string(), "Ae2tdPwUPEZJYT9g1JgQWtLveUHavyRxQGi6hVzoQjct7yyCLGgk3pCyx7h"); - } - { - // from cardano-crypto.js test - auto privateKey = PrivateKey( - parse_hex("d809b1b4b4c74734037f76aace501730a3fe2fca30b5102df99ad3f7c0103e48"), - parse_hex("d54cde47e9041b31f3e6873d700d83f7a937bea746dadfa2c5b0a6a92502356c"), - parse_hex("69272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000") - ); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Extended); - ASSERT_EQ(hex(publicKey.bytes), "e6f04522f875c1563682ca876ddb04c2e2e3ae718e3ff9f11c03dd9f9dccf69869272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000"); - auto address = AddressV2(publicKey); - ASSERT_EQ(address.string(), "Ae2tdPwUPEZCxt4UV1Uj2AMMRvg5pYPypqZowVptz3GYpK4pkcvn3EjkuNH"); - } -} - -TEST(CardanoAddress, PrivateKeyExtended) { - // check extended key lengths, private key 3x32 bytes, public key 64 bytes - auto privateKeyExt = PrivateKey( - parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), - parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), - parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4") - ); - auto publicKeyExt = privateKeyExt.getPublicKey(TWPublicKeyTypeED25519Extended); - ASSERT_EQ(64, publicKeyExt.bytes.size()); - - // Non-extended: both are 32 bytes. - auto privateKeyNonext = PrivateKey( - parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744") - ); - auto publicKeyNonext = privateKeyNonext.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(32, publicKeyNonext.bytes.size()); -} - -TEST(CardanoAddress, FromStringNegativeInvalidString) { - try { - auto address = AddressV3("__INVALID_ADDRESS__"); - } catch (...) { - return; - } - FAIL() << "Expected exception!"; -} - -TEST(CardanoAddress, FromStringNegativeBadChecksumV2) { - try { - auto address = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvm"); - } catch (...) { - return; - } - FAIL() << "Expected exception!"; -} - -TEST(CardanoAddress, DataV3) { - // group addr - auto address = AddressV3("addr1s3xuxwfetyfe7q9u3rfn6je9stlvcgmj8rezd87qjjegdtxm3y3f2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59jz29g8j"); - EXPECT_EQ("4dc3393959139f00bc88d33d4b2582fecc237238f2269fc094b286acdb892295", hex(address.key1)); - EXPECT_EQ("6d0b99fdb1cc851fbc8dbcb7d6710fde28df46360c259d482db679c24c4e50b2", hex(address.groupKey)); - Data data = address.data(); - EXPECT_EQ( - "0104" - "20" "4dc3393959139f00bc88d33d4b2582fecc237238f2269fc094b286acdb892295" - "20" "6d0b99fdb1cc851fbc8dbcb7d6710fde28df46360c259d482db679c24c4e50b2", - hex(data) - ); -} - -TEST(CardanoAddress, FromDataV3) { - Data data = parse_hex("0104204dc3393959139f00bc88d33d4b2582fecc237238f2269fc094b286acdb892295206d0b99fdb1cc851fbc8dbcb7d6710fde28df46360c259d482db679c24c4e50b2"); - auto address = AddressV3(data); - EXPECT_EQ(address.string(), "addr1s3xuxwfetyfe7q9u3rfn6je9stlvcgmj8rezd87qjjegdtxm3y3f2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59jz29g8j"); - EXPECT_EQ("6d0b99fdb1cc851fbc8dbcb7d6710fde28df46360c259d482db679c24c4e50b2", hex(address.groupKey)); -} - -TEST(CardanoAddress, CopyConstructorLegacy) { - AddressV3 address1 = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); - EXPECT_TRUE(address1.legacyAddressV2.has_value()); - AddressV3 address2 = AddressV3(address1); - EXPECT_TRUE(address2.legacyAddressV2.has_value()); - EXPECT_TRUE(*(address2.legacyAddressV2) == *(address1.legacyAddressV2)); - // if it was not a deep copy, double freeing would occur -} - -TEST(CardanoAddress, AssignmentOperatorLegacy) { - AddressV3 addr1leg = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); - EXPECT_TRUE(addr1leg.legacyAddressV2.has_value()); - AddressV3 addr2nonleg = AddressV3("addr1s3hdtrqgs47l7ue5srga8wmk9dzw279x9e7lxadalt6z0fk64nnn2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59j5lempe"); - EXPECT_FALSE(addr2nonleg.legacyAddressV2.has_value()); - AddressV3 addr3leg = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); - EXPECT_TRUE(addr3leg.legacyAddressV2.has_value()); - - AddressV3 address = addr1leg; - EXPECT_TRUE(address.legacyAddressV2.has_value()); - EXPECT_TRUE(*address.legacyAddressV2 == *addr1leg.legacyAddressV2); - address = addr2nonleg; - EXPECT_FALSE(address.legacyAddressV2.has_value()); - address = addr3leg; - EXPECT_TRUE(address.legacyAddressV2.has_value()); - EXPECT_TRUE(*address.legacyAddressV2 == *addr3leg.legacyAddressV2); -} diff --git a/tests/Cardano/TWCardanoAddressTests.cpp b/tests/Cardano/TWCardanoAddressTests.cpp deleted file mode 100644 index f920bffeb33..00000000000 --- a/tests/Cardano/TWCardanoAddressTests.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include -#include -#include -#include "../interface/TWTestUtilities.h" -#include "PrivateKey.h" - -#include - -TEST(TWCardano, Address) { - auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4").get())); - ASSERT_NE(nullptr, privateKey.get()); - auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519Extended(privateKey.get())); - ASSERT_NE(nullptr, publicKey.get()); - ASSERT_EQ(64, publicKey.get()->impl.bytes.size()); - auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeCardano)); - auto addressString = WRAPS(TWAnyAddressDescription(address.get())); - assertStringsEqual(addressString, "addr1s3tl64970vuthz2j0qkz7kd2ya5j3fxuhdnv333vu38e6c37e4dq80ek4raf7hs3adag2tzpuxz7895a2x8xde5f8jqa8lrjyuqfj5k50pm668"); - - auto address2 = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(STRING("addr1s3tl64970vuthz2j0qkz7kd2ya5j3fxuhdnv333vu38e6c37e4dq80ek4raf7hs3adag2tzpuxz7895a2x8xde5f8jqa8lrjyuqfj5k50pm668").get(), TWCoinTypeCardano)); - ASSERT_NE(nullptr, address2.get()); - auto address2String = WRAPS(TWAnyAddressDescription(address2.get())); - assertStringsEqual(address2String, "addr1s3tl64970vuthz2j0qkz7kd2ya5j3fxuhdnv333vu38e6c37e4dq80ek4raf7hs3adag2tzpuxz7895a2x8xde5f8jqa8lrjyuqfj5k50pm668"); - - ASSERT_TRUE(TWAnyAddressEqual(address.get(), address2.get())); -} - -TEST(TWCardano, SigningNotImplemented) { - // not implemented, returns empty data - auto result = WRAPD(TWAnySignerSign(WRAPD(TWDataCreateWithSize(0)).get(), TWCoinType::TWCoinTypeCardano)); - EXPECT_EQ(TWDataSize(result.get()), 0); -} diff --git a/tests/Cardano/TWCoinTypeTests.cpp b/tests/Cardano/TWCoinTypeTests.cpp deleted file mode 100644 index bf21fe8c6e0..00000000000 --- a/tests/Cardano/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWCardanoCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCardano)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("b7a6c5cadab0f64bdc89c77ee4a351463aba5c33f2cef6bbd6542a74a90a3af3")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCardano, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("DdzFFzCqrhstpwKc8WMvPwwBb5oabcTW9zc5ykA37wJR4tYQucvsR9dXb2kEGNXkFJz2PtrpzfRiZkx8R1iNo8NYqdsukVmv7EAybFwC")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCardano, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCardano)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCardano)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCardano), 6); - ASSERT_EQ(TWBlockchainCardano, TWCoinTypeBlockchain(TWCoinTypeCardano)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeCardano)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeCardano)); - assertStringsEqual(symbol, "ADA"); - assertStringsEqual(txUrl, "https://shelleyexplorer.cardano.org/tx/b7a6c5cadab0f64bdc89c77ee4a351463aba5c33f2cef6bbd6542a74a90a3af3"); - assertStringsEqual(accUrl, "https://shelleyexplorer.cardano.org/address/DdzFFzCqrhstpwKc8WMvPwwBb5oabcTW9zc5ykA37wJR4tYQucvsR9dXb2kEGNXkFJz2PtrpzfRiZkx8R1iNo8NYqdsukVmv7EAybFwC"); - assertStringsEqual(id, "cardano"); - assertStringsEqual(name, "Cardano"); -} diff --git a/tests/CborTests.cpp b/tests/CborTests.cpp deleted file mode 100644 index 2c07cc1b1dc..00000000000 --- a/tests/CborTests.cpp +++ /dev/null @@ -1,513 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Cbor.h" - -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Cbor; -using namespace std; - - -TEST(Cbor, EncSample1) { - EXPECT_EQ( - "8205a26178186461793831", - hex(Encode::array({ - Encode::uint(5), - Encode::map({ - make_pair(Encode::string("x"), Encode::uint(100)), - make_pair(Encode::string("y"), Encode::negInt(50)), - }), - }).encoded()) - ); -} - -TEST(Cbor, EncUInt) { - EXPECT_EQ("00", hex(Encode::uint(0).encoded())); - EXPECT_EQ("01", hex(Encode::uint(1).encoded())); - EXPECT_EQ("0a", hex(Encode::uint(10).encoded())); - EXPECT_EQ("17", hex(Encode::uint(23).encoded())); - EXPECT_EQ("1818", hex(Encode::uint(24).encoded())); - EXPECT_EQ("1819", hex(Encode::uint(25).encoded())); - EXPECT_EQ("181a", hex(Encode::uint(26).encoded())); - EXPECT_EQ("181b", hex(Encode::uint(27).encoded())); - EXPECT_EQ("181c", hex(Encode::uint(28).encoded())); - EXPECT_EQ("181d", hex(Encode::uint(29).encoded())); - EXPECT_EQ("181e", hex(Encode::uint(30).encoded())); - EXPECT_EQ("181f", hex(Encode::uint(31).encoded())); - EXPECT_EQ("1820", hex(Encode::uint(32).encoded())); - EXPECT_EQ("183f", hex(Encode::uint(0x3f).encoded())); - EXPECT_EQ("1840", hex(Encode::uint(0x40).encoded())); - EXPECT_EQ("1864", hex(Encode::uint(100).encoded())); - EXPECT_EQ("187f", hex(Encode::uint(0x7f).encoded())); - EXPECT_EQ("1880", hex(Encode::uint(0x80).encoded())); - EXPECT_EQ("18ff", hex(Encode::uint(0xff).encoded())); - EXPECT_EQ("190100", hex(Encode::uint(0x0100).encoded())); - EXPECT_EQ("1903e8", hex(Encode::uint(1000).encoded())); - EXPECT_EQ("198765", hex(Encode::uint(0x8765).encoded())); - EXPECT_EQ("19ffff", hex(Encode::uint(0xffff).encoded())); - EXPECT_EQ("1a00010000", hex(Encode::uint(0x00010000).encoded())); - EXPECT_EQ("1a000f4240", hex(Encode::uint(1000000).encoded())); - EXPECT_EQ("1a00800000", hex(Encode::uint(0x00800000).encoded())); - EXPECT_EQ("1a87654321", hex(Encode::uint(0x87654321).encoded())); - EXPECT_EQ("1affffffff", hex(Encode::uint(0xffffffff).encoded())); - EXPECT_EQ("1b0000000100000000", hex(Encode::uint(0x0000000100000000).encoded())); - EXPECT_EQ("1b000000e8d4a51000", hex(Encode::uint(1000000000000).encoded())); - EXPECT_EQ("1b876543210fedcba9", hex(Encode::uint(0x876543210fedcba9).encoded())); - EXPECT_EQ("1bffffffffffffffff", hex(Encode::uint(0xffffffffffffffff).encoded())); -} - -TEST(Cbor, EncNegInt) { - EXPECT_EQ("20", hex(Encode::negInt(1).encoded())); // -1 - EXPECT_EQ("00", hex(Encode::negInt(0).encoded())); // 0 - EXPECT_EQ("21", hex(Encode::negInt(2).encoded())); - EXPECT_EQ("28", hex(Encode::negInt(9).encoded())); - EXPECT_EQ("37", hex(Encode::negInt(24).encoded())); - EXPECT_EQ("3818", hex(Encode::negInt(25).encoded())); - EXPECT_EQ("38ff", hex(Encode::negInt(0x0100).encoded())); - EXPECT_EQ("390100", hex(Encode::negInt(0x0101).encoded())); - EXPECT_EQ("39ffff", hex(Encode::negInt(0x10000).encoded())); - EXPECT_EQ("3a00010000", hex(Encode::negInt(0x00010001).encoded())); - EXPECT_EQ("3a00800000", hex(Encode::negInt(0x00800001).encoded())); - EXPECT_EQ("3a87654321", hex(Encode::negInt(0x87654322).encoded())); - EXPECT_EQ("3affffffff", hex(Encode::negInt(0x100000000).encoded())); - EXPECT_EQ("3b0000000100000000", hex(Encode::negInt(0x0000000100000001).encoded())); - EXPECT_EQ("3b876543210fedcba9", hex(Encode::negInt(0x876543210fedcbaa).encoded())); - EXPECT_EQ("3bfffffffffffffffe", hex(Encode::negInt(0xffffffffffffffff).encoded())); - - EXPECT_EQ("-9", Decode(Encode::negInt(9).encoded()).dumpToString()); -} - - -TEST(Cbor, EncString) { - EXPECT_EQ("60", hex(Encode::string("").encoded())); - EXPECT_EQ("6141", hex(Encode::string("A").encoded())); - EXPECT_EQ("656162636465", hex(Encode::string("abcde").encoded())); - Data long258(258); - EXPECT_EQ( - "590102000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - hex(Encode::bytes(long258).encoded()) - ); - - EXPECT_EQ("\"abcde\"", Decode(Encode::string("abcde").encoded()).dumpToString()); - EXPECT_EQ("h\"6162636465\"", Decode(Encode::bytes(parse_hex("6162636465")).encoded()).dumpToString()); -} - -TEST(Cbor, EncTag) { - { - Data cbor = Encode::tag(5, Encode::uint(6)).encoded(); - EXPECT_EQ("c506", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("tag 5 6", Decode(cbor).dumpToString()); - } - EXPECT_EQ("d94321191234", hex(Encode::tag(0x4321, Encode::uint(0x1234)).encoded())); -} - -TEST(Cbor, EncInvalid) { - Data invalid = parse_hex("5b99999999999999991234"); // invalid very looong string - EXPECT_FALSE(Decode(invalid).isValid()); - - try { - Encode::fromRaw(invalid); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, DecInt) { - EXPECT_EQ(0, Decode(parse_hex("00")).getValue()); - EXPECT_EQ(1, Decode(parse_hex("01")).getValue()); - EXPECT_EQ(10, Decode(parse_hex("0a")).getValue()); - EXPECT_EQ(23, Decode(parse_hex("17")).getValue()); - EXPECT_EQ(24, Decode(parse_hex("1818")).getValue()); - EXPECT_EQ(25, Decode(parse_hex("1819")).getValue()); - EXPECT_EQ(26, Decode(parse_hex("181a")).getValue()); - EXPECT_EQ(27, Decode(parse_hex("181b")).getValue()); - EXPECT_EQ(28, Decode(parse_hex("181c")).getValue()); - EXPECT_EQ(29, Decode(parse_hex("181d")).getValue()); - EXPECT_EQ(30, Decode(parse_hex("181e")).getValue()); - EXPECT_EQ(31, Decode(parse_hex("181f")).getValue()); - EXPECT_EQ(32, Decode(parse_hex("1820")).getValue()); - EXPECT_EQ(0x3f, Decode(parse_hex("183f")).getValue()); - EXPECT_EQ(0x40, Decode(parse_hex("1840")).getValue()); - EXPECT_EQ(100, Decode(parse_hex("1864")).getValue()); - EXPECT_EQ(0x7f, Decode(parse_hex("187f")).getValue()); - EXPECT_EQ(0x80, Decode(parse_hex("1880")).getValue()); - EXPECT_EQ(0xff, Decode(parse_hex("18ff")).getValue()); - EXPECT_EQ(0x100, Decode(parse_hex("190100")).getValue()); - EXPECT_EQ(1000, Decode(parse_hex("1903e8")).getValue()); - EXPECT_EQ(0x8765, Decode(parse_hex("198765")).getValue()); - EXPECT_EQ(0xffff, Decode(parse_hex("19ffff")).getValue()); - EXPECT_EQ(0x00010000, Decode(parse_hex("1a00010000")).getValue()); - EXPECT_EQ(1000000, Decode(parse_hex("1a000f4240")).getValue()); - EXPECT_EQ(0x00800000, Decode(parse_hex("1a00800000")).getValue()); - EXPECT_EQ(0x87654321, Decode(parse_hex("1a87654321")).getValue()); - EXPECT_EQ(0xffffffff, Decode(parse_hex("1affffffff")).getValue()); - EXPECT_EQ(0x0000000100000000, Decode(parse_hex("1b0000000100000000")).getValue()); - EXPECT_EQ(1000000000000, Decode(parse_hex("1b000000e8d4a51000")).getValue()); - EXPECT_EQ(0x876543210fedcba9, Decode(parse_hex("1b876543210fedcba9")).getValue()); - EXPECT_EQ(0xffffffffffffffff, Decode(parse_hex("1bffffffffffffffff")).getValue()); -} - -TEST(Cbor, DecMinortypeInvalid) { - EXPECT_FALSE(Decode(parse_hex("1c")).isValid()); // 28 unused - EXPECT_FALSE(Decode(parse_hex("1d")).isValid()); // 29 unused - EXPECT_FALSE(Decode(parse_hex("1e")).isValid()); // 30 unused - EXPECT_TRUE(Decode(parse_hex("1b0000000000000000")).isValid()); -} - -TEST(Cbor, DecArray3) { - Decode cbor = Decode(parse_hex("83010203")); - EXPECT_EQ(3, cbor.getArrayElements().size()); -} - -TEST(Cbor, DecArrayNested) { - Data d1 = parse_hex("8301820203820405"); - Decode cbor = Decode(d1); - EXPECT_EQ(3, cbor.getArrayElements().size()); - - EXPECT_EQ(1, cbor.getArrayElements()[0].getValue()); - EXPECT_EQ(2, cbor.getArrayElements()[1].getArrayElements().size()); - EXPECT_EQ(2, cbor.getArrayElements()[1].getArrayElements()[0].getValue()); - EXPECT_EQ(3, cbor.getArrayElements()[1].getArrayElements()[1].getValue()); - EXPECT_EQ(2, cbor.getArrayElements()[2].getArrayElements().size()); - EXPECT_EQ(4, cbor.getArrayElements()[2].getArrayElements()[0].getValue()); - EXPECT_EQ(5, cbor.getArrayElements()[2].getArrayElements()[1].getValue()); -} - -TEST(Cbor, DecEncoded) { - // sometimes getting the encoded version is useful during decoding too - Decode cbor = Decode(parse_hex("8301820203820405")); - EXPECT_EQ(3, cbor.getArrayElements().size()); - EXPECT_EQ("820203", hex(cbor.getArrayElements()[1].encoded())); - EXPECT_EQ("820405", hex(cbor.getArrayElements()[2].encoded())); -} - -TEST(Cbor, DecMemoryref) { - // make sure reference to data is valid even if parent object has been destroyed - Decode* cbor = new Decode(parse_hex("828301020383010203")); - auto elems = cbor->getArrayElements(); - // delete parent - delete cbor; - // also do some new allocation - Decode* dummy = new Decode(parse_hex("5555555555555555")); - // work with the child references - EXPECT_EQ(2, elems.size()); - EXPECT_EQ(3, elems[0].getArrayElements().size()); - EXPECT_EQ(3, elems[1].getArrayElements().size()); - delete dummy; -} - -TEST(Cbor, GetValue) { - EXPECT_EQ(5, Decode(parse_hex("05")).getValue()); -} - -TEST(Cbor, GetValueInvalid) { - try { - Decode(parse_hex("83010203")).getValue(); // array - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, GetString) { - // bytes/string and getString/getBytes work in all combinations - EXPECT_EQ("abcde", Decode(parse_hex("656162636465")).getString()); - EXPECT_EQ("abcde", Decode(parse_hex("456162636465")).getString()); - EXPECT_EQ("6162636465", hex(Decode(parse_hex("656162636465")).getBytes())); - EXPECT_EQ("6162636465", hex(Decode(parse_hex("456162636465")).getBytes())); -} - -TEST(Cbor, GetStringInvalidType) { - try { - Decode cbor = Decode(Encode::uint(5).encoded()); - cbor.getBytes(); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, GetStringInvalidTooShort) { - try { - Decode cbor = Decode(parse_hex("65616263")); // too short - cbor.getBytes(); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, ArrayEmpty) { - Data cbor = Encode::array({}).encoded(); - - EXPECT_EQ("80", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("[]", Decode(cbor).dumpToString()); - - Decode decode(cbor); - EXPECT_EQ(0, decode.getArrayElements().size()); -} - -TEST(Cbor, Array3) { - Data cbor = Encode::array({ - Encode::uint(1), - Encode::uint(2), - Encode::uint(3), - }).encoded(); - - EXPECT_EQ("83010203", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("[1, 2, 3]", Decode(cbor).dumpToString()); - - Decode decode(cbor); - EXPECT_EQ(3, decode.getArrayElements().size()); - EXPECT_EQ(1, decode.getArrayElements()[0].getValue()); - EXPECT_EQ(2, decode.getArrayElements()[1].getValue()); - EXPECT_EQ(3, decode.getArrayElements()[2].getValue()); -} - -TEST(Cbor, ArrayNested) { - Data cbor = Encode::array({ - Encode::uint(1), - Encode::array({ - Encode::uint(2), - Encode::uint(3), - }), - Encode::array({ - Encode::uint(4), - Encode::uint(5), - }), - }).encoded(); - - EXPECT_EQ("8301820203820405", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("[1, [2, 3], [4, 5]]", - Decode(cbor).dumpToString()); - - Decode decode(cbor); - EXPECT_EQ(3, decode.getArrayElements().size()); - EXPECT_EQ(1, decode.getArrayElements()[0].getValue()); - EXPECT_EQ(2, decode.getArrayElements()[1].getArrayElements().size()); - EXPECT_EQ(2, decode.getArrayElements()[1].getArrayElements()[0].getValue()); - EXPECT_EQ(3, decode.getArrayElements()[1].getArrayElements()[1].getValue()); - EXPECT_EQ(2, decode.getArrayElements()[2].getArrayElements().size()); - EXPECT_EQ(4, decode.getArrayElements()[2].getArrayElements()[0].getValue()); - EXPECT_EQ(5, decode.getArrayElements()[2].getArrayElements()[1].getValue()); -} - -TEST(Cbor, Array25) { - auto elem = vector(); - for (int i = 1; i <= 25; ++i) { - elem.push_back(Encode::uint(i)); - } - Data cbor = Encode::array(elem).encoded(); - - EXPECT_EQ("98190102030405060708090a0b0c0d0e0f101112131415161718181819", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]", - Decode(cbor).dumpToString()); - - Decode decode(cbor); - EXPECT_EQ(25, decode.getArrayElements().size()); - for (int i = 1; i <= 25; ++i) { - EXPECT_EQ(i, decode.getArrayElements()[i - 1].getValue()); - } -} - -TEST(Cbor, MapEmpty) { - Data cbor = Encode::map({}).encoded(); - - EXPECT_EQ("a0", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("{}", Decode(cbor).dumpToString()); - - Decode decode(cbor); - EXPECT_EQ(0, decode.getMapElements().size()); -} - -TEST(Cbor, Map2Num) { - Data cbor = Encode::map({ - make_pair(Encode::uint(1), Encode::uint(2)), - make_pair(Encode::uint(3), Encode::uint(4)), - }).encoded(); - - EXPECT_EQ("a201020304", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("{1: 2, 3: 4}", Decode(cbor).dumpToString()); - - Decode decode(cbor); - EXPECT_EQ(2, decode.getMapElements().size()); - EXPECT_EQ(1, decode.getMapElements()[0].first.getValue()); - EXPECT_EQ(2, decode.getMapElements()[0].second.getValue()); -} - -TEST(Cbor, Map2WithArr) { - Data cbor = Encode::map({ - make_pair(Encode::string("a"), Encode::uint(1)), - make_pair(Encode::string("b"), Encode::array({ - Encode::uint(2), - Encode::uint(3), - })), - }).encoded(); - - EXPECT_EQ("a26161016162820203", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("{\"a\": 1, \"b\": [2, 3]}", - Decode(cbor).dumpToString()); - - Decode decode(cbor); - EXPECT_EQ(2, decode.getMapElements().size()); - EXPECT_EQ("a", decode.getMapElements()[0].first.getString()); - EXPECT_EQ(1, decode.getMapElements()[0].second.getValue()); - EXPECT_EQ("b", decode.getMapElements()[1].first.getString()); - EXPECT_EQ(2, decode.getMapElements()[1].second.getArrayElements().size()); - EXPECT_EQ(2, decode.getMapElements()[1].second.getArrayElements()[0].getValue()); - EXPECT_EQ(3, decode.getMapElements()[1].second.getArrayElements()[1].getValue()); -} - -TEST(Cbor, MapNested) { - Data cbor = Encode::map({ - make_pair(Encode::string("a"), Encode::map({ - make_pair(Encode::string("b"), Encode::string("c")), - })), - }).encoded(); - - EXPECT_EQ("a16161a161626163", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("{\"a\": {\"b\": \"c\"}}", - Decode(cbor).dumpToString()); - - Decode decode(cbor); - EXPECT_EQ(1, decode.getMapElements().size()); - EXPECT_EQ("a", decode.getMapElements()[0].first.getString()); - EXPECT_EQ(1, decode.getMapElements()[0].second.getMapElements().size()); - EXPECT_EQ("b", decode.getMapElements()[0].second.getMapElements()[0].first.getString()); - EXPECT_EQ("c", decode.getMapElements()[0].second.getMapElements()[0].second.getString()); -} - -TEST(Cbor, MapIndef) { - Decode cbor = Decode(parse_hex("bf01020304ff")); - EXPECT_EQ("{_ 1: 2, 3: 4}", cbor.dumpToString()); - EXPECT_EQ(2, cbor.getMapElements().size()); - EXPECT_EQ(1, cbor.getMapElements()[0].first.getValue()); - EXPECT_EQ(2, cbor.getMapElements()[0].second.getValue()); -} - -TEST(Cbor, MapIsValidInvalidTooShort) { - { - Decode cbor = Decode(parse_hex("a301020304")); // too short - EXPECT_FALSE(cbor.isValid()); - } - { - Decode cbor = Decode(parse_hex("a3010203")); // too short, partial element - EXPECT_FALSE(cbor.isValid()); - } -} - -TEST(Cbor, MapGetInvalidTooShort1) { - try { - Decode cbor = Decode(parse_hex("a301020304")); // too short - auto elems = cbor.getMapElements(); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, MapGetInvalidTooShort2) { - try { - Decode cbor = Decode(parse_hex("a3010203")); // too short, partial element - auto elems = cbor.getMapElements(); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, ArrayIndef) { - Data cbor = Encode::indefArray() - .addIndefArrayElem(Encode::uint(1)) - .addIndefArrayElem(Encode::uint(2)) - .closeIndefArray() - .encoded(); - - EXPECT_EQ("9f0102ff", hex(cbor)); - EXPECT_TRUE(Decode(cbor).isValid()); - EXPECT_EQ("[_ 1, 2]", - Decode(cbor).dumpToString()); - - Decode decode(cbor); - EXPECT_EQ(2, decode.getArrayElements().size()); - EXPECT_EQ(1, decode.getArrayElements()[0].getValue()); - EXPECT_EQ(2, decode.getArrayElements()[1].getValue()); - - EXPECT_EQ("[_ 1, 2]", Decode(parse_hex("9f0102ff")).dumpToString()); - EXPECT_EQ("", Decode(parse_hex("ff")).dumpToString()); - EXPECT_EQ("spec 1", Decode(parse_hex("e1")).dumpToString()); -} - -TEST(Cbor, ArrayInfefErrorAddNostart) { - try { - Data cbor = Encode::uint(0).addIndefArrayElem(Encode::uint(1)).encoded(); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, ArrayInfefErrorCloseNostart) { - try { - Data cbor = Encode::uint(0).closeIndefArray().encoded(); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, ArrayInfefErrorResultNoclose) { - try { - Data cbor = Encode::indefArray() - .addIndefArrayElem(Encode::uint(1)) - .addIndefArrayElem(Encode::uint(2)) - // close is missing, break command not written - .encoded(); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, ArrayInfefErrorNoBreak) { - EXPECT_TRUE(Decode(parse_hex("9f0102ff")).isValid()); - // without break it's invalid - EXPECT_FALSE(Decode(parse_hex("9f0102")).isValid()); -} - -TEST(Cbor, GetTagValueNotTag) { - try { - Decode cbor = Decode(Encode::string("abc").encoded()); - cbor.getTagValue(); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} - -TEST(Cbor, GetTagElementNotTag) { - try { - Decode cbor = Decode(Encode::string("abc").encoded()); - Decode tagElement = cbor.getTagElement(); - } catch (exception& ex) { - return; - } - FAIL() << "Expected exception"; -} diff --git a/tests/Celo/TWCoinTypeTests.cpp b/tests/Celo/TWCoinTypeTests.cpp deleted file mode 100644 index 7a66fc66eda..00000000000 --- a/tests/Celo/TWCoinTypeTests.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWCeloCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCelo)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xaf41ee58afe633dc7b179c15693cca40fe0372c1d7c70351620105d59d326693")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCelo, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xFBFf95e2Fa7e4Ff3aeA34eFB05fB60F9968a6aaD")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCelo, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCelo)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCelo)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCelo), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeCelo)); - - assertStringsEqual(symbol, "CELO"); - assertStringsEqual(txUrl, "https://explorer.celo.org/tx/0xaf41ee58afe633dc7b179c15693cca40fe0372c1d7c70351620105d59d326693"); - assertStringsEqual(accUrl, "https://explorer.celo.org/address/0xFBFf95e2Fa7e4Ff3aeA34eFB05fB60F9968a6aaD"); - assertStringsEqual(id, "celo"); - assertStringsEqual(name, "Celo"); -} diff --git a/tests/CoinAddressDerivationTests.cpp b/tests/CoinAddressDerivationTests.cpp deleted file mode 100644 index d8dbdd586ef..00000000000 --- a/tests/CoinAddressDerivationTests.cpp +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Coin.h" -#include "HexCoding.h" - -#include - -#include -#include - -namespace TW { - -TEST(Coin, DeriveAddress) { - auto dummyKeyData = parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646"); - const auto privateKey = PrivateKey(dummyKeyData); - const auto privateKeyExt = PrivateKey(dummyKeyData, dummyKeyData, dummyKeyData); - - EXPECT_EQ(TW::deriveAddress(TWCoinTypeAion, privateKey), "0xa0010b0ea04ba4d76ca6e5e9900bacf19bc4402eaec7e36ea7ddd8eed48f60f3"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeBinance, privateKey), "bnb1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0mlq0d0"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeBitcoin, privateKey), "bc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z00ppggv"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeBitcoinCash, privateKey), "bitcoincash:qz7eyzytkl5z6cg6nw20hd62pyyp22mcfuardfd2vn"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeCallisto, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeCosmos, privateKey), "cosmos1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0emlrvp"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeDash, privateKey), "XsyCV5yojxF4y3bYeEiVYqarvRgsWFELZL"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeDecred, privateKey), "Dsp4u8xxTHSZU2ELWTQLQP77xJhgeWrTsGK"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeDigiByte, privateKey), "dgb1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z0c69ssz"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeEthereum, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeEthereumClassic, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeGoChain, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeGroestlcoin, privateKey), "grs1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z0jsaf3d"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeICON, privateKey), "hx4728fc65c31728f0d3538b8783b5394b31a136b9"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeIoTeX, privateKey), "io1nk9x9ajk4rgkzhqjjn7hr6w0k0jg2kj0zgdt6h"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeLitecoin, privateKey), "ltc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z0tamvsu"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeViacoin, privateKey), "via1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z09y9mn2"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeNimiq, privateKey), "NQ74 D40G N3M0 9EJD ET56 UPLR 02VC X6DU 8G1E"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeOntology, privateKey), "AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypePOANetwork, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeXRP, privateKey), "rJHMeqKu8Ep7Fazx8MQG6JunaafBXz93YQ"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeStellar, privateKey), "GDXJHJHWN6GRNOAZXON6XH74ZX6NYFAS5B7642RSJQVJTIPA4ZYUQLEB"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeTezos, privateKey), "tz1gcEWswVU6dxfNQWbhTgaZrUrNUFwrsT4z"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeThunderToken, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeTomoChain, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeTron, privateKey), "TQLCsShbQNXMTVCjprY64qZmEA4rBarpQp"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeVeChain, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeWanchain, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeZcash, privateKey), "t1b9xfAk3kZp5Qk3rinDPq7zzLkJGHTChDS"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeZcoin, privateKey), "aHzpPjmY132KseS4nkiQTbDahTEXqesY89"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeNano, privateKey), "nano_1qepdf4k95dhb5gsmhmq3iddqsxiafwkihunm7irn48jdiwdtnn6pe93k3f6"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeKin, privateKey), "GDXJHJHWN6GRNOAZXON6XH74ZX6NYFAS5B7642RSJQVJTIPA4ZYUQLEB"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeTheta, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeQtum, privateKey), "QdtLm8ccxhuJFF5zCgikpaghbM3thdaGsW"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeNULS, privateKey), "NULSd6HgfXT3m5JBGxeCZXHRQbb82FKgZGT8o"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeEOS, privateKey), "EOS5TrYnZP1RkDSUMzBY4GanCy6AP68kCMdkAb5EACkAwkdgRLShz"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeIoTeX, privateKey), "io1nk9x9ajk4rgkzhqjjn7hr6w0k0jg2kj0zgdt6h"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeDogecoin, privateKey), "DNRTC6GZ5evmM7BZWwPqF54fyDqUqULMyu"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeZilliqa, privateKey), "zil1j2cvtd7j9n7fnxfv2r3neucjw8tp4xz9sp07v4"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeRavencoin, privateKey), "RSZYjMDCP4q3t7NAFXPPnqEGrMZn971pdB"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeAeternity, privateKey), "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeTerra, privateKey), "terra1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0ll9rwp"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeNebulas, privateKey), "n1XTciu9ZRYt3ni7SxNBmivk9Y6XpP6VrhT"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeMonacoin, privateKey), "MRBWtGEKHGCHhmyJ1L4CwaWQZJzM5DnVcs"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeFIO, privateKey), "FIO5TrYnZP1RkDSUMzBY4GanCy6AP68kCMdkAb5EACkAwkdgRLShz"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeAlgorand, privateKey), "52J2J5TPRULLQGN3TPVZ77GN7TOBIEXIP7XGUMSMFKM2DYHGOFEOGBP2T4"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeKusama, privateKey), "Hy8mqcexg5FMwMYnQvzrUvD723qMxDjMRU9HdNCnTsMAypY"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypePolkadot, privateKey), "16PpFrXrC6Ko3pYcyMAx6gPMp3mFFaxgyYMt4G5brkgNcSz8"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeKava, privateKey), "kava1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z09wt76x"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeCardano, privateKeyExt), "addr1sn0sqpku8yfj2dazh7czyspcd5flzkzu6x9wqt0lsn4wfvds9sg3s3jxgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeryv3jxgeryv3jxdnpy03"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeNEO, privateKeyExt), "AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeFilecoin, privateKey), "f1qsx7qwiojh5duxbxhbqgnlyx5hmpcf7mcz5oxsy"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeNEAR, privateKey), "ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeSolana, privateKey), "H4JcMPicKkHcxxDjkyyrLoQj7Kcibd9t815ak4UvTr9M"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeElrond, privateKey), "erd1a6f6fan035ttsxdmn04ellxdlnwpgyhg0lhx5vjv92v6rc8xw9yq83344f"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeOasis, privateKey), "oasis1qzw4h3wmyjtrttduqqrs8udggyy2emwdzqmuzwg4"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeTHORChain, privateKey), "thor1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0luxce7"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeAvalancheCChain, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeXDai, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - EXPECT_EQ(TW::deriveAddress(TWCoinTypeCelo, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); -} - -int countThreadReady = 0; -std::mutex countThreadReadyMutex; - -void useCoinFromThread() { - const int tryCount = 20; - for (int i = 0; i < tryCount; ++i) { - // perform some operations - TW::validateAddress(TWCoinTypeZilliqa, "zil1j8xae6lggm8y63m3y2r7aefu797ze7mhzulnqg"); - TW::validateAddress(TWCoinTypeEthereum, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); - const auto coinTypes = TW::getCoinTypes(); - } - countThreadReadyMutex.lock(); - ++countThreadReady; - countThreadReadyMutex.unlock(); -} - -TEST(Coin, InitMultithread) { - const int numThread = 20; - countThreadReady = 0; - std::thread thread[numThread]; - // execute in threads - for (int i = 0; i < numThread; ++i) { - thread[i] = std::thread(useCoinFromThread); - } - // wait for completion - for (int i = 0; i < numThread; ++i) { - thread[i].join(); - } - // check that all completed OK - ASSERT_EQ(countThreadReady, numThread); -} - -TEST(Coin, SupportedCoins) { - const auto coinTypes = TW::getCoinTypes(); - for (auto c: coinTypes) { - const auto similarTypes = TW::getSimilarCoinTypes(c); - // For all coins, supported coins should include this coin as well - EXPECT_TRUE(std::find(similarTypes.begin(), similarTypes.end(), c) != similarTypes.end()); - } -} - -} // namespace TW diff --git a/tests/Cosmos/SignerTests.cpp b/tests/Cosmos/SignerTests.cpp deleted file mode 100644 index f43ff55238e..00000000000 --- a/tests/Cosmos/SignerTests.cpp +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Coin.h" -#include "HexCoding.h" -#include "Base64.h" -#include "proto/Cosmos.pb.h" -#include "Cosmos/Address.h" -#include "Cosmos/Signer.h" - -#include -#include - -using namespace TW; -using namespace TW::Cosmos; - -TEST(CosmosSigner, SignTx) { - auto input = Proto::SigningInput(); - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(8); - - auto fromAddress = Address("cosmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); - auto toAddress = Address("cosmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_send_coins_message(); - message.set_from_address(fromAddress.string()); - message.set_to_address(toAddress.string()); - auto amountOfTx = message.add_amounts(); - amountOfTx->set_denom("muon"); - amountOfTx->set_amount(1); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("muon"); - amountOfFee->set_amount(200); - - std::string json; - google::protobuf::util::MessageToJsonString(input, &json); - - ASSERT_EQ(R"({"accountNumber":"1037","chainId":"gaia-13003","fee":{"amounts":[{"denom":"muon","amount":"200"}],"gas":"200000"},"sequence":"8","messages":[{"sendCoinsMessage":{"fromAddress":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","toAddress":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573","amounts":[{"denom":"muon","amount":"1"}]}}]})", json); - - auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - - ASSERT_EQ(R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}})", output.json()); - ASSERT_EQ(hex(output.signature()), "fc3ef899d206c88077fec42f21ba0b4df4bd3fd115fdf606ae01d9136fef363f57e9e33a7b9ec6ddab658cd07e3c0067470de94e4e75b979a1085a29f0efd926"); - - /* - the sample tx on testnet - https://hubble.figment.network/chains/gaia-13003/blocks/142933/transactions/3A9206598C3D2E75A5EC074FD33EA53EB18EC729357F0965971C1C51F812AEA3?format=json - */ -} - -TEST(CosmosSigner, SignTxWithMode) { - auto input = Proto::SigningInput(); - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(8); - input.set_mode(Proto::BroadcastMode::ASYNC); - - auto fromAddress = Address("cosmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); - auto toAddress = Address("cosmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_send_coins_message(); - message.set_from_address(fromAddress.string()); - message.set_to_address(toAddress.string()); - auto amountOfTx = message.add_amounts(); - amountOfTx->set_denom("muon"); - amountOfTx->set_amount(1); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("muon"); - amountOfFee->set_amount(200); - - auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); - input.set_private_key(privateKey.data(), privateKey.size()); - - { - auto output = Signer::sign(input); - ASSERT_EQ(R"({"mode":"async","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}})", output.json()); - } - input.set_mode(Proto::BroadcastMode::SYNC); - { - auto output = Signer::sign(input); - ASSERT_EQ(R"({"mode":"sync","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}})", output.json()); - } -} diff --git a/tests/Cosmos/StakingTests.cpp b/tests/Cosmos/StakingTests.cpp deleted file mode 100644 index d7745b4b10c..00000000000 --- a/tests/Cosmos/StakingTests.cpp +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Coin.h" -#include "HexCoding.h" -#include "Base64.h" -#include "proto/Cosmos.pb.h" -#include "Cosmos/Address.h" -#include "Cosmos/Signer.h" - -#include - -using namespace TW; -using namespace TW::Cosmos; - -TEST(CosmosStaking, Staking) { - auto input = Proto::SigningInput(); - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(7); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_stake_message(); - message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); - message.set_validator_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"); - auto& amountOfTx = *message.mutable_amount(); - amountOfTx.set_denom("muon"); - amountOfTx.set_amount(10); - - auto& fee = *input.mutable_fee(); - fee.set_gas(101721); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("muon"); - amountOfFee->set_amount(1018); - - auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - - ASSERT_EQ(output.json(), "{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"1018\",\"denom\":\"muon\"}],\"gas\":\"101721\"},\"memo\":\"\",\"msg\":[{\"type\":\"cosmos-sdk/MsgDelegate\",\"value\":{\"amount\":{\"amount\":\"10\",\"denom\":\"muon\"},\"delegator_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\",\"validator_address\":\"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"wIvfbCsLRCjzeXXoXTKfHLGXRbAAmUp0O134HVfVc6pfdVNJvvzISMHRUHgYcjsSiFlLyR32heia/yLgMDtIYQ==\"}]}}"); - ASSERT_EQ(hex(output.signature()), "c08bdf6c2b0b4428f37975e85d329f1cb19745b000994a743b5df81d57d573aa5f755349befcc848c1d1507818723b1288594bc91df685e89aff22e0303b4861"); -} - -TEST(CosmosStaking, Unstaking) { - auto input = Proto::SigningInput(); - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(7); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_unstake_message(); - message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); - message.set_validator_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"); - auto& amountOfTx = *message.mutable_amount(); - amountOfTx.set_denom("muon"); - amountOfTx.set_amount(10); - - auto& fee = *input.mutable_fee(); - fee.set_gas(101721); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("muon"); - amountOfFee->set_amount(1018); - - auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - - ASSERT_EQ(output.json(), "{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"1018\",\"denom\":\"muon\"}],\"gas\":\"101721\"},\"memo\":\"\",\"msg\":[{\"type\":\"cosmos-sdk/MsgUndelegate\",\"value\":{\"amount\":{\"amount\":\"10\",\"denom\":\"muon\"},\"delegator_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\",\"validator_address\":\"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"j4WpUVohGIHa6/s0bCvuyjq1wtQGqbOtQCz92qPQjisTN44Tz++Ozx1lAP6F0M4+eTA03XerqQ8hZCeAfL/3nw==\"}]}}"); - ASSERT_EQ(hex(output.signature()), "8f85a9515a211881daebfb346c2beeca3ab5c2d406a9b3ad402cfddaa3d08e2b13378e13cfef8ecf1d6500fe85d0ce3e793034dd77aba90f216427807cbff79f"); -} - -TEST(CosmosStaking, Restaking) { - auto input = Proto::SigningInput(); - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(7); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_restake_message(); - message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); - message.set_validator_dst_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); - message.set_validator_src_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"); - - auto& amountOfTx = *message.mutable_amount(); - amountOfTx.set_denom("muon"); - amountOfTx.set_amount(10); - - auto& fee = *input.mutable_fee(); - fee.set_gas(101721); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("muon"); - amountOfFee->set_amount(1018); - - auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - - ASSERT_EQ(output.json(), "{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"1018\",\"denom\":\"muon\"}],\"gas\":\"101721\"},\"memo\":\"\",\"msg\":[{\"type\":\"cosmos-sdk/MsgBeginRedelegate\",\"value\":{\"amount\":{\"amount\":\"10\",\"denom\":\"muon\"},\"delegator_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\",\"validator_dst_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\",\"validator_src_address\":\"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"5k03Yb0loovvzagMCg4gjQJP2woriZVRcOZaXF1FSros6B1X4B8MEm3lpZwrWBJMEJVgyYA9ZaF6FLVI3WxQ2w==\"}]}}"); - ASSERT_EQ(hex(output.signature()), "e64d3761bd25a28befcda80c0a0e208d024fdb0a2b89955170e65a5c5d454aba2ce81d57e01f0c126de5a59c2b58124c109560c9803d65a17a14b548dd6c50db"); -} - -TEST(CosmosStaking, Withdraw) { - auto input = Proto::SigningInput(); - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(7); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_withdraw_stake_reward_message(); - message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); - message.set_validator_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"); - - auto& fee = *input.mutable_fee(); - fee.set_gas(101721); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("muon"); - amountOfFee->set_amount(1018); - - auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - - ASSERT_EQ( output.json(), "{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"1018\",\"denom\":\"muon\"}],\"gas\":\"101721\"},\"memo\":\"\",\"msg\":[{\"type\":\"cosmos-sdk/MsgWithdrawDelegationReward\",\"value\":{\"delegator_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\",\"validator_address\":\"cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"VG8NZzVvavlM+1qyK5dOSZwzEj8sLCkvTw5kh44Oco9GQxBf13FVC+s/I3HwiICqo4+o8jNMEDp3nx2C0tuY1g==\"}]}}"); - ASSERT_EQ(hex(output.signature()), "546f0d67356f6af94cfb5ab22b974e499c33123f2c2c292f4f0e64878e0e728f4643105fd771550beb3f2371f08880aaa38fa8f2334c103a779f1d82d2db98d6"); -} - -TEST(CosmosStaking, WithdrawAllRaw) { - auto input = Proto::SigningInput(); - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(7); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_raw_json_message(); - message.set_type("cosmos-sdk/MsgWithdrawDelegationRewardsAll"); - message.set_value("{\"delegator_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\"}"); - auto& fee = *input.mutable_fee(); - fee.set_gas(101721); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("muon"); - amountOfFee->set_amount(1018); - - auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Signer::sign(input); - - ASSERT_EQ(output.json(), "{\"mode\":\"block\",\"tx\":{\"fee\":{\"amount\":[{\"amount\":\"1018\",\"denom\":\"muon\"}],\"gas\":\"101721\"},\"memo\":\"\",\"msg\":[{\"type\":\"cosmos-sdk/MsgWithdrawDelegationRewardsAll\",\"value\":{\"delegator_address\":\"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02\"}}],\"signatures\":[{\"pub_key\":{\"type\":\"tendermint/PubKeySecp256k1\",\"value\":\"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F\"},\"signature\":\"ImvsgnfbjebxzeBCUPeOcMoOJWMV3IhWM1apV20WiS4K11iA50fe0uXr4Xf/RTxUDXTm56cne/OjOr77BG99Aw==\"}]}}"); - ASSERT_EQ(hex(output.signature()), "226bec8277db8de6f1cde04250f78e70ca0e256315dc88563356a9576d16892e0ad75880e747ded2e5ebe177ff453c540d74e6e7a7277bf3a33abefb046f7d03"); -} diff --git a/tests/Cosmos/TWAnyAddressTests.cpp b/tests/Cosmos/TWAnyAddressTests.cpp deleted file mode 100644 index 5605e54aef0..00000000000 --- a/tests/Cosmos/TWAnyAddressTests.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" - -#include -#include - -#include - -TEST(CosmosAnyAddress, Cosmos) { - auto string = STRING("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); - auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeCosmos)); - auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); - EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); - auto keyHash = WRAPD(TWAnyAddressData(addr.get())); - assertHexEqual(keyHash, "bc2da90c84049370d1b7c528bc164bc588833f21"); -} diff --git a/tests/Cosmos/TWAnySignerTests.cpp b/tests/Cosmos/TWAnySignerTests.cpp deleted file mode 100644 index 26a69e3e71c..00000000000 --- a/tests/Cosmos/TWAnySignerTests.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Cosmos/Address.h" -#include "HexCoding.h" -#include "proto/Cosmos.pb.h" -#include "../interface/TWTestUtilities.h" -#include -#include - -using namespace TW; -using namespace TW::Cosmos; - -TEST(TWAnySignerCosmos, SignTx) { - auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); - Proto::SigningInput input; - input.set_account_number(1037); - input.set_chain_id("gaia-13003"); - input.set_memo(""); - input.set_sequence(8); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto fromAddress = Address("cosmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); - auto toAddress = Address("cosmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_send_coins_message(); - message.set_from_address(fromAddress.string()); - message.set_to_address(toAddress.string()); - auto amountOfTx = message.add_amounts(); - amountOfTx->set_denom("muon"); - amountOfTx->set_amount(1); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("muon"); - amountOfFee->set_amount(200); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeCosmos); - - ASSERT_EQ(output.json(), R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}})"); -} - -TEST(TWAnySignerCosmos, SignJSON) { - auto json = STRING(R"({"accountNumber":"8733","chainId":"cosmoshub-2","fee":{"amounts":[{"denom":"uatom","amount":"5000"}],"gas":"200000"}, "memo":"Testing", "messages":[{"sendCoinsMessage":{"fromAddress":"cosmos1ufwv9ymhqaal6xz47n0jhzm2wf4empfqvjy575","toAddress":"cosmos135qla4294zxarqhhgxsx0sw56yssa3z0f78pm0","amounts":[{"denom":"uatom","amount":"995000"}]}}]})"); - auto key = DATA("c9b0a273831931aa4a5f8d1a570d5021dda91d3319bd3819becdaabfb7b44e3b"); - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeCosmos)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeCosmos)); - assertStringsEqual(result, R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"5000","denom":"uatom"}],"gas":"200000"},"memo":"Testing","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"995000","denom":"uatom"}],"from_address":"cosmos1ufwv9ymhqaal6xz47n0jhzm2wf4empfqvjy575","to_address":"cosmos135qla4294zxarqhhgxsx0sw56yssa3z0f78pm0"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6EsukEXB53GhohQVeDpxtkeH8KQIayd/Co/ApYRYkTm"},"signature":"ULEpUqNzoAnYEx2x22F3ANAiPXquAU9+mqLWoAA/ZOUGTMsdb6vryzsW6AKX2Kqj1pGNdrTcQ58Z09JPyjpgEA=="}]}})"); -} diff --git a/tests/Cosmos/TWCoinTypeTests.cpp b/tests/Cosmos/TWCoinTypeTests.cpp deleted file mode 100644 index 747ba90f2fe..00000000000 --- a/tests/Cosmos/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWCosmosCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCosmos)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCosmos, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCosmos, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCosmos)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCosmos)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCosmos), 6); - ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeCosmos)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeCosmos)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeCosmos)); - assertStringsEqual(symbol, "ATOM"); - assertStringsEqual(txUrl, "https://www.mintscan.io/txs/t123"); - assertStringsEqual(accUrl, "https://www.mintscan.io/account/a12"); - assertStringsEqual(id, "cosmos"); - assertStringsEqual(name, "Cosmos"); -} diff --git a/tests/CryptoOrg/AddressTests.cpp b/tests/CryptoOrg/AddressTests.cpp deleted file mode 100644 index 833a79016fa..00000000000 --- a/tests/CryptoOrg/AddressTests.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Cosmos/Address.h" -#include "PublicKey.h" -#include "PrivateKey.h" -#include - -#include -#include - -using namespace TW; -using namespace TW::Cosmos; - -TEST(CryptoorgAddress, Valid) { - ASSERT_TRUE(Address::isValid(TWCoinTypeCryptoOrg, "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0")); - ASSERT_TRUE(Address::isValid(TWCoinTypeCryptoOrg, "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus")); - ASSERT_TRUE(Address::isValid(TWCoinTypeCryptoOrg, "cro1z53wwe7md6cewz9sqwqzn0aavpaun0gw39h3rd")); - ASSERT_TRUE(Address::isValid(TWCoinTypeCryptoOrg, "cro1y8ua5laceufhqtwzyhahq0qk7rm87hhugtsfey")); -} - -TEST(CryptoorgAddress, Invalid) { - EXPECT_FALSE(Address::isValid(TWCoinTypeCryptoOrg, "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf1")); - EXPECT_FALSE(Address::isValid(TWCoinTypeCryptoOrg, "cro1z53wwe7md6cewz9sqwqzn0aavpaun0gw39h3re")); - EXPECT_FALSE(Address::isValid(TWCoinTypeCryptoOrg, "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")); // valid cosmos -} - -TEST(CryptoorgAddress, FromPrivateKey) { - auto privateKey = PrivateKey(parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e")); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - auto address = Address(TWCoinTypeCryptoOrg, publicKey); - EXPECT_EQ(address.string(), "cro1z53wwe7md6cewz9sqwqzn0aavpaun0gw39h3rd"); - EXPECT_EQ(hex(address.getKeyHash()), "1522e767db6eb19708b0038029bfbd607bc9bd0e"); -} - -TEST(CryptoorgAddress, FromPublicKey) { - auto publicKey = PublicKey(parse_hex("03ed997e396cf4292f5fce5a42bba41599ccd5d96e313154a7c9ea7049de317c77"), TWPublicKeyTypeSECP256k1); - auto address = Address(TWCoinTypeCryptoOrg, publicKey); - EXPECT_EQ(address.string(), "cro1z53wwe7md6cewz9sqwqzn0aavpaun0gw39h3rd"); - EXPECT_EQ(hex(address.getKeyHash()), "1522e767db6eb19708b0038029bfbd607bc9bd0e"); -} - -TEST(CryptoorgAddress, FromString) { - Address address; - EXPECT_TRUE(Address::decode("cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", address)); - EXPECT_EQ(address.string(), "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0"); - EXPECT_EQ(hex(address.getKeyHash()), "c2dcbc3828b42c429cedbaefa943e66679b471ed"); -} diff --git a/tests/CryptoOrg/SignerTests.cpp b/tests/CryptoOrg/SignerTests.cpp deleted file mode 100644 index d3421aae2f7..00000000000 --- a/tests/CryptoOrg/SignerTests.cpp +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "proto/Cosmos.pb.h" -#include "Cosmos/Signer.h" -#include "Cosmos/Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "../interface/TWTestUtilities.h" - -#include -#include - -using namespace TW; -using namespace TW::Cosmos; - -TEST(CryptoorgSigner, SignTx_DDCCE4) { - auto input = Cosmos::Proto::SigningInput(); - input.set_account_number(125798); - input.set_sequence(0); - input.set_chain_id("crypto-org-chain-mainnet-1"); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_send_coins_message(); - message.set_from_address("cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0"); - message.set_to_address("cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus"); - auto amountOfTx = message.add_amounts(); - amountOfTx->set_denom("basecro"); - amountOfTx->set_amount(100000000); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("basecro"); - amountOfFee->set_amount(5000); - - std::string json; - google::protobuf::util::MessageToJsonString(input, &json); - - assertJSONEqual(json, R"( - { - "accountNumber": "125798", - "chainId": "crypto-org-chain-mainnet-1", - "fee": { - "amounts": [ - { - "denom": "basecro", - "amount": "5000" - } - ], - "gas": "200000" - }, - "messages": [ - { - "sendCoinsMessage": { - "fromAddress": "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", - "toAddress": "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus", - "amounts": [ - { - "denom": "basecro", - "amount": "100000000" - } - ] - } - } - ] - } - )"); - - auto privateKey = parse_hex("200e439e39cf1aad465ee3de6166247f914cbc0f823fc2dd48bf16dcd556f39d"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = Cosmos::Signer::sign(input); - - assertJSONEqual(output.json(), R"( - { - "mode": "block", - "tx": { - "fee": { - "amount": [ - { - "amount": "5000", - "denom": "basecro" - } - ], - "gas": "200000" - }, - "memo": "", - "msg": [ - { - "type": "cosmos-sdk/MsgSend", - "value": { - "amount": [ - { - "amount": "100000000", - "denom": "basecro" - } - ], - "from_address": "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", - "to_address": "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus" - } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A4gxsGFiPn6L5Z2IjHEISkXI0IkwfL9exV3GLB171Wvj" - }, - "signature": "5+5rSFFg0FE9cTklQWQHNktBDJsz7UCnMSgF0t0+gYcrIhEWUyTtibXaHZQbKAAaciJ1BkHXYREjU55VswByVg==" - } - ] - } - } - )"); - EXPECT_EQ(hex(output.signature()), "e7ee6b485160d0513d713925416407364b410c9b33ed40a7312805d2dd3e81872b2211165324ed89b5da1d941b28001a7222750641d7611123539e55b3007256"); - - /// https://crypto.org/explorer/tx/DDCCE4052040B05914CADEFE78C0A75BE363AE39504E7EF6B2EDB8A9072AD44B - /// curl -H 'Content-Type: application/json' --data-binary '{"mode":"block","tx":{"fee": ... }}' https://mainnet.crypto.org:1317/txs -} - -TEST(CryptoorgSigner, SignJson) { - auto inputJson = R"({"accountNumber":"125798","chainId":"crypto-org-chain-mainnet-1","fee":{"amounts":[{"denom":"basecro","amount":"5000"}],"gas":"200000"},"messages":[{"sendCoinsMessage":{"fromAddress":"cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0","toAddress":"cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus","amounts":[{"denom":"basecro","amount":"100000000"}]}}]})"; - auto privateKey = parse_hex("200e439e39cf1aad465ee3de6166247f914cbc0f823fc2dd48bf16dcd556f39d"); - - auto outputJson = Cosmos::Signer::signJSON(inputJson, privateKey); - - assertJSONEqual(outputJson, R"( - { - "mode": "block", - "tx": { - "fee": { - "amount": [ - { - "amount": "5000", - "denom": "basecro" - } - ], - "gas": "200000" - }, - "memo": "", - "msg": [ - { - "type": "cosmos-sdk/MsgSend", - "value": { - "amount": [ - { - "amount": "100000000", - "denom": "basecro" - } - ], - "from_address": "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", - "to_address": "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus" - } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A4gxsGFiPn6L5Z2IjHEISkXI0IkwfL9exV3GLB171Wvj" - }, - "signature": "5+5rSFFg0FE9cTklQWQHNktBDJsz7UCnMSgF0t0+gYcrIhEWUyTtibXaHZQbKAAaciJ1BkHXYREjU55VswByVg==" - } - ] - } - } - )"); -} diff --git a/tests/CryptoOrg/TWAnyAddressTests.cpp b/tests/CryptoOrg/TWAnyAddressTests.cpp deleted file mode 100644 index e9771fed810..00000000000 --- a/tests/CryptoOrg/TWAnyAddressTests.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include "HexCoding.h" - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; - -TEST(CryptoorgAnyAddress, IsValid) { - EXPECT_TRUE(TWAnyAddressIsValid(STRING("cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0").get(), TWCoinTypeCryptoOrg)); - EXPECT_TRUE(TWAnyAddressIsValid(STRING("cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus").get(), TWCoinTypeCryptoOrg)); - EXPECT_FALSE(TWAnyAddressIsValid(STRING("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02").get(), TWCoinTypeCryptoOrg)); -} - -TEST(CryptoorgAnyAddress, Create) { - auto string = STRING("cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0"); - auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeCryptoOrg)); - auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); - EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); - auto keyHash = WRAPD(TWAnyAddressData(addr.get())); - assertHexEqual(keyHash, "c2dcbc3828b42c429cedbaefa943e66679b471ed"); -} diff --git a/tests/CryptoOrg/TWAnySignerTests.cpp b/tests/CryptoOrg/TWAnySignerTests.cpp deleted file mode 100644 index 694b34936f0..00000000000 --- a/tests/CryptoOrg/TWAnySignerTests.cpp +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include "proto/Cosmos.pb.h" -#include "HexCoding.h" - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; - -TEST(TWAnySignerCryptoorg, SignTx_DDCCE4) { - auto input = Cosmos::Proto::SigningInput(); - input.set_account_number(125798); - input.set_sequence(0); - input.set_chain_id("crypto-org-chain-mainnet-1"); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_send_coins_message(); - message.set_from_address("cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0"); - message.set_to_address("cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus"); - auto amountOfTx = message.add_amounts(); - amountOfTx->set_denom("basecro"); - amountOfTx->set_amount(100000000); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("basecro"); - amountOfFee->set_amount(5000); - - auto privateKey = parse_hex("200e439e39cf1aad465ee3de6166247f914cbc0f823fc2dd48bf16dcd556f39d"); - input.set_private_key(privateKey.data(), privateKey.size()); - - Cosmos::Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeCryptoOrg); - - assertJSONEqual(output.json(), R"( - { - "mode": "block", - "tx": { - "fee": { - "amount": [ - { - "amount": "5000", - "denom": "basecro" - } - ], - "gas": "200000" - }, - "memo": "", - "msg": [ - { - "type": "cosmos-sdk/MsgSend", - "value": { - "amount": [ - { - "amount": "100000000", - "denom": "basecro" - } - ], - "from_address": "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", - "to_address": "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus" - } - } - ], - "signatures": [ - { - "pub_key": { - "type": "tendermint/PubKeySecp256k1", - "value": "A4gxsGFiPn6L5Z2IjHEISkXI0IkwfL9exV3GLB171Wvj" - }, - "signature": "5+5rSFFg0FE9cTklQWQHNktBDJsz7UCnMSgF0t0+gYcrIhEWUyTtibXaHZQbKAAaciJ1BkHXYREjU55VswByVg==" - } - ] - } - } - )"); - - /// https://crypto.org/explorer/tx/DDCCE4052040B05914CADEFE78C0A75BE363AE39504E7EF6B2EDB8A9072AD44B -} diff --git a/tests/CryptoOrg/TWCoinTypeTests.cpp b/tests/CryptoOrg/TWCoinTypeTests.cpp deleted file mode 100644 index a6e06751ec4..00000000000 --- a/tests/CryptoOrg/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWCryptoorgCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCryptoOrg)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("D87D2EB46B21466886EE149C1DEA3AE6C2E019C7D8C24FA1533A95439DDCE1E2")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCryptoOrg, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("cro10wrflcdc4pys9vvgqm98yg7yv5ltj7d3xehent")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCryptoOrg, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCryptoOrg)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCryptoOrg)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCryptoOrg), 8); - ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeCryptoOrg)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeCryptoOrg)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeCryptoOrg)); - assertStringsEqual(symbol, "CRO"); - assertStringsEqual(txUrl, "https://crypto.org/explorer/tx/D87D2EB46B21466886EE149C1DEA3AE6C2E019C7D8C24FA1533A95439DDCE1E2"); - assertStringsEqual(accUrl, "https://crypto.org/explorer/account/cro10wrflcdc4pys9vvgqm98yg7yv5ltj7d3xehent"); - assertStringsEqual(id, "cryptoorg"); - assertStringsEqual(name, "Crypto.org"); -} diff --git a/tests/Dash/TWCoinTypeTests.cpp b/tests/Dash/TWCoinTypeTests.cpp deleted file mode 100644 index 20fa3257b39..00000000000 --- a/tests/Dash/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWDashCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeDash)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeDash, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeDash, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeDash)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeDash)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeDash), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeDash)); - ASSERT_EQ(0x10, TWCoinTypeP2shPrefix(TWCoinTypeDash)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeDash)); - assertStringsEqual(symbol, "DASH"); - assertStringsEqual(txUrl, "https://blockchair.com/dash/transaction/t123"); - assertStringsEqual(accUrl, "https://blockchair.com/dash/address/a12"); - assertStringsEqual(id, "dash"); - assertStringsEqual(name, "Dash"); -} diff --git a/tests/Decred/AddressTests.cpp b/tests/Decred/AddressTests.cpp deleted file mode 100644 index 49f627dabe9..00000000000 --- a/tests/Decred/AddressTests.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Decred/Address.h" - -#include "Coin.h" -#include "HDWallet.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Decred; - -TEST(DecredAddress, FromPublicKey) { - const auto publicKey = PublicKey(parse_hex("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"), TWPublicKeyTypeSECP256k1); - const auto address = Address(publicKey); - ASSERT_EQ(address.string(), "DsmcYVbP1Nmag2H4AS17UTvmWXmGeA7nLDx"); -} - -TEST(DecredAddress, Valid) { - ASSERT_TRUE(Address::isValid("DsmcYVbP1Nmag2H4AS17UTvmWXmGeA7nLDx")); - ASSERT_TRUE(Address::isValid("Dcur2mcGjmENx4DhNqDctW5wJCVyT3Qeqkx")); -} - -TEST(DecredAddress, Invalid) { - ASSERT_FALSE(Address::isValid("rnBFvgZphmN39GWzUJeUitaP22Fr9be75H")); - ASSERT_FALSE(Address::isValid("t3gQDEavk5VzAAHK8TrQu2BWDLxEiF1unBm")); -} - -TEST(DecredAddress, FromString) { - const auto string = "DsmcYVbP1Nmag2H4AS17UTvmWXmGeA7nLDx"; - const auto address = Address(string); - - ASSERT_EQ(address.string(), string); -} - -TEST(DecredAddress, Derive) { - const auto mnemonic = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; - const auto wallet = HDWallet(mnemonic, ""); - const auto path = TW::derivationPath(TWCoinTypeDecred); - const auto address = TW::deriveAddress(TWCoinTypeDecred, wallet.getKey(TWCoinTypeDecred, path)); - ASSERT_EQ(address, "DsVMHD5D86dpRnt2GPZvv4bYUJZg6B9Pzqa"); -} diff --git a/tests/Decred/SignerTests.cpp b/tests/Decred/SignerTests.cpp deleted file mode 100644 index 8ad2081c970..00000000000 --- a/tests/Decred/SignerTests.cpp +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Decred/Address.h" -#include "Decred/Signer.h" -#include "proto/Decred.pb.h" - -#include "Hash.h" -#include "HexCoding.h" -#include "PrivateKey.h" - -#include -#include - -using namespace TW; -using namespace TW::Decred; - -TEST(DecredSigner, SignP2PKH) { - const auto privateKey = PrivateKey(parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - const auto keyhash = Hash::ripemd(Hash::blake256(publicKey.bytes)); - - const auto address = Address(publicKey); - ASSERT_EQ(address.string(), "DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - - - // For this example, create a fake transaction that represents what would - // ordinarily be the real transaction that is being spent. It contains a - // single output that pays to address in the amount of 1 DCR. - auto originTx = Transaction(); - - auto txInOrigin = TransactionInput(); - txInOrigin.previousOutput = OutPoint(std::array{}, UINT32_MAX, 0); - txInOrigin.valueIn = 100'000'000; - txInOrigin.script = Bitcoin::Script(Data{OP_0, OP_0}); - originTx.inputs.push_back(txInOrigin); - - auto txOutOrigin = TransactionOutput(); - txOutOrigin.value = 100'000'000; - txOutOrigin.script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); - originTx.outputs.push_back(txOutOrigin); - - ASSERT_EQ(hex(originTx.hash()), "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a"); - - - // Setup input - Bitcoin::Proto::SigningInput input; - input.set_hash_type(TWBitcoinSigHashTypeAll); - input.set_amount(100'000'000); - input.set_byte_fee(1); - input.set_to_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - input.set_change_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - - auto utxoKey0 = parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c"); - input.add_private_key(utxoKey0.data(), utxoKey0.size()); - - auto utxo0 = input.add_utxo(); - auto utxo0Script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); - utxo0->set_script(utxo0Script.bytes.data(), utxo0Script.bytes.size()); - utxo0->set_amount(100'000'000); - utxo0->mutable_out_point()->set_hash(originTx.hash().data(), originTx.hash().size()); - utxo0->mutable_out_point()->set_index(0); - - - // Create the transaction to redeem the fake transaction. - auto redeemTx = Transaction(); - - auto txIn = TransactionInput(); - txIn.previousOutput = OutPoint(originTx.hash(), 0, 0); - txIn.valueIn = 100'000'000; - redeemTx.inputs.push_back(txIn); - - auto txOut = TransactionOutput(); - redeemTx.outputs.push_back(txOut); - - auto plan = input.mutable_plan(); - plan->set_amount(100'000'000); - plan->set_available_amount(100'000'000); - plan->set_fee(0); - plan->set_change(0); - auto utxop0 = plan->add_utxos(); - *utxop0 = *utxo0; - - // Sign - auto signer = Signer(std::move(input)); - signer.transaction = redeemTx; - signer.txPlan.amount = 100'000'000; - const auto result = signer.sign(); - - ASSERT_TRUE(result); - - const auto expectedSignature = "47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f0121" - "02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5"; - EXPECT_EQ(hex(result.payload().inputs[0].script.bytes), expectedSignature); - - const auto expectedEncoded = - "0100" // Serialize type - "0000" // Version - "01" // Inputs - "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a" // Hash - "00000000" // Index - "00" // Tree - "ffffffff" // Sequence - "01" // Outputs - "0000000000000000" // Value - "0000" // Version - "00" // Script - "00000000" // Lock time - "00000000" // Expiry - "01" - "00e1f50500000000" // Value - "00000000" // Block height - "ffffffff" // Block index - "6a47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f012102a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5"; - auto encoded = Data(); - result.payload().encode(encoded); - EXPECT_EQ(hex(encoded), expectedEncoded); -} - -TEST(DecredSigner, SignP2SH) { - const auto privateKey = PrivateKey(parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - const auto keyhash = Hash::ripemd(Hash::blake256(publicKey.bytes)); - - const auto address = Address(publicKey); - ASSERT_EQ(address.string(), "DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - - - // For this example, create a fake transaction that represents what would - // ordinarily be the real transaction that is being spent. It contains a - // single output that pays to address in the amount of 1 DCR. - auto originTx = Transaction(); - - auto txInOrigin = TransactionInput(); - txInOrigin.previousOutput = OutPoint(std::array{}, UINT32_MAX, 0); - txInOrigin.valueIn = 100'000'000; - txInOrigin.script = Bitcoin::Script(Data{OP_0, OP_0}); - originTx.inputs.push_back(txInOrigin); - - auto txOutOrigin = TransactionOutput(); - txOutOrigin.value = 100'000'000; - txOutOrigin.script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); - originTx.outputs.push_back(txOutOrigin); - - ASSERT_EQ(hex(originTx.hash()), "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a"); - - - // Setup input - Bitcoin::Proto::SigningInput input; - input.set_hash_type(TWBitcoinSigHashTypeAll); - input.set_amount(100'000'000); - input.set_byte_fee(1); - input.set_to_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - input.set_change_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - - auto utxoKey0 = parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c"); - input.add_private_key(utxoKey0.data(), utxoKey0.size()); - - auto redeemScript = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); - auto scriptHash = Hash::ripemd(Hash::sha256(redeemScript.bytes)); - auto scriptString = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); - (*input.mutable_scripts())[hex(scriptHash.begin(), scriptHash.end())] = scriptString; - - auto utxo0 = input.add_utxo(); - auto utxo0Script = Bitcoin::Script::buildPayToScriptHash(scriptHash); - utxo0->set_script(utxo0Script.bytes.data(), utxo0Script.bytes.size()); - utxo0->set_amount(100'000'000); - utxo0->mutable_out_point()->set_hash(originTx.hash().data(), originTx.hash().size()); - utxo0->mutable_out_point()->set_index(0); - - auto plan = input.mutable_plan(); - plan->set_amount(100'000'000); - plan->set_available_amount(100'000'000); - plan->set_fee(0); - plan->set_change(0); - auto utxop0 = plan->add_utxos(); - *utxop0 = *utxo0; - - // Create the transaction to redeem the fake transaction. - auto redeemTx = Transaction(); - - auto txIn = TransactionInput(); - txIn.previousOutput = OutPoint(originTx.hash(), 0, 0); - txIn.valueIn = 100'000'000; - redeemTx.inputs.push_back(txIn); - - auto txOut = TransactionOutput(); - redeemTx.outputs.push_back(txOut); - - - // Sign - auto signer = Signer(std::move(input)); - signer.transaction = redeemTx; - signer.txPlan.amount = 100'000'000; - const auto result = signer.sign(); - - ASSERT_TRUE(result); - - const auto expectedSignature = "47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f012102a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf51976a914f5eba6730a4052ddeef0a93d93d24004f49db51e88ac1976a914f5eba6730a4052ddeef0a93d93d24004f49db51e88ac"; - EXPECT_EQ(hex(result.payload().inputs[0].script.bytes), expectedSignature); - - const auto expectedEncoded = - "0100" // Serialize type - "0000" // Version - "01" // Inputs - "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a" // Hash - "00000000" // Index - "00" // Tree - "ffffffff" // Sequence - "01" // Outputs - "0000000000000000" // Value - "0000" // Version - "00" // Script - "00000000" // Lock time - "00000000" // Expiry - "01" - "00e1f50500000000" // Value - "00000000" // Block height - "ffffffff" // Block index - "9e47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f012102a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf51976a914f5eba6730a4052ddeef0a93d93d24004f49db51e88ac1976a914f5eba6730a4052ddeef0a93d93d24004f49db51e88ac"; - auto encoded = Data(); - result.payload().encode(encoded); - EXPECT_EQ(hex(encoded), expectedEncoded); -} - -TEST(DecredSigner, SignNegativeNoUtxo) { - const auto privateKey = PrivateKey(parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - const auto keyhash = Hash::ripemd(Hash::blake256(publicKey.bytes)); - - const auto address = Address(publicKey); - ASSERT_EQ(address.string(), "DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - - auto originTx = Transaction(); - - auto txInOrigin = TransactionInput(); - txInOrigin.previousOutput = OutPoint(std::array{}, UINT32_MAX, 0); - txInOrigin.valueIn = 100'000'000; - txInOrigin.script = Bitcoin::Script(Data{OP_0, OP_0}); - originTx.inputs.push_back(txInOrigin); - - auto txOutOrigin = TransactionOutput(); - txOutOrigin.value = 100'000'000; - txOutOrigin.script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); - originTx.outputs.push_back(txOutOrigin); - - ASSERT_EQ(hex(originTx.hash()), "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a"); - - // Setup input - Bitcoin::Proto::SigningInput input; - input.set_hash_type(TWBitcoinSigHashTypeAll); - input.set_amount(100'000'000); - input.set_byte_fee(1); - input.set_to_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - input.set_change_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - - auto utxoKey0 = parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c"); - input.add_private_key(utxoKey0.data(), utxoKey0.size()); - - // Create the transaction to redeem the fake transaction. - auto redeemTx = Transaction(); - - auto txIn = TransactionInput(); - txIn.previousOutput = OutPoint(originTx.hash(), 0, 0); - txIn.valueIn = 100'000'000; - redeemTx.inputs.push_back(txIn); - - auto txOut = TransactionOutput(); - redeemTx.outputs.push_back(txOut); - - // Sign - auto signer = Signer(std::move(input)); - signer.transaction = redeemTx; - signer.txPlan.amount = 100'000'000; - const auto result = signer.sign(); - - // Fails as there are 0 utxos - ASSERT_FALSE(result); -} - -TEST(DecredSigner, SignP2PKH_NoPlan) { - const auto privateKey = PrivateKey(parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - const auto keyhash = Hash::ripemd(Hash::blake256(publicKey.bytes)); - - const auto address = Address(publicKey); - ASSERT_EQ(address.string(), "DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - - - // For this example, create a fake transaction that represents what would - // ordinarily be the real transaction that is being spent. It contains a - // single output that pays to address in the amount of 1 DCR. - auto originTx = Transaction(); - - auto txInOrigin = TransactionInput(); - txInOrigin.previousOutput = OutPoint(std::array{}, UINT32_MAX, 0); - txInOrigin.valueIn = 150'000'000; - txInOrigin.script = Bitcoin::Script(Data{OP_0, OP_0}); - originTx.inputs.push_back(txInOrigin); - - auto txOutOrigin = TransactionOutput(); - txOutOrigin.value = 100'000'000; - txOutOrigin.script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); - originTx.outputs.push_back(txOutOrigin); - - ASSERT_EQ(hex(originTx.hash()), "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a"); - - - // Setup input - Bitcoin::Proto::SigningInput input; - input.set_hash_type(TWBitcoinSigHashTypeAll); - input.set_amount(100'000'000); - input.set_byte_fee(1); - input.set_to_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - input.set_change_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); - - auto utxoKey0 = parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c"); - input.add_private_key(utxoKey0.data(), utxoKey0.size()); - - auto utxo0 = input.add_utxo(); - auto utxo0Script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); - utxo0->set_script(utxo0Script.bytes.data(), utxo0Script.bytes.size()); - utxo0->set_amount(150'000'000); - utxo0->mutable_out_point()->set_hash(originTx.hash().data(), originTx.hash().size()); - utxo0->mutable_out_point()->set_index(0); - - - // Create the transaction to redeem the fake transaction. - auto redeemTx = Transaction(); - - auto txIn = TransactionInput(); - txIn.previousOutput = OutPoint(originTx.hash(), 0, 0); - txIn.valueIn = 100'000'000; - redeemTx.inputs.push_back(txIn); - - auto txOut = TransactionOutput(); - redeemTx.outputs.push_back(txOut); - - - // Sign - auto signer = Signer(std::move(input)); - signer.transaction = redeemTx; - //signer.txPlan.utxos.push_back(*utxo0); - //signer.txPlan.amount = 100'000'000; - const auto result = signer.sign(); - - ASSERT_TRUE(result); - ASSERT_TRUE(result.payload().inputs.size() >= 1); - - const auto expectedSignature = "47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f0121" - "02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5"; - EXPECT_EQ(hex(result.payload().inputs[0].script.bytes), expectedSignature); - - const auto expectedEncoded = - "0100" // Serialize type - "0000" // Version - "01" // Inputs - "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a" // Hash - "00000000" // Index - "00" // Tree - "ffffffff" // Sequence - "01" // Outputs - "0000000000000000" // Value - "0000" // Version - "00" // Script - "00000000" // Lock time - "00000000" // Expiry - "01" - "00e1f50500000000" // Value - "00000000" // Block height - "ffffffff" // Block index - "6a47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f012102a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5"; - auto encoded = Data(); - result.payload().encode(encoded); - EXPECT_EQ(hex(encoded), expectedEncoded); -} - -TEST(DecredSigning, SignP2WPKH_NegativeAddressWrongType) { - auto hash0 = parse_hex("fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f"); - auto hash1 = parse_hex("ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a"); - - // Setup input - Bitcoin::Proto::SigningInput input; - input.set_hash_type(TWBitcoinSigHashTypeAll); - input.set_amount(335'790'000); - input.set_byte_fee(1); - input.set_to_address("1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx"); - input.set_change_address("1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU"); - - auto utxoKey0 = parse_hex("bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"); - input.add_private_key(utxoKey0.data(), utxoKey0.size()); - - auto utxoKey1 = parse_hex("619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"); - input.add_private_key(utxoKey1.data(), utxoKey1.size()); - - auto scriptPub1 = Bitcoin::Script(parse_hex("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); - auto scriptHash = std::vector(); - scriptPub1.matchPayToWitnessPublicKeyHash(scriptHash); - auto scriptHashHex = hex(scriptHash.begin(), scriptHash.end()); - ASSERT_EQ(scriptHashHex, "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1"); - - auto redeemScript = Bitcoin::Script::buildPayToPublicKeyHash(scriptHash); - auto scriptString = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); - (*input.mutable_scripts())[scriptHashHex] = scriptString; - - auto utxo0 = input.add_utxo(); - auto utxo0Script = parse_hex("2103c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432ac"); - utxo0->set_script(utxo0Script.data(), utxo0Script.size()); - utxo0->set_amount(625'000'000); - utxo0->mutable_out_point()->set_hash(hash0.data(), hash0.size()); - utxo0->mutable_out_point()->set_index(0); - utxo0->mutable_out_point()->set_sequence(UINT32_MAX); - - auto utxo1 = input.add_utxo(); - auto utxo1Script = parse_hex("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1"); - utxo1->set_script(utxo1Script.data(), utxo1Script.size()); - utxo1->set_amount(600'000'000); - utxo1->mutable_out_point()->set_hash(hash1.data(), hash1.size()); - utxo1->mutable_out_point()->set_index(1); - utxo1->mutable_out_point()->set_sequence(UINT32_MAX); - - // Sign - auto result = Signer(std::move(input)).sign(); - - ASSERT_FALSE(result) << std::to_string(result.error()); -} diff --git a/tests/Decred/TWAnySignerTests.cpp b/tests/Decred/TWAnySignerTests.cpp deleted file mode 100644 index e1d08bbb221..00000000000 --- a/tests/Decred/TWAnySignerTests.cpp +++ /dev/null @@ -1,114 +0,0 @@ - -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" - -#include "HexCoding.h" -#include "proto/Bitcoin.pb.h" -#include "proto/Decred.pb.h" -#include -#include -#include -#include - - -namespace TW::Decred { - -Bitcoin::Proto::SigningInput createInput() { - const int64_t utxoValue = 39900000; - const int64_t amount = 10000000; - - auto input = Bitcoin::Proto::SigningInput(); - input.set_hash_type(TWBitcoinSigHashTypeAll); - input.set_amount(amount); - input.set_byte_fee(1); - input.set_to_address("Dsesp1V6DZDEtcq2behmBVKdYqKMdkh96hL"); - input.set_change_address("DsUoWCAxprdGNtKQqambFbTcSBgH1SHn9Gp"); - input.set_coin_type(TWCoinTypeDecred); - - auto& utxo = *input.add_utxo(); - - auto hash = parse_hex("fdbfe9dd703f306794a467f175be5bd9748a7925033ea1cf9889d7cf4dd11550"); - auto script = parse_hex("76a914b75fdec70b2e731795dd123ab40f918bf099fee088ac"); - auto utxoKey = parse_hex("ba005cd605d8a02e3d5dfd04234cef3a3ee4f76bfbad2722d1fb5af8e12e6764"); - - utxo.set_amount(utxoValue); - utxo.set_script(script.data(), script.size()); - - auto& outpoint = *utxo.mutable_out_point(); - outpoint.set_hash(hash.data(), hash.size()); - outpoint.set_index(0); - - input.add_private_key(utxoKey.data(), utxoKey.size()); - return input; -} - -TEST(TWAnySignerDecred, Signing) { - auto input = createInput(); - - const int64_t utxoValue = 39900000; - const int64_t amount = 10000000; - const int64_t fee = 100000; - - auto& plan = *input.mutable_plan(); - plan.set_amount(amount); - plan.set_available_amount(utxoValue); - plan.set_fee(fee); - plan.set_change(utxoValue - amount - fee); - auto& planUtxo = *plan.add_utxos(); - planUtxo = input.utxo(0); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeDecred); - - ASSERT_EQ(output.error(), Common::Proto::OK); - ASSERT_EQ(hex(output.encoded()), "0100000001fdbfe9dd703f306794a467f175be5bd9748a7925033ea1cf9889d7cf4dd1155000000000000000000002809698000000000000001976a914989b1aecabf1c24e213cc0f2d8a22ffee25dd4e188ac40b6c6010000000000001976a9142a194fc92e27fef9cc2b057bc9060c580cbb484888ac000000000000000001000000000000000000000000ffffffff6a47304402206ee887c9239e5fff0048674bdfff2a8cfbeec6cd4a3ccebcc12fac44b24cc5ac0220718f7c760818fde18bc5ba8457d43d5a145cc4cf13d2a5557cba9107e9f4558d0121026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6"); -} - -TEST(TWAnySignerDecred, Plan) { - auto input = createInput(); - - Bitcoin::Proto::TransactionPlan plan; - ANY_PLAN(input, plan, TWCoinTypeDecred); - - EXPECT_EQ(plan.amount(), 10000000); - EXPECT_EQ(plan.available_amount(), 39900000); - EXPECT_EQ(plan.fee(), 254); - EXPECT_EQ(plan.change(), 29899746); - EXPECT_EQ(plan.utxos_size(), 1); - EXPECT_EQ(plan.branch_id(), ""); -} - -TEST(TWAnySignerDecred, PlanAndSign) { - auto input = createInput(); - - Bitcoin::Proto::TransactionPlan plan; - ANY_PLAN(input, plan, TWCoinTypeDecred); - - EXPECT_EQ(plan.amount(), 10000000); - EXPECT_EQ(plan.available_amount(), 39900000); - EXPECT_EQ(plan.fee(), 254); - EXPECT_EQ(plan.change(), 29899746); - EXPECT_EQ(plan.utxos_size(), 1); - EXPECT_EQ(plan.branch_id(), ""); - - // copy over plan fields - *input.mutable_plan() = plan; - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeDecred); - - ASSERT_EQ(output.error(), Common::Proto::OK); - ASSERT_EQ(output.encoded().size(), 251); - ASSERT_EQ(hex(output.encoded()), "0100000001fdbfe9dd703f306794a467f175be5bd9748a7925033ea1cf9889d7cf4dd1155000000000000000000002809698000000000000001976a914989b1aecabf1c24e213cc0f2d8a22ffee25dd4e188ace23bc8010000000000001976a9142a194fc92e27fef9cc2b057bc9060c580cbb484888ac000000000000000001000000000000000000000000ffffffff6a47304402203e6ee9e16d6bc36bb4242f7a4cac333a1c2a150ea16143412b88b721f6ae16bf02201019affdf815a5c22e4b0fb7e4685c4707094922d6a41354f9055d3bb0f26e630121026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6"); -} - -TEST(TWAnySignerDecred, SupportsJSON) { - ASSERT_FALSE(TWAnySignerSupportsJSON(TWCoinTypeDecred)); -} - -} // namespace diff --git a/tests/Decred/TWCoinTypeTests.cpp b/tests/Decred/TWCoinTypeTests.cpp deleted file mode 100644 index 1fb338d04f3..00000000000 --- a/tests/Decred/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWDecredCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeDecred)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeDecred, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeDecred, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeDecred)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeDecred)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeDecred), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeDecred)); - ASSERT_EQ(0x1a, TWCoinTypeP2shPrefix(TWCoinTypeDecred)); - ASSERT_EQ(0x7, TWCoinTypeStaticPrefix(TWCoinTypeDecred)); - assertStringsEqual(symbol, "DCR"); - assertStringsEqual(txUrl, "https://dcrdata.decred.org/tx/t123"); - assertStringsEqual(accUrl, "https://dcrdata.decred.org/address/a12"); - assertStringsEqual(id, "decred"); - assertStringsEqual(name, "Decred"); -} diff --git a/tests/DerivationPathTests.cpp b/tests/DerivationPathTests.cpp deleted file mode 100644 index 4b2269ad864..00000000000 --- a/tests/DerivationPathTests.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "DerivationPath.h" - -#include - -namespace TW { - -TEST(DerivationPath, InitWithIndices) { - const auto path = DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeEthereum), 0, 0, 0); - ASSERT_EQ(path.indices[0], DerivationPathIndex(44, /* hardened: */true)); - ASSERT_EQ(path.indices[1], DerivationPathIndex(60, /* hardened: */true)); - ASSERT_EQ(path.indices[2], DerivationPathIndex(0, /* hardened: */true)); - ASSERT_EQ(path.indices[3], DerivationPathIndex(0, /* hardened: */false)); - ASSERT_EQ(path.indices[4], DerivationPathIndex(0, /* hardened: */false)); -} - -TEST(DerivationPath, InitWithString) { - ASSERT_NO_THROW(DerivationPath("m/44'/60'/0'/0/0")); - const auto path = DerivationPath("m/44'/60'/0'/0/0"); - - ASSERT_EQ(path.indices[0], DerivationPathIndex(44, /* hardened: */true)); - ASSERT_EQ(path.indices[1], DerivationPathIndex(60, /* hardened: */true)); - ASSERT_EQ(path.indices[2], DerivationPathIndex(0, /* hardened: */true)); - ASSERT_EQ(path.indices[3], DerivationPathIndex(0, /* hardened: */false)); - ASSERT_EQ(path.indices[4], DerivationPathIndex(0, /* hardened: */false)); - - ASSERT_EQ(path.purpose(), 44); - ASSERT_EQ(path.coin(), 60); - ASSERT_EQ(path.account(), 0); - ASSERT_EQ(path.change(), 0); - ASSERT_EQ(path.address(), 0); -} - -TEST(DerivationPath, InitInvalid) { - ASSERT_THROW(DerivationPath("a/b/c"), std::invalid_argument); - ASSERT_THROW(DerivationPath("m/44'/60''/"), std::invalid_argument); -} - -TEST(DerivationPath, IndexOutOfBounds) { - DerivationPath path; - - EXPECT_EQ(path.indices.size(), 0); - - EXPECT_EQ(path.purpose(), TWPurposeBIP44); - EXPECT_EQ(path.coin(), TWCoinTypeBitcoin); - EXPECT_EQ(path.account(), 0); - EXPECT_EQ(path.change(), 0); - EXPECT_EQ(path.address(), 0); - - ASSERT_NO_THROW(path.setPurpose(TWPurposeBIP44)); - ASSERT_NO_THROW(path.setCoin(TWCoinTypeBitcoin)); - ASSERT_NO_THROW(path.setAccount(0)); - ASSERT_NO_THROW(path.setChange(0)); - ASSERT_NO_THROW(path.setAddress(0)); -} - -TEST(DerivationPath, String) { - const auto path = DerivationPath("m/44'/60'/0'/0/0"); - ASSERT_EQ(path.string(), "m/44'/60'/0'/0/0"); -} - -TEST(DerivationPath, Equal) { - const auto path1 = DerivationPath("m/44'/60'/0'/0/0"); - const auto path2 = DerivationPath("44'/60'/0'/0/0"); - ASSERT_EQ(path1, path2); -} - -} // namespace diff --git a/tests/DigiByte/TWCoinTypeTests.cpp b/tests/DigiByte/TWCoinTypeTests.cpp deleted file mode 100644 index 0140cef4893..00000000000 --- a/tests/DigiByte/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWDigiByteCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeDigiByte)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeDigiByte, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeDigiByte, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeDigiByte)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeDigiByte)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeDigiByte), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeDigiByte)); - ASSERT_EQ(0x3f, TWCoinTypeP2shPrefix(TWCoinTypeDigiByte)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeDigiByte)); - assertStringsEqual(symbol, "DGB"); - assertStringsEqual(txUrl, "https://digiexplorer.info/tx/t123"); - assertStringsEqual(accUrl, "https://digiexplorer.info/address/a12"); - assertStringsEqual(id, "digibyte"); - assertStringsEqual(name, "DigiByte"); -} diff --git a/tests/Dogecoin/TWCoinTypeTests.cpp b/tests/Dogecoin/TWCoinTypeTests.cpp deleted file mode 100644 index decba7e836e..00000000000 --- a/tests/Dogecoin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWDogecoinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeDogecoin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeDogecoin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeDogecoin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeDogecoin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeDogecoin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeDogecoin), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeDogecoin)); - ASSERT_EQ(0x16, TWCoinTypeP2shPrefix(TWCoinTypeDogecoin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeDogecoin)); - assertStringsEqual(symbol, "DOGE"); - assertStringsEqual(txUrl, "https://blockchair.com/dogecoin/transaction/t123"); - assertStringsEqual(accUrl, "https://blockchair.com/dogecoin/address/a12"); - assertStringsEqual(id, "doge"); - assertStringsEqual(name, "Dogecoin"); -} diff --git a/tests/EOS/AddressTests.cpp b/tests/EOS/AddressTests.cpp deleted file mode 100644 index 25ff60f2dd9..00000000000 --- a/tests/EOS/AddressTests.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "EOS/Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include - -#include - -using namespace TW; -using namespace TW::EOS; - -TEST(EOSAddress, Invalid) { - ASSERT_FALSE(Address::isValid("abc")); - ASSERT_FALSE(Address::isValid("65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF")); - ASSERT_FALSE(Address::isValid("EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjT")); - ASSERT_FALSE(Address::isValid("PUB_5hieQEFWh68h6bjaYAY25Ptd2bmqLCaFsunaneh9gZsmSgUBUe")); - ASSERT_FALSE(Address::isValid("PUB_K1_5hieQEFWh68h6bjaYAY25Ptd2bmqLCaFsunaneh9gZsmSgUBUe")); - - ASSERT_THROW(Address("PUB_K1_65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF"), std::invalid_argument); - ASSERT_THROW(EOS::Address(Data(0)), std::invalid_argument); -} - -TEST(EOSAddress, Base58) { - ASSERT_EQ( - Address("EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF").string(), - "EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF" - ); - ASSERT_EQ( - Address("EOS55hdeEZHoArE8LLTv6drj2yR1K1AH8wAPT4kjTVSnkmQc3nzwQ").string(), - "EOS55hdeEZHoArE8LLTv6drj2yR1K1AH8wAPT4kjTVSnkmQc3nzwQ" - ); - ASSERT_EQ( - Address("PUB_R1_5hieQEFWh68h6bjaYAY25Ptd2bmqLCaFsunaneh9gZsmSgUBUe").string(), - "PUB_R1_5hieQEFWh68h6bjaYAY25Ptd2bmqLCaFsunaneh9gZsmSgUBUe" - ); - ASSERT_EQ( - Address("PUB_R1_7M9ckjr6p5CmS3N3yLPg9vcTB5NHmLcMHwZ3iGccEVfbjJRHv3").string(), - "PUB_R1_7M9ckjr6p5CmS3N3yLPg9vcTB5NHmLcMHwZ3iGccEVfbjJRHv3" - ); -} - -TEST(EOSAddress, FromPrivateKey) { - std::string privArray[] { "8e14ef506fee5e0aaa32f03a45242d32d0eb993ffe25ce77542ef07219db667c", - "e2bfd815c5923f404388a3257aa5527f0f52e92ce364e1e26a04d270c901edda", - "e6b783120a21cb234d8e15077ce186c47261d1043781ab8b16b84f2acd377668", - "bb96c0a4a6ec9c93ccc0b2cbad6b0e8110b9ca4731aef9c6937b99552a319b03" }; - - Type privTypes[] { Type::Legacy, Type::Legacy, Type::ModernR1, Type::ModernR1 }; - - std::string pubArray[] { "EOS6TFKUKVvtvjRq9T4fV9pdxNUuJke92nyb4rzSFtZfdR5ssmVuY", - "EOS5YtaCcbPJ3BknNBTDezE9eJoGNnAVuUwT8bnxhSRS5dqRvyfxr", - "PUB_R1_67itCyDj42CRgtpyP4fLbAccBYnVHGeZQujQAeK3fyNbvfvZM6", - "PUB_R1_5DpVkbrMBDnY4JRhiEdHLmdLDKGQLNfL7X7it2pqT7Uk83ccDL" }; - - for (int i = 0; i < 4; i++) { - const auto privateKey = PrivateKey(parse_hex(privArray[i])); - const auto publicKey = PublicKey(privateKey.getPublicKey(privTypes[i] == Type::Legacy ? TWPublicKeyTypeSECP256k1 : TWPublicKeyTypeNIST256p1)); - const auto address = Address(publicKey, privTypes[i]); - - ASSERT_EQ(address.string(), pubArray[i]); - } -} - -TEST(EOSAddress, IsValid) { - ASSERT_TRUE(Address::isValid("EOS6Vm7RWMS1KKAM9kDXgggpu4sJkFMEpARhmsWA84tk4P22m29AV")); - ASSERT_TRUE(Address::isValid("PUB_R1_6pQRUVU5vdneRnmjSiZPsvu3zBqcptvg6iK2Vz4vKo4ugnzow3")); - ASSERT_TRUE(Address::isValid("EOS5mGcPvsqFDe8YRrA3yMMjQgjrCa6yiCho79KViDhvxh4ajQjgS")); - ASSERT_TRUE(Address::isValid("PUB_R1_82dMu3zSSfyHYc4cvWJ6SPsHZWB5mBNAyhL53xiM5xpqmfqetN")); - - ASSERT_NO_THROW(Address(parse_hex("039d91164ea04f4e751762643ef4ae520690af361b8e677cf341fd213419956b356cb721b7"), Type::ModernR1)); - ASSERT_NO_THROW(Address(parse_hex("02d3c8e736a9a50889766caf3c37bd16e2fecc7340b3130e25d4c01b153f996a10a78afc0e"), Type::Legacy)); -} \ No newline at end of file diff --git a/tests/EOS/SignatureTests.cpp b/tests/EOS/SignatureTests.cpp deleted file mode 100644 index 28bb3ee51a2..00000000000 --- a/tests/EOS/SignatureTests.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "EOS/Transaction.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::EOS; - -TEST(EOSSignature, Serialization) { - Data buf; - Signature *sig = new Signature(parse_hex("1f14262320d5b145220fb94d8fe204117edd25e52bbe9557b6e0909dd00307af266f5be1deef001446979523ac9de32c7eae5e5be4180b5a60c0e6bf14b2dd3e05"), Type::ModernK1); - sig->serialize(buf); - - ASSERT_EQ( - hex(buf), - "001f14262320d5b145220fb94d8fe204117edd25e52bbe9557b6e0909dd00307af266f5be1deef001446979523ac9de32c7eae5e5be4180b5a60c0e6bf14b2dd3e05" - ); - - ASSERT_EQ( - sig->string(), - "SIG_K1_JwtfgsdSx5RuF5aejedQ7FJTexaKMrQyYosPUWUrU1mzdLx6JUgLTZJd7zWA8q8VdnXht3YmVt7jafmD2eEK7hTRpT9rY5" - ); - - delete sig; - sig = new Signature(parse_hex("1f5c419d16f573ddbf07d2eb959621f690f9cb856ea2d113e3af02b3b40005488410e82ffa37a079e119844d213f4eb066a640507db68851752bea6e61eb864d84"), Type::ModernR1); - buf.clear(); - sig->serialize(buf); - - ASSERT_EQ( - hex(buf), - "011f5c419d16f573ddbf07d2eb959621f690f9cb856ea2d113e3af02b3b40005488410e82ffa37a079e119844d213f4eb066a640507db68851752bea6e61eb864d84" - ); - - ASSERT_EQ( - sig->string(), - "SIG_R1_K7KpdLYqa6ebCP22TuiYAY9YoJh1dTWTZEVkdPzdoadFL6f8PkMYk5N8wtsF11cneEJ91XnEZP6wDJHhRyqr1fr68ouYcz" - ); - - delete sig; - ASSERT_THROW(sig = new Signature(parse_hex("1f5c419d16f573ddbf07d2eb959621f690f9cb856ea2d113e3af02b3b40005488410e82ffa37a079e119844d213f4eb066a640507db68851752bea6e61eb864d84"), Type::Legacy), std::invalid_argument); - ASSERT_THROW(sig = new Signature(parse_hex("011f5c419d16f573ddbf07d2eb959621f690f9cb856ea2d113e3af02b3b40005488410e82ffa37a079e119844d213f4eb066a640507db68851752bea6e61eb864d84"), Type::ModernR1), std::invalid_argument); -} diff --git a/tests/EOS/TWAnySignerTests.cpp b/tests/EOS/TWAnySignerTests.cpp deleted file mode 100644 index 537ae52b042..00000000000 --- a/tests/EOS/TWAnySignerTests.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" -#include -#include "HexCoding.h" -#include "proto/EOS.pb.h" - -#include - -using namespace TW; -using namespace TW::EOS; - -TEST(TWAnySignerEOS, Sign) { - Proto::SigningInput input; - auto chainId = parse_hex("cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f"); - auto refBlock = parse_hex("000067d6f6a7e7799a1f3d487439a679f8cf95f1c986f35c0d2fa320f51a7144"); - auto key = parse_hex("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd"); - - auto& asset = *input.mutable_asset(); - asset.set_amount(300000); - asset.set_decimals(4); - asset.set_symbol("TKN"); - - input.set_chain_id(chainId.data(), chainId.size()); - input.set_reference_block_id(refBlock.data(), refBlock.size()); - input.set_reference_block_time(1554209118); - input.set_currency("token"); - input.set_sender("token"); - input.set_recipient("eosio"); - input.set_memo("my second transfer"); - input.set_private_key(key.data(), key.size()); - input.set_private_key_type(Proto::KeyType::MODERNK1); - - { - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEOS); - - EXPECT_EQ(output.error(), Common::Proto::OK); - EXPECT_EQ(output.json_encoded(), R"({"compression":"none","packed_context_free_data":"","packed_trx":"7c59a35cd6679a1f3d4800000000010000000080a920cd000000572d3ccdcd010000000080a920cd00000000a8ed3232330000000080a920cd0000000000ea3055e09304000000000004544b4e00000000126d79207365636f6e64207472616e7366657200","signatures":["SIG_K1_KfCdjsrTnx5cBpbA5cUdHZAsRYsnC9uKzuS1shFeqfMCfdZwX4PBm9pfHwGRT6ffz3eavhtkyNci5GoFozQAx8P8PBnDmj"]})"); - } - - input.set_private_key_type(Proto::KeyType::LEGACY); - { - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEOS); - EXPECT_EQ(output.error(), Common::Proto::Error_internal); - EXPECT_TRUE(output.json_encoded().empty()); - } -} diff --git a/tests/EOS/TWCoinTypeTests.cpp b/tests/EOS/TWCoinTypeTests.cpp deleted file mode 100644 index 65412f55772..00000000000 --- a/tests/EOS/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWEOSCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeEOS)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeEOS, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeEOS, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeEOS)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeEOS)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeEOS), 4); - ASSERT_EQ(TWBlockchainEOS, TWCoinTypeBlockchain(TWCoinTypeEOS)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeEOS)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeEOS)); - assertStringsEqual(symbol, "EOS"); - assertStringsEqual(txUrl, "https://bloks.io/transaction/t123"); - assertStringsEqual(accUrl, "https://bloks.io/account/a12"); - assertStringsEqual(id, "eos"); - assertStringsEqual(name, "EOS"); -} diff --git a/tests/EOS/TransactionTests.cpp b/tests/EOS/TransactionTests.cpp deleted file mode 100644 index 8d2df015518..00000000000 --- a/tests/EOS/TransactionTests.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "EOS/Transaction.h" -#include "EOS/Signer.h" -#include "EOS/Action.h" -#include "EOS/Address.h" -#include "PrivateKey.h" -#include "HexCoding.h" -#include "Hash.h" - -#include - -using namespace TW; -using namespace TW::EOS; - -static std::string k1Sigs[5] { - "SIG_K1_KfCdjsrTnx5cBpbA5cUdHZAsRYsnC9uKzuS1shFeqfMCfdZwX4PBm9pfHwGRT6ffz3eavhtkyNci5GoFozQAx8P8PBnDmj", - "SIG_K1_K6wW678ngyWT7fgR4nNqm5XoKZBp9NDN4tKsctyzzADjXn15iAH9tcnQ393t6uvsqYxHKjdnxxduT1nyKMLiZbRqL7dHYr", - "SIG_K1_K6cUbZX6xfWcV5iotVprnf12Lc5AmV8SKmN5hVdv39gcM8wfEcwcNScvTuGLWpWzDT463dyhNmUfMB4nqt7tJVFnzx8mSi", - "SIG_K1_Khj7xhMd8HxrT6dUzuwiFM1MfMHtog5jCygJj7ADvdmUGkzZkmjymZXucEAud3whJ2qsMcxHcKtLWs8Ndm6be14qjTAH2a", - "SIG_K1_K93MjjE39CSH7kwJBgoRsSF2VaH6a8psQKU29nSg4xxxrVhz2iQuubyyB5t2ACZFFYSkNHSdYia5efhnW6Z9SPtbQTquMY" -}; - -static std::string r1Sigs[5] { - "SIG_R1_Kbywy4Mjun4Ymrh23Xk5yRtKJxcDWaDjQjLKERAny6Vs6oT1DYoEdoAj9AJK9iukHdEd9HBYnf3GmLtA55JFY5VaNszJMa", - "SIG_R1_KAhJJg4QGYBWY7hG6BKGAbW57fg6g8xTh3LG3Sss3bGv4BwiwHmRV1jsgh6hrnVRUoCaKMbJQzzWy9HXy6PnDmfJ6fbZMJ", - "SIG_R1_KxAwVKfpLr2MeK4aSAp5LSi2Vohsp94Uhk5UvZZDUJqd7ccBkhc2kYY1L6z5rjRNNo7BeP1Qr6H2xPFqo54YQ6DjczAqLW", - "SIG_R1_K1isJT8pJhkrHi3mcvrfY12nY6jirMCWaAHWuBXvu2ondcm3QHkgdaTwERskftZ9cqB5k2r8ajoYS4VWsiivjbd56D6pxX", - "SIG_R1_KWtgvnj2LaaYdtBTjM7bTR23LPBytDHFE7gPEfGTZ7PWc4yc6piPuPUHsVJVkvKmpW2gEUhq3toCfjkt34itSxMgekovdG" -}; - -TEST(EOSTransaction, Serialization) { - ASSERT_THROW(TransferAction("token", "eosio", "token", Asset::fromString("-20.1234 TKN"), "my first transfer"), std::invalid_argument); - - Data referenceBlockId = parse_hex("000046dc08ad384ca452d92c59348aba888fcbb6ef1ebffc3181617706664d4c"); - int32_t referenceBlockTime = 1554121728; - auto chainId = parse_hex("cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f"); - - Transaction tx {referenceBlockId, referenceBlockTime}; - tx.actions.push_back(TransferAction("token", "eosio", "token", Asset::fromString("20.1234 TKN"), "my first transfer")); - ASSERT_EQ(tx.actions.back().serialize().dump(), "{\"account\":\"token\",\"authorizations\":[{\"actor\":\"eosio\",\"permission\":\"active\"}],\"data\":\"0000000000ea30550000000080a920cd121203000000000004544b4e00000000116d79206669727374207472616e73666572\",\"name\":\"transfer\"}"); - - Data buf; - tx.serialize(buf); - - Signer signer {chainId}; - - ASSERT_EQ( - hex(buf), - "1e04a25cdc46a452d92c00000000010000000080a920cd000000572d3ccdcd010000000000ea305500000000a8ed3232320000000000ea30550000000080a920cd121203000000000004544b4e00000000116d79206669727374207472616e7366657200" - ); - - ASSERT_EQ( - hex(signer.hash(tx)), - "5de974bb90b940b462688609735a1dd522fa853aba765c30d14bedd27d719dd1" - ); - - // make transaction invalid and see if signing succeeds - tx.maxNetUsageWords = UINT32_MAX; - ASSERT_THROW(signer.sign(PrivateKey(Hash::sha256(std::string("A"))), Type::ModernK1, tx), std::invalid_argument); - - referenceBlockId = parse_hex("000067d6f6a7e7799a1f3d487439a679f8cf95f1c986f35c0d2fa320f51a7144"); - referenceBlockTime = 1554209118; - - Transaction tx2 {referenceBlockId, referenceBlockTime}; - tx2.actions.push_back(TransferAction("token", "token", "eosio", Asset::fromString("30.0000 TKN"), "my second transfer")); - - buf.clear(); - tx2.serialize(buf); - ASSERT_EQ( - hex(buf), - "7c59a35cd6679a1f3d4800000000010000000080a920cd000000572d3ccdcd010000000080a920cd00000000a8ed3232330000000080a920cd0000000000ea3055e09304000000000004544b4e00000000126d79207365636f6e64207472616e7366657200" - ); - - ASSERT_EQ( - hex(signer.hash(tx2)), - "4dac38a8ad7f095a09ec0eb0cbd060c9d8ea0a842535d369c9ce526cdf1b5d85" - ); - - ASSERT_NO_THROW(tx2.serialize()); - - // verify k1 sigs - for (int i = 0; i < 5; i++) { - PrivateKey pk(Hash::sha256(std::string(i + 1, 'A'))); - ASSERT_NO_THROW(signer.sign(pk, Type::ModernK1, tx2)); - - ASSERT_EQ( - tx2.signatures.back().string(), - k1Sigs[i] - ); - } - - // verify r1 sigs - for (int i = 0; i < 5; i++) { - PrivateKey pk(Hash::sha256(std::string(i + 1, 'A'))); - ASSERT_NO_THROW(signer.sign(pk, Type::ModernR1, tx2)); - - ASSERT_EQ( - tx2.signatures.back().string(), - r1Sigs[i] - ); - } -} \ No newline at end of file diff --git a/tests/Elrond/AddressTests.cpp b/tests/Elrond/AddressTests.cpp deleted file mode 100644 index 3dd1fb6f166..00000000000 --- a/tests/Elrond/AddressTests.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include - -#include "HexCoding.h" -#include "PublicKey.h" -#include "PrivateKey.h" -#include "Elrond/Address.h" -#include "TestAccounts.h" - -using namespace TW; -using namespace TW::Elrond; - - -TEST(ElrondAddress, Valid) { - ASSERT_TRUE(Address::isValid(ALICE_BECH32)); - ASSERT_TRUE(Address::isValid(BOB_BECH32)); - ASSERT_TRUE(Address::isValid(CAROL_BECH32)); -} - -TEST(ElrondAddress, Invalid) { - ASSERT_FALSE(Address::isValid("")); - ASSERT_FALSE(Address::isValid("foo")); - ASSERT_FALSE(Address::isValid("10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); - ASSERT_FALSE(Address::isValid("xerd10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); - ASSERT_FALSE(Address::isValid("foo10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); - ASSERT_FALSE(Address::isValid(ALICE_PUBKEY_HEX)); -} - -TEST(ElrondAddress, FromString) { - Address alice, bob, carol; - ASSERT_TRUE(Address::decode(ALICE_BECH32, alice)); - ASSERT_TRUE(Address::decode(BOB_BECH32, bob)); - ASSERT_TRUE(Address::decode(CAROL_BECH32, carol)); - - ASSERT_EQ(ALICE_PUBKEY_HEX, hex(alice.getKeyHash())); - ASSERT_EQ(BOB_PUBKEY_HEX, hex(bob.getKeyHash())); - ASSERT_EQ(CAROL_PUBKEY_HEX, hex(carol.getKeyHash())); -} - -TEST(ElrondAddress, FromData) { - const auto alice = Address(parse_hex(ALICE_PUBKEY_HEX)); - const auto bob = Address(parse_hex(BOB_PUBKEY_HEX)); - const auto carol = Address(parse_hex(CAROL_PUBKEY_HEX)); - - ASSERT_EQ(ALICE_BECH32, alice.string()); - ASSERT_EQ(BOB_BECH32, bob.string()); - ASSERT_EQ(CAROL_BECH32, carol.string()); -} - -TEST(ElrondAddress, FromPrivateKey) { - auto aliceKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); - auto alice = Address(aliceKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(ALICE_BECH32, alice.string()); - - auto bobKey = PrivateKey(parse_hex(BOB_SEED_HEX)); - auto bob = Address(bobKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(BOB_BECH32, bob.string()); - - auto carolKey = PrivateKey(parse_hex(CAROL_SEED_HEX)); - auto carol = Address(carolKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(CAROL_BECH32, carol.string()); -} - -TEST(ElrondAddress, FromPublicKey) { - auto alice = PublicKey(parse_hex(ALICE_PUBKEY_HEX), TWPublicKeyTypeED25519); - ASSERT_EQ(ALICE_BECH32, Address(alice).string()); - - auto bob = PublicKey(parse_hex(BOB_PUBKEY_HEX), TWPublicKeyTypeED25519); - ASSERT_EQ(BOB_BECH32, Address(bob).string()); - - auto carol = PublicKey(parse_hex(CAROL_PUBKEY_HEX), TWPublicKeyTypeED25519); - ASSERT_EQ(CAROL_BECH32, Address(carol).string()); -} diff --git a/tests/Elrond/SerializationTests.cpp b/tests/Elrond/SerializationTests.cpp deleted file mode 100644 index 35110688955..00000000000 --- a/tests/Elrond/SerializationTests.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include -#include "boost/format.hpp" - -#include "HexCoding.h" -#include "Elrond/Serialization.h" -#include "TestAccounts.h" - -using namespace TW; -using namespace TW::Elrond; - -TEST(ElrondSerialization, SignableString) { - Proto::TransactionMessage message; - message.set_nonce(42); - message.set_value("43"); - message.set_sender("alice"); - message.set_receiver("bob"); - message.set_data("foo"); - message.set_chain_id("1"); - message.set_version(1); - - string jsonString = serializeTransaction(message); - ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"bob","sender":"alice","gasPrice":0,"gasLimit":0,"data":"Zm9v","chainID":"1","version":1})", jsonString); -} - -TEST(ElrondSerialization, SignableStringWithRealData) { - Proto::TransactionMessage message; - message.set_nonce(15); - message.set_value("100"); - message.set_sender(ALICE_BECH32); - message.set_receiver(BOB_BECH32); - message.set_gas_price(1000000000); - message.set_gas_limit(50000); - message.set_data("foo"); - message.set_chain_id("1"); - message.set_version(1); - - string expected = (boost::format(R"({"nonce":15,"value":"100","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1})") % BOB_BECH32 % ALICE_BECH32).str(); - string actual = serializeTransaction(message); - ASSERT_EQ(expected, actual); -} - -TEST(ElrondSerialization, SignableStringWithoutData) { - Proto::TransactionMessage message; - message.set_nonce(42); - message.set_value("43"); - message.set_sender("feed"); - message.set_receiver("abba"); - message.set_chain_id("1"); - message.set_version(1); - - string jsonString = serializeTransaction(message); - ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"abba","sender":"feed","gasPrice":0,"gasLimit":0,"chainID":"1","version":1})", jsonString); -} diff --git a/tests/Elrond/SignerTests.cpp b/tests/Elrond/SignerTests.cpp deleted file mode 100644 index 39a019caf65..00000000000 --- a/tests/Elrond/SignerTests.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include "boost/format.hpp" - -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "Elrond/Signer.h" -#include "Elrond/Address.h" -#include "TestAccounts.h" - -using namespace TW; -using namespace TW::Elrond; - - -TEST(ElrondSigner, Sign) { - auto input = Proto::SigningInput(); - auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - input.mutable_transaction()->set_nonce(0); - input.mutable_transaction()->set_value("0"); - input.mutable_transaction()->set_sender(ALICE_BECH32); - input.mutable_transaction()->set_receiver(BOB_BECH32); - input.mutable_transaction()->set_gas_price(1000000000); - input.mutable_transaction()->set_gas_limit(50000); - input.mutable_transaction()->set_data("foo"); - input.mutable_transaction()->set_chain_id("1"); - input.mutable_transaction()->set_version(1); - - auto output = Signer::sign(input); - auto signature = output.signature(); - auto encoded = output.encoded(); - auto expectedSignature = "b5fddb8c16fa7f6123cb32edc854f1e760a3eb62c6dc420b5a4c0473c58befd45b621b31a448c5b59e21428f2bc128c80d0ee1caa4f2bf05a12be857ad451b00"; - auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); - - ASSERT_EQ(expectedSignature, signature); - ASSERT_EQ(expectedEncoded, encoded); -} - -TEST(ElrondSigner, SignJSON) { - // Shuffle some fields, assume arbitrary order in the input - auto input = (boost::format(R"({"transaction" : {"data":"foo","value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"chainId":"1","version":1}})") % BOB_BECH32 % ALICE_BECH32).str(); - auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); - - auto encoded = Signer::signJSON(input, privateKey.bytes); - auto expectedSignature = "b5fddb8c16fa7f6123cb32edc854f1e760a3eb62c6dc420b5a4c0473c58befd45b621b31a448c5b59e21428f2bc128c80d0ee1caa4f2bf05a12be857ad451b00"; - auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); - - ASSERT_EQ(expectedEncoded, encoded); -} - -TEST(ElrondSigner, SignWithoutData) { - auto input = Proto::SigningInput(); - auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - input.mutable_transaction()->set_nonce(0); - input.mutable_transaction()->set_value("0"); - input.mutable_transaction()->set_sender(ALICE_BECH32); - input.mutable_transaction()->set_receiver(BOB_BECH32); - input.mutable_transaction()->set_gas_price(1000000000); - input.mutable_transaction()->set_gas_limit(50000); - input.mutable_transaction()->set_data(""); - input.mutable_transaction()->set_chain_id("1"); - input.mutable_transaction()->set_version(1); - - auto output = Signer::sign(input); - auto signature = output.signature(); - auto encoded = output.encoded(); - auto expectedSignature = "3079d37bfbdbe66fbb4c4b186144f9d9ad5b4b08fbcd6083be0688cf1171123109dfdefdbabf91425c757ca109b6db6d674cb9aeebb19a1a51333565abb53109"; - auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); - - ASSERT_EQ(expectedSignature, signature); - ASSERT_EQ(expectedEncoded, encoded); -} - -TEST(ElrondSigner, SignJSONWithoutData) { - // Shuffle some fields, assume arbitrary order in the input - auto input = (boost::format(R"({"transaction" : {"value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"chainId":"1","version":1}})") % BOB_BECH32 % ALICE_BECH32).str(); - auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); - - auto encoded = Signer::signJSON(input, privateKey.bytes); - auto expectedSignature = "3079d37bfbdbe66fbb4c4b186144f9d9ad5b4b08fbcd6083be0688cf1171123109dfdefdbabf91425c757ca109b6db6d674cb9aeebb19a1a51333565abb53109"; - auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); - - ASSERT_EQ(expectedEncoded, encoded); -} diff --git a/tests/Elrond/TWAnySignerTests.cpp b/tests/Elrond/TWAnySignerTests.cpp deleted file mode 100644 index 0124614bf06..00000000000 --- a/tests/Elrond/TWAnySignerTests.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include "boost/format.hpp" - -#include -#include "HexCoding.h" -#include "../interface/TWTestUtilities.h" -#include "Elrond/Signer.h" -#include "TestAccounts.h" - -using namespace TW; -using namespace TW::Elrond; - - -TEST(TWAnySignerElrond, Sign) { - auto input = Proto::SigningInput(); - auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - input.mutable_transaction()->set_nonce(0); - input.mutable_transaction()->set_value("0"); - input.mutable_transaction()->set_sender(ALICE_BECH32); - input.mutable_transaction()->set_receiver(BOB_BECH32); - input.mutable_transaction()->set_gas_price(1000000000); - input.mutable_transaction()->set_gas_limit(50000); - input.mutable_transaction()->set_data("foo"); - input.mutable_transaction()->set_chain_id("1"); - input.mutable_transaction()->set_version(1); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeElrond); - - auto signature = output.signature(); - auto encoded = output.encoded(); - auto expectedSignature = "b5fddb8c16fa7f6123cb32edc854f1e760a3eb62c6dc420b5a4c0473c58befd45b621b31a448c5b59e21428f2bc128c80d0ee1caa4f2bf05a12be857ad451b00"; - auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); - - ASSERT_EQ(expectedSignature, signature); - ASSERT_EQ(expectedEncoded, encoded); -} - -TEST(TWAnySignerElrond, SignJSON) { - // Shuffle some fields, assume arbitrary order in the input - auto input = STRING((boost::format(R"({"transaction" : {"data":"foo","value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"chainId":"1","version":1}})") % BOB_BECH32 % ALICE_BECH32).str().c_str()); - auto privateKey = DATA(ALICE_SEED_HEX); - auto encoded = WRAPS(TWAnySignerSignJSON(input.get(), privateKey.get(), TWCoinTypeElrond)); - auto expectedSignature = "b5fddb8c16fa7f6123cb32edc854f1e760a3eb62c6dc420b5a4c0473c58befd45b621b31a448c5b59e21428f2bc128c80d0ee1caa4f2bf05a12be857ad451b00"; - auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeElrond)); - assertStringsEqual(encoded, expectedEncoded.c_str()); -} diff --git a/tests/Elrond/TWCoinTypeTests.cpp b/tests/Elrond/TWCoinTypeTests.cpp deleted file mode 100644 index 900d36abd6d..00000000000 --- a/tests/Elrond/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWElrondCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeElrond)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeElrond, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeElrond, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeElrond)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeElrond)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeElrond), 18); - ASSERT_EQ(TWBlockchainElrondNetwork, TWCoinTypeBlockchain(TWCoinTypeElrond)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeElrond)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeElrond)); - assertStringsEqual(symbol, "eGLD"); - assertStringsEqual(txUrl, "https://explorer.elrond.com/transactions/1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8"); - assertStringsEqual(accUrl, "https://explorer.elrond.com/address/erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj"); - assertStringsEqual(id, "elrond"); - assertStringsEqual(name, "Elrond"); -} diff --git a/tests/Elrond/TestAccounts.h b/tests/Elrond/TestAccounts.h deleted file mode 100644 index 55060002abd..00000000000 --- a/tests/Elrond/TestAccounts.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -const auto ALICE_BECH32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"; -const auto ALICE_SEED_HEX = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf"; -const auto ALICE_PUBKEY_HEX = "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293"; -const auto BOB_BECH32 = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r"; -const auto BOB_SEED_HEX = "e3a3a3d1ac40d42d8fd4c569a9749b65a1250dd3d10b6f4e438297662ea4850e"; -const auto BOB_PUBKEY_HEX = "c70cf50b238372fffaf7b7c5723b06b57859d424a2da621bcc1b2f317543aa36"; -const auto CAROL_BECH32 = "erd19nu5t7hszckwah5nlcadmk5rlchtugzplznskffpwecygcu0520s9tnyy0"; -const auto CAROL_SEED_HEX = "a926316cc3daf8ff25ba3e417797e6dfd51f62ae735ab07148234732f7314052"; -const auto CAROL_PUBKEY_HEX = "2cf945faf0162ceede93fe3addda83fe2ebe2041f8a70b2521767044638fa29f"; diff --git a/tests/Ethereum/AddressTests.cpp b/tests/Ethereum/AddressTests.cpp deleted file mode 100644 index f2fab5a7c5a..00000000000 --- a/tests/Ethereum/AddressTests.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ethereum/Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" - -#include - -using namespace TW; -using namespace TW::Ethereum; - -TEST(EthereumAddress, Invalid) { - ASSERT_FALSE(Address::isValid("abc")); - ASSERT_FALSE(Address::isValid("aaeb60f3e94c9b9a09f33669435e7ef1beaed")); - ASSERT_FALSE(Address::isValid("fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")); -} - -TEST(EthereumAddress, EIP55) { - ASSERT_EQ( - Address(parse_hex("5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")).string(), - "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" - ); - ASSERT_EQ( - Address(parse_hex("0x5AAEB6053F3E94C9b9A09f33669435E7Ef1BEAED")).string(), - "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed" - ); - ASSERT_EQ( - Address(parse_hex("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")).string(), - "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359" - ); - ASSERT_EQ( - Address(parse_hex("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB")).string(), - "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB" - ); - ASSERT_EQ( - Address(parse_hex("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb")).string(), - "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb" - ); -} - -TEST(EthereumAddress, String) { - const auto address = Address("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); - ASSERT_EQ(address.string(), "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); -} - -TEST(EthereumAddress, FromPrivateKey) { - const auto privateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); - const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); - const auto address = Address(publicKey); - - ASSERT_EQ(address.string(), "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309"); -} - -TEST(EthereumAddress, IsValid) { - ASSERT_FALSE(Address::isValid("abc")); - ASSERT_TRUE(Address::isValid("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")); -} diff --git a/tests/Ethereum/FeeTests.cpp b/tests/Ethereum/FeeTests.cpp deleted file mode 100644 index 643cbb12d4f..00000000000 --- a/tests/Ethereum/FeeTests.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ethereum/Fee.h" -#include "uint256.h" -#include "../interface/TWTestUtilities.h" - -#include -#include - -using namespace TW::Ethereum; -using namespace TW; - -extern std::string TESTS_ROOT; - -static nlohmann::json load_json(std::string path) { - std::ifstream stream(path); - nlohmann::json json; - stream >> json; - return json; -} - -TEST(EthereumFee, suggestBaseFeeAndTip) { - const auto path = TESTS_ROOT + "/Ethereum/Data/eth_feeHistory3.json"; - const auto history = load_json(path); - - auto fee = Fee::suggestFee(history).dump(); - auto expected = R"|( - { - "baseFee": "44208904215", - "maxPriorityFee": "1500000000" - } - )|"; - assertJSONEqual(fee, expected); -} - -TEST(EthereumFee, suggestHighBaseFee) { - const auto path = TESTS_ROOT + "/Ethereum/Data/eth_feeHistory4.json"; - const auto history = load_json(path); - - auto fee = Fee::suggestFee(history).dump(); - auto expected = R"|( - { - "baseFee": "1098508176069", - "maxPriorityFee": "23492129063" - } - )|"; - assertJSONEqual(fee, expected); -} diff --git a/tests/Ethereum/RLPTests.cpp b/tests/Ethereum/RLPTests.cpp deleted file mode 100644 index ed8dd1181c4..00000000000 --- a/tests/Ethereum/RLPTests.cpp +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ethereum/RLP.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Ethereum; -using boost::multiprecision::uint256_t; - -std::string stringifyItems(const RLP::DecodedItem& di); - -std::string stringifyData(const Data& data) { - if (data.size() == 0) return "0"; - // try if only letters - bool isLettersOnly = true; - for(auto i: data) { - if (!((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || i == ' ' || i == ',')) { - isLettersOnly = false; - break; - } - } - if (isLettersOnly) return std::string("'") + std::string(data.begin(), data.end()) + "'"; - // try if it can be parsed (recursive) - if (data.size() >= 2) { - try { - const auto di = RLP::decode(data); - if (di.decoded.size() > 0 && di.remainder.size() == 0) { - return stringifyItems(di); - } - } catch (...) {} - } - // any other: as hex string - return hex(data); -} - -std::string stringifyItems(const RLP::DecodedItem& di) { - const auto n = di.decoded.size(); - if (n == 0) { - return "-"; - } - if (n == 1) { - return stringifyData(di.decoded[0]); - } - std::string res = "(" + std::to_string(n) + ": "; - int count = 0; - for(auto i: di.decoded) { - if (count++) res += " "; - res += stringifyData(i); - } - res += ")"; - return res; -} - -std::string decodeHelper(const std::string& hexData) { - const auto data = parse_hex(hexData); - const auto di = RLP::decode(data); - return stringifyItems(di); -} - -TEST(RLP, EncodeString) { - EXPECT_EQ(hex(RLP::encode("")), "80"); - EXPECT_EQ(hex(RLP::encode("d")), "64"); - EXPECT_EQ(hex(RLP::encode("dog")), "83646f67"); -} - -TEST(RLP, EncodeInteger) { - EXPECT_EQ(hex(RLP::encode(0)), "80"); - EXPECT_EQ(hex(RLP::encode(127)), "7f"); - EXPECT_EQ(hex(RLP::encode(128)), "8180"); - EXPECT_EQ(hex(RLP::encode(255)), "81ff"); - EXPECT_EQ(hex(RLP::encode(256)), "820100"); - EXPECT_EQ(hex(RLP::encode(1024)), "820400"); - EXPECT_EQ(hex(RLP::encode(0xffff)), "82ffff"); - EXPECT_EQ(hex(RLP::encode(0x010000)), "83010000"); - EXPECT_EQ(hex(RLP::encode(0xffffff)), "83ffffff"); - EXPECT_EQ(hex(RLP::encode(static_cast(0xffffffffULL))), "84ffffffff"); - EXPECT_EQ(hex(RLP::encode(static_cast(0xffffffffffffffULL))), "87ffffffffffffff"); -} - -TEST(RLP, EncodeUInt256) { - EXPECT_EQ(hex(RLP::encode(uint256_t(0))), "80"); - EXPECT_EQ(hex(RLP::encode(uint256_t(1))), "01"); - EXPECT_EQ(hex(RLP::encode(uint256_t(127))), "7f"); - EXPECT_EQ(hex(RLP::encode(uint256_t(128))), "8180"); - EXPECT_EQ(hex(RLP::encode(uint256_t(256))), "820100"); - EXPECT_EQ(hex(RLP::encode(uint256_t(1024))), "820400"); - EXPECT_EQ(hex(RLP::encode(uint256_t(0xffffff))), "83ffffff"); - EXPECT_EQ(hex(RLP::encode(uint256_t(0xffffffffULL))), "84ffffffff"); - EXPECT_EQ(hex(RLP::encode(uint256_t(0xffffffffffffffULL))), "87ffffffffffffff"); - EXPECT_EQ( - hex(RLP::encode(uint256_t("0x102030405060708090a0b0c0d0e0f2"))), - "8f102030405060708090a0b0c0d0e0f2" - ); - EXPECT_EQ( - hex(RLP::encode(uint256_t("0x0100020003000400050006000700080009000a000b000c000d000e01"))), - "9c0100020003000400050006000700080009000a000b000c000d000e01" - ); - EXPECT_EQ( - hex(RLP::encode(uint256_t("0x0100000000000000000000000000000000000000000000000000000000000000"))), - "a00100000000000000000000000000000000000000000000000000000000000000" - ); -} - -TEST(RLP, EncodeList) { - EXPECT_EQ(hex(RLP::encodeList(std::vector())), "c0"); - EXPECT_EQ(hex(RLP::encodeList(std::vector{1, 2, 3})), "c3010203"); - EXPECT_EQ(hex(RLP::encodeList(std::vector{"a", "b"})), "c26162"); - EXPECT_EQ(hex(RLP::encodeList(std::vector{"cat", "dog"})), "c88363617483646f67"); - { - const auto encoded = RLP::encodeList(std::vector(1024)); - EXPECT_EQ(hex(subData(encoded, 0, 20)), "f904008080808080808080808080808080808080"); - } -} - -TEST(RLP, EncodeListNested) { - const auto l11 = RLP::encodeList(std::vector{1, 2, 3}); - const auto l12 = RLP::encodeList(std::vector{"apple", "banana", "cherry"}); - const auto l21 = RLP::encodeList(std::vector{parse_hex("abcdef"), parse_hex("00010203040506070809")}); - const auto l22 = RLP::encodeList(std::vector{"bitcoin", "beeenbee", "eth"}); - const auto l1 = RLP::encodeList(std::vector{l11, l12}); - const auto l2 = RLP::encodeList(std::vector{l21, l22}); - const auto encoded = RLP::encodeList(std::vector{l1, l2}); - EXPECT_EQ(hex(encoded), "f8479cdb84c301020395d4856170706c658662616e616e6186636865727279a9e890cf83abcdef8a0001020304050607080996d587626974636f696e88626565656e62656583657468"); -} - -TEST(RLP, EncodeInvalid) { - ASSERT_TRUE(RLP::encode(-1).empty()); - ASSERT_TRUE(RLP::encodeList(std::vector{0, -1}).empty()); -} - -TEST(RLP, DecodeInteger) { - EXPECT_EQ(decodeHelper("00"), "00"); // not the primary encoding for 0 - EXPECT_EQ(decodeHelper("01"), "01"); - EXPECT_EQ(decodeHelper("09"), "09"); - EXPECT_EQ(decodeHelper("7f"), "7f"); - EXPECT_EQ(decodeHelper("80"), "0"); - EXPECT_EQ(decodeHelper("8180"), "80"); - EXPECT_EQ(decodeHelper("81ff"), "ff"); - EXPECT_EQ(decodeHelper("820100"), "0100"); - EXPECT_EQ(decodeHelper("820400"), "0400"); - EXPECT_EQ(decodeHelper("82ffff"), "ffff"); - EXPECT_EQ(decodeHelper("83010000"), "010000"); - EXPECT_EQ(decodeHelper("83ffffff"), "ffffff"); - EXPECT_EQ(decodeHelper("84ffffffff"), "ffffffff"); - EXPECT_EQ(decodeHelper("87ffffffffffffff"), "ffffffffffffff"); -} - -TEST(RLP, DecodeString) { - EXPECT_EQ(decodeHelper("80"), "0"); - EXPECT_EQ(decodeHelper("64"), "'d'"); - EXPECT_EQ(decodeHelper("83646f67"), "'dog'"); - EXPECT_EQ(decodeHelper("83636174"), "'cat'"); - EXPECT_EQ(decodeHelper("8f102030405060708090a0b0c0d0e0f2"), "102030405060708090a0b0c0d0e0f2"); - EXPECT_EQ(decodeHelper("9c0100020003000400050006000700080009000a000b000c000d000e01"), "0100020003000400050006000700080009000a000b000c000d000e01"); - EXPECT_EQ(decodeHelper("a00100000000000000000000000000000000000000000000000000000000000000"), "0100000000000000000000000000000000000000000000000000000000000000"); - // long string - EXPECT_EQ(decodeHelper("b87674686973206973206120612076657279206c6f6e6720737472696e672c2074686973206973206120612076657279206c6f6e6720737472696e672c2074686973206973206120612076657279206c6f6e6720737472696e672c2074686973206973206120612076657279206c6f6e6720737472696e67"), - "'this is a a very long string, this is a a very long string, this is a a very long string, this is a a very long string'"); -} - -TEST(RLP, DecodeList) { - // empty list - EXPECT_EQ(decodeHelper("c0"), "-"); - // short list - EXPECT_EQ(decodeHelper("c3010203"), "(3: 01 02 03)"); - EXPECT_EQ(decodeHelper("c26162"), "(2: 'a' 'b')"); - EXPECT_EQ(decodeHelper("c88363617483646f67"), "(2: 'cat' 'dog')"); - - // long list, raw ether transfer tx - EXPECT_EQ(decodeHelper("f86b81a985051f4d5ce982520894515778891c99e3d2e7ae489980cb7c77b37b5e76861b48eb57e0008025a0ad01c32a7c974df9d0bd48c8d7e0ecab62e90811917aa7dc0c966751a0c3f475a00dc77d9ec68484481bdf87faac14378f4f18d477f84c0810d29480372c1bbc65"), - "(9: " - "a9 " // nonce - "051f4d5ce9 " // gas price - "5208 " // gas limit - "515778891c99e3d2e7ae489980cb7c77b37b5e76 " // to - "1b48eb57e000 " // amount - "0 " // data - "25 " // v - "ad01c32a7c974df9d0bd48c8d7e0ecab62e90811917aa7dc0c966751a0c3f475 " // r - "0dc77d9ec68484481bdf87faac14378f4f18d477f84c0810d29480372c1bbc65" // s - ")" - ); - - // long list, raw token transfer tx - EXPECT_EQ(decodeHelper("f8aa81d485077359400082db9194dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000c6b6b55c8c4971145a842cc4e5db92d879d0b3e00000000000000000000000000000000000000000000000000000000002faf0801ca02843d8ed66b9623392dc336dd36d5dd5a630b2019962869b6e50fdb4ecb5b6aca05d9ea377bc65e2921f7fc257de8135530cc74e3188b6ba57a4b9cb284393050a"), - "(9: " - "d4 " - "0773594000 " - "db91 " - "dac17f958d2ee523a2206206994597c13d831ec7 " - "0 " - "a9059cbb000000000000000000000000c6b6b55c8c4971145a842cc4e5db92d879d0b3e00000000000000000000000000000000000000000000000000000000002faf080 " - "1c " - "2843d8ed66b9623392dc336dd36d5dd5a630b2019962869b6e50fdb4ecb5b6ac " - "5d9ea377bc65e2921f7fc257de8135530cc74e3188b6ba57a4b9cb284393050a" - ")" - ); - - { - // long list, with 2-byte size - const std::string elem = "0123"; - const int n = 500; - std::vector longarr; - for (auto i = 0; i < n; ++i) longarr.push_back(elem); - - const Data encoded = RLP::encodeList(longarr); - ASSERT_EQ(hex(subData(encoded, 0, 20)), "f909c48430313233843031323384303132338430"); - - auto decoded = RLP::decode(encoded); - ASSERT_EQ(decoded.decoded.size(), n); - for (int i = 0; i < 20; i++) { - EXPECT_EQ(hex(decoded.decoded[i]), "30313233"); - } - } - { - // long list, with 3-byte size - const std::string elem = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; - const int n = 650; - std::vector longarr; - for (auto i = 0; i < n; ++i) longarr.push_back(elem); - - const Data encoded = RLP::encodeList(longarr); - ASSERT_EQ(encoded.size(), 66304); - ASSERT_EQ(hex(subData(encoded, 0, 30)), "fa0102fcb864303132333435363738393031323334353637383930313233"); - - auto decoded = RLP::decode(encoded); - ASSERT_EQ(decoded.decoded.size(), n); - } - - // nested list - EXPECT_EQ(decodeHelper("f8479cdb84c301020395d4856170706c658662616e616e6186636865727279a9e890cf83abcdef8a0001020304050607080996d587626974636f696e88626565656e62656583657468"), - "(2: (2: (3: 01 02 03) (3: 'apple' 'banana' 'cherry')) (2: (2: abcdef 00010203040506070809) (3: 'bitcoin' 'beeenbee' 'eth')))"); -} - -TEST(RLP, DecodeInvalid) { - // decode empty data - EXPECT_THROW(RLP::decode(Data()), std::invalid_argument); - - // incorrect length - EXPECT_THROW(RLP::decode(parse_hex("0x81636174")), std::invalid_argument); - EXPECT_THROW(RLP::decode(parse_hex("0xb9ffff")), std::invalid_argument); - EXPECT_THROW(RLP::decode(parse_hex("0xc883636174")), std::invalid_argument); - - // some tests are from https://github.com/ethereum/tests/blob/develop/RLPTests/invalidRLPTest.json - // int32 overflow - EXPECT_THROW(RLP::decode(parse_hex("0xbf0f000000000000021111")), std::invalid_argument); - - // wrong size list - EXPECT_THROW(RLP::decode(parse_hex("0xf80180")), std::invalid_argument); - - // bytes should be single byte - EXPECT_THROW(RLP::decode(parse_hex("0x8100")), std::invalid_argument); - - // leading zeros in long length list - EXPECT_THROW(RLP::decode(parse_hex("fb00000040000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")), std::invalid_argument); - EXPECT_THROW(RLP::decode(parse_hex("f800")), std::invalid_argument); -} - -TEST(RLP, putVarInt) { - EXPECT_EQ(hex(RLP::putVarInt(0)), "00"); - EXPECT_EQ(hex(RLP::putVarInt(1)), "01"); - EXPECT_EQ(hex(RLP::putVarInt(0x21)), "21"); - EXPECT_EQ(hex(RLP::putVarInt(0xff)), "ff"); - EXPECT_EQ(hex(RLP::putVarInt(0x100)), "0100"); - EXPECT_EQ(hex(RLP::putVarInt(0x4321)), "4321"); - EXPECT_EQ(hex(RLP::putVarInt(0x654321)), "654321"); - EXPECT_EQ(hex(RLP::putVarInt(0x87654321)), "87654321"); - EXPECT_EQ(hex(RLP::putVarInt(0xa987654321)), "a987654321"); - EXPECT_EQ(hex(RLP::putVarInt(0xcba987654321)), "cba987654321"); - EXPECT_EQ(hex(RLP::putVarInt(0xedcba987654321)), "edcba987654321"); - EXPECT_EQ(hex(RLP::putVarInt(0x21edcba987654321)), "21edcba987654321"); - EXPECT_EQ(hex(RLP::putVarInt(0xffffffffffffffff)), "ffffffffffffffff"); -} - -TEST(RLP, parseVarInt) { - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("00"), 0))), "00"); - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("01"), 0))), "01"); - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("fc"), 0))), "fc"); - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("ff"), 0))), "ff"); - EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("abcd"), 1))), "cd"); - EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("0102"), 0))), "0102"); - EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("0100"), 0))), "0100"); - EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("fedc"), 0))), "fedc"); - EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("ffff"), 0))), "ffff"); - EXPECT_EQ(hex(store(RLP::parseVarInt(3, parse_hex("010203"), 0))), "010203"); - EXPECT_EQ(hex(store(RLP::parseVarInt(4, parse_hex("01020304"), 0))), "01020304"); - EXPECT_EQ(hex(store(RLP::parseVarInt(5, parse_hex("0102030405"), 0))), "0102030405"); - EXPECT_EQ(hex(store(RLP::parseVarInt(6, parse_hex("010203040506"), 0))), "010203040506"); - EXPECT_EQ(hex(store(RLP::parseVarInt(7, parse_hex("01020304050607"), 0))), "01020304050607"); - EXPECT_EQ(hex(store(RLP::parseVarInt(8, parse_hex("0102030405060708"), 0))), "0102030405060708"); - EXPECT_EQ(hex(store(RLP::parseVarInt(8, parse_hex("abcd0102030405060708"), 2))), "0102030405060708"); - EXPECT_THROW(RLP::parseVarInt(0, parse_hex("01"), 0), std::invalid_argument); // wrong size - EXPECT_THROW(RLP::parseVarInt(9, parse_hex("010203040506070809"), 0), std::invalid_argument); // wrong size - EXPECT_THROW(RLP::parseVarInt(4, parse_hex("0102"), 0), std::invalid_argument); // too short - EXPECT_THROW(RLP::parseVarInt(4, parse_hex("01020304"), 2), std::invalid_argument); // too short - EXPECT_THROW(RLP::parseVarInt(2, parse_hex("0002"), 0), std::invalid_argument); // starts with 0 -} diff --git a/tests/Ethereum/TWAnySignerTests.cpp b/tests/Ethereum/TWAnySignerTests.cpp deleted file mode 100644 index b57e9b26232..00000000000 --- a/tests/Ethereum/TWAnySignerTests.cpp +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" -#include -#include "HexCoding.h" -#include "uint256.h" -#include "proto/Ethereum.pb.h" -#include "Ethereum/ABI/Function.h" -#include "Ethereum/ABI/ParamBase.h" -#include "Ethereum/ABI/ParamAddress.h" - -#include - -using namespace TW; -using namespace TW::Ethereum; -using namespace TW::Ethereum::ABI; - -TEST(TWEthereumSigner, EmptyValue) { - auto str = std::string(""); - uint256_t zero = load(str); - - ASSERT_EQ(zero, uint256_t(0)); -} - -TEST(TWEthereumSigner, BigInt) { - // Check uint256_t loading - Data expectedData = {0x52, 0x08}; - auto value = uint256_t(21000); - auto loaded = load(expectedData); - ASSERT_EQ(loaded, value); - - // Check proto storing - Proto::SigningInput input; - auto storedData = store(value); - input.set_gas_limit(storedData.data(), storedData.size()); - ASSERT_EQ(hex(input.gas_limit()), hex(expectedData)); - - // Check proto loading - auto protoLoaded = load(input.gas_limit()); - ASSERT_EQ(protoLoaded, value); -} - -TEST(TWAnySignerEthereum, Sign) { - // from http://thetokenfactory.com/#/factory - // https://etherscan.io/tx/0x63879f20909a315bcffe629bc03b20e5bc65ba2a377bd7152e3b69c4bd4cd6cc - Proto::SigningInput input; - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(11)); - auto gasPrice = store(uint256_t(20000000000)); - auto gasLimit = store(uint256_t(1000000)); - auto data = parse_hex("0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161094b38038061094b83398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b5050505050506106da806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f357806318160ddd1461016857806323b872dd14610171578063313ce5671461025c57806354fd4d501461026857806370a08231146102c657806395d89b41146102f4578063a9059cbb14610352578063cae9ca51146103f7578063dd62ed3e146105be575b6105f2610002565b6040805160038054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6102e260025481565b610662600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101c4575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101d05750600082115b156106bf57600160a060020a0383811660008181526020818152604080832080548801905588851680845281842080548990039055600183528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016106c3565b61067660045460ff1681565b6040805160068054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b6105f46005805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03166000908152602081905260408120548290108015906103845750600082115b156106ca5733600160a060020a0390811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610162565b604080516020604435600481810135601f810184900484028501840190955284845261066294813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a03908116600081815260016020908152604080832094881680845294825280832087905580518781529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a383600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105965780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f19250505015156106d257610002565b6102e2600435602435600160a060020a03828116600090815260016020908152604080832093851683529290522054610162565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080519115158252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b505050505081565b5060005b9392505050565b506000610162565b5060016106c35600000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000754204275636b73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003544f540000000000000000000000000000000000000000000000000000000000"); - auto key = parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646"); - - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - // tx_mode not set, Legacy is the default - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_private_key(key.data(), key.size()); - auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); - transfer.set_data(data.data(), data.size()); - - std::string expected = "f90a9e0b8504a817c800830f42408080b90a4b60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161094b38038061094b83398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b5050505050506106da806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f357806318160ddd1461016857806323b872dd14610171578063313ce5671461025c57806354fd4d501461026857806370a08231146102c657806395d89b41146102f4578063a9059cbb14610352578063cae9ca51146103f7578063dd62ed3e146105be575b6105f2610002565b6040805160038054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6102e260025481565b610662600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101c4575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101d05750600082115b156106bf57600160a060020a0383811660008181526020818152604080832080548801905588851680845281842080548990039055600183528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016106c3565b61067660045460ff1681565b6040805160068054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b6105f46005805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03166000908152602081905260408120548290108015906103845750600082115b156106ca5733600160a060020a0390811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610162565b604080516020604435600481810135601f810184900484028501840190955284845261066294813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a03908116600081815260016020908152604080832094881680845294825280832087905580518781529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a383600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105965780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f19250505015156106d257610002565b6102e2600435602435600160a060020a03828116600090815260016020908152604080832093851683529290522054610162565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080519115158252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b505050505081565b5060005b9392505050565b506000610162565b5060016106c35600000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000754204275636b73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003544f54000000000000000000000000000000000000000000000000000000000026a042556c4f2a3f4e4e639cca524d1da70e60881417d4643e5382ed110a52719eafa0172f591a2a763d0bd6b13d042d8c5eb66e87f129c9dc77ada66b6041012db2b3"; - - { - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), expected); - ASSERT_EQ(hex(output.data()), hex(data)); - } -} - -TEST(TWAnySignerEthereum, SignERC20TransferAsERC20) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasPrice = store(uint256_t(42000000000)); // 0x09c7652400 - auto gasLimit = store(uint256_t(78009)); // 130B9 - auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; - auto token = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI - auto amount = uint256_t(2000000000000000000); - auto amountData = store(amount); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - // tx_mode not set, Legacy is the default - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_to_address(token); - input.set_private_key(key.data(), key.size()); - auto& erc20 = *input.mutable_transaction()->mutable_erc20_transfer(); - erc20.set_to(toAddress); - erc20.set_amount(amountData.data(), amountData.size()); - - // https://etherscan.io/tx/0x199a7829fc5149e49b452c2cab76d8fa5a9682fee6e4891b8acb697ac142513e - std::string expected = "f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98cea0032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931"; - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), expected); - - // expected payload - Data payload; - { - auto func = Function("transfer", std::vector>{ - std::make_shared(parse_hex(toAddress)), - std::make_shared(amount) - }); - func.encode(payload); - } - ASSERT_EQ(hex(output.data()), hex(payload)); - ASSERT_EQ(hex(output.data()), "a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000"); -} - -TEST(TWAnySignerEthereum, SignERC20TransferAsGenericContract) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasPrice = store(uint256_t(42000000000)); // 0x09c7652400 - auto gasLimit = store(uint256_t(78009)); // 130B9 - auto toAddress = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI - // payload: transfer(0x5322b34c88ed0691971bf52a7047448f0f4efc84, 2000000000000000000) - auto data = parse_hex("0xa9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000"); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - // tx_mode not set, Legacy is the default - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_to_address(toAddress); - input.set_private_key(key.data(), key.size()); - auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); - transfer.set_data(data.data(), data.size()); - - // https://etherscan.io/tx/0x199a7829fc5149e49b452c2cab76d8fa5a9682fee6e4891b8acb697ac142513e - std::string expected = "f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98cea0032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931"; - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), expected); - ASSERT_EQ(hex(output.data()), hex(data)); -} - -TEST(TWAnySignerEthereum, SignERC20TransferInvalidAddress) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasPrice = store(uint256_t(42000000000)); // 0x09c7652400 - auto gasLimit = store(uint256_t(78009)); // 130B9 - auto invalidAddress = "0xdeadbeef"; - auto amount = store(uint256_t(2000000000000000000)); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - // tx_mode not set, Legacy is the default - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_to_address(invalidAddress); - input.set_private_key(key.data(), key.size()); - auto& erc20 = *input.mutable_transaction()->mutable_erc20_transfer(); - erc20.set_to(invalidAddress); - erc20.set_amount(amount.data(), amount.size()); - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), ""); -} - -TEST(TWAnySignerEthereum, SignERC20Approve) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasPrice = store(uint256_t(42000000000)); // 0x09c7652400 - auto gasLimit = store(uint256_t(78009)); // 130B9 - auto spenderAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; - auto token = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI - auto amount = store(uint256_t(2000000000000000000)); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - // tx_mode not set, Legacy is the default - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_to_address(token); - input.set_private_key(key.data(), key.size()); - auto& erc20 = *input.mutable_transaction()->mutable_erc20_approve(); - erc20.set_spender(spenderAddress); - erc20.set_amount(amount.data(), amount.size()); - - std::string expected = "f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844095ea7b30000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0d8136d66da1e0ba8c7208d5c4f143167f54b89a0fe2e23440653bcca28b34dc1a049222a79339f1a9e4641cb4ad805c49c225ae704299ffc10627bf41c035c464a"; - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), expected); -} - -TEST(TWAnySignerEthereum, SignERC721Transfer) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasPrice = store(uint256_t(42000000000)); - auto gasLimit = store(uint256_t(78009)); - auto tokenContract = "0x4e45e92ed38f885d39a733c14f1817217a89d425"; - auto fromAddress = "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc"; - auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; - auto tokenId = parse_hex("23c47ee5"); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - // tx_mode not set, Legacy is the default - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_to_address(tokenContract); - input.set_private_key(key.data(), key.size()); - auto& erc721 = *input.mutable_transaction()->mutable_erc721_transfer(); - erc721.set_from(fromAddress); - erc721.set_to(toAddress); - erc721.set_token_id(tokenId.data(), tokenId.size()); - - std::string expected = "f8ca808509c7652400830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b86423b872dd000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee526a0d38440a4dc140a4100d301eb49fcc35b64439e27d1d8dd9b55823dca04e6e659a03b5f56a57feabc3406f123d6f8198cd7d7e2ced7e2d58d375f076952ecd9ce88"; - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), expected); - ASSERT_EQ(hex(output.data()), "23b872dd000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee5"); -} - -TEST(TWAnySignerEthereum, SignERC1155Transfer) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasPrice = store(uint256_t(42000000000)); - auto gasLimit = store(uint256_t(78009)); - auto tokenContract = "0x4e45e92ed38f885d39a733c14f1817217a89d425"; - auto fromAddress = "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc"; - auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; - auto tokenId = parse_hex("23c47ee5"); - auto value = uint256_t(2000000000000000000); - auto valueData = store(value); - auto data = parse_hex("01020304"); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - // tx_mode not set, Legacy is the default - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_to_address(tokenContract); - input.set_private_key(key.data(), key.size()); - auto& erc1155 = *input.mutable_transaction()->mutable_erc1155_transfer(); - erc1155.set_from(fromAddress); - erc1155.set_to(toAddress); - erc1155.set_token_id(tokenId.data(), tokenId.size()); - erc1155.set_value(valueData.data(), valueData.size()); - erc1155.set_data(data.data(), data.size()); - - std::string expected = "f9014a808509c7652400830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b8e4f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000026a010315488201ac801ce346bffd1570de147615462d7e7db3cf08cf558465c6b79a06643943b24593bc3904a9fda63bb169881730994c973ab80f07d66a698064573"; - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), expected); - - // expected payload - Data payload; - { - auto func = Function("safeTransferFrom", std::vector>{ - std::make_shared(parse_hex(fromAddress)), - std::make_shared(parse_hex(toAddress)), - std::make_shared(uint256_t(0x23c47ee5)), - std::make_shared(value), - std::make_shared(data) - }); - func.encode(payload); - } - ASSERT_EQ(hex(output.data()), hex(payload)); - ASSERT_EQ(hex(output.data()), "f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000"); -} - -TEST(TWAnySignerEthereum, SignJSON) { - auto json = STRING(R"({"chainId":"AQ==","gasPrice":"1pOkAA==","gasLimit":"Ugg=","toAddress":"0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1","transaction":{"transfer":{"amount":"A0i8paFgAA=="}}})"); - auto key = DATA("17209af590a86462395d5881e60d11c7fa7d482cfb02b5a01b93c2eeef243543"); - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeEthereum)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeEthereum)); - assertStringsEqual(result, "f86a8084d693a400825208947d8bf18c7ce84b3e175b339c4ca93aed1dd166f1870348bca5a160008025a0fe5802b49e04c6b1705088310e133605ed8b549811a18968ad409ea02ad79f21a05bf845646fb1e1b9365f63a7fd5eb5e984094e3ed35c3bed7361aebbcbf41f10"); -} - -TEST(TWAnySignerEthereum, PlanNotSupported) { - // Ethereum does not use plan(), call it nonetheless - Proto::SigningInput input; - auto inputData = input.SerializeAsString(); - auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData.data(), inputData.size())); - auto outputTWData = WRAPD(TWAnySignerPlan(inputTWData.get(), TWCoinTypeEthereum)); - EXPECT_EQ(TWDataSize(outputTWData.get()), 0); -} - -TEST(TWAnySignerEthereum, SignERC1559Transfer_1442) { - auto chainId = store(uint256_t(3)); - auto nonce = store(uint256_t(6)); - auto gasLimit = store(uint256_t(21100)); - auto maxInclusionFeePerGas = store(uint256_t(2000000000)); - auto maxFeePerGas = store(uint256_t(3000000000)); - auto toAddress = "0xB9F5771C27664bF2282D98E09D7F50cEc7cB01a7"; - auto value = uint256_t(543210987654321); - auto valueData = store(value); - auto key = parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - input.set_tx_mode(Proto::TransactionMode::Enveloped); // EIP1559 - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); - input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); - input.set_to_address(toAddress); - input.set_private_key(key.data(), key.size()); - auto& transfer = *input.mutable_transaction()->mutable_transfer(); - transfer.set_amount(valueData.data(), valueData.size()); - transfer.set_data(Data().data(), 0); - - // https://ropsten.etherscan.io/tx/0x14429509307efebfdaa05227d84c147450d168c68539351fbc01ed87c916ab2e - std::string expected = "02f8710306847735940084b2d05e0082526c94b9f5771c27664bf2282d98e09d7f50cec7cb01a78701ee0c29f50cb180c080a092c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c64a06487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"; - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - EXPECT_EQ(hex(output.encoded()), expected); - EXPECT_EQ(hex(output.v()), "00"); - EXPECT_EQ(hex(output.r()), "92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c64"); - EXPECT_EQ(hex(output.s()), "6487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"); - EXPECT_EQ(hex(output.data()), ""); -} - -TEST(TWAnySignerEthereum, SignERC20Transfer_1559) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasLimit = store(uint256_t(78009)); // 130B9 - auto maxInclusionFeePerGas = store(uint256_t(2000000000)); // 77359400 - auto maxFeePerGas = store(uint256_t(3000000000)); // B2D05E00 - auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; - auto token = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI - auto amount = uint256_t(2000000000000000000); - auto amountData = store(amount); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - input.set_tx_mode(Proto::TransactionMode::Enveloped); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); - input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); - input.set_to_address(token); - input.set_private_key(key.data(), key.size()); - auto& erc20 = *input.mutable_transaction()->mutable_erc20_transfer(); - erc20.set_to(toAddress); - erc20.set_amount(amountData.data(), amountData.size()); - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), "02f8b00180847735940084b2d05e00830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000c080a0adfcfdf98d4ed35a8967a0c1d78b42adb7c5d831cf5a3272654ec8f8bcd7be2ea011641e065684f6aa476f4fd250aa46cd0b44eccdb0a6e1650d658d1998684cdf"); -} - -TEST(TWAnySignerEthereum, SignERC20Approve_1559) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasLimit = store(uint256_t(78009)); // 130B9 - auto maxInclusionFeePerGas = store(uint256_t(2000000000)); - auto maxFeePerGas = store(uint256_t(3000000000)); - auto spenderAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; - auto token = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI - auto amount = store(uint256_t(2000000000000000000)); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - input.set_tx_mode(Proto::TransactionMode::Enveloped); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); - input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); - input.set_to_address(token); - input.set_private_key(key.data(), key.size()); - auto& erc20 = *input.mutable_transaction()->mutable_erc20_approve(); - erc20.set_spender(spenderAddress); - erc20.set_amount(amount.data(), amount.size()); - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), "02f8b00180847735940084b2d05e00830130b9946b175474e89094c44da98b954eedeac495271d0f80b844095ea7b30000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000c080a05a43dda3dc193480ee532a5ed67ba8fbd2e3afb9eee218f4fb955b415d592925a01300e5b5f51c8cd5bf80f018cea3fb347fae589e65355068ac44ffc996313c60"); -} - -TEST(TWAnySignerEthereum, SignERC721Transfer_1559) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasLimit = store(uint256_t(78009)); - auto maxInclusionFeePerGas = store(uint256_t(2000000000)); - auto maxFeePerGas = store(uint256_t(3000000000)); - auto tokenContract = "0x4e45e92ed38f885d39a733c14f1817217a89d425"; - auto fromAddress = "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc"; - auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; - auto tokenId = parse_hex("23c47ee5"); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - input.set_tx_mode(Proto::TransactionMode::Enveloped); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); - input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); - input.set_to_address(tokenContract); - input.set_private_key(key.data(), key.size()); - auto& erc721 = *input.mutable_transaction()->mutable_erc721_transfer(); - erc721.set_from(fromAddress); - erc721.set_to(toAddress); - erc721.set_token_id(tokenId.data(), tokenId.size()); - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), "02f8d00180847735940084b2d05e00830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b86423b872dd000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee5c080a0dbd591d1eac39bad62d7c158d5e1d55e7014d2218998f8980462e2f283f42d4aa05acadb904484a0fb5526a4c64b8addb8aac4f6548f90199e40eb787b79faed4a"); -} - -TEST(TWAnySignerEthereum, SignERC1155Transfer_1559) { - auto chainId = store(uint256_t(1)); - auto nonce = store(uint256_t(0)); - auto gasLimit = store(uint256_t(78009)); - auto maxInclusionFeePerGas = store(uint256_t(2000000000)); - auto maxFeePerGas = store(uint256_t(3000000000)); - auto tokenContract = "0x4e45e92ed38f885d39a733c14f1817217a89d425"; - auto fromAddress = "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc"; - auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; - auto tokenId = parse_hex("23c47ee5"); - auto value = uint256_t(2000000000000000000); - auto valueData = store(value); - auto data = parse_hex("01020304"); - auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); - - Proto::SigningInput input; - input.set_chain_id(chainId.data(), chainId.size()); - input.set_nonce(nonce.data(), nonce.size()); - input.set_tx_mode(Proto::TransactionMode::Enveloped); - input.set_gas_limit(gasLimit.data(), gasLimit.size()); - input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); - input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); - input.set_to_address(tokenContract); - input.set_private_key(key.data(), key.size()); - auto& erc1155 = *input.mutable_transaction()->mutable_erc1155_transfer(); - erc1155.set_from(fromAddress); - erc1155.set_to(toAddress); - erc1155.set_token_id(tokenId.data(), tokenId.size()); - erc1155.set_value(valueData.data(), valueData.size()); - erc1155.set_data(data.data(), data.size()); - - // sign test - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeEthereum); - - ASSERT_EQ(hex(output.encoded()), "02f901500180847735940084b2d05e00830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b8e4f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000c080a0533df41dda5540c57257b7fe89c29cefff0155c333e063220df2bf9680fcc15aa036a844fd20de5a51de96ceaaf078558e87d86426a4a5d4b215ee1fd0fa397f8a"); -} diff --git a/tests/Ethereum/TWCoinTypeTests.cpp b/tests/Ethereum/TWCoinTypeTests.cpp deleted file mode 100644 index 1bdfedd0e25..00000000000 --- a/tests/Ethereum/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWEthereumCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeEthereum)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x9edaf0f7d9c6629c31bbf0471fc07d696c73b566b93783f7e25d8d5d2b62fa4f")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeEthereum, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x5bb497e8d9fe26e92dd1be01e32076c8e024d167")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeEthereum, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeEthereum)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeEthereum)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeEthereum), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeEthereum)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeEthereum)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeEthereum)); - assertStringsEqual(symbol, "ETH"); - assertStringsEqual(txUrl, "https://etherscan.io/tx/0x9edaf0f7d9c6629c31bbf0471fc07d696c73b566b93783f7e25d8d5d2b62fa4f"); - assertStringsEqual(accUrl, "https://etherscan.io/address/0x5bb497e8d9fe26e92dd1be01e32076c8e024d167"); - assertStringsEqual(id, "ethereum"); - assertStringsEqual(name, "Ethereum"); -} diff --git a/tests/EthereumClassic/TWCoinTypeTests.cpp b/tests/EthereumClassic/TWCoinTypeTests.cpp deleted file mode 100644 index 6774a3c294e..00000000000 --- a/tests/EthereumClassic/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWEthereumClassicCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeEthereumClassic)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x66004165d3901819dc22e568931591d2e4287bda54995f4ce2701a12016f5997")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeEthereumClassic, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x9eab4b0fc468a7f5d46228bf5a76cb52370d068d")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeEthereumClassic, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeEthereumClassic)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeEthereumClassic)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeEthereumClassic), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeEthereumClassic)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeEthereumClassic)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeEthereumClassic)); - assertStringsEqual(symbol, "ETC"); - assertStringsEqual(txUrl, "https://blockscout.com/etc/mainnet/tx/0x66004165d3901819dc22e568931591d2e4287bda54995f4ce2701a12016f5997"); - assertStringsEqual(accUrl, "https://blockscout.com/etc/mainnet/address/0x9eab4b0fc468a7f5d46228bf5a76cb52370d068d"); - assertStringsEqual(id, "classic"); - assertStringsEqual(name, "Ethereum Classic"); -} diff --git a/tests/FIO/AddressTests.cpp b/tests/FIO/AddressTests.cpp deleted file mode 100644 index f78e3ae25f2..00000000000 --- a/tests/FIO/AddressTests.cpp +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "FIO/Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include - -#include - -using namespace TW; -using namespace TW::FIO; - -TEST(FIOAddress, ValidateString) { - ASSERT_FALSE(Address::isValid("abc")); - ASSERT_FALSE(Address::isValid("65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF")); - ASSERT_FALSE(Address::isValid("EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjT")); - - ASSERT_TRUE(Address::isValid("FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o")); -} - -TEST(FIOAddress, ValidateData) { - Address address("FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); - EXPECT_EQ(address.bytes.size(), 37); - Data addrData = TW::data(address.bytes.data(), address.bytes.size()); - - EXPECT_EQ(Address::isValid(addrData), true); - - // create invalid data, too short - Data addressDataShort = subData(addrData, 0, addrData.size() - 1); - EXPECT_EQ(Address::isValid(addressDataShort), false); -} - -TEST(FIOAddress, FromString) { - Address addr("FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); - ASSERT_EQ(addr.string(), "FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); - ASSERT_EQ(hex(addr.bytes), "0271195c66ec2799e436757a70cd8431d4b17733a097b18a5f7f1b6b085978ff0f343fc54e"); -} - -TEST(FIOAddress, FromStringInvalid) { - try { - Address address("WRP5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); - } catch (std::invalid_argument&) { - return; // ok - } - ADD_FAILURE() << "Missed expected exeption"; -} - -TEST(FIOAddress, FromPublicKey) { - auto key = PrivateKey(parse_hex("ea8eb60b7e5868e218f248e032769020b4fea5dcfd02f2992861eaf4fb534854")); - auto publicKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); - EXPECT_EQ(hex(publicKey.bytes), "0271195c66ec2799e436757a70cd8431d4b17733a097b18a5f7f1b6b085978ff0f"); - auto address = Address(publicKey); - - ASSERT_EQ(address.string(), "FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); -} - -TEST(FIOAddress, GetPublicKey) { - const auto publicKeyHex = "0271195c66ec2799e436757a70cd8431d4b17733a097b18a5f7f1b6b085978ff0f"; - const PublicKey publicKey(parse_hex(publicKeyHex), TWPublicKeyTypeSECP256k1); - auto address = Address(publicKey); - EXPECT_EQ(hex(address.publicKey().bytes), publicKeyHex); -} diff --git a/tests/FIO/SignerTests.cpp b/tests/FIO/SignerTests.cpp deleted file mode 100644 index cdf1277a11b..00000000000 --- a/tests/FIO/SignerTests.cpp +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "FIO/Actor.h" -#include "FIO/Signer.h" - -#include "HexCoding.h" -#include "Hash.h" -#include "Base58.h" - -#include - -using namespace TW; -using namespace TW::FIO; -using namespace std; - - -TEST(FIOSigner, SignEncode) { - string sig1 = Signer::signatureToBsase58(parse_hex("1f4fccc30bcba876963aef6de584daf7258306c02f4528fe25b116b517de8b349968bdc080cd6bef36f5a46d31a7c01ed0806ad215bb66a94f61e27a895d610983"));; - EXPECT_EQ("SIG_K1_K5hJTPeiV4bDkNR13mf66N2DY5AtVL4NU1iWE4G4dsczY2q68oCcUVxhzFdxjgV2eAeb2jNV1biqtCJ3SaNL8kkNgoZ43H", sig1); -} - -TEST(FIOSigner, SignInternals) { - // 5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf - PrivateKey pk = PrivateKey(parse_hex("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035")); - { - Data pk2 = parse_hex("80"); - append(pk2, pk.bytes); - EXPECT_EQ("5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri", Base58::bitcoin.encodeCheck(pk2)); - } - Data rawData = parse_hex("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd774a26285e19fac10ac5390000000001003056372503a85b0000c6eaa6645232017016f2cc12266c6b00000000a8ed3232bd010f6164616d4066696f746573746e657403034254432a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c7877703730643776034554482a30786365356342366339324461333762624261393142643430443443394434443732344133613846353103424e422a626e6231747333646735346170776c76723968757076326e306a366534367135347a6e6e75736a6b397300000000000000007016f2cc12266c6b0e726577617264734077616c6c6574000000000000000000000000000000000000000000000000000000000000000000"); - Data hash = Hash::sha256(rawData); - EXPECT_EQ("0f3cca0f50da4200b2858f65de1ea4530a9afd9e4bfc0b6b7196e36c25cc7a8b", hex(hash)); - Data sign2 = Signer::signData(pk, rawData); - EXPECT_EQ("1f4ae8d1b993f94d0de4b249d5185481770de0711863ad640b3aac21de598fcc02761c6e5395106bafb7b09aab1c7aa5ac0573dbd821c2d255725391a5105d30d1", hex(sign2)); - - string sigStr = Signer::signatureToBsase58(sign2); - EXPECT_EQ("SIG_K1_K54CA1jmhgWrSdvrNrkokPyvqh7dwsSoQHNU9xgD3Ezf6cJySzhKeUubVRqmpYdnjoP1DM6SorroVAgrCu3qqvJ9coAQ6u", sigStr); - EXPECT_TRUE(Signer::verify(pk.getPublicKey(TWPublicKeyTypeSECP256k1), hash, sign2)); -} - -TEST(FIOSigner, Actor) { - { - const auto addr1 = "FIO6cDpi7vPnvRwMEdXtLnAmFwygaQ8CzD7vqKLBJ2GfgtHBQ4PPy"; - Address addr = Address(addr1); - EXPECT_EQ(addr1, addr.string()); - - uint64_t shortenedKey = Actor::shortenKey(addr.bytes); - EXPECT_EQ(1518832697283783336U, shortenedKey); - string name = Actor::name(shortenedKey); - EXPECT_EQ("2odzomo2v4pec", name); - } - const int n = 4; - const string addrArr[n] = { - "FIO6cDpi7vPnvRwMEdXtLnAmFwygaQ8CzD7vqKLBJ2GfgtHBQ4PPy", - "FIO7uMZoeei5HtXAD24C4yCkpWWbf24bjYtrRNjWdmGCXHZccwuiE", - "FIO7bxrQUTbQ4mqcoefhWPz1aFieN4fA9RQAiozRz7FrUChHZ7Rb8", - "FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf", - }; - const string actorArr[n] = { - "2odzomo2v4pe", - "hhq2g4qgycfb", - "5kmx4qbqlpld", - "qdfejz2a5wpl", - }; - for (int i = 0; i < n; ++i) { - Address addr = Address(addrArr[i]); - EXPECT_EQ(addrArr[i], addr.string()); - - string actor = Actor::actor(addr); - EXPECT_EQ(actorArr[i], actor); - } -} diff --git a/tests/FIO/TWCoinTypeTests.cpp b/tests/FIO/TWCoinTypeTests.cpp deleted file mode 100644 index fcbb7a47bb6..00000000000 --- a/tests/FIO/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWFIOCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeFIO)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("930d1d3cf8988b39b5f64b64e9d61314a3e05a155d9e3505bdf863aab1adddf3")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeFIO, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("f5axfpgffiqz")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeFIO, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeFIO)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeFIO)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeFIO), 9); - ASSERT_EQ(TWBlockchainFIO, TWCoinTypeBlockchain(TWCoinTypeFIO)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeFIO)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeFIO)); - assertStringsEqual(symbol, "FIO"); - assertStringsEqual(txUrl, "https://explorer.fioprotocol.io/transaction/930d1d3cf8988b39b5f64b64e9d61314a3e05a155d9e3505bdf863aab1adddf3"); - assertStringsEqual(accUrl, "https://explorer.fioprotocol.io/account/f5axfpgffiqz"); - assertStringsEqual(id, "fio"); - assertStringsEqual(name, "FIO"); -} diff --git a/tests/Fantom/TWCoinTypeTests.cpp b/tests/Fantom/TWCoinTypeTests.cpp deleted file mode 100644 index acd79755342..00000000000 --- a/tests/Fantom/TWCoinTypeTests.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWFantomCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeFantom)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb0a741d882291951de1fac72e90b9baf886ddb0c9c87658a0c206490dfaa5202")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeFantom, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x9474feb9917b87da6f0d830ba66ee0035835c0d3")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeFantom, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeFantom)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeFantom)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeFantom), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeFantom)); - - assertStringsEqual(symbol, "FTM"); - assertStringsEqual(txUrl, "https://ftmscan.com/tx/0xb0a741d882291951de1fac72e90b9baf886ddb0c9c87658a0c206490dfaa5202"); - assertStringsEqual(accUrl, "https://ftmscan.com/address/0x9474feb9917b87da6f0d830ba66ee0035835c0d3"); - assertStringsEqual(id, "fantom"); - assertStringsEqual(name, "Fantom"); -} diff --git a/tests/Filecoin/AddressTests.cpp b/tests/Filecoin/AddressTests.cpp deleted file mode 100644 index db657eda357..00000000000 --- a/tests/Filecoin/AddressTests.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright © 2017-2020 Trust. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Filecoin/Address.h" -#include "HexCoding.h" - -#include -#include - -using namespace TW; -using namespace TW::Filecoin; - -// clang-format off - -struct address_test { - std::string encoded; - const char* hex; -}; - -static const address_test validAddresses[] = { - // ID addresses - {"f00", "0000"}, - {"f01", "0001"}, - {"f010", "000a"}, - {"f0150", "009601"}, - {"f0499", "00f303"}, - {"f01024", "008008"}, - {"f01729", "00c10d"}, - {"f0999999", "00bf843d"}, - {"f018446744073709551615", "00ffffffffffffffffff01"}, - // secp256k1 addresses - {"f15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq", "01ea0f0ea039b291a0f08fd179e0556a8c3277c0d3"}, - {"f12fiakbhe2gwd5cnmrenekasyn6v5tnaxaqizq6a", "01d1500504e4d1ac3e89ac891a4502586fabd9b417"}, - {"f1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva", "01b06e7a6f0f551de261fe3a6fe182b422ee0bc6b6"}, - {"f1xtwapqc6nh4si2hcwpr3656iotzmlwumogqbuaa", "01bcec07c05e69f92468e2b3e3bf77c874f2c5da8c"}, - {"f1xcbgdhkgkwht3hrrnui3jdopeejsoatkzmoltqy", "01b882619d46558f3d9e316d11b48dcf211327026a"}, - {"f17uoq6tp427uzv7fztkbsnn64iwotfrristwpryy", "01fd1d0f4dfcd7e99afcb99a8326b7dc459d32c628"}, - // Actor addresses - {"f24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as6i", "02e54dea4f9bc5b47d261819826d5e1fbf8bc5503b"}, - {"f25nml2cfbljvn4goqtclhifepvfnicv6g7mfmmvq", "02eb58bd08a15a6ade19d0989674148fa95a8157c6"}, - {"f2nuqrg7vuysaue2pistjjnt3fadsdzvyuatqtfei", "026d21137eb4c4814269e894d296cf6500e43cd714"}, - {"f24dd4ox4c2vpf5vk5wkadgyyn6qtuvgcpxxon64a", "02e0c7c75f82d55e5ed55db28033630df4274a984f"}, - {"f2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y", "02316b4c1ff5d4afb7826ceab5bb0f2c3e0f364053"}, - // BLS addresses - {"f3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss4a","03ad58df696e2d4e91ea86c881e938ba4ea81b395e12797b84b9cf314b9546705e839c7a99d606b247ddb4f9ac7a3414dd"}, - {"f3wmuu6crofhqmm3v4enos73okk2l366ck6yc4owxwbdtkmpk42ohkqxfitcpa57pjdcftql4tojda2poeruwa","03b3294f0a2e29e0c66ebc235d2fedca5697bf784af605c75af608e6a63d5cd38ea85ca8989e0efde9188b382f9372460d"}, - {"f3s2q2hzhkpiknjgmf4zq3ejab2rh62qbndueslmsdzervrhapxr7dftie4kpnpdiv2n6tvkr743ndhrsw6d3a","0396a1a3e4ea7a14d49985e661b22401d44fed402d1d0925b243c923589c0fbc7e32cd04e29ed78d15d37d3aaa3fe6da33"}, - {"f3q22fijmmlckhl56rn5nkyamkph3mcfu5ed6dheq53c244hfmnq2i7efdma3cj5voxenwiummf2ajlsbxc65a","0386b454258c589475f7d16f5aac018a79f6c1169d20fc33921dd8b5ce1cac6c348f90a3603624f6aeb91b64518c2e8095"}, - {"f3u5zgwa4ael3vuocgc5mfgygo4yuqocrntuuhcklf4xzg5tcaqwbyfabxetwtj4tsam3pbhnwghyhijr5mixa","03a7726b038022f75a384617585360cee629070a2d9d28712965e5f26ecc40858382803724ed34f2720336f09db631f074"}, -}; - -static const std::string invalidAddresses[] = { - "", - "f0-1", // Negative :) - "f018446744073709551616", // Greater than max uint64_t - "f15ihq5ibzwki2b4ep2f46avlkr\0zhpqgtga7pdrq", // Embedded NUL - "t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq", // Test net - "a15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq", // Unknown net - "f95ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq", // Unknown address type - // Invalid checksum cases - "f15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7rdrr", - "f24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as66", - "f3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss44", -}; - -TEST(FilecoinAddress, IsValid) { - for (const auto& test : validAddresses) { - ASSERT_TRUE(Address::isValid(test.encoded)) - << "is_valid() != true: " << test.encoded << std::endl; - ASSERT_TRUE(Address::isValid(parse_hex(test.hex))) - << "is_valid() != true: " << test.hex << std::endl; - } - for (const auto& address : invalidAddresses) - ASSERT_FALSE(Address::isValid(address)) << "is_valid() != false: " << address << std::endl; - - ASSERT_FALSE(Address::isValid(parse_hex("00"))) << "Empty varuint"; - ASSERT_FALSE(Address::isValid(parse_hex("00ff"))) << "Short varuint"; - ASSERT_FALSE(Address::isValid(parse_hex("00ff00ff"))) << "Varuint with hole"; - ASSERT_FALSE(Address::isValid(parse_hex("000101"))) << "Long varuint"; - ASSERT_FALSE(Address::isValid(parse_hex("000000"))) << "Long varuint"; - ASSERT_FALSE(Address::isValid(parse_hex("00ffffffffffffffffff02"))) << "Overflow"; -} - -TEST(FilecoinAddress, String) { - for (const auto& test : validAddresses) { - Address a(parse_hex(test.hex)); - ASSERT_EQ(a.string(), test.encoded) << "Address(" << test.hex << ")"; - } -} - -TEST(FilecoinAddress, FromString) { - for (const auto& test : validAddresses) { - Address a(test.encoded); - ASSERT_EQ(hex(a.bytes), test.hex) << "Address(" << test.encoded << ")"; - } -} diff --git a/tests/Filecoin/TWAnySignerTests.cpp b/tests/Filecoin/TWAnySignerTests.cpp deleted file mode 100644 index ee9c85f7f95..00000000000 --- a/tests/Filecoin/TWAnySignerTests.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" -#include - -#include "Data.h" -#include "HexCoding.h" -#include "proto/Filecoin.pb.h" -#include "uint256.h" - -#include - -using namespace TW; -using namespace Filecoin; - -TEST(TWAnySignerFilecoin, Sign) { - Proto::SigningInput input; - auto privateKey = parse_hex("1d969865e189957b9824bd34f26d5cbf357fda1a6d844cbf0c9ab1ed93fa7dbe"); - auto toAddress = - "f3um6uo3qt5of54xjbx3hsxbw5mbsc6auxzrvfxekn5bv3duewqyn2tg5rhrlx73qahzzpkhuj7a34iq7oifsq"; - uint64_t nonce = 2; - // 600 FIL - // auto value = parse_hex("2086ac351052600000"); - auto value = store(uint256_t(600) * uint256_t(1'000'000'000) * uint256_t(1'000'000'000)); - uint64_t gasLimit = 1000; - // auto gasFeeCap = parse_hex("25f273933db5700000"); - auto gasFeeCap = store(uint256_t(700) * uint256_t(1'000'000'000) * uint256_t(1'000'000'000)); - // auto gasPremium = parse_hex("2b5e3af16b18800000"); - auto gasPremium = store(uint256_t(800) * uint256_t(1'000'000'000) * uint256_t(1'000'000'000)); - - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_to(toAddress); - input.set_nonce(nonce); - input.set_value(value.data(), value.size()); - input.set_gas_limit(gasLimit); - input.set_gas_fee_cap(gasFeeCap.data(), gasFeeCap.size()); - input.set_gas_premium(gasPremium.data(), gasPremium.size()); - - auto inputString = input.SerializeAsString(); - auto inputData = WRAPD(TWDataCreateWithBytes((const byte*)inputString.data(), inputString.size())); - - auto outputData = WRAPD(TWAnySignerSign(inputData.get(), TWCoinTypeFilecoin)); - - Proto::SigningOutput output; - output.ParseFromArray(TWDataBytes(outputData.get()), static_cast(TWDataSize(outputData.get()))); - - ASSERT_EQ(output.json(), R"({"Message":{"From":"f1z4a36sc7mfbv4z3qwutblp2flycdui3baffytbq","GasFeeCap":"700000000000000000000","GasLimit":1000,"GasPremium":"800000000000000000000","Nonce":2,"To":"f3um6uo3qt5of54xjbx3hsxbw5mbsc6auxzrvfxekn5bv3duewqyn2tg5rhrlx73qahzzpkhuj7a34iq7oifsq","Value":"600000000000000000000"},"Signature":{"Data":"jMRu+OZ/lfppgmqSfGsntFrRLWZnUg3ZYmJTTRLsVt4V1310vR3VKGJpaE6S4sNvDOE6sEgmN9YmfTkPVK2qMgE=","Type":1}})"); -} - -TEST(TWAnySignerFilecoin, SignJSON) { - auto json = STRING( - R"({ - "to": "f3um6uo3qt5of54xjbx3hsxbw5mbsc6auxzrvfxekn5bv3duewqyn2tg5rhrlx73qahzzpkhuj7a34iq7oifsq", - "nonce": "2", - "value": "IIasNRBSYAAA", - "gasLimit": 1000, - "gasFeeCap": "JfJzkz21cAAA", - "gasPremium": "K1468WsYgAAA" - })"); - auto key = DATA("1d969865e189957b9824bd34f26d5cbf357fda1a6d844cbf0c9ab1ed93fa7dbe"); - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeFilecoin)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeFilecoin)); - assertStringsEqual(result, R"({"Message":{"From":"f1z4a36sc7mfbv4z3qwutblp2flycdui3baffytbq","GasFeeCap":"700000000000000000000","GasLimit":1000,"GasPremium":"800000000000000000000","Nonce":2,"To":"f3um6uo3qt5of54xjbx3hsxbw5mbsc6auxzrvfxekn5bv3duewqyn2tg5rhrlx73qahzzpkhuj7a34iq7oifsq","Value":"600000000000000000000"},"Signature":{"Data":"jMRu+OZ/lfppgmqSfGsntFrRLWZnUg3ZYmJTTRLsVt4V1310vR3VKGJpaE6S4sNvDOE6sEgmN9YmfTkPVK2qMgE=","Type":1}})"); -} diff --git a/tests/Filecoin/TWCoinTypeTests.cpp b/tests/Filecoin/TWCoinTypeTests.cpp deleted file mode 100644 index 9e5ff95f72e..00000000000 --- a/tests/Filecoin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - -TEST(TWFilecoinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeFilecoin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("bafy2bzacedsgjcd6xfhrrymmfrqubb44otlyhvgqkgsh533d5j5hwniiqespm")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeFilecoin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("f1abjxfbp274xpdqcpuaykwkfb43omjotacm2p3za")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeFilecoin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeFilecoin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeFilecoin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeFilecoin), 18); - ASSERT_EQ(TWBlockchainFilecoin, TWCoinTypeBlockchain(TWCoinTypeFilecoin)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeFilecoin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeFilecoin)); - assertStringsEqual(symbol, "FIL"); - assertStringsEqual(txUrl, "https://filfox.info/en/message/bafy2bzacedsgjcd6xfhrrymmfrqubb44otlyhvgqkgsh533d5j5hwniiqespm"); - assertStringsEqual(accUrl, "https://filfox.info/en/address/f1abjxfbp274xpdqcpuaykwkfb43omjotacm2p3za"); - assertStringsEqual(id, "filecoin"); - assertStringsEqual(name, "Filecoin"); -} diff --git a/tests/GoChain/TWCoinTypeTests.cpp b/tests/GoChain/TWCoinTypeTests.cpp deleted file mode 100644 index 7e8b1b75a94..00000000000 --- a/tests/GoChain/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWGoChainCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeGoChain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeGoChain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeGoChain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeGoChain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeGoChain)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeGoChain), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeGoChain)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeGoChain)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeGoChain)); - assertStringsEqual(symbol, "GO"); - assertStringsEqual(txUrl, "https://explorer.gochain.io/tx/t123"); - assertStringsEqual(accUrl, "https://explorer.gochain.io/addr/a12"); - assertStringsEqual(id, "gochain"); - assertStringsEqual(name, "GoChain"); -} diff --git a/tests/Groestlcoin/AddressTests.cpp b/tests/Groestlcoin/AddressTests.cpp deleted file mode 100644 index d3910b6c382..00000000000 --- a/tests/Groestlcoin/AddressTests.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Groestlcoin/Address.h" - -#include "Coin.h" -#include "HDWallet.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Groestlcoin; - -TEST(GroestlcoinAddress, FromPublicKey) { - const auto publicKey = PublicKey(parse_hex("03b85cc59b67c35851eb5060cfc3a759a482254553c5857075c9e247d74d412c91"), TWPublicKeyTypeSECP256k1); - const auto address = Address(publicKey, 36); - ASSERT_EQ(address.string(), "Fj62rBJi8LvbmWu2jzkaUX1NFXLEqDLoZM"); -} - -TEST(GroestlcoinAddress, Valid) { - ASSERT_TRUE(Address::isValid(std::string("Fj62rBJi8LvbmWu2jzkaUX1NFXLEqDLoZM"))); -} - -TEST(GroestlcoinAddress, Invalid) { - ASSERT_FALSE(Address::isValid(std::string("1JAd7XCBzGudGpJQSDSfpmJhiygtLQWaGL"))); // Valid bitcoin address -} - -TEST(GroestlcoinAddress, FromString) { - const auto string = "Fj62rBJi8LvbmWu2jzkaUX1NFXLEqDLoZM"; - const auto address = Address(string); - - ASSERT_EQ(address.string(), string); -} - -TEST(GroestlcoinAddress, Derive) { - const auto mnemonic = "all all all all all all all all all all all all"; - const auto wallet = HDWallet(mnemonic, ""); - const auto path = TW::derivationPath(TWCoinTypeGroestlcoin); - const auto address = TW::deriveAddress(TWCoinTypeGroestlcoin, wallet.getKey(TWCoinTypeGroestlcoin, path)); - ASSERT_EQ(address, "grs1qw4teyraux2s77nhjdwh9ar8rl9dt7zww8r6lne"); -} diff --git a/tests/Groestlcoin/TWCoinTypeTests.cpp b/tests/Groestlcoin/TWCoinTypeTests.cpp deleted file mode 100644 index 8b621892521..00000000000 --- a/tests/Groestlcoin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWGroestlcoinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeGroestlcoin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeGroestlcoin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeGroestlcoin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeGroestlcoin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeGroestlcoin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeGroestlcoin), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeGroestlcoin)); - ASSERT_EQ(0x5, TWCoinTypeP2shPrefix(TWCoinTypeGroestlcoin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeGroestlcoin)); - assertStringsEqual(symbol, "GRS"); - assertStringsEqual(txUrl, "https://blockchair.com/groestlcoin/transaction/t123"); - assertStringsEqual(accUrl, "https://blockchair.com/groestlcoin/address/a12"); - assertStringsEqual(id, "groestlcoin"); - assertStringsEqual(name, "Groestlcoin"); -} diff --git a/tests/HDWallet/HDWalletTests.cpp b/tests/HDWallet/HDWalletTests.cpp deleted file mode 100644 index 1ad89cb705d..00000000000 --- a/tests/HDWallet/HDWalletTests.cpp +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HDWallet.h" -#include "Mnemonic.h" -#include "Bitcoin/Address.h" -#include "Bitcoin/CashAddress.h" -#include "Bitcoin/SegwitAddress.h" -#include "Ethereum/Address.h" -#include "HexCoding.h" -#include "PublicKey.h" -#include "Hash.h" -#include "Base58.h" -#include "Coin.h" -#include "../interface/TWTestUtilities.h" - -#include - -extern std::string TESTS_ROOT; - -namespace TW { - -const auto mnemonic1 = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; -const auto passphrase = "passphrase"; - -TEST(HDWallet, generate) { - { - HDWallet wallet = HDWallet(128, passphrase); - EXPECT_TRUE(Mnemonic::isValid(wallet.getMnemonic())); - EXPECT_EQ(wallet.getPassphrase(), passphrase); - EXPECT_EQ(wallet.getEntropy().size(), 16); - } - { - HDWallet wallet = HDWallet(256, passphrase); - EXPECT_TRUE(Mnemonic::isValid(wallet.getMnemonic())); - EXPECT_EQ(wallet.getPassphrase(), passphrase); - EXPECT_EQ(wallet.getEntropy().size(), 32); - } -} - -TEST(HDWallet, generateInvalid) { - EXPECT_EXCEPTION(HDWallet(64, passphrase), "Invalid strength"); - EXPECT_EXCEPTION(HDWallet(129, passphrase), "Invalid strength"); - EXPECT_EXCEPTION(HDWallet(512, passphrase), "Invalid strength"); -} - -TEST(HDWallet, createFromMnemonic) { - { - HDWallet wallet = HDWallet(mnemonic1, passphrase); - EXPECT_EQ(wallet.getMnemonic(), mnemonic1); - EXPECT_EQ(wallet.getPassphrase(), passphrase); - EXPECT_EQ(hex(wallet.getEntropy()), "ba5821e8c356c05ba5f025d9532fe0f21f65d594"); - EXPECT_EQ(hex(wallet.getSeed()), "143cd5fc27ae46eb423efebc41610473f5e24a80f2ca2e2fa7bf167e537f58f4c68310ae487fce82e25bad29bab2530cf77fd724a5ebfc05a45872773d7ee2d6"); - } - { // empty passphrase - HDWallet wallet = HDWallet(mnemonic1, ""); - EXPECT_EQ(wallet.getMnemonic(), mnemonic1); - EXPECT_EQ(wallet.getPassphrase(), ""); - EXPECT_EQ(hex(wallet.getEntropy()), "ba5821e8c356c05ba5f025d9532fe0f21f65d594"); - EXPECT_EQ(hex(wallet.getSeed()), "354c22aedb9a37407adc61f657a6f00d10ed125efa360215f36c6919abd94d6dbc193a5f9c495e21ee74118661e327e84a5f5f11fa373ec33b80897d4697557d"); - } -} - -TEST(HDWallet, entropyLength_createFromMnemonic) { - { // 12 words - HDWallet wallet = HDWallet("oil oil oil oil oil oil oil oil oil oil oil oil", ""); - EXPECT_EQ(wallet.getEntropy().size(), 16); - EXPECT_EQ(hex(wallet.getEntropy()), "99d33a674ce99d33a674ce99d33a674c"); - } - { // 12 words, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json - HDWallet wallet = HDWallet("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", ""); - EXPECT_EQ(wallet.getEntropy().size(), 16); - EXPECT_EQ(hex(wallet.getEntropy()), "00000000000000000000000000000000"); - } - { // 15 words - HDWallet wallet = HDWallet("history step cheap card humble screen raise seek robot slot coral roof spoil wreck caution", ""); - EXPECT_EQ(wallet.getEntropy().size(), 20); - EXPECT_EQ(hex(wallet.getEntropy()), "6c3aac9b9146ef832c4e18bb3980c0dddd25fc49"); - } - { // 18 words - HDWallet wallet = HDWallet("caught hockey split gun symbol code payment copy broccoli silly shed secret stove tell citizen staff photo high", ""); - EXPECT_EQ(wallet.getEntropy().size(), 24); - EXPECT_EQ(hex(wallet.getEntropy()), "246d8f48b3fdc65a2869801c791715614d6bbd8a56a0a3ad"); - } - { // 21 words - HDWallet wallet = HDWallet("diary shine country alpha bridge coast loan hungry hip media sell crucial swarm share gospel lake visa coin dizzy physical basket", ""); - EXPECT_EQ(wallet.getEntropy().size(), 28); - EXPECT_EQ(hex(wallet.getEntropy()), "3d58bcc40381bc59a0c37a6bf14f0d9a3db78a5933e5f4a5ad00d1f1"); - } - { // 24 words - HDWallet wallet = HDWallet("poet spider smile swift roof pilot subject save hand diet ice universe over brown inspire ugly wide economy symbol shove episode patient plug swamp", ""); - EXPECT_EQ(wallet.getEntropy().size(), 32); - EXPECT_EQ(hex(wallet.getEntropy()), "a73a3732edebbb49f5fdfe68c7b5c0f6e9de3a1d5760faa8c771e384bf4229b6"); - } - { // 24 words, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json - HDWallet wallet = HDWallet("letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", ""); - EXPECT_EQ(wallet.getEntropy().size(), 32); - EXPECT_EQ(hex(wallet.getEntropy()), "8080808080808080808080808080808080808080808080808080808080808080"); - } -} - -TEST(HDWallet, createFromSpanishMnemonic) { - { - EXPECT_EXCEPTION(HDWallet("llanto radical atraer riesgo actuar masa fondo cielo dieta archivo sonrisa mamut", ""), "Invalid mnemonic"); - } - { - HDWallet wallet = HDWallet("llanto radical atraer riesgo actuar masa fondo cielo dieta archivo sonrisa mamut", "", false); - EXPECT_EQ(wallet.getMnemonic(), "llanto radical atraer riesgo actuar masa fondo cielo dieta archivo sonrisa mamut"); - EXPECT_EQ(wallet.getPassphrase(), ""); - EXPECT_EQ(hex(wallet.getEntropy()), ""); - EXPECT_EQ(hex(wallet.getSeed()), "ec8f8703432fc7d32e699ee056e9d84b1435e6a64a6a40ad63dbde11eab189a276ddcec20f3326d3c6ee39cbd018585b104fc3633b801c011063ae4c318fb9b6"); - } -} - -TEST(HDWallet, createFromMnemonicInvalid) { - EXPECT_EXCEPTION(HDWallet("THIS IS AN INVALID MNEMONIC", passphrase), "Invalid mnemonic"); - EXPECT_EXCEPTION(HDWallet("", passphrase), "Invalid mnemonic"); - - EXPECT_EXCEPTION(HDWallet("", passphrase, false), "Invalid mnemonic"); - HDWallet walletUnchecked = HDWallet("THIS IS AN INVALID MNEMONIC", passphrase, false); -} - -TEST(HDWallet, createFromEntropy) { - { - HDWallet wallet = HDWallet(parse_hex("ba5821e8c356c05ba5f025d9532fe0f21f65d594"), passphrase); - EXPECT_EQ(wallet.getMnemonic(), mnemonic1); - } -} - -TEST(HDWallet, createFromEntropyInvalid) { - EXPECT_EXCEPTION(HDWallet(parse_hex(""), passphrase), "Invalid mnemonic data"); - EXPECT_EXCEPTION(HDWallet(parse_hex("123456"), passphrase), "Invalid mnemonic data"); -} - -TEST(HDWallet, recreateFromEntropy) { - { - HDWallet wallet1 = HDWallet(mnemonic1, passphrase); - EXPECT_EQ(wallet1.getMnemonic(), mnemonic1); - EXPECT_EQ(hex(wallet1.getEntropy()), "ba5821e8c356c05ba5f025d9532fe0f21f65d594"); - HDWallet wallet2 = HDWallet(wallet1.getEntropy(), passphrase); - EXPECT_EQ(wallet2.getMnemonic(), wallet1.getMnemonic()); - EXPECT_EQ(wallet2.getEntropy(), wallet1.getEntropy()); - EXPECT_EQ(wallet2.getSeed(), wallet1.getSeed()); - } -} - -TEST(HDWallet, privateKeyFromXPRV) { - const std::string xprv = "xprv9yqEgpMG2KCjvotCxaiMkzmKJpDXz2xZi3yUe4XsURvo9DUbPySW1qRbdeDLiSxZt88hESHUhm2AAe2EqfWM9ucdQzH3xv1HoKoLDqHMK9n"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); - ASSERT_TRUE(privateKey); - auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); - auto address = Bitcoin::CashAddress(publicKey); - - EXPECT_EQ(hex(publicKey.bytes), "025108168f7e5aad52f7381c18d8f880744dbee21dc02c15abe512da0b1cca7e2f"); - EXPECT_EQ(address.string(), "bitcoincash:qp3y0dyg6ya8nt4n3algazn073egswkytqs00z7rz4"); -} - -TEST(HDWallet, privateKeyFromXPRV_Invalid) { - const std::string xprv = "xprv9y0000"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); - ASSERT_FALSE(privateKey); -} - -TEST(HDWallet, privateKeyFromXPRV_InvalidVersion) { - { - // Version bytes (first 4) are invalid, 0x00000000 - const std::string xprv = "11117pE7xwz2GARukXY8Vj2ge4ozfX4HLgy5ztnJXjr5btzJE8EbtPhZwrcPWAodW2aFeYiXkXjSxJYm5QrnhSKFXDgACcFdMqGns9VLqESCq3"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); - ASSERT_FALSE(privateKey); - } - { - // Version bytes (first 4) are invalid, 0xdeadbeef - const std::string xprv = "pGoh3VZXR4mTkT4bfqj4paog12KmHkAWkdLY8HNsZagD1ihVccygLr1ioLBhVQsny47uEh5swP3KScFc4JJrazx1Y7xvzmH2y5AseLgVMwomBTg2"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); - ASSERT_FALSE(privateKey); - } -} - -TEST(HDWallet, privateKeyFromExtended_InvalidCurve) { - // invalid coin & curve, should fail - const std::string xprv = "xprv9yqEgpMG2KCjvotCxaiMkzmKJpDXz2xZi3yUe4XsURvo9DUbPySW1qRbdeDLiSxZt88hESHUhm2AAe2EqfWM9ucdQzH3xv1HoKoLDqHMK9n"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinType(123456), DerivationPath(TWPurposeBIP44, 123456, 0, 0, 0)); - ASSERT_FALSE(privateKey); -} - -TEST(HDWallet, privateKeyFromXPRV_Invalid45) { - // 45th byte is not 0 - const std::string xprv = "xprv9yqEgpMG2KCjvotCxaiMkzmKJpDXz2xZi3yUe4XsURvo9DUbPySW1qRbhw2dJ8QexahgVSfkjxU4FgmN4GLGN3Ui8oLqC6433CeyPUNVHHh"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); - ASSERT_FALSE(privateKey); -} - -TEST(HDWallet, privateKeyFromMptv) { - const std::string mptv = "Mtpv7SkyM349Svcf1WiRtB5hC91ZZkVsGuv3kz1V7tThGxBFBzBLFnw6LpaSvwpHHuy8dAfMBqpBvaSAHzbffvhj2TwfojQxM7Ppm3CzW67AFL5"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(mptv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 4)); - auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); - - auto witness = Data{0x00, 0x14}; - auto keyHash = Hash::sha256ripemd(publicKey.bytes.data(), 33); - witness.insert(witness.end(), keyHash.begin(), keyHash.end()); - - auto prefix = Data{TW::p2shPrefix(TWCoinTypeLitecoin)}; - auto redeemScript = Hash::sha256ripemd(witness.data(), witness.size()); - prefix.insert(prefix.end(), redeemScript.begin(), redeemScript.end()); - - auto address = Bitcoin::Address(prefix); - - EXPECT_EQ(hex(publicKey.bytes), "02c36f9c3051e9cfbb196ecc35311f3ad705ea6798ffbe6b039e70f6bd047e6f2c"); - EXPECT_EQ(address.string(), "MBzcCaoLk9626cLj2UVvcxs6nsVUi39zEy"); -} - -TEST(HDWallet, privateKeyFromZprv) { - const std::string zprv = "zprvAdzGEQ44z4WPLNCRpDaup2RumWxLGgR8PQ9UVsSmJigXsHVDaHK1b6qGM2u9PmxB2Gx264ctAz4yRoN3Xwf1HZmKcn6vmjqwsawF4WqQjfd"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(zprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoin), 0, 0, 5)); - auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); - auto address = Bitcoin::SegwitAddress(publicKey, 0, "bc"); - - EXPECT_EQ(hex(publicKey.bytes), "022dc3f5a3fcfd2d1cc76d0cb386eaad0e30247ba729da0d8847a2713e444fdafa"); - EXPECT_EQ(address.string(), "bc1q5yyq60jepll68hds7exa7kpj20gsvdu0aztw5x"); -} - -TEST(HDWallet, privateKeyFromDGRV) { - const std::string dgpv = "dgpv595jAJYGBLanByCJXRzrWBZFVXdNisfuPmKRDquCQcwBbwKbeR21AtkETf4EpjBsfsK3kDZgMqhcuky1B9PrT5nxiEcjghxpUVYviHXuCmc"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(dgpv, TWCoinTypeDogecoin, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeDogecoin), 0, 0, 1)); - auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); - auto address = Bitcoin::Address(publicKey, TW::p2pkhPrefix(TWCoinTypeDogecoin)); - - EXPECT_EQ(hex(publicKey.bytes), "03eb6bf281990ee074a39c71ed8ce78c486066ac433bcf066dd5eb08f87d3a6c34"); - EXPECT_EQ(address.string(), "D5taDndQJ1fDF3AM1yWavmJY2BgSi17CUv"); -} - -TEST(HDWallet, privateKeyFromXPRVForDGB) { - const std::string xprvForDgb = "xprv9ynLofyuR3uCqCMJADwzBaPnXB53EVe5oLujvPfdvCxae3NzgEpYjZMgcUeS8EUeYfYVLG61ZgPXm9TZWiwBnLVCgd551vCwpXC19hX3mFJ"; - auto privateKey = HDWallet::getPrivateKeyFromExtended(xprvForDgb, TWCoinTypeDigiByte, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeDigiByte), 0, 0, 1)); - auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); - auto address = Bitcoin::Address(publicKey, TW::p2pkhPrefix(TWCoinTypeDigiByte)); - - EXPECT_EQ(hex(publicKey.bytes), "03238a5c541c2cbbf769dbe0fb2a373c22db4da029370767fbe746d59da4de07f1"); - EXPECT_EQ(address.string(), "D9Gv7jWSVsS9Y5q98C79WyfEj6P2iM5Nzs"); -} - -TEST(HDWallet, DeriveWithLeadingZerosEth) { - // Derivation test case with leading zeroes, see https://blog.polychainlabs.com/bitcoin,/bip32,/bip39,/kdf/2021/05/17/inconsistent-bip32-derivations.html - const auto mnemonic = "name dash bleak force moral disease shine response menu rescue more will"; - const auto derivationPath = "m/44'/60'"; - const auto coin = TWCoinTypeEthereum; - auto wallet = HDWallet(mnemonic, ""); - const auto addr = Ethereum::Address(wallet.getKey(coin, DerivationPath(derivationPath)).getPublicKey(TW::publicKeyType(coin))); - EXPECT_EQ(addr.string(), "0x0ba17e928471c64AaEaf3ABfB3900EF4c27b380D"); -} - -static nlohmann::json getVectors() { - const std::string vectorsJsonPath = std::string(TESTS_ROOT) + "/HDWallet/bip39_vectors.json"; - auto vectorsJson = loadJson(vectorsJsonPath)["english"]; - return vectorsJson; -} - -TEST(HDWallet, Bip39Vectors) { - // BIP39 test vectors, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json - const auto passphrase = "TREZOR"; - const auto vectors = getVectors(); - for (const auto& v: vectors) { - const std::string entropy = v[0]; - const std::string mnemonic = v[1]; - const std::string seed = v[2]; - const std::string xprv = v[3]; - { // from mnemonic - HDWallet wallet = HDWallet(mnemonic, passphrase); - EXPECT_EQ(wallet.getMnemonic(), mnemonic); - EXPECT_EQ(wallet.getPassphrase(), passphrase); - EXPECT_EQ(hex(wallet.getEntropy()), entropy); - EXPECT_EQ(hex(wallet.getSeed()), seed); - EXPECT_EQ(wallet.getRootKey(TWCoinTypeBitcoin, TWHDVersionXPRV), xprv); - } - { // from entropy - HDWallet wallet = HDWallet(parse_hex(entropy), passphrase); - EXPECT_EQ(wallet.getMnemonic(), mnemonic); - EXPECT_EQ(wallet.getPassphrase(), passphrase); - EXPECT_EQ(hex(wallet.getEntropy()), entropy); - EXPECT_EQ(hex(wallet.getSeed()), seed); - EXPECT_EQ(wallet.getRootKey(TWCoinTypeBitcoin, TWHDVersionXPRV), xprv); - } - } -} - -} // namespace diff --git a/tests/HECO/TWCoinTypeTests.cpp b/tests/HECO/TWCoinTypeTests.cpp deleted file mode 100644 index 3f2f2ae6b96..00000000000 --- a/tests/HECO/TWCoinTypeTests.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWHECOCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeECOChain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x2e62832615f5b68b3bbcd72046a24260ce47052841c1679841b9c574d3959f13")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeECOChain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xc5a5b3e49e5d06afe163553c942dc59b4e358cf1")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeECOChain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeECOChain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeECOChain)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeECOChain), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeECOChain)); - - assertStringsEqual(symbol, "HT"); - assertStringsEqual(txUrl, "https://hecoinfo.com/tx/0x2e62832615f5b68b3bbcd72046a24260ce47052841c1679841b9c574d3959f13"); - assertStringsEqual(accUrl, "https://hecoinfo.com/address/0xc5a5b3e49e5d06afe163553c942dc59b4e358cf1"); - assertStringsEqual(id, "heco"); - assertStringsEqual(name, "Huobi ECO Chain"); -} diff --git a/tests/Harmony/AddressTests.cpp b/tests/Harmony/AddressTests.cpp deleted file mode 100644 index e5ed260abda..00000000000 --- a/tests/Harmony/AddressTests.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Harmony/Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" - -#include - -using namespace TW; -using namespace TW::Harmony; - -TEST(HarmonyAddress, FromString) { - Address sender; - ASSERT_TRUE(Address::decode("one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe", sender)); - Address receiver; - ASSERT_TRUE(Address::decode("one1tp7xdd9ewwnmyvws96au0e7e7mz6f8hjqr3g3p", receiver)); - - ASSERT_EQ("ed1ebe4fd1f73f86388f231997859ca42c07da5d", hex(sender.getKeyHash())); - ASSERT_EQ("587c66b4b973a7b231d02ebbc7e7d9f6c5a49ef2", hex(receiver.getKeyHash())); -} - -TEST(HarmonyAddress, FromData) { - const auto address = Address(parse_hex("0x587c66b4b973a7b231d02ebbc7e7d9f6c5a49ef2")); - const auto address_2 = Address(parse_hex("0xed1ebe4fd1f73f86388f231997859ca42c07da5d")); - ASSERT_EQ(address.string(), "one1tp7xdd9ewwnmyvws96au0e7e7mz6f8hjqr3g3p"); - ASSERT_EQ(address_2.string(), "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe"); -} - -TEST(HarmonyAddress, InvalidHarmonyAddress) { - ASSERT_FALSE(Address::isValid("one1a50tun737ulcvwy0yvve0pe")); - ASSERT_FALSE(Address::isValid("oe1tp7xdd9ewwnmyvws96au0ee7e7mz6f8hjqr3g3p")); -} - -TEST(HarmonyAddress, FromPrivateKey) { - const auto privateKey = - PrivateKey(parse_hex("e2f88b4974ae763ca1c2db49218802c2e441293a09eaa9ab681779e05d1b7b94")); - const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); - const auto address = Address(publicKey); - ASSERT_EQ(address.string(), "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe"); -} diff --git a/tests/Harmony/TWAnyAddressTests.cpp b/tests/Harmony/TWAnyAddressTests.cpp deleted file mode 100644 index 759eef52c84..00000000000 --- a/tests/Harmony/TWAnyAddressTests.cpp +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" - -#include -#include - -#include - -TEST(HarmonyAnyAddress, Harmony) { - auto string = STRING("one1c8dpswxg2p50znzecnq0peuxlxtcm9je7q7yje"); - auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeHarmony)); - auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); - - EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); - - auto keyHash = WRAPD(TWAnyAddressData(addr.get())); - assertHexEqual(keyHash, "c1da1838c85068f14c59c4c0f0e786f9978d9659"); -} diff --git a/tests/Harmony/TWAnySignerTests.cpp b/tests/Harmony/TWAnySignerTests.cpp deleted file mode 100644 index 08415e5356a..00000000000 --- a/tests/Harmony/TWAnySignerTests.cpp +++ /dev/null @@ -1,74 +0,0 @@ - -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "../interface/TWTestUtilities.h" -#include "proto/Harmony.pb.h" -#include "uint256.h" -#include - -#include - -using namespace TW; -using namespace Harmony; - -static auto TEST_RECEIVER = "one129r9pj3sk0re76f7zs3qz92rggmdgjhtwge62k"; - -static uint256_t LOCAL_NET = 0x2; - -TEST(TWAnySignerHarmony, Sign) { - Proto::SigningInput input; - - auto transactionMessage = input.mutable_transaction_message(); - transactionMessage->set_to_address(TEST_RECEIVER); - const auto privateKey = parse_hex("4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48"); - - input.set_private_key(privateKey.data(), privateKey.size()); - - auto value = store(LOCAL_NET); - input.set_chain_id(value.data(), value.size()); - - value = store(uint256_t("0x1")); - transactionMessage->set_nonce(value.data(), value.size()); - - value = store(uint256_t("")); - transactionMessage->set_gas_price(value.data(), value.size()); - - value = store(uint256_t("0x5208")); - transactionMessage->set_gas_limit(value.data(), value.size()); - - value = store(uint256_t("0x1")); - transactionMessage->set_from_shard_id(value.data(), value.size()); - - value = store(uint256_t("0x0")); - transactionMessage->set_to_shard_id(value.data(), value.size()); - - value = store(uint256_t("0x6bfc8da5ee8220000")); - transactionMessage->set_amount(value.data(), value.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeHarmony); - - auto shouldBeV = "28"; - auto shouldBeR = "84cc200aab11f5e1b2f7ece0d56ec67385ac50cefb6e3dc2a2f3e036ed575a5c"; - auto shouldBeS = "643f18005b790cac8d8e7dc90e6147df0b83874b52db198864694ea28a79e6fc"; - - ASSERT_EQ(hex(output.v()), shouldBeV); - ASSERT_EQ(hex(output.r()), shouldBeR); - ASSERT_EQ(hex(output.s()), shouldBeS); - - ASSERT_EQ(hex(output.encoded()), "f86a0180825208018094514650ca30b3c79f693e14220115434236d44aeb8906bfc8da5ee82200008028a084cc200aab11f5e1b2f7ece0d56ec67385ac50cefb6e3dc2a2f3e036ed575a5ca0643f18005b790cac8d8e7dc90e6147df0b83874b52db198864694ea28a79e6fc"); -} - -TEST(TWAnySignerHarmony, SignJSON) { - auto json = STRING(R"({"chainId":"Ag==","transactionMessage":{"nonce":"AQ==","gasPrice":"AA==","gasLimit":"Ugg=","toAddress":"one129r9pj3sk0re76f7zs3qz92rggmdgjhtwge62k","amount":"Br/I2l7oIgAA","fromShardId":"AQ==","toShardId":"AA=="}})"); - auto key = DATA("4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48"); - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeHarmony)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeHarmony)); - assertStringsEqual(result, "f86a0180825208018094514650ca30b3c79f693e14220115434236d44aeb8906bfc8da5ee82200008028a084cc200aab11f5e1b2f7ece0d56ec67385ac50cefb6e3dc2a2f3e036ed575a5ca0643f18005b790cac8d8e7dc90e6147df0b83874b52db198864694ea28a79e6fc"); -} diff --git a/tests/Harmony/TWCoinTypeTests.cpp b/tests/Harmony/TWCoinTypeTests.cpp deleted file mode 100644 index 5541e8ca94f..00000000000 --- a/tests/Harmony/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWHarmonyCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeHarmony)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeHarmony, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeHarmony, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeHarmony)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeHarmony)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeHarmony), 18); - ASSERT_EQ(TWBlockchainHarmony, TWCoinTypeBlockchain(TWCoinTypeHarmony)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeHarmony)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeHarmony)); - assertStringsEqual(symbol, "ONE"); - assertStringsEqual(txUrl, "https://explorer.harmony.one/#/tx/t123"); - assertStringsEqual(accUrl, "https://explorer.harmony.one/#/address/a12"); - assertStringsEqual(id, "harmony"); - assertStringsEqual(name, "Harmony"); -} diff --git a/tests/HashTests.cpp b/tests/HashTests.cpp deleted file mode 100644 index e665470ddae..00000000000 --- a/tests/HashTests.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Hash.h" -#include "HexCoding.h" - -#include - -using namespace std; -using namespace TW; - -const string brownFox = "The quick brown fox jumps over the lazy dog"; -const string brownFoxDot = "The quick brown fox jumps over the lazy dog."; - -TEST(HashTests, Blake2b) { - auto content = string("Hello world"); - auto hashed = Hash::blake2b(content, 64); - auto result = hex(hashed); - - ASSERT_EQ(result, string("6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183")); -} - -TEST(HashTests, Blake2bPersonal) { - auto personal_string = string("MyApp Files Hash"); - auto personal_data = Data(personal_string.begin(), personal_string.end()); - auto content = string("the same content"); - auto hashed = Hash::blake2b(content, 32, personal_data); - auto result = hex(hashed); - - ASSERT_EQ(result, string("20d9cd024d4fb086aae819a1432dd2466de12947831b75c5a30cf2676095d3b4")); -} - -TEST(HashTests, Sha512_256) { - auto tests = { - make_tuple(string(""), string("c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a")), - make_tuple(brownFox, string("dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d")), - make_tuple(brownFoxDot, string("1546741840f8a492b959d9b8b2344b9b0eb51b004bba35c0aebaac86d45264c3")), - }; - for (auto& test : tests) { - auto hashed = Hash::sha512_256(get<0>(test)); - ASSERT_EQ(hex(hashed), get<1>(test)); - } -} - -TEST(HashTests, Sha1) { - auto tests = { - make_tuple(string(""), string("da39a3ee5e6b4b0d3255bfef95601890afd80709")), - make_tuple(brownFox, string("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")), - make_tuple(brownFoxDot, string("408d94384216f890ff7a0c3528e8bed1e0b01621")), - }; - for (auto& test: tests) { - const auto hash = Hash::sha1(TW::data(get<0>(test))); - EXPECT_EQ(hex(hash), get<1>(test)); - } -} - -TEST(HashTests, hmac256) { - const Data key = parse_hex("531cbfcf12a168faff61af28bf437377397b4bf435ee732cf4ac95761a651f14"); - const Data data = parse_hex("f300888ca4f512cebdc0020ff0f7224c7f896315e90e172bed65d005138f224d"); - const auto expectedHmac = "a7301d5563614e3955750e4480aabf7753f44b4975308aeb8e23c31e114962ab"; - const Data hmac = Hash::hmac256(key, data); - EXPECT_EQ(hex(hmac), expectedHmac); -} - -// More tests in TWHashTests diff --git a/tests/ICON/TWCoinTypeTests.cpp b/tests/ICON/TWCoinTypeTests.cpp deleted file mode 100644 index bffbd0459f0..00000000000 --- a/tests/ICON/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWICONCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeICON)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeICON, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeICON, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeICON)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeICON)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeICON), 18); - ASSERT_EQ(TWBlockchainIcon, TWCoinTypeBlockchain(TWCoinTypeICON)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeICON)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeICON)); - assertStringsEqual(symbol, "ICX"); - assertStringsEqual(txUrl, "https://tracker.icon.foundation/transaction/t123"); - assertStringsEqual(accUrl, "https://tracker.icon.foundation/address/a12"); - assertStringsEqual(id, "icon"); - assertStringsEqual(name, "ICON"); -} diff --git a/tests/Icon/AddressTests.cpp b/tests/Icon/AddressTests.cpp deleted file mode 100644 index f5fbc583a2c..00000000000 --- a/tests/Icon/AddressTests.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Icon/Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" - -#include - -using namespace TW; -using namespace TW::Icon; - -TEST(IconAddress, Validation) { - ASSERT_TRUE(Address::isValid("cx116f042497e5f34268b1b91e742680f84cf4e9f3")); - ASSERT_TRUE(Address::isValid("hx116f042497e5f34268b1b91e742680f84cf4e9f3")); - - ASSERT_FALSE(Address::isValid("abc")); - ASSERT_FALSE(Address::isValid("dshadghasdghsadadsadjsad")); - ASSERT_FALSE(Address::isValid("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")); -} - -TEST(IconAddress, String) { - const auto address = Address("hx116f042497e5f34268b1b91e742680f84cf4e9f3"); - ASSERT_EQ(address.string(), "hx116f042497e5f34268b1b91e742680f84cf4e9f3"); - - const auto address2 = Address("cx116f042497e5f34268b1b91e742680f84cf4e9f3"); - ASSERT_EQ(address2.string(), "cx116f042497e5f34268b1b91e742680f84cf4e9f3"); -} - -TEST(IconAddress, FromPrivateKey) { - const auto privateKey = PrivateKey(parse_hex("94d1a980d5e528067d44bf8a60d646f556e40ca71e17cd4ead2d56f89e4bd20f")); - const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); - const auto address = Address(publicKey, TypeAddress); - - ASSERT_EQ(address.string(), "hx98c0832ca5bd8e8bf355ca9491888aa9725c2c48"); -} diff --git a/tests/Icon/TWAnySignerTests.cpp b/tests/Icon/TWAnySignerTests.cpp deleted file mode 100644 index a1b6e5ce289..00000000000 --- a/tests/Icon/TWAnySignerTests.cpp +++ /dev/null @@ -1,48 +0,0 @@ - -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" -#include -#include "Data.h" -#include "HexCoding.h" -#include "uint256.h" -#include "proto/Icon.pb.h" - -#include - -using namespace TW; -using namespace TW::Icon; - -TEST(TWAnySignerIcon, Sign) { - auto key = parse_hex("2d42994b2f7735bbc93a3e64381864d06747e574aa94655c516f9ad0a74eed79"); - auto input = Proto::SigningInput(); - - input.set_from_address("hxbe258ceb872e08851f1f59694dac2558708ece11"); - input.set_to_address("hx5bfdb090f43a808005ffc27c25b213145e80b7cd"); - - auto value = uint256_t(1000000000000000000); - auto valueData = store(value); - input.set_value(valueData.data(), valueData.size()); - - auto stepLimit = uint256_t("74565"); - auto stepLimitData = store(stepLimit); - input.set_step_limit(stepLimitData.data(), stepLimitData.size()); - - auto one = uint256_t("01"); - auto oneData = store(one); - input.set_network_id(oneData.data(), oneData.size()); - input.set_nonce(oneData.data(), oneData.size()); - - input.set_timestamp(1516942975500598); - input.set_private_key(key.data(), key.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeICON); - - auto expected = std::string("{\"from\":\"hxbe258ceb872e08851f1f59694dac2558708ece11\",\"nid\":\"0x1\",\"nonce\":\"0x1\",\"signature\":\"xR6wKs+IA+7E91bT8966jFKlK5mayutXCvayuSMCrx9KB7670CsWa0B7LQzgsxU0GLXaovlAT2MLs1XuDiSaZQE=\",\"stepLimit\":\"0x12345\",\"timestamp\":\"0x563a6cf330136\",\"to\":\"hx5bfdb090f43a808005ffc27c25b213145e80b7cd\",\"value\":\"0xde0b6b3a7640000\",\"version\":\"0x3\"}"); - ASSERT_EQ(output.encoded(), expected); -} diff --git a/tests/IoTeX/AddressTests.cpp b/tests/IoTeX/AddressTests.cpp deleted file mode 100644 index 60616bf1002..00000000000 --- a/tests/IoTeX/AddressTests.cpp +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include - -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" - -#include "IoTeX/Address.h" - -namespace TW::IoTeX { - -TEST(IoTeXAddress, Invalid) { - ASSERT_FALSE(Address::isValid("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8")); - ASSERT_FALSE(Address::isValid("io187wzp08vnhjpkydnr97qlh8kh0dpkkytfam8j")); - ASSERT_FALSE(Address::isValid("it187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j")); - ASSERT_FALSE(Address::isValid("ko187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j")); - ASSERT_FALSE(Address::isValid("io187wzp18vnhjjpkydnr97qlh8kh0dpkkytfam8j")); - ASSERT_FALSE(Address::isValid("io187wzp08vnhjbpkydnr97qlh8kh0dpkkytfam8j")); - ASSERT_FALSE(Address::isValid("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8k")); -} - -TEST(IoTeXAddress, Valid) { - ASSERT_TRUE(Address::isValid("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j")); -} - -TEST(IoTeXAddress, FromString) { - Address address; - ASSERT_TRUE(Address::decode("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", address)); - ASSERT_EQ("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", address.string()); - - ASSERT_FALSE(Address::decode("io187wzp08vnhjbpkydnr97qlh8kh0dpkkytfam8j", address)); -} - -TEST(IoTeXAddress, FromPrivateKey) { - const auto privateKey = PrivateKey(parse_hex("0806c458b262edd333a191e92f561aff338211ee3e18ab315a074a2d82aa343f")); - const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); - const auto address = Address(publicKey); - ASSERT_EQ(address.string(), "io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j"); - - EXPECT_THROW({ - try - { - const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const auto address = Address(publicKey); - } - catch( const std::invalid_argument& e ) - { - EXPECT_STREQ("address may only be an extended SECP256k1 public key", e.what()); - throw; - } - }, std::invalid_argument); -} - -TEST(IoTeXAddress, FromKeyHash) { - const auto keyHash = parse_hex("3f9c20bcec9de520d88d98cbe07ee7b5ded0dac4"); - const auto address = Address(keyHash); - ASSERT_EQ(address.string(), "io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j"); - - EXPECT_THROW({ - try - { - const auto keyHash = parse_hex("3f9c20bcec9de520d88d98cbe07ee7b5ded0da"); - const auto address = Address(keyHash); - } - catch( const std::invalid_argument& e ) - { - EXPECT_STREQ("invalid address data", e.what()); - throw; - } - }, std::invalid_argument); -} - -} // namespace TW::IoTeX diff --git a/tests/IoTeX/StakingTests.cpp b/tests/IoTeX/StakingTests.cpp deleted file mode 100644 index fd0e074d312..00000000000 --- a/tests/IoTeX/StakingTests.cpp +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Data.h" -#include "HexCoding.h" -#include "IoTeX/Signer.h" -#include "IoTeX/Staking.h" -#include "PrivateKey.h" -#include "proto/IoTeX.pb.h" -#include "../interface/TWTestUtilities.h" -#include -#include - -using namespace TW; -using namespace TW::IoTeX; - -TEST(TWIoTeXStaking, Create) { - std::string IOTEX_STAKING_CANDIDATE = "io19d0p3ah4g8ww9d7kcxfq87yxe7fnr8rpth5shj"; - std::string IOTEX_STAKING_PAYLOAD = "payload"; - std::string IOTEX_STAKING_AMOUNT = "100"; - Data candidate(IOTEX_STAKING_CANDIDATE.begin(), IOTEX_STAKING_CANDIDATE.end()); - Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); - Data amount(IOTEX_STAKING_AMOUNT.begin(), IOTEX_STAKING_AMOUNT.end()); - - auto stake = stakingCreate(candidate, amount, 10000, true, payload); - ASSERT_EQ(hex(stake), "0a29696f313964307033616834673877773964376b63786671383779786537666e723872" - "7074683573686a120331303018904e20012a077061796c6f6164"); -} - -TEST(TWIoTeXStaking, AddDeposit) { - std::string IOTEX_STAKING_PAYLOAD = "payload"; - std::string IOTEX_STAKING_AMOUNT = "10"; - Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); - Data amount(IOTEX_STAKING_AMOUNT.begin(), IOTEX_STAKING_AMOUNT.end()); - - auto stake = stakingAddDeposit(10, amount, payload); - - ASSERT_EQ(hex(stake), "080a120231301a077061796c6f6164"); -} - -TEST(TWIoTeXStaking, Unstake) { - std::string IOTEX_STAKING_PAYLOAD = "payload"; - Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); - - auto stake = stakingUnstake(10, payload); - - ASSERT_EQ(hex(stake), "080a12077061796c6f6164"); -} - -TEST(TWIoTeXStaking, Withdraw) { - std::string IOTEX_STAKING_PAYLOAD = "payload"; - Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); - - auto stake = stakingWithdraw(10, payload); - - ASSERT_EQ(hex(stake), "080a12077061796c6f6164"); -} - -TEST(TWIoTeXStaking, Restake) { - std::string IOTEX_STAKING_PAYLOAD = "payload"; - Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); - - auto stake = stakingRestake(10, 1000, true, payload); - - ASSERT_EQ(hex(stake), "080a10e807180122077061796c6f6164"); -} - -TEST(TWIoTeXStaking, ChangeCandidate) { - std::string IOTEX_STAKING_CANDIDATE = "io1xpq62aw85uqzrccg9y5hnryv8ld2nkpycc3gza"; - std::string IOTEX_STAKING_PAYLOAD = "payload"; - Data candidate(IOTEX_STAKING_CANDIDATE.begin(), IOTEX_STAKING_CANDIDATE.end()); - Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); - - auto stake = stakingChangeCandidate(10, candidate, payload); - - ASSERT_EQ(hex(stake), "080a1229696f3178707136326177383575717a72636367397935686e727976386c" - "64326e6b7079636333677a611a077061796c6f6164"); -} - -TEST(TWIoTeXStaking, Transfer) { - std::string IOTEX_STAKING_CANDIDATE = "io1xpq62aw85uqzrccg9y5hnryv8ld2nkpycc3gza"; - std::string IOTEX_STAKING_PAYLOAD = "payload"; - Data candidate(IOTEX_STAKING_CANDIDATE.begin(), IOTEX_STAKING_CANDIDATE.end()); - Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); - - auto stake = stakingTransfer(10, candidate, payload); - - ASSERT_EQ(hex(stake), "080a1229696f3178707136326177383575717a72636367397935686e727976386c6432" - "6e6b7079636333677a611a077061796c6f6164"); -} - -TEST(TWIoTeXStaking, CandidateRegister) { - std::string IOTEX_STAKING_NAME = "test"; - std::string IOTEX_STAKING_OPERATOR = "io10a298zmzvrt4guq79a9f4x7qedj59y7ery84he"; - std::string IOTEX_STAKING_REWARD = "io13sj9mzpewn25ymheukte4v39hvjdtrfp00mlyv"; - std::string IOTEX_STAKING_OWNER = "io19d0p3ah4g8ww9d7kcxfq87yxe7fnr8rpth5shj"; - std::string IOTEX_STAKING_AMOUNT = "100"; - std::string IOTEX_STAKING_PAYLOAD = "payload"; - Data name(IOTEX_STAKING_NAME.begin(), IOTEX_STAKING_NAME.end()); - Data operatorAddress(IOTEX_STAKING_OPERATOR.begin(), IOTEX_STAKING_OPERATOR.end()); - Data reward(IOTEX_STAKING_REWARD.begin(), IOTEX_STAKING_REWARD.end()); - Data amount(IOTEX_STAKING_AMOUNT.begin(), IOTEX_STAKING_AMOUNT.end()); - Data owner(IOTEX_STAKING_OWNER.begin(), IOTEX_STAKING_OWNER.end()); - Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); - - auto stake = - candidateRegister(name, operatorAddress, reward, amount, 10000, false, owner, payload); - - ASSERT_EQ(hex(stake), - "0a5c0a04746573741229696f3130613239387a6d7a7672743467757137396139663478377165646a3539" - "7937657279383468651a29696f3133736a396d7a7065776e3235796d6865756b74653476333968766a64" - "7472667030306d6c7976120331303018904e2a29696f313964307033616834673877773964376b637866" - "71383779786537666e7238727074683573686a32077061796c6f6164"); -} - -TEST(TWIoTeXStaking, CandidateUpdate) { - std::string IOTEX_STAKING_NAME = "test"; - std::string IOTEX_STAKING_OPERATOR = "io1cl6rl2ev5dfa988qmgzg2x4hfazmp9vn2g66ng"; - std::string IOTEX_STAKING_REWARD = "io1juvx5g063eu4ts832nukp4vgcwk2gnc5cu9ayd"; - Data name(IOTEX_STAKING_NAME.begin(), IOTEX_STAKING_NAME.end()); - Data operatorAddress(IOTEX_STAKING_OPERATOR.begin(), IOTEX_STAKING_OPERATOR.end()); - Data reward(IOTEX_STAKING_REWARD.begin(), IOTEX_STAKING_REWARD.end()); - - auto stake = candidateUpdate(name, operatorAddress, reward); - - ASSERT_EQ(hex(stake), "0a04746573741229696f31636c36726c32657635646661393838716d677a6732783468" - "66617a6d7039766e326736366e671a29696f316a757678356730363365753474733833" - "326e756b7034766763776b32676e6335637539617964"); -} - -Proto::SigningInput createSigningInput() -{ - auto keyhex = parse_hex("cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1"); - auto input = Proto::SigningInput(); - input.set_version(1); - input.set_nonce(0); - input.set_gaslimit(1000000); - input.set_gasprice("10"); - input.set_privatekey(keyhex.data(), keyhex.size()); - return input; -} - -TEST(TWIoTeXStaking, SignAll) { - { // sign stakecreate - auto input = createSigningInput(); - Proto::SigningOutput output; - auto& action = *input.mutable_stakecreate(); - action.set_candidatename("io19d0p3ah4g8ww9d7kcxfq87yxe7fnr8rpth5shj"); - action.set_stakedamount("100"); - action.set_stakedduration(10000); - action.set_autostake(true); - action.set_payload("payload"); - ANY_SIGN(input, TWCoinTypeIoTeX); - ASSERT_EQ(hex(output.encoded()), - "0a4b080118c0843d22023130c2023e0a29696f313964307033616834673877773964376b63786671" - "3837797865" - "37666e7238727074683573686a120331303018904e20012a077061796c6f6164124104755ce6d890" - "3f6b3793bd" - "db4ea5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c" - "0bc76ef30d" - "d6a1038ed9da8daf331a412e8bac421bab88dcd99c26ac8ffbf27f11ee57a41e7d2537891bfed5ae" - "d8e2e026d4" - "6e55d1b856787bc1cd7c1216a6e2534c5b5d1097c3afe8e657aa27cbbb0801"); - // signed action's hash - ASSERT_EQ(hex(output.hash()), - "f1785e47b4200c752bb6518bd18097a41e075438b8c18c9cb00e1ae2f38ce767"); - } - { // sign stakeadddeposit - auto input = createSigningInput(); - Proto::SigningOutput output; - auto& action = *input.mutable_stakeadddeposit(); - action.set_bucketindex(10); - action.set_amount("10"); - action.set_payload("payload"); - ANY_SIGN(input, TWCoinTypeIoTeX); - ASSERT_EQ( - hex(output.encoded()), - "0a1c080118c0843d22023130da020f080a120231301a077061796c6f6164124104755ce6d8903f6b3793" - "bddb4ea5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0b" - "c76ef30dd6a1038ed9da8daf331a41a48ab1feba8181d760de946aefed7d815a89fd9b1ab503d2392bb5" - "5e1bb75eec42dddc8bd642f89accc3a37b3cf15a103a95d66695fdf0647b202869fdd66bcb01"); - // signed action's hash - ASSERT_EQ(hex(output.hash()), - "ca8937d6f224a4e4bf93cb5605581de2d26fb0481e1dfc1eef384ee7ccf94b73"); - } - { // sign stakeunstake - auto input = createSigningInput(); - Proto::SigningOutput output; - auto& action = *input.mutable_stakeunstake(); - action.set_bucketindex(10); - action.set_payload("payload"); - ANY_SIGN(input, TWCoinTypeIoTeX); - ASSERT_EQ( - hex(output.encoded()), - "0a18080118c0843d22023130ca020b080a12077061796c6f6164124104755ce6d8903f6b3793bddb4ea5" - "d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef30d" - "d6a1038ed9da8daf331a4100adee39b48e1d3dbbd65298a57c7889709fc4df39987130da306f6997374a" - "184b7e7c232a42f21e89b06e6e7ceab81303c6b7483152d08d19ac829b22eb81e601"); - // signed action's hash - ASSERT_EQ(hex(output.hash()), - "bed58b64a6c4e959eca60a86f0b2149ce0e1dd527ac5fd26aef725ebf7c22a7d"); - } - { // sign stakewithdraw - auto input = createSigningInput(); - Proto::SigningOutput output; - auto& action = *input.mutable_stakewithdraw(); - action.set_bucketindex(10); - action.set_payload("payload"); - ANY_SIGN(input, TWCoinTypeIoTeX); - ASSERT_EQ( - hex(output.encoded()), - "0a18080118c0843d22023130d2020b080a12077061796c6f6164124104755ce6d8903f6b3793bddb4ea5" - "d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef30d" - "d6a1038ed9da8daf331a4152644d102186be6640d46b517331f3402e24424b0d85129595421d28503d75" - "340b2922f5a0d4f667bbd6f576d9816770286b2ce032ba22eaec3952e24da4756b00"); - // signed action's hash - ASSERT_EQ(hex(output.hash()), - "28049348cf34f1aa927caa250e7a1b08778c44efaf73b565b6fa9abe843871b4"); - } - { // sign stakerestake - auto input = createSigningInput(); - Proto::SigningOutput output; - auto& action = *input.mutable_stakerestake(); - action.set_bucketindex(10); - action.set_stakedduration(1000); - action.set_autostake(true); - action.set_payload("payload"); - ANY_SIGN(input, TWCoinTypeIoTeX); - ASSERT_EQ( - hex(output.encoded()), - "0a1d080118c0843d22023130e20210080a10e807180122077061796c6f6164124104755ce6d8903f6b37" - "93bddb4ea5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c" - "0bc76ef30dd6a1038ed9da8daf331a41e2e763aed5b1fd1a8601de0f0ae34eb05162e34b0389ae3418ee" - "dbf762f64959634a968313a6516dba3a97b34efba4753bbed3a33d409ecbd45ac75007cd8e9101"); - // signed action's hash - ASSERT_EQ(hex(output.hash()), - "8816e8f784a1fce40b54d1cd172bb6976fd9552f1570c73d1d9fcdc5635424a9"); - } - { // sign stakechangecandidate - auto input = createSigningInput(); - Proto::SigningOutput output; - auto& action = *input.mutable_stakechangecandidate(); - action.set_bucketindex(10); - action.set_candidatename("io1xpq62aw85uqzrccg9y5hnryv8ld2nkpycc3gza"); - action.set_payload("payload"); - ANY_SIGN(input, TWCoinTypeIoTeX); - ASSERT_EQ( - hex(output.encoded()), - "0a43080118c0843d22023130ea0236080a1229696f3178707136326177383575717a7263636739793568" - "6e727976386c64326e6b7079636333677a611a077061796c6f6164124104755ce6d8903f6b3793bddb4e" - "a5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef3" - "0dd6a1038ed9da8daf331a41d519eb3747163b945b862989b7e82a7f8468001e9683757cb88d5ddd95f8" - "1895047429e858bd48f7d59a88bfec92de231d216293aeba1e4fbe11461d9c9fc99801"); - // signed action's hash - ASSERT_EQ(hex(output.hash()), - "186526b5b9fe74e25beb52c83c41780a69108160bef2ddaf3bffb9f1f1e5e73a"); - } - { // sign staketransfer - auto input = createSigningInput(); - Proto::SigningOutput output; - auto& action = *input.mutable_staketransferownership(); - action.set_bucketindex(10); - action.set_voteraddress("io1xpq62aw85uqzrccg9y5hnryv8ld2nkpycc3gza"); - action.set_payload("payload"); - ANY_SIGN(input, TWCoinTypeIoTeX); - ASSERT_EQ( - hex(output.encoded()), - "0a43080118c0843d22023130f20236080a1229696f3178707136326177383575717a7263636739793568" - "6e727976386c64326e6b7079636333677a611a077061796c6f6164124104755ce6d8903f6b3793bddb4e" - "a5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef3" - "0dd6a1038ed9da8daf331a41fa26db427ab87a56a129196c1604f2e22c4dd2a1f99b2217bc916260757d" - "00093d9e6dccdf53e3b0b64e41a69d71c238fbf9281625164694a74dfbeba075d0ce01"); - // signed action's hash - ASSERT_EQ(hex(output.hash()), - "74b2e1d6a09ba5d1298fa422d5850991ae516865077282196295a38f93c78b85"); - } - { // sign candidateupdate - auto input = createSigningInput(); - Proto::SigningOutput output; - auto& action = *input.mutable_candidateupdate(); - action.set_name("test"); - action.set_operatoraddress("io1cl6rl2ev5dfa988qmgzg2x4hfazmp9vn2g66ng"); - action.set_rewardaddress("io1juvx5g063eu4ts832nukp4vgcwk2gnc5cu9ayd"); - ANY_SIGN(input, TWCoinTypeIoTeX); - ASSERT_EQ( - hex(output.encoded()), - "0a69080118c0843d2202313082035c0a04746573741229696f31636c36726c3265763564666139383871" - "6d677a673278346866617a6d7039766e326736366e671a29696f316a7576783567303633657534747338" - "33326e756b7034766763776b32676e6335637539617964124104755ce6d8903f6b3793bddb4ea5d3589d" - "637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef30dd6a103" - "8ed9da8daf331a4101885c9c6684a4a8f2f5bf11f8326f27be48658f292e8f55ec8a11a604bb0c563a11" - "ebf12d995ca1c152e00f8e0f0edf288db711aa10dbdfd5b7d73b4a28e1f701"); - // signed action's hash - ASSERT_EQ(hex(output.hash()), - "ca1a28f0e9a58ffc67037cc75066dbdd8e024aa2b2e416e4d6ce16c3d86282e5"); - } - { // sign candidateregister - auto input = createSigningInput(); - Proto::SigningOutput output; - input.set_gasprice("1000"); - auto& cbi = *input.mutable_candidateregister()->mutable_candidate(); - cbi.set_name("test"); - cbi.set_operatoraddress("io10a298zmzvrt4guq79a9f4x7qedj59y7ery84he"); - cbi.set_rewardaddress("io13sj9mzpewn25ymheukte4v39hvjdtrfp00mlyv"); - - auto& action = *input.mutable_candidateregister(); - action.set_stakedamount("100"); - action.set_stakedduration(10000); - action.set_autostake(false); - action.set_owneraddress("io19d0p3ah4g8ww9d7kcxfq87yxe7fnr8rpth5shj"); - action.set_payload("payload"); - ANY_SIGN(input, TWCoinTypeIoTeX); - ASSERT_EQ(hex(output.encoded()), - "0aaa01080118c0843d220431303030fa029a010a5c0a04746573741229696f3130613239387a6d7a" - "7672743467" - "757137396139663478377165646a35397937657279383468651a29696f3133736a396d7a7065776e" - "3235796d68" - "65756b74653476333968766a647472667030306d6c7976120331303018904e2a29696f3139643070" - "3361683467" - "3877773964376b63786671383779786537666e7238727074683573686a32077061796c6f61641241" - "04755ce6d8" - "903f6b3793bddb4ea5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99" - "a5c1335b58" - "3c0bc76ef30dd6a1038ed9da8daf331a417819b5bcb635e3577acc8ca757f2c3d6afa451c2b6ff8a" - "9179b141ac" - "68e2c50305679e5d09d288da6f0fb52876a86c74deab6a5247edc6d371de5c2f121e159400"); - // signed action's hash - ASSERT_EQ(hex(output.hash()), - "35f53a536e014b32b85df50483ef04849b80ad60635b3b1979c5ba1096b65237"); - } -} diff --git a/tests/IoTeX/TWAnySignerTests.cpp b/tests/IoTeX/TWAnySignerTests.cpp deleted file mode 100644 index 3bcb630ece4..00000000000 --- a/tests/IoTeX/TWAnySignerTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ - -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" -#include -#include "HexCoding.h" -#include "proto/IoTeX.pb.h" - -#include - -using namespace TW; -using namespace TW::IoTeX; - -TEST(TWAnySignerIoTeX, Sign) { - auto key = parse_hex("68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748"); - Proto::SigningInput input; - input.set_version(1); - input.set_nonce(1); - input.set_gaslimit(1); - input.set_gasprice("1"); - input.set_privatekey(key.data(), key.size()); - auto& transfer = *input.mutable_transfer(); - transfer.set_amount("1"); - transfer.set_recipient("io1e2nqsyt7fkpzs5x7zf2uk0jj72teu5n6aku3tr"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeIoTeX); - - ASSERT_EQ(hex(output.encoded()), "0a39080110011801220131522e0a01311229696f3165326e7173797437666b707a733578377a6632756b306a6a3732746575356e36616b75337472124104fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5cf762f20459c9100eb9d4d7597f5817bf21e10b53a0120b9ec1ba5cddfdcb669b1a41ec9757ae6c9009315830faaab250b6db0e9535b00843277f596ae0b2b9efc0bd4e14138c056fc4cdfa285d13dd618052b3d1cb7a3f554722005a2941bfede96601"); -} diff --git a/tests/IoTeX/TWCoinTypeTests.cpp b/tests/IoTeX/TWCoinTypeTests.cpp deleted file mode 100644 index ce42df73442..00000000000 --- a/tests/IoTeX/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWIoTeXCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeIoTeX)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeIoTeX, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeIoTeX, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeIoTeX)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeIoTeX)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeIoTeX), 18); - ASSERT_EQ(TWBlockchainIoTeX, TWCoinTypeBlockchain(TWCoinTypeIoTeX)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeIoTeX)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeIoTeX)); - assertStringsEqual(symbol, "IOTX"); - assertStringsEqual(txUrl, "https://iotexscan.io/action/t123"); - assertStringsEqual(accUrl, "https://iotexscan.io/address/a12"); - assertStringsEqual(id, "iotex"); - assertStringsEqual(name, "IoTeX"); -} diff --git a/tests/Kava/TWCoinTypeTests.cpp b/tests/Kava/TWCoinTypeTests.cpp deleted file mode 100644 index 1e2ed1b261b..00000000000 --- a/tests/Kava/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWKavaCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeKava)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("2988DF83FCBFAA38179D583A96734CBD071541D6768221BB23111BC8136D5E6A")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeKava, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("kava1jf9aaj9myrzsnmpdr7twecnaftzmku2mdpy2a7")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeKava, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeKava)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeKava)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeKava), 6); - ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeKava)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeKava)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeKava)); - assertStringsEqual(symbol, "KAVA"); - assertStringsEqual(txUrl, "https://kava.mintscan.io/txs/2988DF83FCBFAA38179D583A96734CBD071541D6768221BB23111BC8136D5E6A"); - assertStringsEqual(accUrl, "https://kava.mintscan.io/account/kava1jf9aaj9myrzsnmpdr7twecnaftzmku2mdpy2a7"); - assertStringsEqual(id, "kava"); - assertStringsEqual(name, "Kava"); -} diff --git a/tests/Keystore/StoredKeyTests.cpp b/tests/Keystore/StoredKeyTests.cpp deleted file mode 100644 index 84266df0ccd..00000000000 --- a/tests/Keystore/StoredKeyTests.cpp +++ /dev/null @@ -1,386 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Keystore/StoredKey.h" - -#include "Coin.h" -#include "HexCoding.h" -#include "Data.h" -#include "PrivateKey.h" -#include "Mnemonic.h" - -#include -#include - -extern std::string TESTS_ROOT; - -namespace TW::Keystore { - -using namespace std; - -const auto passwordString = "password"; -const auto password = TW::data(string(passwordString)); -const auto mnemonic = "team engine square letter hero song dizzy scrub tornado fabric divert saddle"; -const TWCoinType coinTypeBc = TWCoinTypeBitcoin; -const TWCoinType coinTypeBnb = TWCoinTypeBinance; -const TWCoinType coinTypeBsc = TWCoinTypeSmartChain; -const TWCoinType coinTypeEth = TWCoinTypeEthereum; -const TWCoinType coinTypeBscLegacy = TWCoinTypeSmartChainLegacy; - -TEST(StoredKey, CreateWithMnemonic) { - auto key = StoredKey::createWithMnemonic("name", password, mnemonic); - EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); - const Data& mnemo2Data = key.payload.decrypt(password); - EXPECT_EQ(string(mnemo2Data.begin(), mnemo2Data.end()), string(mnemonic)); - EXPECT_EQ(key.accounts.size(), 0); - EXPECT_EQ(key.wallet(password).getMnemonic(), string(mnemonic)); - - const auto json = key.json(); - EXPECT_EQ(json["name"], "name"); - EXPECT_EQ(json["type"], "mnemonic"); - EXPECT_EQ(json["version"], 3); -} - -TEST(StoredKey, CreateWithMnemonicInvalid) { - try { - auto key = StoredKey::createWithMnemonic("name", password, "_THIS_IS_NOT_A_VALID_MNEMONIC_"); - } catch (std::invalid_argument&) { - // expedcted exception OK - return; - } - FAIL() << "Missing excpected excpetion"; -} - -TEST(StoredKey, CreateWithMnemonicRandom) { - const auto key = StoredKey::createWithMnemonicRandom("name", password); - EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); - // random mnemonic: check only length and validity - const Data& mnemo2Data = key.payload.decrypt(password); - EXPECT_TRUE(mnemo2Data.size() >= 36); - EXPECT_TRUE(Mnemonic::isValid(string(mnemo2Data.begin(), mnemo2Data.end()))); - EXPECT_EQ(key.accounts.size(), 0); -} - -TEST(StoredKey, CreateWithMnemonicAddDefaultAddress) { - auto key = StoredKey::createWithMnemonicAddDefaultAddress("name", password, mnemonic, coinTypeBc); - EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); - const Data& mnemo2Data = key.payload.decrypt(password); - EXPECT_EQ(string(mnemo2Data.begin(), mnemo2Data.end()), string(mnemonic)); - EXPECT_EQ(key.accounts.size(), 1); - EXPECT_EQ(key.accounts[0].coin, coinTypeBc); - EXPECT_EQ(key.accounts[0].address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); - EXPECT_EQ(hex(key.privateKey(coinTypeBc, password).bytes), "d2568511baea8dc347f14c4e0479eb8ebe29eb5f664ed796e755896250ffd11f"); -} - -TEST(StoredKey, CreateWithPrivateKeyAddDefaultAddress) { - const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); - auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", password, coinTypeBc, privateKey); - EXPECT_EQ(key.type, StoredKeyType::privateKey); - EXPECT_EQ(key.accounts.size(), 1); - EXPECT_EQ(key.accounts[0].coin, coinTypeBc); - EXPECT_EQ(key.accounts[0].address, "bc1q375sq4kl2nv0mlmup3vm8znn4eqwu7mt6hkwhr"); - EXPECT_EQ(hex(key.privateKey(coinTypeBc, password).bytes), hex(privateKey)); - - const auto json = key.json(); - EXPECT_EQ(json["name"], "name"); - EXPECT_EQ(json["type"], "private-key"); - EXPECT_EQ(json["version"], 3); -} - -TEST(StoredKey, CreateWithPrivateKeyAddDefaultAddressInvalid) { - try { - const auto privateKeyInvalid = parse_hex("0001020304"); - auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", password, coinTypeBc, privateKeyInvalid); - } catch (std::invalid_argument&) { - // expected exception ok - return; - } - FAIL() << "Missing expected exception"; -} - -TEST(StoredKey, AccountGetCreate) { - auto key = StoredKey::createWithMnemonic("name", password, mnemonic); - EXPECT_EQ(key.accounts.size(), 0); - - // not exists - EXPECT_FALSE(key.account(coinTypeBc).has_value()); - EXPECT_EQ(key.accounts.size(), 0); - - auto wallet = key.wallet(password); - // not exists, wallet null, not create - EXPECT_FALSE(key.account(coinTypeBc, nullptr).has_value()); - EXPECT_EQ(key.accounts.size(), 0); - - // not exists, wallet nonnull, create - std::optional acc3 = key.account(coinTypeBc, &wallet); - EXPECT_TRUE(acc3.has_value()); - EXPECT_EQ(acc3->coin, coinTypeBc); - EXPECT_EQ(key.accounts.size(), 1); - - // exists - std::optional acc4 = key.account(coinTypeBc); - EXPECT_TRUE(acc4.has_value()); - EXPECT_EQ(acc4->coin, coinTypeBc); - EXPECT_EQ(key.accounts.size(), 1); - - // exists, wallet nonnull, not create - std::optional acc5 = key.account(coinTypeBc, &wallet); - EXPECT_TRUE(acc5.has_value()); - EXPECT_EQ(acc5->coin, coinTypeBc); - EXPECT_EQ(key.accounts.size(), 1); - - // exists, wallet null, not create - std::optional acc6 = key.account(coinTypeBc, nullptr); - EXPECT_TRUE(acc6.has_value()); - EXPECT_EQ(acc6->coin, coinTypeBc); - EXPECT_EQ(key.accounts.size(), 1); -} - -TEST(StoredKey, AccountGetDoesntChange) { - auto key = StoredKey::createWithMnemonic("name", password, mnemonic); - auto wallet = key.wallet(password); - EXPECT_EQ(key.accounts.size(), 0); - - vector coins = {coinTypeBc, coinTypeEth, coinTypeBnb}; - // retrieve multiple accounts, which will be created - vector accounts; - for (auto coin: coins) { - std::optional account = key.account(coin, &wallet); - accounts.push_back(*account); - - // check - ASSERT_TRUE(account.has_value()); - EXPECT_EQ(account->coin, coin); - } - - // Check again; make sure returned references don't change - for (auto i = 0; i < accounts.size(); ++i) { - // check - EXPECT_EQ(accounts[i].coin, coins[i]); - } -} - -TEST(StoredKey, AddRemoveAccount) { - auto key = StoredKey::createWithMnemonic("name", password, mnemonic); - EXPECT_EQ(key.accounts.size(), 0); - - { - const auto derivationPath = DerivationPath("m/84'/0'/0'/0/0"); - key.addAccount("bc1qaucw06s3agez8tyyk4zj9kt0q2934e3mcewdpf", coinTypeBc, derivationPath, "zpub6rxtad3SPT1C5GUDjPiKQ5oJN5DBeMbdUR7LrdYt12VbU7TBSpGUkdLvfVYGuj1N5edkDoZ3bu1fdN1HprQYfCBdsSH5CaAAygHGsanwtTe"); - EXPECT_EQ(key.accounts.size(), 1); - } - { - const auto derivationPath = DerivationPath("m/714'/0'/0'/0/0"); - key.addAccount("bnb1utrnnjym7ustgw7pgyvtmnxay4qmt3ahh276nu", coinTypeBnb, derivationPath, ""); - key.addAccount("0x23b02dC8f67eD6cF8DCa47935791954286ffe7c9", coinTypeBsc, derivationPath, ""); - EXPECT_EQ(key.accounts.size(), 3); - } - { - const auto derivationPath = DerivationPath("m/60'/0'/0'/0/0"); - key.addAccount("0xC0d97f61A84A0708225F15d54978D628Fe2C5E62", coinTypeEth, derivationPath, ""); - key.addAccount("0xC0d97f61A84A0708225F15d54978D628Fe2C5E62", coinTypeBscLegacy, derivationPath, ""); - EXPECT_EQ(key.accounts.size(), 5); - } - - key.removeAccount(coinTypeBc); - key.removeAccount(coinTypeBnb); - key.removeAccount(coinTypeBsc); - key.removeAccount(coinTypeEth); - key.removeAccount(coinTypeBscLegacy); - EXPECT_EQ(key.accounts.size(), 0); -} - -TEST(StoredKey, FixAddress) { - { - auto key = StoredKey::createWithMnemonic("name", password, mnemonic); - key.fixAddresses(password); - } - { - const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); - auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", password, coinTypeBc, privateKey); - key.fixAddresses(password); - } -} - -TEST(StoredKey, WalletInvalid) { - const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); - auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", password, coinTypeBc, privateKey); - try { - auto wallet = key.wallet(password); - } catch (std::invalid_argument&) { - // expected exception ok - return; - } - FAIL() << "Missing expected exception"; -} - -TEST(StoredKey, LoadNonexistent) { - ASSERT_THROW(StoredKey::load(TESTS_ROOT + "/Keystore/Data/nonexistent.json"), invalid_argument); -} - -TEST(StoredKey, LoadLegacyPrivateKey) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/legacy-private-key.json"); - EXPECT_EQ(key.type, StoredKeyType::privateKey); - EXPECT_EQ(key.id, "3051ca7d-3d36-4a4a-acc2-09e9083732b0"); - EXPECT_EQ(key.accounts[0].coin, TWCoinTypeEthereum); - EXPECT_EQ(hex(key.payload.decrypt(TW::data("testpassword"))), "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"); -} - -TEST(StoredKey, LoadLivepeerKey) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/livepeer.json"); - EXPECT_EQ(key.type, StoredKeyType::privateKey); - EXPECT_EQ(key.id, "70ea3601-ee21-4e94-a7e4-66255a987d22"); - EXPECT_EQ(key.accounts[0].coin, TWCoinTypeEthereum); - EXPECT_EQ(hex(key.payload.decrypt(TW::data("Radchenko"))), "09b4379d9a41a71d94ee36357bccb4d77b45e7fd9307e2c0f673dd54c0558c73"); -} - -TEST(StoredKey, LoadPBKDF2Key) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/pbkdf2.json"); - EXPECT_EQ(key.type, StoredKeyType::privateKey); - EXPECT_EQ(key.id, "3198bc9c-6672-5ab3-d995-4942343ae5b6"); - - const auto& payload = key.payload; - ASSERT_TRUE(payload.kdfParams.which() == 1); - EXPECT_EQ(boost::get(payload.kdfParams).desiredKeyLength, 32); - EXPECT_EQ(boost::get(payload.kdfParams).iterations, 262144); - EXPECT_EQ(hex(boost::get(payload.kdfParams).salt), "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"); - - EXPECT_EQ(hex(payload.decrypt(TW::data("testpassword"))), "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"); -} - -TEST(StoredKey, LoadLegacyMnemonic) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/legacy-mnemonic.json"); - EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); - EXPECT_EQ(key.id, "629aad29-0b22-488e-a0e7-b4219d4f311c"); - - const auto data = key.payload.decrypt(password); - const auto mnemonic = string(reinterpret_cast(data.data())); - EXPECT_EQ(mnemonic, "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn back"); - - EXPECT_EQ(key.accounts[0].coin, TWCoinTypeEthereum); - EXPECT_EQ(key.accounts[0].derivationPath.string(), "m/44'/60'/0'/0/0"); - EXPECT_EQ(key.accounts[0].address, ""); - EXPECT_EQ(key.accounts[1].coin, coinTypeBc); - EXPECT_EQ(key.accounts[1].derivationPath.string(), "m/84'/0'/0'/0/0"); - EXPECT_EQ(key.accounts[1].address, ""); - EXPECT_EQ(key.accounts[1].extendedPublicKey, "zpub6r97AegwVxVbJeuDAWP5KQgX5y4Q6KyFUrsFQRn8yzSXrnmpwg1ZKHSWwECR1Kiqgr4h93WN5kdS48KC6hVFniuZHqVFXjULZZkCwurqyPn"); -} - -TEST(StoredKey, LoadFromWeb3j) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/web3j.json"); - EXPECT_EQ(key.type, StoredKeyType::privateKey); - EXPECT_EQ(key.id, "86066d8c-8dba-4d81-afd4-934e2a2b72a2"); - const auto password = parse_hex("2d6eefbfbd4622efbfbdefbfbd516718efbfbdefbfbdefbfbdefbfbd59efbfbd30efbfbdefbfbd3a4348efbfbd2aefbfbdefbfbd49efbfbd27efbfbd0638efbfbdefbfbdefbfbd4cefbfbd6befbfbdefbfbd6defbfbdefbfbd63efbfbd5aefbfbd61262b70efbfbdefbfbdefbfbdefbfbdefbfbdc7aa373163417cefbfbdefbfbdefbfbd44efbfbdefbfbd1d10efbfbdefbfbdefbfbd61dc9e5b124befbfbd11efbfbdefbfbd2fefbfbdefbfbd3d7c574868efbfbdefbfbdefbfbd37043b7b5c1a436471592f02efbfbd18efbfbdefbfbd2befbfbdefbfbd7218efbfbd6a68efbfbdcb8e5f3328773ec48174efbfbd67efbfbdefbfbdefbfbdefbfbdefbfbd2a31efbfbd7f60efbfbdd884efbfbd57efbfbd25efbfbd590459efbfbd37efbfbd2bdca20fefbfbdefbfbdefbfbdefbfbd39450113efbfbdefbfbdefbfbd454671efbfbdefbfbdd49fefbfbd47efbfbdefbfbdefbfbdefbfbd00efbfbdefbfbdefbfbdefbfbd05203f4c17712defbfbd7bd1bbdc967902efbfbdc98a77efbfbd707a36efbfbd12efbfbdefbfbd57c78cefbfbdefbfbdefbfbd10efbfbdefbfbdefbfbde1a1bb08efbfbdefbfbd26efbfbdefbfbd58efbfbdefbfbdc4b1efbfbd295fefbfbd0eefbfbdefbfbdefbfbd0e6eefbfbd"); - const auto data = key.payload.decrypt(password); - EXPECT_EQ(hex(data), "043c5429c7872502531708ec0d821c711691402caf37ef7ba78a8c506f10653b"); -} - -TEST(StoredKey, ReadWallet) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key.json"); - - EXPECT_EQ(key.type, StoredKeyType::privateKey); - EXPECT_EQ(key.id, "e13b209c-3b2f-4327-bab0-3bef2e51630d"); - EXPECT_EQ(key.name, "Test Account"); - - const auto header = key.payload; - - EXPECT_EQ(header.cipher, "aes-128-ctr"); - EXPECT_EQ(hex(header.encrypted), "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c"); - EXPECT_EQ(hex(header.mac), "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"); - EXPECT_EQ(hex(header.cipherParams.iv), "83dbcc02d8ccb40e466191a123791e0e"); - - ASSERT_TRUE(header.kdfParams.which() == 0); - EXPECT_EQ(boost::get(header.kdfParams).desiredKeyLength, 32); - EXPECT_EQ(boost::get(header.kdfParams).n, 262144); - EXPECT_EQ(boost::get(header.kdfParams).p, 8); - EXPECT_EQ(boost::get(header.kdfParams).r, 1); - EXPECT_EQ(hex(boost::get(header.kdfParams).salt), "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"); -} - -TEST(StoredKey, ReadMyEtherWallet) { - ASSERT_NO_THROW(StoredKey::load(TESTS_ROOT + "/Keystore/Data/myetherwallet.uu")); -} - -TEST(StoredKey, InvalidPassword) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key.json"); - - ASSERT_THROW(key.payload.decrypt(password), DecryptionError); -} - -TEST(StoredKey, EmptyAccounts) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/empty-accounts.json"); - - ASSERT_NO_THROW(key.payload.decrypt(TW::data("testpassword"))); -} - -TEST(StoredKey, Decrypt) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key.json"); - const auto privateKey = key.payload.decrypt(TW::data("testpassword")); - - EXPECT_EQ(hex(privateKey), "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"); -} - -TEST(StoredKey, CreateWallet) { - const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); - const auto key = StoredKey::createWithPrivateKey("name", password, privateKey); - const auto decrypted = key.payload.decrypt(password); - - EXPECT_EQ(hex(decrypted), hex(privateKey)); -} - -TEST(StoredKey, CreateAccounts) { - string mnemonicPhrase = "team engine square letter hero song dizzy scrub tornado fabric divert saddle"; - auto key = StoredKey::createWithMnemonic("name", password, mnemonicPhrase); - const auto wallet = key.wallet(password); - - EXPECT_EQ(key.account(TWCoinTypeEthereum, &wallet)->address, "0x494f60cb6Ac2c8F5E1393aD9FdBdF4Ad589507F7"); - EXPECT_EQ(key.account(TWCoinTypeEthereum, &wallet)->extendedPublicKey, ""); - - EXPECT_EQ(key.account(coinTypeBc, &wallet)->address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); - EXPECT_EQ(key.account(coinTypeBc, &wallet)->extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); -} - -TEST(StoredKey, DecodingEthereumAddress) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key.json"); - - EXPECT_EQ(key.accounts[0].address, "0x008AeEda4D805471dF9b2A5B0f38A0C3bCBA786b"); -} - -TEST(StoredKey, DecodingBitcoinAddress) { - const auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/key_bitcoin.json"); - - EXPECT_EQ(key.accounts[0].address, "3PWazDi9n1Hfyq9gXFxDxzADNL8RNYyK2y"); -} - -TEST(StoredKey, RemoveAccount) { - auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/legacy-mnemonic.json"); - EXPECT_EQ(key.accounts.size(), 2); - key.removeAccount(TWCoinTypeEthereum); - EXPECT_EQ(key.accounts.size(), 1); - EXPECT_EQ(key.accounts[0].coin, coinTypeBc); -} - -TEST(StoredKey, MissingAddress) { - auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/missing-address.json"); - EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); - - const auto wallet = key.wallet(password); - EXPECT_EQ(wallet.getMnemonic(), "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"); - EXPECT_TRUE(Mnemonic::isValid(wallet.getMnemonic())); - - key.fixAddresses(password); - - EXPECT_EQ(key.account(TWCoinTypeEthereum, nullptr)->address, "0xA3Dcd899C0f3832DFDFed9479a9d828c6A4EB2A7"); - EXPECT_EQ(key.account(coinTypeBc, nullptr)->address, "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); -} - -TEST(StoredKey, EtherWalletAddressNo0x) { - auto key = StoredKey::load(TESTS_ROOT + "/Keystore/Data/ethereum-wallet-address-no-0x.json"); - key.fixAddresses(TW::data("15748c4e3dca6ae2110535576ab0c398cb79d985707c68ee6c9f9df9d421dd53")); - EXPECT_EQ(key.account(TWCoinTypeEthereum, nullptr)->address, "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309"); -} - -} // namespace TW::Keystore diff --git a/tests/Kin/TWCoinTypeTests.cpp b/tests/Kin/TWCoinTypeTests.cpp deleted file mode 100644 index 9120ef0e2f9..00000000000 --- a/tests/Kin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWKinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeKin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeKin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeKin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeKin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeKin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeKin), 5); - ASSERT_EQ(TWBlockchainStellar, TWCoinTypeBlockchain(TWCoinTypeKin)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeKin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeKin)); - assertStringsEqual(symbol, "KIN"); - assertStringsEqual(txUrl, "https://www.kin.org/blockchainInfoPage/?&dataType=public&header=Transaction&id=t123"); - assertStringsEqual(accUrl, "https://www.kin.org/blockchainAccount/?&dataType=public&header=accountID&id=a12"); - assertStringsEqual(id, "kin"); - assertStringsEqual(name, "Kin"); -} diff --git a/tests/Kusama/AddressTests.cpp b/tests/Kusama/AddressTests.cpp deleted file mode 100644 index 7a7229a1167..00000000000 --- a/tests/Kusama/AddressTests.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Kusama/Address.h" -#include "PublicKey.h" -#include "PrivateKey.h" -#include -#include - -using namespace TW; -using namespace TW::Kusama; - -TEST(KusamaAddress, Validation) { - // Substrate ed25519 - ASSERT_FALSE(Address::isValid("5FqqU2rytGPhcwQosKRtW1E3ha6BJKAjHgtcodh71dSyXhoZ")); - // Polkadot ed25519 - ASSERT_FALSE(Address::isValid("15AeCjMpcSt3Fwa47jJBd7JzQ395Kr2cuyF5Zp4UBf1g9ony")); - // Polkadot sr25519 - ASSERT_FALSE(Address::isValid("15AeCjMpcSt3Fwa47jJBd7JzQ395Kr2cuyF5Zp4UBf1g9ony")); - // Bitcoin - ASSERT_FALSE(Address::isValid("1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA")); - - // Kusama ed25519 - ASSERT_TRUE(Address::isValid("FHKAe66mnbk8ke8zVWE9hFVFrJN1mprFPVmD5rrevotkcDZ")); - // Kusama secp256k1 - ASSERT_TRUE(Address::isValid("FxQFyTorsjVsjjMyjdgq8w5vGx8LiA1qhWbRYcFijxKKchx")); - // Kusama sr25519 - ASSERT_TRUE(Address::isValid("EJ5UJ12GShfh7EWrcNZFLiYU79oogdtXFUuDDZzk7Wb2vCe")); -} - -TEST(KusamaAddress, FromPrivateKey) { - // from subkey: tiny escape drive pupil flavor endless love walk gadget match filter luxury - auto privateKey = PrivateKey(parse_hex("0xa21981f3bb990c40837df44df639541ff57c5e600f9eb4ac00ed8d1f718364e5")); - auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(address.string(), "CeVXtoU4py9e7F6upfM2ZarVave299TjcdaTSxhDDZrYgnM"); -} - -TEST(KusamaAddress, FromPublicKey) { - auto publicKey = PublicKey(parse_hex("0x032eb287017c5cde2940b5dd062d413f9d09f8aa44723fc80bf46b96c81ac23d"), TWPublicKeyTypeED25519); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "CeVXtoU4py9e7F6upfM2ZarVave299TjcdaTSxhDDZrYgnM"); -} - -TEST(KusamaAddress, FromString) { - auto address = Address("CeVXtoU4py9e7F6upfM2ZarVave299TjcdaTSxhDDZrYgnM"); - ASSERT_EQ(address.string(), "CeVXtoU4py9e7F6upfM2ZarVave299TjcdaTSxhDDZrYgnM"); -} diff --git a/tests/Kusama/SignerTests.cpp b/tests/Kusama/SignerTests.cpp deleted file mode 100644 index f9ef03d127f..00000000000 --- a/tests/Kusama/SignerTests.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Polkadot/Signer.h" -#include "Polkadot/Extrinsic.h" -#include "SS58Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "proto/Polkadot.pb.h" -#include "uint256.h" - -#include -#include - - -namespace TW::Polkadot { - extern PrivateKey privateKey; - extern PublicKey toPublicKey; - auto genesisHashKSM = parse_hex("b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe"); - -TEST(PolkadotSigner, SignTransferKSM) { - auto blockHash = parse_hex("4955dd4813f3e91ef3fd5a825b928af2fc50a71380085f753ccef00bb1582891"); - auto toAddress = SS58Address(toPublicKey, TWSS58AddressTypeKusama); - - auto input = Proto::SigningInput(); - input.set_block_hash(blockHash.data(), blockHash.size()); - input.set_genesis_hash(genesisHashKSM.data(), genesisHashKSM.size()); - input.set_nonce(0); - input.set_spec_version(2019); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::KUSAMA); - input.set_transaction_version(2); - - auto balanceCall = input.mutable_balance_call(); - auto& transfer = *balanceCall->mutable_transfer(); - auto value = store(uint256_t(12345)); - transfer.set_to_address(toAddress.string()); - transfer.set_value(value.data(), value.size()); - - auto extrinsic = Extrinsic(input); - auto preimage = extrinsic.encodePayload(); - auto output = Signer::sign(input); - - ASSERT_EQ(hex(preimage), "04008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c0000000e307000002000000b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe4955dd4813f3e91ef3fd5a825b928af2fc50a71380085f753ccef00bb1582891"); - ASSERT_EQ(hex(output.encoded()), "25028488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee000765cfa76cfe19499f4f19ef7dc4527652ec5b2e6b5ecfaf68725dafd48ae2694ad52e61f44152a544784e847de10ddb2c56bee4406574dcbcfdb5e5d35b6d0300000004008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c0"); -} - -} // namespace diff --git a/tests/Kusama/TWAnySignerTests.cpp b/tests/Kusama/TWAnySignerTests.cpp deleted file mode 100644 index d3bf8fd5adb..00000000000 --- a/tests/Kusama/TWAnySignerTests.cpp +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "proto/Polkadot.pb.h" -#include "uint256.h" -#include "../interface/TWTestUtilities.h" -#include - -#include - -using namespace TW; -using namespace TW::Polkadot; - -TEST(TWAnySignerKusama, Sign) { - auto key = parse_hex("0x8cdc538e96f460da9d639afc5c226f477ce98684d77fb31e88db74c1f1dd86b2"); - auto genesisHash = parse_hex("0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe"); - - Proto::SigningInput input; - input.set_block_hash(genesisHash.data(), genesisHash.size()); - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - input.set_nonce(1); - input.set_spec_version(2019); - input.set_private_key(key.data(), key.size()); - input.set_network(Proto::Network::KUSAMA); - input.set_transaction_version(2); - - auto balanceCall = input.mutable_balance_call(); - auto& transfer = *balanceCall->mutable_transfer(); - auto value = store(uint256_t(10000000000)); - transfer.set_to_address("CtwdfrhECFs3FpvCGoiE4hwRC4UsSiM8WL899HjRdQbfYZY"); - transfer.set_value(value.data(), value.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeKusama); - - ASSERT_EQ(hex(output.encoded()), "350284f41296779fd61a5bed6c2f506cc6c9ea93d6aeb357b9c69717193f434ba24ae700cd78b46eff36c433e642d7e9830805aab4f43eef70067ef32c8b2a294c510673a841c5f8a6e8900c03be40cfa475ae53e6f8aa61961563cb7cc0fa169ef9630d00040004000e33fdfb980e4499e5c3576e742a563b6a4fc0f6f598b1917fd7a6fe393ffc720700e40b5402"); -} diff --git a/tests/Kusama/TWCoinTypeTests.cpp b/tests/Kusama/TWCoinTypeTests.cpp deleted file mode 100644 index 929af6a5bc1..00000000000 --- a/tests/Kusama/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWKusamaCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeKusama)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xcbe0c2e2851c1245bedaae4d52f06eaa6b4784b786bea2f0bff11af7715973dd")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeKusama, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("DbCNECPna3k6MXFWWNZa5jGsuWycqEE6zcUxZYkxhVofrFk")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeKusama, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeKusama)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeKusama)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeKusama), 12); - ASSERT_EQ(TWBlockchainPolkadot, TWCoinTypeBlockchain(TWCoinTypeKusama)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeKusama)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeKusama)); - assertStringsEqual(symbol, "KSM"); - assertStringsEqual(txUrl, "https://kusama.subscan.io/extrinsic/0xcbe0c2e2851c1245bedaae4d52f06eaa6b4784b786bea2f0bff11af7715973dd"); - assertStringsEqual(accUrl, "https://kusama.subscan.io/account/DbCNECPna3k6MXFWWNZa5jGsuWycqEE6zcUxZYkxhVofrFk"); - assertStringsEqual(id, "kusama"); - assertStringsEqual(name, "Kusama"); -} diff --git a/tests/Litecoin/TWCoinTypeTests.cpp b/tests/Litecoin/TWCoinTypeTests.cpp deleted file mode 100644 index 3d96044505d..00000000000 --- a/tests/Litecoin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWLitecoinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeLitecoin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeLitecoin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeLitecoin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeLitecoin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeLitecoin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeLitecoin), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeLitecoin)); - ASSERT_EQ(0x32, TWCoinTypeP2shPrefix(TWCoinTypeLitecoin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeLitecoin)); - assertStringsEqual(symbol, "LTC"); - assertStringsEqual(txUrl, "https://blockchair.com/litecoin/transaction/t123"); - assertStringsEqual(accUrl, "https://blockchair.com/litecoin/address/a12"); - assertStringsEqual(id, "litecoin"); - assertStringsEqual(name, "Litecoin"); -} diff --git a/tests/Monacoin/TWCoinTypeTests.cpp b/tests/Monacoin/TWCoinTypeTests.cpp deleted file mode 100644 index e539e3924e2..00000000000 --- a/tests/Monacoin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWMonacoinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeMonacoin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeMonacoin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeMonacoin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeMonacoin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeMonacoin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeMonacoin), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeMonacoin)); - ASSERT_EQ(0x37, TWCoinTypeP2shPrefix(TWCoinTypeMonacoin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeMonacoin)); - assertStringsEqual(symbol, "MONA"); - assertStringsEqual(txUrl, "https://blockbook.electrum-mona.org/tx/t123"); - assertStringsEqual(accUrl, "https://blockbook.electrum-mona.org/address/a12"); - assertStringsEqual(id, "monacoin"); - assertStringsEqual(name, "Monacoin"); -} diff --git a/tests/NEAR/AccountTests.cpp b/tests/NEAR/AccountTests.cpp deleted file mode 100644 index 36d38993b78..00000000000 --- a/tests/NEAR/AccountTests.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "NEAR/Account.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::NEAR; - -TEST(NEARAccount, Validation) { - ASSERT_FALSE(Account::isValid("a")); - ASSERT_FALSE(Account::isValid("!?:")); - ASSERT_FALSE(Account::isValid("11111111111111111111111111111111222222222222222222222222222222223")); - - ASSERT_TRUE(Account::isValid("9902c136629fc630416e50d4f2fef6aff867ea7e.lockup.near")); - ASSERT_TRUE(Account::isValid("app_1.alice.near")); - ASSERT_TRUE(Account::isValid("test-trust.vlad.near")); - ASSERT_TRUE(Account::isValid("deadbeef")); -} diff --git a/tests/NEAR/AddressTests.cpp b/tests/NEAR/AddressTests.cpp deleted file mode 100644 index 1da6ca22089..00000000000 --- a/tests/NEAR/AddressTests.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "NEAR/Address.h" -#include "Base58.h" -#include "PrivateKey.h" -#include - -#include - -using namespace TW; -using namespace TW::NEAR; - -TEST(NEARAddress, Validation) { - ASSERT_FALSE(Address::isValid("abc")); - ASSERT_FALSE(Address::isValid("65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF")); - ASSERT_FALSE(Address::isValid("EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjT")); - - ASSERT_TRUE(Address::isValid("NEAR2758Nk7CMUcxTwXdjVdSxNEidiZQWMZN3USJzj76q5ia3v2v2v")); - ASSERT_TRUE(Address::isValid("917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d")); -} - -TEST(NEARAddress, FromString) { - ASSERT_EQ( - Address("NEAR2758Nk7CMUcxTwXdjVdSxNEidiZQWMZN3USJzj76q5ia3v2v2v").string(), - "917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d" - ); - ASSERT_EQ( - Address("9685af3fe2dc231e5069ccff8ec6950eb961d42ebb9116a8ab9c0d38f9e45249").string(), - "9685af3fe2dc231e5069ccff8ec6950eb961d42ebb9116a8ab9c0d38f9e45249" - ); -} - -TEST(NEARAddress, FromPrivateKey) { - auto fullKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); - auto key = PrivateKey(Data(fullKey.begin(), fullKey.begin() + 32)); - auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); - auto address = Address(publicKey); - - ASSERT_EQ(address.string(), "917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d"); -} diff --git a/tests/NEAR/SerializationTests.cpp b/tests/NEAR/SerializationTests.cpp deleted file mode 100644 index 9d637de3293..00000000000 --- a/tests/NEAR/SerializationTests.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Base58.h" -#include "proto/NEAR.pb.h" -#include "NEAR/Serialization.h" - -#include -#include - -namespace TW::NEAR { - -TEST(NEARSerialization, SerializeTransferTransaction) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); - - auto input = Proto::SigningInput(); - input.set_signer_id("test.near"); - input.set_nonce(1); - input.set_receiver_id("whatever.near"); - - input.add_actions(); - auto& transfer = *input.mutable_actions(0)->mutable_transfer(); - Data deposit(16, 0); - deposit[0] = 1; - transfer.set_deposit(deposit.data(), deposit.size()); - - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); - input.set_block_hash(blockHash.data(), blockHash.size()); - - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); - input.set_private_key(privateKey.data(), 32); - - auto serialized = transactionData(input); - auto serializedHex = hex(serialized); - - ASSERT_EQ(serializedHex, "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000"); -} - -} diff --git a/tests/NEAR/SignerTests.cpp b/tests/NEAR/SignerTests.cpp deleted file mode 100644 index a0b0da08920..00000000000 --- a/tests/NEAR/SignerTests.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Base64.h" -#include "Base58.h" -#include "proto/NEAR.pb.h" -#include "NEAR/Signer.h" - -#include -#include - -namespace TW::NEAR { - -TEST(NEARSigner, SignTx) { - auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); - - auto input = Proto::SigningInput(); - input.set_signer_id("test.near"); - input.set_nonce(1); - input.set_receiver_id("whatever.near"); - - input.add_actions(); - auto& transfer = *input.mutable_actions(0)->mutable_transfer(); - Data deposit(16, 0); - deposit[0] = 1; - // uint128_t / little endian byte order - transfer.set_deposit(deposit.data(), deposit.size()); - - auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); - input.set_block_hash(blockHash.data(), blockHash.size()); - - auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); - input.set_private_key(privateKey.data(), 32); - - auto output = Signer::sign(std::move(input)); - - auto signed_transaction = output.signed_transaction(); - auto outputInBase64 = Base64::encode(Data(signed_transaction.begin(), signed_transaction.end())); - - ASSERT_EQ(outputInBase64, "CQAAAHRlc3QubmVhcgCRez0mjUtY9/7BsVC9aNab4+5dTMOYVeNBU4Rlu3eGDQEAAAAAAAAADQAAAHdoYXRldmVyLm5lYXIPpHP9JpAd8pa+atxMxN800EDvokNSJLaYaRDmMML+9gEAAAADAQAAAAAAAAAAAAAAAAAAAACWmoMzIYbul1Xkg5MlUlgG4Ymj0tK7S0dg6URD6X4cTyLe7vAFmo6XExAO2m4ZFE2n6KDvflObIHCLodjQIb0B"); -} - -} diff --git a/tests/NEAR/TWAnySignerTests.cpp b/tests/NEAR/TWAnySignerTests.cpp deleted file mode 100644 index 49326b7cb4a..00000000000 --- a/tests/NEAR/TWAnySignerTests.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "proto/NEAR.pb.h" -#include "../interface/TWTestUtilities.h" -#include -#include - -namespace TW::NEAR { - -TEST(TWAnySignerNEAR, Sign) { - - auto privateKey = parse_hex("8737b99bf16fba78e1e753e23ba00c4b5423ac9c45d9b9caae9a519434786568"); - auto blockHash = parse_hex("0fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6"); - // uint128_t / little endian byte order - auto deposit = parse_hex("01000000000000000000000000000000"); - - Proto::SigningInput input; - input.set_signer_id("test.near"); - input.set_nonce(1); - input.set_receiver_id("whatever.near"); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_block_hash(blockHash.data(), blockHash.size()); - - auto& action = *input.add_actions(); - auto& transfer = *action.mutable_transfer(); - transfer.set_deposit(deposit.data(), deposit.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeNEAR); - - ASSERT_EQ(hex(output.signed_transaction()), "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef601000000030100000000000000000000000000000000969a83332186ee9755e4839325525806e189a3d2d2bb4b4760e94443e97e1c4f22deeef0059a8e9713100eda6e19144da7e8a0ef7e539b20708ba1d8d021bd01"); -} - -} // namespace TW::NEAR diff --git a/tests/NEAR/TWCoinTypeTests.cpp b/tests/NEAR/TWCoinTypeTests.cpp deleted file mode 100644 index e5b5951a368..00000000000 --- a/tests/NEAR/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWNEARCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNEAR)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("FPQAMaVnvFHNwNBJWnTttXfdJhp5FvMGGDJEesB8gvbL")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNEAR, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("test-trust.vlad.near")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNEAR, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNEAR)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNEAR)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNEAR), 24); - ASSERT_EQ(TWBlockchainNEAR, TWCoinTypeBlockchain(TWCoinTypeNEAR)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNEAR)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNEAR)); - assertStringsEqual(symbol, "NEAR"); - assertStringsEqual(txUrl, "https://explorer.near.org/transactions/FPQAMaVnvFHNwNBJWnTttXfdJhp5FvMGGDJEesB8gvbL"); - assertStringsEqual(accUrl, "https://explorer.near.org/accounts/test-trust.vlad.near"); - assertStringsEqual(id, "near"); - assertStringsEqual(name, "NEAR"); -} diff --git a/tests/NEO/AddressTests.cpp b/tests/NEO/AddressTests.cpp deleted file mode 100644 index fa6adb863c0..00000000000 --- a/tests/NEO/AddressTests.cpp +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "PublicKey.h" -#include "HexCoding.h" -#include "NEO/Address.h" -#include "NEO/Signer.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::NEO; - -TEST(NEOAddress, FromPublicKey) { - const auto publicKey = PublicKey(parse_hex("0222b2277d039d67f4197a638dd5a1d99c290b17aa8c4a16ccee5165fe612de66a"), TWPublicKeyTypeSECP256k1); - const auto address = Address(publicKey); - EXPECT_EQ(string("AKmrAHRD9ZDUnu4m3vWWonpsojo4vgSuqp"), address.string()); -} - -TEST(NEOAddress, FromString) { - string neoAddress = "AXkgwcMJTy9wTAXHsbyhauxh7t2Tt31MmC"; - const auto address = Address(neoAddress); - EXPECT_EQ(address.string(), neoAddress); -} - -TEST(NEOAddress, isValid) { - string neoAddress = "AQAsqiyHS4SSVWZ4CmMmnCxWg7vJ84GEj4"; - string bitcoinAddress = "1Ma2DrB78K7jmAwaomqZNRMCvgQrNjE2QC"; - - EXPECT_TRUE(Address::isValid(neoAddress)); - EXPECT_FALSE(Address::isValid(bitcoinAddress)); -} - -TEST(NEOAddress, validation) { - EXPECT_FALSE(Address::isValid("abc")); - EXPECT_FALSE(Address::isValid("abeb60f3e94c1b9a09f33669435e7ef12eacd")); - EXPECT_FALSE(Address::isValid("abcb60f3e94c9b9a09f33669435e7ef1beaedads")); - EXPECT_TRUE(Address::isValid("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD")); -} - -TEST(NEOAddress, fromPubKey) { - auto address = Address(PublicKey(parse_hex("031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"), TWPublicKeyTypeNIST256p1)); - EXPECT_EQ("AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5", address.string()); -} - -TEST(NEOAddress, fromString) { - auto b58Str = "AYTxeseHT5khTWhtWX1pFFP1mbQrd4q1zz"; - auto address = Address(b58Str); - EXPECT_EQ(b58Str, address.string()); - auto errB58Str = "AATxeseHT5khTWhtWX1pFFP1mbQrd4q1zz"; - EXPECT_THROW(new Address(errB58Str), std::invalid_argument); -} - - -TEST(NEOAddress, Valid) { - ASSERT_TRUE(Address::isValid("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD")); -} - -TEST(NEOAddress, Invalid) { - ASSERT_FALSE(Address::isValid("ANDfjwrUr54515515155WKRMyxFwvVwnZD")); -} - -TEST(NEOAddress, FromPrivateKey) { - auto key = PrivateKey(parse_hex("0x2A9EAB0FEC93CD94FA0A209AC5604602C1F0105FB02EAB398E17B4517C2FFBAB")); - auto publicKey = key.getPublicKey(TWPublicKeyTypeNIST256p1); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "AQCSMB3oSDA1dHPn6GXN6KB4NHmdo1fX41"); -} - diff --git a/tests/NEO/CoinReferenceTests.cpp b/tests/NEO/CoinReferenceTests.cpp deleted file mode 100644 index 9c707d5bdeb..00000000000 --- a/tests/NEO/CoinReferenceTests.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "uint256.h" -#include "HexCoding.h" -#include "NEO/CoinReference.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::NEO; - -TEST(NEOCoinReference, Serialize) { - auto coinReference = CoinReference(); - string prevHash = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"; - coinReference.prevHash = load(parse_hex(prevHash)); - coinReference.prevIndex = 1; - EXPECT_EQ(prevHash + "0100", hex(coinReference.serialize())); -} - -TEST(NEOCoinReference, Deserialize) { - auto coinReference = CoinReference(); - coinReference.deserialize(parse_hex("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a0100")); - EXPECT_EQ("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a", hex(store(coinReference.prevHash))); - EXPECT_EQ(1, coinReference.prevIndex); -} diff --git a/tests/NEO/SignerTests.cpp b/tests/NEO/SignerTests.cpp deleted file mode 100644 index 6f3b10fd913..00000000000 --- a/tests/NEO/SignerTests.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "PublicKey.h" -#include "HexCoding.h" -#include "NEO/Address.h" -#include "NEO/Signer.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::NEO; - -TEST(NEOSigner, FromPublicPrivateKey) { - auto hexPrvKey = "4646464646464646464646464646464646464646464646464646464646464646"; - auto hexPubKey = "031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"; - auto signer = Signer(PrivateKey(parse_hex(hexPrvKey))); - auto prvKey = signer.getPrivateKey(); - auto pubKey = signer.getPublicKey(); - - EXPECT_EQ(hexPrvKey, hex(prvKey.bytes)); - EXPECT_EQ(hexPubKey, hex(pubKey.bytes)); - - auto address = signer.getAddress(); - EXPECT_TRUE(Address::isValid(address.string())); - - EXPECT_EQ(Address(pubKey), address); -} - -TEST(NEOSigner, SigningData) { - auto signer = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); - auto verScript = "ba7908ddfe5a1177f2c9d3fa1d3dc71c9c289a3325b3bdd977e20c50136959ed02d1411efa5e8b897d970ef7e2325e6c0a3fdee4eb421223f0d86e455879a9ad"; - auto invocationScript = string("401642b3d538e138f34b32330e381a7fe3f5151fcf958f2030991e72e2e25043143e4a1ebd239634efba279c96fa0ab04a15aa15179d73a7ef5a886ac8a06af484401642b3d538e138f34b32330e381a7fe3f5151fcf958f2030991e72e2e25043143e4a1ebd239634efba279c96fa0ab04a15aa15179d73a7ef5a886ac8a06af484401642b3d538e138f34b32330e381a7fe3f5151fcf958f2030991e72e2e25043143e4a1ebd239634efba279c96fa0ab04a15aa15179d73a7ef5a886ac8a06af484"); - invocationScript = string(invocationScript.rbegin(), invocationScript.rend()); - - EXPECT_EQ(verScript, hex(signer.sign(parse_hex(invocationScript)))); -} - -TEST(NEOAccount, validity) { - auto hexPrvKey = "4646464646464646464646464646464646464646464646464646464646464646"; - auto hexPubKey = "031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"; - auto signer = Signer(PrivateKey(parse_hex(hexPrvKey))); - auto prvKey = signer.getPrivateKey(); - auto pubKey = signer.getPublicKey(); - EXPECT_EQ(hexPrvKey, hex(prvKey.bytes)); - EXPECT_EQ(hexPubKey, hex(pubKey.bytes)); -} - -TEST(NEOSigner, SigningTransaction) { - auto signer = Signer(PrivateKey(parse_hex("F18B2F726000E86B4950EBEA7BFF151F69635951BC4A31C44F28EE6AF7AEC128"))); - auto transaction = Transaction(); - transaction.type = TransactionType::TT_ContractTransaction; - transaction.version = 0x00; - - CoinReference coin; - coin.prevHash = load(parse_hex("9c85b39cd5677e2bfd6bf8a711e8da93a2f1d172b2a52c6ca87757a4bccc24de")); //reverse hash - coin.prevIndex = (uint16_t) 1; - transaction.inInputs.push_back(coin); - - { - TransactionOutput out; - out.assetId = load(parse_hex("9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5")); - out.value = (int64_t) 1 * 100000000; - auto scriptHash = TW::NEO::Address("Ad9A1xPbuA5YBFr1XPznDwBwQzdckAjCev").toScriptHash(); - out.scriptHash = load(scriptHash); - transaction.outputs.push_back(out); - } - - { - TransactionOutput out; - out.assetId = load(parse_hex("9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5")); - out.value = (int64_t) 892 * 100000000; - auto scriptHash = TW::NEO::Address("AdtSLMBqACP4jv8tRWwyweXGpyGG46eMXV").toScriptHash(); - out.scriptHash = load(scriptHash); - transaction.outputs.push_back(out); - } - signer.sign(transaction); - auto signedTx = transaction.serialize(); - EXPECT_EQ(hex(signedTx), "800000019c85b39cd5677e2bfd6bf8a711e8da93a2f1d172b2a52c6ca87757a4bccc24de0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500e1f50500000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500fcbbc414000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac0141405046619c8e20e1fdeec92ce95f3019f6e7cc057294eb16b2d5e55c105bf32eb27e1fc01c1858576228f1fef8c0945a8ad69688e52a4ed19f5b85f5eff7e961d7232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); -} diff --git a/tests/NEO/TWAnySignerTests.cpp b/tests/NEO/TWAnySignerTests.cpp deleted file mode 100644 index 3bead1f67e6..00000000000 --- a/tests/NEO/TWAnySignerTests.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" -#include -#include "HexCoding.h" -#include "proto/NEO.pb.h" - -#include - -using namespace TW; -using namespace TW::NEO; - -Proto::SigningInput createInput() { - const std::string NEO_ASSET_ID = "9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5"; - const std::string GAS_ASSET_ID = "e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c60"; - - Proto::SigningInput input; - auto privateKey = parse_hex("F18B2F726000E86B4950EBEA7BFF151F69635951BC4A31C44F28EE6AF7AEC128"); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_fee(12345); //too low - input.set_gas_asset_id(GAS_ASSET_ID); - input.set_gas_change_address("AdtSLMBqACP4jv8tRWwyweXGpyGG46eMXV"); - -#define ADD_UTXO_INPUT(hash, index , value, assetId) \ - { \ - auto utxo = input.add_inputs(); \ - utxo->set_prev_hash(parse_hex(hash).data(), parse_hex(hash).size()); \ - utxo->set_prev_index(index); \ - utxo->set_asset_id(assetId); \ - utxo->set_value(value); \ - } - - ADD_UTXO_INPUT("c61508268c5d0343af1875c60e569493100824dbdba108b31789e0e33bcb50fb", 1, 98899890000, GAS_ASSET_ID); - ADD_UTXO_INPUT("4eb2f96937a0d4dc96b77ba69a29e1de9574cbd62b16d881f1ee2061a291d70b", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("3fee0109d155dcfab272176117306b45b176914c88e8c379933c246a9e29ea0b", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("6ea9ce8c578bfeeecdf281f498e2a764689df3b93d6855a3cc45bd6b5213c426", 0, 400000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("f75ad3cbd277d83ee240e08f99a97ffd7e42a82a868e0f7043414f6d6147262b", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("054734e98f442b3e73a940ca8f594859ece1c7ddac14130b0e2f5e2799b85931", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("8b0c42d448912fc28c674fdcf8e21e4667d7d2133666168eaa0570488a9c5036", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 1, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 2, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 3, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 4, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 5, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 6, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 7, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 8, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 9, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("cf83bce600626b6077e136581c1aecc78a0bbb7d7649b1f580b6be881087ec40", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("9bd7572ba8df685e262369897d24f7217b42be496b9eed16e16a889dd83b394e", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("b4ee250397dde2f1001d782d3c803c38992447d3b351cdc9bf20cfaa2cbf995b", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("e1019ca259a1615f77263324156a70007b76cb4f26b01b2956b8f85e6842ac62", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("bd379df2aca526ac600919aaba0e59d4a1ad4e2f22d18966063cf45e431d016f", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("164c3f843b9b7bfa6a7376a1548f343acb5cdfa0193b8f31e8c9a647ea63ea7d", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("4acec74a76161eafe70e0791b1f504b5ba1d175fd4f340d5bf56804e25505e92", 0, 300000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("895c6629a71c84cbdc8956abea9ca2d9d215e909e6173b1a1a96289186a67796", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("54828143c4c3a0e1b09102e4ed29220b141089c2bc4200b1042eeb12e5e49296", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("5345e4abc86f7ace47112f5a91c129175833bafcaf9f1e1bcbbaf4d019c1c69d", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("c83e19d0d4210df97b3bc7768dc7184ae3acfc1b5b3ac9b05d2be0fe5a636b9f", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("3456b03f5cb688ce26ab1d09b7a15799136c8c886ca7c3c6bcb2363e61bb1bb1", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 10, 34000000000, NEO_ASSET_ID); - // all inputs below must be unused in this tx - ADD_UTXO_INPUT("e5a7887521b8b3aaf2d5426617ddabe8ef8ea3eab31c80a977c3b8f339df5be0", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("1455e9dd3cd6a04d81cd47acc07a7335212029ebbdcd0abc3e52c33f8b77f6eb", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("da711260085211b5573801d0dfe064235c69e61a55f9c15449ac55cc02b9adee", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("04486cfed371103dd51a89205b2c8bcc45ad887c49a768a62465f35810437bef", 0, 500000000, NEO_ASSET_ID); - ADD_UTXO_INPUT("a5f27055a442db0e65103561900456d37af4233267960daded870c1ab2219ef4", 0, 500000000, NEO_ASSET_ID); - - { - auto output = input.add_outputs(); - output->set_asset_id(NEO_ASSET_ID); - output->set_to_address("Ad9A1xPbuA5YBFr1XPznDwBwQzdckAjCev"); - output->set_change_address("AdtSLMBqACP4jv8tRWwyweXGpyGG46eMXV"); - output->set_amount(25000000000); - } - - return input; -} - -TEST(TWAnySignerNEO, Sign) { - Proto::SigningInput input = createInput(); - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeNEO); - - // https://testnet-explorer.o3.network/transactions/0x7b138c753c24f474d0f70af30a9d79756e0ee9c1f38c12ed07fbdf6fc5132eaf - ASSERT_EQ(hex(output.encoded()), "8000001efb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00039b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50083064905000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ace72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c605013cf0617000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac014140dc261ac093a87640441bf0c3ad4a55ec727932b9175f600618bb5275f31aacf122956bc88746dc666759a2d67f120fe3ce1659f916d22a91e0b02421d3bddbd1232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); -} - -TEST(TWAnySignerNEO, Plan) { - Proto::SigningInput input = createInput(); - Proto::TransactionPlan plan; - ANY_PLAN(input, plan, TWCoinTypeNEO); - - EXPECT_EQ(plan.inputs_size(), 30); - EXPECT_EQ(plan.outputs_size(), 2); - EXPECT_EQ(plan.fee(), 1408000); - EXPECT_EQ(plan.error(), Common::Proto::OK); -} diff --git a/tests/NEO/TWCoinTypeTests.cpp b/tests/NEO/TWCoinTypeTests.cpp deleted file mode 100644 index 8b986b73a4d..00000000000 --- a/tests/NEO/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWNEOCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNEO)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("e0ddf7c81c732df26180aca0c36d5868ad009fdbbe6e7a56ebafc14bba41cd53")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNEO, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("AcxuqWhTureEQGeJgbmtSWNAtssjMLU7pb")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNEO, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNEO)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNEO)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNEO), 8); - ASSERT_EQ(TWBlockchainNEO, TWCoinTypeBlockchain(TWCoinTypeNEO)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNEO)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNEO)); - assertStringsEqual(symbol, "NEO"); - assertStringsEqual(txUrl, "https://neoscan.io/transaction/e0ddf7c81c732df26180aca0c36d5868ad009fdbbe6e7a56ebafc14bba41cd53"); - assertStringsEqual(accUrl, "https://neoscan.io/address/AcxuqWhTureEQGeJgbmtSWNAtssjMLU7pb"); - assertStringsEqual(id, "neo"); - assertStringsEqual(name, "NEO"); -} \ No newline at end of file diff --git a/tests/NEO/TransactionAttributeTests.cpp b/tests/NEO/TransactionAttributeTests.cpp deleted file mode 100644 index b99ca38141b..00000000000 --- a/tests/NEO/TransactionAttributeTests.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "uint256.h" -#include "HexCoding.h" -#include "NEO/ReadData.h" -#include "NEO/TransactionAttribute.h" -#include "NEO/TransactionAttributeUsage.h" - -#include -#include - -using namespace std; -using namespace TW; -using namespace TW::NEO; - -TEST(NEOTransactionAttribute, Serialize) { - auto transactionAttribute = TransactionAttribute(); - string data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"; - transactionAttribute.usage = TransactionAttributeUsage::TAU_ContractHash; - transactionAttribute.data = parse_hex(data); - EXPECT_EQ("00" + data, hex(transactionAttribute.serialize())); - - data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4b"; - transactionAttribute.usage = TransactionAttributeUsage::TAU_Vote; - transactionAttribute.data = parse_hex(data); - EXPECT_EQ("30" + data, hex(transactionAttribute.serialize())); - - transactionAttribute.usage = TransactionAttributeUsage::TAU_ECDH02; - transactionAttribute.data = parse_hex(data); - EXPECT_EQ("02" + data, hex(transactionAttribute.serialize())); - - data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af"; - transactionAttribute.usage = TransactionAttributeUsage::TAU_Script; - transactionAttribute.data = parse_hex(data); - EXPECT_EQ("20" + data, hex(transactionAttribute.serialize())); - - data = "bd"; - transactionAttribute.usage = TransactionAttributeUsage::TAU_DescriptionUrl; - transactionAttribute.data = parse_hex(data); - EXPECT_EQ("81" + data, hex(transactionAttribute.serialize())); - - data = "bdecbb623eee6f9ade28d5a8ff5fb3ea"; - transactionAttribute.usage = TransactionAttributeUsage::TAU_Remark; - transactionAttribute.data = parse_hex(data); - EXPECT_EQ("f0" + data, hex(transactionAttribute.serialize())); -} - -TEST(NEOTransactionAttribute, Deserialize) { - auto transactionAttribute = TransactionAttribute(); - string data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"; - transactionAttribute.deserialize(parse_hex("00" + data)); - EXPECT_EQ(TransactionAttributeUsage::TAU_ContractHash, transactionAttribute.usage); - EXPECT_EQ(data, hex(transactionAttribute.data)); - - data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4b"; - transactionAttribute.deserialize(parse_hex("30" + data)); - EXPECT_EQ(TransactionAttributeUsage::TAU_Vote, transactionAttribute.usage); - EXPECT_EQ(data, hex(transactionAttribute.data)); - - transactionAttribute.deserialize(parse_hex("02" + data)); - EXPECT_EQ(TransactionAttributeUsage::TAU_ECDH02, transactionAttribute.usage); - EXPECT_EQ(data, hex(transactionAttribute.data)); - - data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af"; - transactionAttribute.deserialize(parse_hex("20" + data)); - EXPECT_EQ(TransactionAttributeUsage::TAU_Script, transactionAttribute.usage); - EXPECT_EQ(data, hex(transactionAttribute.data)); - - data = "bd"; - transactionAttribute.deserialize(parse_hex("81" + data)); - EXPECT_EQ(TransactionAttributeUsage::TAU_DescriptionUrl, transactionAttribute.usage); - EXPECT_EQ(data, hex(transactionAttribute.data)); - - data = "bdecbb623eee6f9ade28d5a8ff5fb3ea"; - transactionAttribute.deserialize(parse_hex("f0" + data)); - EXPECT_EQ(TransactionAttributeUsage::TAU_Remark, transactionAttribute.usage); - EXPECT_EQ(data, hex(transactionAttribute.data)); - - EXPECT_THROW(transactionAttribute.deserialize(parse_hex("b1" + data)), std::invalid_argument); -} - -TEST(NEOTransactionAttribute, DeserializeInitialPositionAfterData) { - auto transactionAttribute = TransactionAttribute(); - EXPECT_THROW(transactionAttribute.deserialize(Data(), 1), std::invalid_argument); - - EXPECT_THROW(transactionAttribute.deserialize(Data({1}), 2), std::invalid_argument); -} diff --git a/tests/NEO/TransactionTests.cpp b/tests/NEO/TransactionTests.cpp deleted file mode 100644 index 1c0aa39ecf5..00000000000 --- a/tests/NEO/TransactionTests.cpp +++ /dev/null @@ -1,250 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "uint256.h" -#include "HexCoding.h" -#include "NEO/Transaction.h" -#include "NEO/TransactionType.h" -#include "NEO/TransactionAttributeUsage.h" -#include "NEO/TransactionAttribute.h" - -#include -#include - -using namespace std; -using namespace TW; -using namespace TW::NEO; - -TEST(NEOTransaction, SerializeDeserializeEmpty) { - auto transaction = Transaction(); - EXPECT_EQ(transaction, transaction); - - EXPECT_EQ(0, transaction.attributes.size()); - EXPECT_EQ(0, transaction.inInputs.size()); - EXPECT_EQ(0, transaction.outputs.size()); - auto serialized = transaction.serialize(); - - auto deserializedTransaction = Transaction(); - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); -} - -TEST(NEOTransaction, SerializeDeserializeEmptyCollections) { - auto transaction = Transaction(); - transaction.type = TransactionType::TT_EnrollmentTransaction; - transaction.version = 0x07; - const string zeroVarLong = "00"; - auto serialized = transaction.serialize(); - EXPECT_EQ("2007" + zeroVarLong + zeroVarLong + zeroVarLong, hex(serialized)); - - auto deserializedTransaction = Transaction(); - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); - EXPECT_EQ(transaction, transaction); -} - -TEST(NEOTransaction, SerializeDeserializeAttribute) { - auto transaction = Transaction(); - transaction.type = TransactionType::TT_ContractTransaction; - transaction.version = 0x07; - const string zeroVarLong = "00"; - const string oneVarLong = "01"; - transaction.attributes.push_back(TransactionAttribute()); - transaction.attributes[0].usage = TransactionAttributeUsage::TAU_ContractHash; - transaction.attributes[0].data = parse_hex("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"); - auto serialized = transaction.serialize(); - EXPECT_EQ("8007" + oneVarLong + hex(transaction.attributes[0].serialize()) + zeroVarLong + zeroVarLong, hex(serialized)); - - auto deserializedTransaction = Transaction(); - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); - - transaction.attributes.push_back(TransactionAttribute()); - transaction.attributes[1].usage = TransactionAttributeUsage::TAU_ECDH02; - transaction.attributes[1].data = parse_hex("b7ecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"); - serialized = transaction.serialize(); - const string twoVarLong = "02"; - string expectedSerialized = "8007" + twoVarLong; - expectedSerialized += hex(transaction.attributes[0].serialize()); - expectedSerialized += hex(transaction.attributes[1].serialize()); - expectedSerialized += zeroVarLong + zeroVarLong; - EXPECT_EQ(expectedSerialized, hex(serialized)); - - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); - EXPECT_EQ(transaction, transaction); -} - -TEST(NEOTransaction, SerializeDeserializeInputs) { - auto transaction = Transaction(); - transaction.type = TransactionType::TT_ContractTransaction; - transaction.version = 0x07; - const string zeroVarLong = "00"; - const string oneVarLong = "01"; - transaction.inInputs.push_back(CoinReference()); - transaction.inInputs[0].prevHash = load(parse_hex("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); - transaction.inInputs[0].prevIndex = 0xa; - auto serialized = transaction.serialize(); - EXPECT_EQ("8007" + zeroVarLong + oneVarLong + hex(transaction.inInputs[0].serialize()) + zeroVarLong, hex(serialized)); - - auto deserializedTransaction = Transaction(); - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); - - transaction.inInputs.push_back(CoinReference()); - transaction.inInputs[1].prevHash = load(parse_hex("bdecbb623eee4f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); - transaction.inInputs[1].prevIndex = 0xbc; - serialized = transaction.serialize(); - const string twoVarLong = "02"; - string expectedSerialized = "8007" + zeroVarLong + twoVarLong; - expectedSerialized += hex(transaction.inInputs[0].serialize()); - expectedSerialized += hex(transaction.inInputs[1].serialize()); - expectedSerialized += zeroVarLong; - EXPECT_EQ(expectedSerialized, hex(serialized)); - - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); - EXPECT_EQ(transaction, transaction); -} - -TEST(NEOTransaction, SerializeDeserializeOutputs) { - auto transaction = Transaction(); - transaction.type = TransactionType::TT_ContractTransaction; - transaction.version = 0x07; - const string zeroVarLong = "00"; - const string oneVarLong = "01"; - transaction.outputs.push_back(TransactionOutput()); - transaction.outputs[0].assetId = load(parse_hex("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); - transaction.outputs[0].scriptHash = load(parse_hex("cbb23e6f9ade28d5a8ff3eac9d73af039e821b1b")); - transaction.outputs[0].value = 0x2; - auto serialized = transaction.serialize(); - EXPECT_EQ("8007" + zeroVarLong + zeroVarLong + oneVarLong + hex(transaction.outputs[0].serialize()), hex(serialized)); - - auto deserializedTransaction = Transaction(); - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); - - transaction.outputs.push_back(TransactionOutput()); - transaction.outputs[1].assetId = load(parse_hex("bdecbb623eee6a9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); - transaction.outputs[1].scriptHash = load(parse_hex("cbb23e6f9a3e28d5a8ff3eac9d73af039e821b1b")); - transaction.outputs[1].value = 0x2; - serialized = transaction.serialize(); - const string twoVarLong = "02"; - string expectedSerialized = "8007" + zeroVarLong + zeroVarLong + twoVarLong; - expectedSerialized += hex(transaction.outputs[0].serialize()); - expectedSerialized += hex(transaction.outputs[1].serialize()); - EXPECT_EQ(expectedSerialized, hex(serialized)); - - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); -} - -TEST(NEOTransaction, SerializeDeserialize) { - auto transaction = Transaction(); - transaction.type = TransactionType::TT_ContractTransaction; - transaction.version = 0x07; - const string oneVarLong = "01"; - - transaction.attributes.push_back(TransactionAttribute()); - transaction.attributes[0].usage = TransactionAttributeUsage::TAU_ContractHash; - transaction.attributes[0].data = parse_hex("bdecbb623eee6f9ade28d5a8ff5fbdea9c9d73af039e0286201b3b0291fb4d4a"); - - transaction.inInputs.push_back(CoinReference()); - transaction.inInputs[0].prevHash = load(parse_hex("bdecbb623eee679ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); - transaction.inInputs[0].prevIndex = 0xa; - - transaction.outputs.push_back(TransactionOutput()); - transaction.outputs[0].assetId = load(parse_hex("bdecbb623eee6f9ad328d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); - transaction.outputs[0].scriptHash = load(parse_hex("cbb23e6f9ade28a5a8ff3eac9d73af039e821b1b")); - transaction.outputs[0].value = 0x2; - - auto serialized = transaction.serialize(); - string expectedSerialized = "8007"; - expectedSerialized += oneVarLong + hex(transaction.attributes[0].serialize()); - expectedSerialized += oneVarLong + hex(transaction.inInputs[0].serialize()); - expectedSerialized += oneVarLong + hex(transaction.outputs[0].serialize()); - ASSERT_EQ(expectedSerialized, hex(serialized)); - - auto deserializedTransaction = Transaction(); - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); - - transaction.outputs.push_back(TransactionOutput()); - transaction.outputs[1].assetId = load(parse_hex("bdecbb623eee6a9a3e28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); - transaction.outputs[1].scriptHash = load(parse_hex("cbb23e6f9a3e28d5a8ff3eac9da3af039e821b1b")); - transaction.outputs[1].value = 0x2; - serialized = transaction.serialize(); - const string twoVarLong = "02"; - expectedSerialized = "8007"; - expectedSerialized += oneVarLong + hex(transaction.attributes[0].serialize()); - expectedSerialized += oneVarLong + hex(transaction.inInputs[0].serialize()); - expectedSerialized += twoVarLong + hex(transaction.outputs[0].serialize()); - expectedSerialized += hex(transaction.outputs[1].serialize()); - EXPECT_EQ(expectedSerialized, hex(serialized)); - - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); - - transaction.inInputs.push_back(CoinReference()); - transaction.inInputs[1].prevHash = load(parse_hex("bdecbb623e3e6f9ade28d5a8ff4fb3ea9c9d73af039e0286201b3b0291fb4d4a")); - transaction.inInputs[1].prevIndex = 0xbc; - transaction.inInputs.push_back(CoinReference()); - transaction.inInputs[2].prevHash = load(parse_hex("bdecbb624eee6f9ade28d5a8ff3fb3ea9c9d73af039e0286201b3b0291fb4d4a")); - transaction.inInputs[2].prevIndex = 0x1f; - - serialized = transaction.serialize(); - const string threeVarLong = "03"; - expectedSerialized = "8007"; - expectedSerialized += oneVarLong + hex(transaction.attributes[0].serialize()); - expectedSerialized += threeVarLong + hex(transaction.inInputs[0].serialize()); - expectedSerialized += hex(transaction.inInputs[1].serialize()); - expectedSerialized += hex(transaction.inInputs[2].serialize()); - expectedSerialized += twoVarLong + hex(transaction.outputs[0].serialize()); - expectedSerialized += hex(transaction.outputs[1].serialize()); - EXPECT_EQ(expectedSerialized, hex(serialized)); - - deserializedTransaction.deserialize(serialized); - EXPECT_EQ(transaction, deserializedTransaction); -} - -TEST(NEOTransaction, SerializeDeserializeMiner) { - string block2tn = "0000d11f7a2800000000"; - std::unique_ptr deserializedTransaction(Transaction::deserializeFrom(parse_hex(block2tn))); - auto serialized = deserializedTransaction->serialize(); - std::unique_ptr serializedTransaction(Transaction::deserializeFrom(serialized)); - - EXPECT_EQ(*deserializedTransaction, *serializedTransaction); - - string notMiner = "1000d11f7a2800000000"; - EXPECT_THROW( - std::unique_ptr deserializedTransaction(Transaction::deserializeFrom(parse_hex(notMiner))), - std::invalid_argument - ); -} - -TEST(NEOTransaction, GetHash) { - string block2tn = "0000d11f7a2800000000"; - std::unique_ptr deserializedTransaction(Transaction::deserializeFrom(parse_hex(block2tn))); - - Data hash = parse_hex("8e3a32ba3a7e8bdb0ad9a2ad064713e45bd20eb0dab0d2e77df5b5ce985276d0"); - // It is flipped on the https://github.com/NeoResearch/neopt/blob/master/tests/ledger_Tests/Transaction.Test.cpp - hash = Data(hash.rbegin(), hash.rend()); - - EXPECT_EQ(hex(hash), hex(deserializedTransaction->getHash())); -} - -TEST(NEOTransaction, SerializeSize) { - auto transaction = Transaction(); - transaction.type = TransactionType::TT_EnrollmentTransaction; - transaction.version = 0x07; - const string zeroVarLong = "00"; - auto serialized = transaction.serialize(); - auto verSerialized = parse_hex("2007" + zeroVarLong + zeroVarLong + zeroVarLong); - EXPECT_EQ(hex(verSerialized), hex(serialized)); - EXPECT_EQ(verSerialized, serialized); - - EXPECT_EQ(serialized.size(), transaction.size()); -} diff --git a/tests/NULS/AddressTests.cpp b/tests/NULS/AddressTests.cpp deleted file mode 100644 index 9adba2e43e9..00000000000 --- a/tests/NULS/AddressTests.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -#include "NULS/Address.h" - -#include "HexCoding.h" -#include "PrivateKey.h" -#include - -using namespace TW; -using namespace TW::NULS; - - -TEST(NULSAddress, StaticInvalid) { - ASSERT_FALSE(Address::isValid("abc")); - ASSERT_FALSE(Address::isValid("aaeb60f3e94c9b9a09f33669435e7ef1beaed")); - ASSERT_FALSE(Address::isValid("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z")); - ASSERT_TRUE(Address::isValid("NULSd6HgUxmcJWc88iELEJ7RH9XHsazBQqnJc")); - ASSERT_TRUE(Address::isValid("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2")); -} - -TEST(NULSAddress, ChainID) { - const auto address = Address("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2"); - ASSERT_TRUE(address.chainID() == 1); -} - -TEST(NULSAddress, Type) { - const auto address = Address("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2"); - ASSERT_TRUE(address.type() == 1); -} - -TEST(NULSAddress, FromString) { - const auto address = Address("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2"); - ASSERT_EQ(address.string(), "NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2"); -} - -TEST(NULSAddress, FromPrivateKey) { - const auto privateKey = - PrivateKey(parse_hex("a1269039e4ffdf43687852d7247a295f0b5bc55e6dda031cffaa3295ca0a9d7a")); - const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const auto address = Address(publicKey); - - ASSERT_EQ(address.string(), "NULSd6HghWa4CN5qdxqMwYVikQxRZyj57Jn4L"); -} - -TEST(NULSAddress, FromCompressedPublicKey) { - const auto publicKey = - PublicKey(parse_hex("0244d50ff36c3136b4bf81f0c74b066695bc2af43e28d7f0ca1d48fcfd084bea66"), TWPublicKeyTypeSECP256k1); - const auto address = Address(publicKey); - - ASSERT_EQ(address.string(), "NULSd6HgUiMKPNi221bPfqvvho8QpuYBvn1x3"); -} - -TEST(NULSAddress, FromPrivateKey33) { - const auto privateKey = PrivateKey(parse_hex("d77580833f0b3c35b7114c23d6b66790d726c308baf237ec8c369152f2c08d27")); - const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const auto address = Address(publicKey); - - ASSERT_EQ(address.string(), "NULSd6HgXx8YkwEjePLWUmdRSZzPQzK6BXnsB"); -} diff --git a/tests/NULS/TWAnySignerTests.cpp b/tests/NULS/TWAnySignerTests.cpp deleted file mode 100644 index bfab5b9177b..00000000000 --- a/tests/NULS/TWAnySignerTests.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "proto/NULS.pb.h" -#include -#include "uint256.h" - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; -using namespace TW::NULS; - -TEST(TWAnySignerNULS, Sign) { - auto privateKey = parse_hex("0x9ce21dad67e0f0af2599b41b515a7f7018059418bab892a7b68f283d489abc4b"); - auto amount = store(uint256_t(10000000)); - auto balance = store(uint256_t(100000000)); - std::string nonce = "0000000000000000"; - Proto::SigningInput input; - - input.set_from("NULSd6Hgj7ZoVgsPN9ybB4C1N2TbvkgLc8Z9H"); - input.set_to("NULSd6Hgied7ym6qMEfVzZanMaa9qeqA6TZSe"); - input.set_amount(amount.data(), amount.size()); - input.set_chain_id(1); - input.set_idassets_id(1); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_balance(balance.data(), balance.size()); - input.set_timestamp(1569228280); - input.set_nonce(nonce.data(), nonce.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeNULS); - - EXPECT_EQ(hex(output.encoded()), "0200f885885d00008c0117010001f7ec6473df12e751d64cf20a8baa7edd50810f8101000100201d9a0000000000000000000000000000000000000000000000000000000000080000000000000000000117010001f05e7878971f3374515eabb6f16d75219d8873120100010080969800000000000000000000000000000000000000000000000000000000000000000000000000692103958b790c331954ed367d37bac901de5c2f06ac8368b37d7bd6cd5ae143c1d7e3463044022028019c0099e2233c7adb84bb03a9a5666ece4a5b65a026a090fa460f3679654702204df0fcb8762b5944b3aba033fa1a287ccb098150035dd8b66f52dc58d3d0843a"); -} diff --git a/tests/NULS/TWCoinTypeTests.cpp b/tests/NULS/TWCoinTypeTests.cpp deleted file mode 100644 index 3d048c09faf..00000000000 --- a/tests/NULS/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWNULSCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNULS)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNULS, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNULS, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNULS)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNULS)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNULS), 8); - ASSERT_EQ(TWBlockchainNULS, TWCoinTypeBlockchain(TWCoinTypeNULS)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNULS)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNULS)); - assertStringsEqual(symbol, "NULS"); - assertStringsEqual(txUrl, "https://nulscan.io/transaction/info?hash=t123"); - assertStringsEqual(accUrl, "https://nulscan.io/address/info?address=a12"); - assertStringsEqual(id, "nuls"); - assertStringsEqual(name, "NULS"); -} diff --git a/tests/Nano/AddressTests.cpp b/tests/Nano/AddressTests.cpp deleted file mode 100644 index 8812a0c152b..00000000000 --- a/tests/Nano/AddressTests.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Nano/Address.h" -#include "HexCoding.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::Nano; - -TEST(NanoAddress, FromPublicKey) { - { - const auto publicKey = PublicKey(parse_hex("5114aad86a390897d2a91b33b931b3a59a7df9e63eb3694f9430122f5622ae50"), TWPublicKeyTypeED25519Blake2b); - const auto address = Address(publicKey); - ASSERT_EQ(string("nano_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg"), address.string()); - } - - { - const auto publicKey = PublicKey(parse_hex("03e20ec6b4a39a629815ae02c0a1393b9225e3b890cae45b59f42fa29be9668d"), TWPublicKeyTypeED25519); - ASSERT_THROW(Address address(publicKey), std::invalid_argument); - } -} - -TEST(NanoAddress, FromString) { - { - string nanoAddress = "nano_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg"; - const auto address = Address(nanoAddress); - ASSERT_EQ(address.string(), nanoAddress); - ASSERT_EQ(hex(address.bytes), "5114aad86a390897d2a91b33b931b3a59a7df9e63eb3694f9430122f5622ae50"); - } - - { - string xrbAddress = "xrb_1111111111111111111111111111111111111111111111111111hifc8npp"; - string nanoAddress = "nano_1111111111111111111111111111111111111111111111111111hifc8npp"; - const auto address = Address(xrbAddress); - ASSERT_EQ(address.string(), nanoAddress); - ASSERT_EQ(hex(address.bytes), "0000000000000000000000000000000000000000000000000000000000000000"); - } -} - -TEST(NanoAddress, isValid) { - string nanodeAddress = "nano_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg"; - string faultyChecksumAddress = "xrb_1111111111111111111111111111111111111111111111111111hi111111"; - string bitcoinAddress = "1Ma2DrB78K7jmAwaomqZNRMCvgQrNjE2QC"; - - ASSERT_TRUE(Address::isValid(nanodeAddress)); - ASSERT_FALSE(Address::isValid(faultyChecksumAddress)); - ASSERT_FALSE(Address::isValid(bitcoinAddress)); -} diff --git a/tests/Nano/SignerTests.cpp b/tests/Nano/SignerTests.cpp deleted file mode 100644 index 30dd6845de7..00000000000 --- a/tests/Nano/SignerTests.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Nano/Signer.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Nano; - -const std::string kPrivateKey{"173c40e97fe2afcd24187e74f6b603cb949a5365e72fbdd065a6b165e2189e34"}; -const std::string kRepOfficial1{"xrb_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4"}; -const std::string kRepNanode{"xrb_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg"}; - -TEST(NanoSigner, sign1) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); - - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_link_block(linkBlock.data(), linkBlock.size()); - input.set_representative(kRepOfficial1); - input.set_balance("96242336390000000000000000000"); - - // https://www.nanode.co/block/f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696 - const auto signer = Signer(input); - ASSERT_EQ(hex(signer.blockHash), "f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696"); - const auto signature = signer.sign(); - ASSERT_EQ(hex(signature), "d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09"); - const Proto::SigningOutput out = signer.build(); - EXPECT_EQ(hex(out.signature()), "d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09"); - EXPECT_EQ(hex(out.block_hash()), "f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696"); - EXPECT_EQ( - "{\"account\":\"nano_1bhbsc9yuh15anq3owu1izw1nk7bhhqefrkhfo954fyt8dk1q911buk1kk4c\"," - "\"balance\":\"96242336390000000000000000000\"," - "\"link\":\"491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507\"," - "\"link_as_account\":\"nano_1kazsap8mc481zbqbcqjytpf9mmigj87qr5k5fhf97579t4k8fa94octjx6d\"," - "\"previous\":\"0000000000000000000000000000000000000000000000000000000000000000\"," - "\"representative\":\"nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4\"," - "\"signature\":\"d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09\"," - "\"type\":\"state\"}", - out.json()); -} - -TEST(NanoSigner, sign2) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - const auto parentBlock = parse_hex("f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696"); - - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_parent_block(parentBlock.data(), parentBlock.size()); - input.set_representative(kRepNanode); - input.set_balance("96242336390000000000000000000"); - - // https://www.nanode.co/block/2568bf76336f7a415ca236dab97c1df9de951ca057a2e79df1322e647a259e7b - const auto signer = Signer(input); - ASSERT_EQ(hex(signer.blockHash), "2568bf76336f7a415ca236dab97c1df9de951ca057a2e79df1322e647a259e7b"); - const auto signature = signer.sign(); - ASSERT_EQ(hex(signature), "3a0687542405163d5623808052042b3482360a82cc003d178a0c0d8bfbca86450975d0faec60ae5ac37feba9a8e2205c8540317b26f2c589c2a6578b03870403"); -} - -TEST(NanoSigner, sign3) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - const auto parentBlock = parse_hex("2568bf76336f7a415ca236dab97c1df9de951ca057a2e79df1322e647a259e7b"); - const auto linkBlock = parse_hex("d7384845d2ae530b45a5dd50ee50757f988329f652781767af3f1bc2322f52b9"); - - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_parent_block(parentBlock.data(), parentBlock.size()); - input.set_link_block(linkBlock.data(), linkBlock.size()); - input.set_representative(kRepNanode); - input.set_balance("196242336390000000000000000000"); - input.set_work("123456789"); - - // https://www.nanode.co/block/1ca240212838d053ecaa9dceee598c52a6080067edecaeede3319eb0b7db6525 - const auto signer = Signer(input); - ASSERT_EQ(hex(signer.blockHash), "1ca240212838d053ecaa9dceee598c52a6080067edecaeede3319eb0b7db6525"); - const auto signature = signer.sign(); - ASSERT_EQ(hex(signature), "e980d45365ae2fb291950019f7c19a3d5fa5df2736ca7e7ca1984338b4686976cb7efdda2894ddcea480f82645b50f2340c9d0fc69a05621bdc355783a21820d"); - const Proto::SigningOutput out = signer.build(); - EXPECT_EQ( - "{\"account\":\"nano_1bhbsc9yuh15anq3owu1izw1nk7bhhqefrkhfo954fyt8dk1q911buk1kk4c\"," - "\"balance\":\"196242336390000000000000000000\"," - "\"link\":\"d7384845d2ae530b45a5dd50ee50757f988329f652781767af3f1bc2322f52b9\"," - "\"link_as_account\":\"nano_3osrb34x7dkm3f4tdqcixsa9czwrienzenmr4xmtyhruras4ynosarg1sdiq\"," - "\"previous\":\"2568bf76336f7a415ca236dab97c1df9de951ca057a2e79df1322e647a259e7b\"," - "\"representative\":\"nano_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg\"," - "\"signature\":\"e980d45365ae2fb291950019f7c19a3d5fa5df2736ca7e7ca1984338b4686976cb7efdda2894ddcea480f82645b50f2340c9d0fc69a05621bdc355783a21820d\"," - "\"type\":\"state\",\"work\":\"123456789\"}", - out.json()); -} - -TEST(NanoSigner, sign4) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - const auto parentBlock = parse_hex("1ca240212838d053ecaa9dceee598c52a6080067edecaeede3319eb0b7db6525"); - - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_parent_block(parentBlock.data(), parentBlock.size()); - input.set_link_recipient("xrb_3wm37qz19zhei7nzscjcopbrbnnachs4p1gnwo5oroi3qonw6inwgoeuufdp"); - input.set_representative(kRepNanode); - input.set_balance("126242336390000000000000000000"); - - // https://www.nanode.co/block/32ac7d8f5a16a498abf203b8dfee623c9e111ff25e7339f8cd69ec7492b23edd - const auto signer = Signer(input); - ASSERT_EQ(hex(signer.blockHash), "32ac7d8f5a16a498abf203b8dfee623c9e111ff25e7339f8cd69ec7492b23edd"); - const auto signature = signer.sign(); - ASSERT_EQ(hex(signature), "bcb806e140c9e2bc71c51ebbd941b4d99cee3d97fd50e3006eabc5e325c712662e2dc163ee32660875d67815ce4721e122389d2e64f1c9ad4555a9d3d8c33802"); -} - -TEST(NanoSigner, signInvalid1) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - - // Missing link_block - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_representative(kRepOfficial1); - input.set_balance("96242336390000000000000000000"); - - ASSERT_THROW(Signer signer(input), std::invalid_argument); -} - -TEST(NanoSigner, signInvalid2) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); - - // Missing representative - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_link_block(linkBlock.data(), linkBlock.size()); - input.set_balance("96242336390000000000000000000"); - - ASSERT_THROW(Signer signer(input), std::invalid_argument); -} - -TEST(NanoSigner, signInvalid3) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); - - // Missing balance - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_link_block(linkBlock.data(), linkBlock.size()); - input.set_representative(kRepOfficial1); - - ASSERT_THROW(Signer signer(input), std::invalid_argument); -} - -TEST(NanoSigner, signInvalid4) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); - - // Account first block cannot be 0 balance - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_link_block(linkBlock.data(), linkBlock.size()); - input.set_representative(kRepOfficial1); - input.set_balance("0"); - - ASSERT_THROW(Signer signer(input), std::invalid_argument); -} - -TEST(NanoSigner, signInvalid5) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - - // First block must use link_block not link_recipient - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_link_recipient("xrb_3wm37qz19zhei7nzscjcopbrbnnachs4p1gnwo5oroi3qonw6inwgoeuufdp"); - input.set_representative(kRepOfficial1); - input.set_balance("96242336390000000000000000000"); - - ASSERT_THROW(Signer signer(input), std::invalid_argument); -} - -TEST(NanoSigner, signInvalid6) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); - - // Invalid representative value - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_link_block(linkBlock.data(), linkBlock.size()); - input.set_representative("xrb_4wm37qz19zhei7nzscjcopbrbnnachs4p1gnwo5oroi3qonw6inwgoeuufdp"); - input.set_balance("96242336390000000000000000000"); - - ASSERT_THROW(Signer signer(input), std::invalid_argument); -} - -TEST(NanoSigner, signInvalid7) { - const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); - const auto parentBlock = parse_hex("f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696"); - - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_parent_block(parentBlock.data(), parentBlock.size()); - input.set_link_recipient("xrb_4wm37qz19zhei7nzscjcopbrbnnachs4p1gnwo5oroi3qonw6inwgoeuufdp"); - input.set_representative(kRepOfficial1); - input.set_balance("1.2.3"); - - ASSERT_THROW(Signer signer(input), std::invalid_argument); -} diff --git a/tests/Nano/TWAnySignerTests.cpp b/tests/Nano/TWAnySignerTests.cpp deleted file mode 100644 index 54638c40b5b..00000000000 --- a/tests/Nano/TWAnySignerTests.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © 2019 Mart Roosmaa. -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "proto/Nano.pb.h" -#include "../interface/TWTestUtilities.h" -#include -#include - -using namespace TW; -using namespace TW::Nano; - -TEST(TWAnySignerNano, sign) { - const auto privateKey = parse_hex("173c40e97fe2afcd24187e74f6b603cb949a5365e72fbdd065a6b165e2189e34"); - const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); - - auto input = Proto::SigningInput(); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_link_block(linkBlock.data(), linkBlock.size()); - input.set_representative("xrb_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4"); - input.set_balance("96242336390000000000000000000"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeNano); - - EXPECT_EQ( - "{\"account\":\"nano_1bhbsc9yuh15anq3owu1izw1nk7bhhqefrkhfo954fyt8dk1q911buk1kk4c\"," - "\"balance\":\"96242336390000000000000000000\"," - "\"link\":\"491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507\"," - "\"link_as_account\":\"nano_1kazsap8mc481zbqbcqjytpf9mmigj87qr5k5fhf97579t4k8fa94octjx6d\"," - "\"previous\":\"0000000000000000000000000000000000000000000000000000000000000000\"," - "\"representative\":\"nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4\"," - "\"signature\":" - "\"d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5" - "ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09\"," - "\"type\":\"state\"}", - output.json()); -} - -TEST(TWAnySignerNano, SignJSON) { - auto json = STRING(R"({"link_block":"SR/KLGmoRgfTdKrx9qzTznB0TFvgchte05RlPoUjNQc=","representative":"nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4","balance":"96242336390000000000000000000"})"); - auto key = DATA("173c40e97fe2afcd24187e74f6b603cb949a5365e72fbdd065a6b165e2189e34"); - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeNano)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeNano)); - assertStringsEqual(result, R"({"account":"nano_1bhbsc9yuh15anq3owu1izw1nk7bhhqefrkhfo954fyt8dk1q911buk1kk4c","balance":"96242336390000000000000000000","link":"491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507","link_as_account":"nano_1kazsap8mc481zbqbcqjytpf9mmigj87qr5k5fhf97579t4k8fa94octjx6d","previous":"0000000000000000000000000000000000000000000000000000000000000000","representative":"nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4","signature":"d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09","type":"state"})"); -} diff --git a/tests/Nano/TWCoinTypeTests.cpp b/tests/Nano/TWCoinTypeTests.cpp deleted file mode 100644 index 1a7ac8ab7af..00000000000 --- a/tests/Nano/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWNanoCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNano)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("C264DB7BF40738F0CEFF19B606746CB925B713E4B8699A055699E0DC8ABBC70F")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNano, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("nano_1wpj616kwhe1y38y1mspd8aub8i334cwybqco511iyuxm55zx8d67ptf1tsf")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNano, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNano)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNano)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNano), 30); - ASSERT_EQ(TWBlockchainNano, TWCoinTypeBlockchain(TWCoinTypeNano)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNano)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNano)); - assertStringsEqual(symbol, "NANO"); - assertStringsEqual(txUrl, "https://nanocrawler.cc/explorer/block/C264DB7BF40738F0CEFF19B606746CB925B713E4B8699A055699E0DC8ABBC70F"); - assertStringsEqual(accUrl, "https://nanocrawler.cc/explorer/account/nano_1wpj616kwhe1y38y1mspd8aub8i334cwybqco511iyuxm55zx8d67ptf1tsf"); - assertStringsEqual(id, "nano"); - assertStringsEqual(name, "Nano"); -} diff --git a/tests/Nebulas/AddressTests.cpp b/tests/Nebulas/AddressTests.cpp deleted file mode 100644 index 3cca8382bb1..00000000000 --- a/tests/Nebulas/AddressTests.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Nebulas/Address.h" -#include "../src/Base58.h" -#include "HexCoding.h" -#include "PrivateKey.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::Nebulas; - -TEST(NebulasAddress, Invalid) { - ASSERT_FALSE(Address::isValid("abc")); - ASSERT_FALSE(Address::isValid("a1TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv")); - ASSERT_FALSE(Address::isValid("n2TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv")); - // normal address test - ASSERT_TRUE(Address::isValid("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY")); - // contract address test - ASSERT_TRUE(Address::isValid("n1zUNqeBPvsyrw5zxp9mKcDdLTjuaEL7s39")); -} - -TEST(NebulasAddress, String) { - ASSERT_THROW(Address("abc"), std::invalid_argument); - ASSERT_EQ(Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY").string(), - "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); - ASSERT_EQ(Address(Base58::bitcoin.decode("n1TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv")).string(), - "n1TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv" - ); - - const auto address = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); - ASSERT_EQ(address.string(), "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); -} - -TEST(NebulasAddress, Data) { - Data data; - EXPECT_THROW(Address(data).string(), std::invalid_argument); - ASSERT_EQ(Address(Base58::bitcoin.decode("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY")).string(), - "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); -} - -TEST(NebulasAddress, FromPrivateKey) { - const auto privateKey = PrivateKey(parse_hex("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); - const auto address = Address(publicKey); - ASSERT_EQ(address.string(), "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); - - EXPECT_THROW(Address(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)), std::invalid_argument); -} diff --git a/tests/Nebulas/TWAnySignerTests.cpp b/tests/Nebulas/TWAnySignerTests.cpp deleted file mode 100644 index 69008c4d8a6..00000000000 --- a/tests/Nebulas/TWAnySignerTests.cpp +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" -#include -#include "HexCoding.h" -#include "uint256.h" -#include "proto/Nebulas.pb.h" - -#include - -using namespace TW; -using namespace TW::Nebulas; - -TEST(TWAnySignerNebulas, Sign) { - Proto::SigningInput input; - input.set_from_address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); - input.set_to_address("n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"); - auto value = store(uint256_t(7)); - input.set_nonce(value.data(),value.size()); - value = store(uint256_t(1000000)); - input.set_gas_price(value.data(),value.size()); - value = store(uint256_t(200000)); - input.set_gas_limit(value.data(),value.size()); - value = store(uint256_t(11000000000000000000ULL)); - input.set_amount(value.data(),value.size()); - input.set_payload(""); - value = store(uint256_t(1560052938)); - input.set_timestamp(value.data(),value.size()); - - const auto privateKey = parse_hex("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b"); - input.set_private_key(privateKey.data(), privateKey.size()); - auto chainid = store(uint256_t(1)); - input.set_chain_id(chainid.data(), chainid.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeNebulas); - - EXPECT_EQ(hex(output.signature()), "f53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500"); - EXPECT_EQ(output.raw(), "CiBQXdR2neMqnEu21q/U+OHqZHSBX9Q0hNiRfL2eCZO4hRIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6CAoGYmluYXJ5QAFKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAADDUBYAWJB9T9KkUH/jkYrCUE47M2MOl14Zfnpq1CWJseEYKngsPw19+1boXlc64Gl5Gt1gKb3+0MdRP26klFTmc9qjkfnFQA="); -} diff --git a/tests/Nebulas/TWCoinTypeTests.cpp b/tests/Nebulas/TWCoinTypeTests.cpp deleted file mode 100644 index c3eac69cffa..00000000000 --- a/tests/Nebulas/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWNebulasCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNebulas)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNebulas, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNebulas, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNebulas)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNebulas)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNebulas), 18); - ASSERT_EQ(TWBlockchainNebulas, TWCoinTypeBlockchain(TWCoinTypeNebulas)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNebulas)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNebulas)); - assertStringsEqual(symbol, "NAS"); - assertStringsEqual(txUrl, "https://explorer.nebulas.io/#/tx/t123"); - assertStringsEqual(accUrl, "https://explorer.nebulas.io/#/address/a12"); - assertStringsEqual(id, "nebulas"); - assertStringsEqual(name, "Nebulas"); -} diff --git a/tests/Nebulas/TransactionTests.cpp b/tests/Nebulas/TransactionTests.cpp deleted file mode 100644 index 2774e02d623..00000000000 --- a/tests/Nebulas/TransactionTests.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Nebulas/Signer.h" -#include "HexCoding.h" -#include "Base64.h" -#include "PrivateKey.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::Nebulas; - -extern std::string htmlescape(const std::string& str); - -TEST(NebulasTransaction, serialize) { - auto from = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); - auto to = Address("n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"); - auto transaction = Transaction( - /* to: */ from, - /* nonce: */ 7, - /* gasPrice: */ 1000000, - /* gasLimit: */ 200000, - /* to: */ to, - /* amount: */ 11000000000000000000ULL, - /* timestamp: */ 1560052938, - /* payload: */ std::string()); - - const auto privateKey = PrivateKey(parse_hex("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")); - auto signer = Signer(1); - signer.sign(privateKey, transaction); - transaction.serializeToRaw(); - - ASSERT_EQ(TW::Base64::encode(transaction.raw), "CiBQXdR2neMqnEu21q/U+OHqZHSBX9Q0hNiRfL2eCZO4hRIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6CAoGYmluYXJ5QAFKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAADDUBYAWJB9T9KkUH/jkYrCUE47M2MOl14Zfnpq1CWJseEYKngsPw19+1boXlc64Gl5Gt1gKb3+0MdRP26klFTmc9qjkfnFQA="); -} - -TEST(NebulasTransaction, binaryPayload) { - auto from = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); - auto to = Address("n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"); - auto transaction = Transaction( - /* to: */ from, - /* nonce: */ 7, - /* gasPrice: */ 1000000, - /* gasLimit: */ 200000, - /* to: */ to, - /* amount: */ 11000000000000000000ULL, - /* timestamp: */ 1560052938, - /* payload: */ std::string("{\"binary\":\"test\"}")); - - const auto privateKey = PrivateKey(parse_hex("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")); - auto signer = Signer(1); - signer.sign(privateKey, transaction); - ASSERT_EQ(TW::Base64::encode(transaction.raw), "CiB1Oqj7bxLQMHEoNyg/vFHmsTrGdkpTf/5qFDkYPB3bkxIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6PQoGYmluYXJ5EjN7IkRhdGEiOnsiZGF0YSI6WzExNiwxMDEsMTE1LDExNl0sInR5cGUiOiJCdWZmZXIifX1AAUoQAAAAAAAAAAAAAAAAAA9CQFIQAAAAAAAAAAAAAAAAAAMNQFgBYkGHXq+JWPaEyeB19bqL3QB5jyM961WLq7PMTpnGM4iLtBjCkngjS81kgPM2TE4qKDcpzqjum/NccrZtUPQLGk0MAQ=="); -} - -TEST(NebulasTransaction, htmlescape) { - // test for escaped label - auto test = ("test&<>\x20\x28\x20\x29"); - auto result = htmlescape(test); - ASSERT_EQ(result, "test\\u0026\\u003c\\u003e\\u2028\\u2029"); -} - -TEST(NebulasTransaction, serializeUnsigned) { - auto from = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); - auto to = Address("n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"); - auto transaction = Transaction( - /* to: */ from, - /* nonce: */ 7, - /* gasPrice: */ 1000000, - /* gasLimit: */ 200000, - /* to: */ to, - /* amount: */ 11000000000000000000ULL, - /* timestamp: */ 1560052938, - /* payload: */ std::string()); - - ASSERT_THROW(transaction.serializeToRaw(),std::logic_error); -} \ No newline at end of file diff --git a/tests/Nimiq/AddressTests.cpp b/tests/Nimiq/AddressTests.cpp deleted file mode 100644 index 2bc4cfa2403..00000000000 --- a/tests/Nimiq/AddressTests.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Nimiq/Address.h" -#include "Nimiq/Signer.h" - -#include -#include - -using namespace TW; -using namespace TW::Nimiq; - -TEST(NimiqAddress, IsValid) { - // No address - ASSERT_FALSE(Address::isValid("")); - // Invalid country code - ASSERT_FALSE(Address::isValid("DE86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA")); - // Invalid checksum - ASSERT_FALSE(Address::isValid("NQ42 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA")); - // Too short - ASSERT_FALSE(Address::isValid("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0ML")); - // Too long - ASSERT_FALSE(Address::isValid("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA 0MLA")); - // Valid, without spaces - ASSERT_TRUE(Address::isValid("NQ862H8FYGU5RM77QSN9LYLHC56ACYYR0MLA")); - // Valid, normal format - ASSERT_TRUE(Address::isValid("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA")); -} - -TEST(NimiqAddress, String) { - // Address to string - ASSERT_EQ( - Address(parse_hex("5b3e9e5f32b89abafc3708765dc8f00216cefbb1")).string(), - "NQ61 BCY9 UPRJ P2DB MY1P 11T5 TJ7G 08BC VXVH" - ); - // Without spaces - ASSERT_EQ( - Address("NQ862H8FYGU5RM77QSN9LYLHC56ACYYR0MLA").string(), - "NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA" - ); - // With spaces - ASSERT_EQ( - Address("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA").string(), - "NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA" - ); -} - -TEST(NimiqAddress, FromPublicKey) { - const auto publicKey = Signer::publicKeyFromBytes( - parse_hex("70c7492aaa9c9ac7a05bc0d9c5db2dae9372029654f71f0c7f95deed5099b702")); - const auto address = Address(publicKey); - ASSERT_EQ(address.string(), "NQ27 GBAY EVHP HK5X 6JHV JGFJ 5M3H BF4Y G7GD"); -} diff --git a/tests/Nimiq/TWAnySignerTests.cpp b/tests/Nimiq/TWAnySignerTests.cpp deleted file mode 100644 index 5a827a87009..00000000000 --- a/tests/Nimiq/TWAnySignerTests.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "proto/Nimiq.pb.h" -#include - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; -using namespace TW::Nimiq; - -TEST(TWAnySignerNimiq, Sign) { - auto privateKey = parse_hex("e3cc33575834add098f8487123cd4bca543ee859b3e8cfe624e7e6a97202b756"); - - Proto::SigningInput input; - - input.set_destination("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA"); - input.set_fee(1000); - input.set_value(42042042); - input.set_validity_start_height(314159); - input.set_private_key(privateKey.data(), privateKey.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeNimiq); - - EXPECT_EQ(hex(output.encoded()), "0070c7492aaa9c9ac7a05bc0d9c5db2dae9372029654f71f0c7f95deed5099b7021450ffc385cd4e7c6ac9a7e91614ca67ff90568a00000000028182ba00000000000003e80004cb2f2a74dc7f6e0ab58a0bf52cc6e8801b0cca132dd4229d9a3e3a3d2f90e4d8f045d981b771bf5fc3851a98f3c617b1a943228f963e910e061808a721cfa0e3cad50b"); -} diff --git a/tests/Nimiq/TWCoinTypeTests.cpp b/tests/Nimiq/TWCoinTypeTests.cpp deleted file mode 100644 index 40f585625e3..00000000000 --- a/tests/Nimiq/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWNimiqCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNimiq)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNimiq, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNimiq, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNimiq)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNimiq)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNimiq), 5); - ASSERT_EQ(TWBlockchainNimiq, TWCoinTypeBlockchain(TWCoinTypeNimiq)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNimiq)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNimiq)); - assertStringsEqual(symbol, "NIM"); - assertStringsEqual(txUrl, "https://nimiq.watch/#t123"); - assertStringsEqual(accUrl, "https://nimiq.watch/#a12"); - assertStringsEqual(id, "nimiq"); - assertStringsEqual(name, "Nimiq"); -} diff --git a/tests/Oasis/AddressTests.cpp b/tests/Oasis/AddressTests.cpp deleted file mode 100644 index 543fb51fdcf..00000000000 --- a/tests/Oasis/AddressTests.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Oasis/Address.h" -#include "PublicKey.h" -#include "PrivateKey.h" -#include -#include - -using namespace TW; -using namespace TW::Oasis; - -TEST(OasisAddress, Valid) { - ASSERT_TRUE(Address::isValid("oasis1qp0cnmkjl22gky6p6qeghjytt4v7dkxsrsmueweh")); -} - -TEST(OasisAddress, Invalid) { - ASSERT_FALSE(Address::isValid("oasis1qp0cnmkjl22gky6p6qeghjytt4v7dkxsrsmuewehj")); - ASSERT_FALSE(Address::isValid("oasi1qp0cnmkjl22gky6p6qeghjytt4v7dkxsrsmueweh")); -} - -TEST(OasisAddress, ForceInvalid) { - try { - auto addressString = "oasis1qp0cnmkjl22gky6p6qeghjytt4v7dkxsrsmuewehj"; - auto address = Address( addressString ); - } catch( std::invalid_argument& e1 ) { - return; - } - FAIL() << "This test should generate an exception as it an invalid address"; -} - -TEST(OasisAddress, FromWrongData) { - try { - auto dataString = "asdadfasdfsdfwrwrsadasdasdsad"; - auto address = Address( data( dataString ) ); - } catch( std::invalid_argument& e1 ) { - return; - } - FAIL() << "This test should generate an exception as it an invalid data"; -} - -TEST(OasisAddress, FromPrivateKey) { - auto privateKey = PrivateKey(parse_hex("4f8b5676990b00e23d9904a92deb8d8f428ff289c8939926358f1d20537c21a0")); - auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(address.string(), "oasis1qzawzy5kaa2xgphenf3r0f5enpr3mx5dps559yxm"); -} - -TEST(OasisAddress, FromPublicKey) { - auto publicKey = PublicKey(parse_hex("aba52c0dcb80c2fe96ed4c3741af40c573a0500c0d73acda22795c37cb0f1739"), TWPublicKeyTypeED25519); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "oasis1qphdkldpttpsj2j3l9sde9h26cwpfwqwwuhvruyu"); -} - -TEST(OasisAddress, WrongPublicKeyType) { - try { - auto publicKey = PublicKey(parse_hex("aba52c0dcb80c2fe96ed4c3741af40c573a0500c0d73acda22795c37cb0f1739"), TWPublicKeyTypeED25519Extended); - auto address = Address(publicKey); - } catch( std::invalid_argument& e1 ) { - return; - } - FAIL() << "TWPublicKeyTypeED25519Extended should generate an exception as it an invalid publicKey type"; -} - -TEST(OasisAddress, FromString) { - Address address; - ASSERT_TRUE(Address::decode("oasis1hts399h023jqd7v6vgm6dxvcguwe4rgvqqgvq38n", address)); - ASSERT_EQ(address.string(), "oasis1hts399h023jqd7v6vgm6dxvcguwe4rgvqqgvq38n"); - - ASSERT_FALSE(Address::decode("oasis1hts399h023jqd7v6vgm6dxvcguwe4rgvqqgvq38ng", address)); -} diff --git a/tests/Oasis/SignerTests.cpp b/tests/Oasis/SignerTests.cpp deleted file mode 100644 index 9e8b454f8d7..00000000000 --- a/tests/Oasis/SignerTests.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Oasis/Signer.h" -#include "Oasis/Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" - -#include - -using namespace TW; -using namespace TW::Oasis; - -TEST(OasisSigner, Sign) { - auto input = Proto::SigningInput(); - auto& transfer = *input.mutable_transfer(); - - transfer.set_gas_price(0); - transfer.set_gas_amount("0"); - transfer.set_nonce(0); - transfer.set_to("oasis1qrrnesqpgc6rfy2m50eew5d7klqfqk69avhv4ak5"); - transfer.set_amount("10000000"); - - // The use of this context thing is explained here --> https://docs.oasis.dev/oasis-core/common-functionality/crypto#domain-separation - transfer.set_context("oasis-core/consensus: tx for chain a245619497e580dd3bc1aa3256c07f68b8dcc13f92da115eadc3b231b083d3c4"); - - auto key = parse_hex("4f8b5676990b00e23d9904a92deb8d8f428ff289c8939926358f1d20537c21a0"); - input.set_private_key(key.data(), key.size()); - - Proto::SigningOutput output = Signer::sign(input); - - ASSERT_EQ(hex(output.encoded()),"a273756e747275737465645f7261775f76616c7565585ea4656e6f6e636500666d6574686f64707374616b696e672e5472616e7366657263666565a2636761730066616d6f756e74410064626f6479a262746f5500c73cc001463434915ba3f39751beb7c0905b45eb66616d6f756e744400989680697369676e6174757265a26a7075626c69635f6b6579582093d8f8a455f50527976a8aa87ebde38d5606efa86cb985d3fb466aff37000e3b697369676e61747572655840e331ce731ed819106586152b13cd98ecf3248a880bdc71174ee3d83f6d5f3f8ee8fc34c19b22032f2f1e3e06d382720125d7a517fba9295c813228cc2b63170b"); -} diff --git a/tests/Oasis/TWAnySignerTests.cpp b/tests/Oasis/TWAnySignerTests.cpp deleted file mode 100644 index 0cae4b34bcd..00000000000 --- a/tests/Oasis/TWAnySignerTests.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include "HexCoding.h" -#include "proto/Oasis.pb.h" - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; -using namespace TW::Oasis; - -TEST(TWAnySignerOasis, Sign) { - auto input = Proto::SigningInput(); - auto output = Proto::SigningOutput(); - auto& transfer = *input.mutable_transfer(); - - transfer.set_gas_price(0); - transfer.set_gas_amount("0"); - transfer.set_nonce(0); - transfer.set_to("oasis1qrrnesqpgc6rfy2m50eew5d7klqfqk69avhv4ak5"); - transfer.set_amount("10000000"); - transfer.set_context("oasis-core/consensus: tx for chain a245619497e580dd3bc1aa3256c07f68b8dcc13f92da115eadc3b231b083d3c4"); - - - auto key = parse_hex("4f8b5676990b00e23d9904a92deb8d8f428ff289c8939926358f1d20537c21a0"); - input.set_private_key(key.data(), key.size()); - - ANY_SIGN(input, TWCoinTypeOasis); - - EXPECT_EQ("a273756e747275737465645f7261775f76616c7565585ea4656e6f6e636500666d6574686f64707374616b696e672e5472616e7366657263666565a2636761730066616d6f756e74410064626f6479a262746f5500c73cc001463434915ba3f39751beb7c0905b45eb66616d6f756e744400989680697369676e6174757265a26a7075626c69635f6b6579582093d8f8a455f50527976a8aa87ebde38d5606efa86cb985d3fb466aff37000e3b697369676e61747572655840e331ce731ed819106586152b13cd98ecf3248a880bdc71174ee3d83f6d5f3f8ee8fc34c19b22032f2f1e3e06d382720125d7a517fba9295c813228cc2b63170b", - hex(output.encoded())); -} diff --git a/tests/Oasis/TWCoinTypeTests.cpp b/tests/Oasis/TWCoinTypeTests.cpp deleted file mode 100644 index ab703446d39..00000000000 --- a/tests/Oasis/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWOasisCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOasis)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0b9bd4983f1c88a1c71bf33562b6ba02b3064e01697d15a0de4bfe1922ec74b8")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeOasis, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("oasis1qrx376dmwuckmruzn9vq64n49clw72lywctvxdf4")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeOasis, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeOasis)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeOasis)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeOasis), 9); - ASSERT_EQ(TWBlockchainOasisNetwork, TWCoinTypeBlockchain(TWCoinTypeOasis)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeOasis)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeOasis)); - assertStringsEqual(symbol, "ROSE"); - assertStringsEqual(txUrl, "https://oasisscan.com/transactions/0b9bd4983f1c88a1c71bf33562b6ba02b3064e01697d15a0de4bfe1922ec74b8"); - assertStringsEqual(accUrl, "https://oasisscan.com/accounts/detail/oasis1qrx376dmwuckmruzn9vq64n49clw72lywctvxdf4"); - assertStringsEqual(id, "oasis"); - assertStringsEqual(name, "Oasis"); -} diff --git a/tests/Ontology/AccountTests.cpp b/tests/Ontology/AccountTests.cpp deleted file mode 100644 index e6dcbfa56e9..00000000000 --- a/tests/Ontology/AccountTests.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Hash.h" -#include "HexCoding.h" -#include "PrivateKey.h" - -#include "Ontology/Signer.h" - -#include - -using namespace TW; -using namespace TW::Ontology; - -TEST(OntologyAccount, validity) { - auto hexPrvKey = "4646464646464646464646464646464646464646464646464646464646464646"; - auto hexPubKey = "031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"; - auto signer = Signer(PrivateKey(parse_hex(hexPrvKey))); - auto prvKey = signer.getPrivateKey(); - auto pubKey = signer.getPublicKey(); - EXPECT_EQ(hexPrvKey, hex(prvKey.bytes)); - EXPECT_EQ(hexPubKey, hex(pubKey.bytes)); -} \ No newline at end of file diff --git a/tests/Ontology/AddressTests.cpp b/tests/Ontology/AddressTests.cpp deleted file mode 100644 index f27c2cdd691..00000000000 --- a/tests/Ontology/AddressTests.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "PublicKey.h" - -#include "Ontology/Address.h" -#include "Ontology/Signer.h" - -#include - -using namespace TW; -using namespace TW::Ontology; - -TEST(OntologyAddress, validation) { - ASSERT_FALSE(Address::isValid("abc")); - ASSERT_FALSE(Address::isValid("abeb60f3e94c1b9a09f33669435e7ef12eacd")); - ASSERT_FALSE(Address::isValid("abcb60f3e94c9b9a09f33669435e7ef1beaedads")); - ASSERT_TRUE(Address::isValid("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD")); -} - -TEST(OntologyAddress, fromPubKey) { - auto address = Address( - PublicKey(parse_hex("031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"), TWPublicKeyTypeSECP256k1)); - EXPECT_EQ("AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5", address.string()); -} - -TEST(OntologyAddress, fromString) { - auto b58Str = "AYTxeseHT5khTWhtWX1pFFP1mbQrd4q1zz"; - auto address = Address(b58Str); - EXPECT_EQ(b58Str, address.string()); - auto errB58Str = "AATxeseHT5khTWhtWX1pFFP1mbQrd4q1zz"; - ASSERT_THROW(new Address(errB58Str), std::runtime_error); -} - -TEST(OntologyAddress, fromMultiPubKeys) { - auto signer1 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); - auto signer2 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); - auto signer3 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464658"))); - std::vector pubKeys{signer1.getPublicKey().bytes, signer2.getPublicKey().bytes, signer3.getPublicKey().bytes}; - uint8_t m = 2; - auto multiAddress = Address(m, pubKeys); - EXPECT_EQ("AYGWgijVZnrUa2tRoCcydsHUXR1111DgdW", multiAddress.string()); -} \ No newline at end of file diff --git a/tests/Ontology/ParamsBuilderTests.cpp b/tests/Ontology/ParamsBuilderTests.cpp deleted file mode 100644 index bb8a3540d1e..00000000000 --- a/tests/Ontology/ParamsBuilderTests.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "PublicKey.h" - -#include "Ontology/Address.h" -#include "Ontology/Ont.h" -#include "Ontology/ParamsBuilder.h" - -#include - -using namespace TW; -using namespace TW::Ontology; - -TEST(ParamsBuilder, pushInt) { - std::vector numVector{0, - 1, - 2, - 127, - 128, - 129, - 65534, - 65535, - 65536, - 65537, - 4294967294, - 4294967295, - 4294967296, - 68719476735, - 68719476736, - 72057594037927935, - 1152921504606846975}; - std::vector codeVector{"00", - "51", - "52", - "017f", - "028000", - "028100", - "03feff00", - "03ffff00", - "03000001", - "03010001", - "05feffffff00", - "05ffffffff00", - "050000000001", - "05ffffffff0f", - "050000000010", - "08ffffffffffffff00", - "08ffffffffffffff0f"}; - for (auto index = 0; index < numVector.size(); index++) { - auto builder = ParamsBuilder(); - builder.push(numVector[index]); - EXPECT_EQ(codeVector[index], hex(builder.getBytes())); - } -} - -TEST(ParamsBuilder, balanceInvokeCode) { - auto balanceParam = Address("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD").data; - auto invokeCode = ParamsBuilder::buildNativeInvokeCode(Ont().contractAddress(), 0x00, - "balanceOf", balanceParam); - auto hexInvokeCode = - "1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f661400000000000000000000000000" - "000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b65"; - EXPECT_EQ(hexInvokeCode, hex(invokeCode)); -} - -TEST(ParamsBuilder, transferInvokeCode) { - auto fromAddress = Address("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD").data; - auto toAddress = Address("Af1n2cZHhMZumNqKgw9sfCNoTWu9de4NDn").data; - uint64_t amount = 1; - std::list transferParam{fromAddress, toAddress, amount}; - std::vector args{transferParam}; - auto invokeCode = - ParamsBuilder::buildNativeInvokeCode(Ont().contractAddress(), 0x00, "transfer", args); - auto hexInvokeCode = - "00c66b1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b76a7cc814feec06b79ed299ea06fcb94abac41aaf3e" - "ad76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068" - "164f6e746f6c6f67792e4e61746976652e496e766f6b65"; - EXPECT_EQ(hexInvokeCode, hex(invokeCode)); -} \ No newline at end of file diff --git a/tests/Ontology/TWAnySignerTests.cpp b/tests/Ontology/TWAnySignerTests.cpp deleted file mode 100644 index bb6786dc0ef..00000000000 --- a/tests/Ontology/TWAnySignerTests.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "../interface/TWTestUtilities.h" - -#include "Ontology/OngTxBuilder.h" -#include "Ontology/OntTxBuilder.h" - -#include - -#include - -using namespace TW; -using namespace TW::Ontology; - -TEST(TWAnySingerOntology, OntBalanceOf) { - // curl -H "Content-Type: application/json" -X POST -d '{"Action":"sendrawtransaction", - // "Version":"1.0.0","00d1885602ec0000000000000000000000000000000000000000000000000000000000000000000000004d1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f661400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b650000"}' - // http://polaris2.ont.io:20334/api/v1/transaction?preExec=1 - // - // {"Action":"sendrawtransaction","Desc":"SUCCESS","Error":0,"Result":{"State":1,"Gas":20000,"Result":"00","Notify":[]},"Version":"1.0.0"} - auto input = Proto::SigningInput(); - input.set_contract("ONT"); - input.set_method("balanceOf"); - input.set_query_address("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD"); - input.set_nonce(3959576200); - auto data = OntTxBuilder::build(input); - auto rawTx = hex(data); - EXPECT_EQ("00d1885602ec000000000000000000000000000000000000000000000000000000000000000000000000" - "4d1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f6614000000000000000000" - "00000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b650000", - rawTx); -} - -TEST(TWAnySingerOntology, OntDecimals) { - // curl -H "Content-Type: application/json" -X POST -d '{"Action":"sendrawtransaction", - // "Version":"1.0.0","Data":"00d1bdc12a48000000000000000000000000000000000000000000000000000000000000000000000000380008646563696d616c731400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b650000"}' - // http://polaris2.ont.io:20334/api/v1/transaction?preExec=1 - // - //{"Action":"sendrawtransaction","Desc":"SUCCESS","Error":0,"Result":{"State":1,"Gas":20000,"Result":"","Notify":[]},"Version":"1.0.0"} - auto input = Proto::SigningInput(); - input.set_contract("ONT"); - input.set_method("decimals"); - input.set_nonce(1210761661); - auto data = OntTxBuilder::build(input); - auto rawTx = hex(data); - EXPECT_EQ("00d1bdc12a48000000000000000000000000000000000000000000000000000000000000000000000000" - "380008646563696d616c731400000000000000000000000000000000000000010068164f6e746f6c6f67" - "792e4e61746976652e496e766f6b650000", - rawTx); -} - -TEST(TWAnySingerOntology, OntTransfer) { - // tx on polaris test net. - // https://explorer.ont.io/transaction/4a672ce813d3fac9042e9472cf9b470f8a5e59a2deb41fd7b23a1f7479a155d5/testnet - auto ownerPrivateKey = - parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); - auto payerPrivateKey = - parse_hex("4646464646464646464646464646464646464646464646464646464646464652"); - auto input = Proto::SigningInput(); - input.set_contract("ONT"); - input.set_method("transfer"); - input.set_nonce(2338116610); - input.set_owner_private_key(ownerPrivateKey.data(), ownerPrivateKey.size()); - input.set_payer_private_key(payerPrivateKey.data(), payerPrivateKey.size()); - input.set_to_address("Af1n2cZHhMZumNqKgw9sfCNoTWu9de4NDn"); - input.set_amount(1); - input.set_gas_price(500); - input.set_gas_limit(20000); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeOntology); - - EXPECT_EQ("00d102d45c8bf401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" - "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" - "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" - "00000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140301766d925382a6e" - "bb2ebeb18d3741954c9370dcf6d9c45b34ce7b18bc42dcdb7cff28ddaf7f1048822c0ca21a0c4926323a" - "2497875b963f3b8cbd3717aa6e7c2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e4" - "47125f927b7486ac414038466b25ac49a22ba8c301328ef049a61711b257987e85e25d63e0444a14e860" - "305a4cd3bb6ea2fe80fd293abb3c592e679c42c546cbf3baa051a07b28b374a6232103d9fd62df332403" - "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", - hex(output.encoded())); -} - -TEST(TWAnySingerOntology, OngDecimals) { - // curl -H "Content-Type: application/json" -X POST -d '{"Action":"sendrawtransaction", - // "Version":"1.0.0","Data":"00d1e3f2e679000000000000000000000000000000000000000000000000000000000000000000000000380008646563696d616c731400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b650000"}' - // http://polaris2.ont.io:20334/api/v1/transaction?preExec=1 - // - // {"Action":"sendrawtransaction","Desc":"SUCCESS","Error":0,"Result":{"State":1,"Gas":20000,"Result":"09","Notify":[]},"Version":"1.0.0"} - auto input = Proto::SigningInput(); - input.set_contract("ONG"); - input.set_method("decimals"); - input.set_nonce(2045178595); - auto data = OngTxBuilder::build(input); - auto rawTx = hex(data); - EXPECT_EQ("00d1e3f2e679000000000000000000000000000000000000000000000000000000000000000000000000" - "380008646563696d616c731400000000000000000000000000000000000000020068164f6e746f6c6f67" - "792e4e61746976652e496e766f6b650000", - rawTx); -} - -TEST(TWAnySingerOntology, OngBalanceOf) { - // curl -H "Content-Type: application/json" -X POST -d '{"Action":"sendrawtransaction", - // "Version":"1.0.0","Data":"00d1ab1ad0cf0000000000000000000000000000000000000000000000000000000000000000000000004d1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f661400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b650000"}' - // http://polaris2.ont.io:20334/api/v1/transaction?preExec=1 - // - //{"Action":"sendrawtransaction","Desc":"SUCCESS","Error":0,"Result":{"State":1,"Gas":20000,"Result":"27e74d240609","Notify":[]},"Version":"1.0.0"} - auto input = Proto::SigningInput(); - input.set_contract("ONG"); - input.set_method("balanceOf"); - input.set_query_address("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD"); - input.set_nonce(3486522027); - auto data = OngTxBuilder::build(input); - auto rawTx = hex(data); - EXPECT_EQ("00d1ab1ad0cf000000000000000000000000000000000000000000000000000000000000000000000000" - "4d1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f6614000000000000000000" - "00000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b650000", - rawTx); -} - -TEST(TWAnySingerOntology, OngTransfer) { - // tx on polaris test net. - // https://explorer.ont.io/transaction/8a1e59396dcb72d9095088f50d1023294bf9c7b79ba693bd641578f748cbd4e6/testnet - auto ownerPrivateKey = - parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); - auto payerPrivateKey = - parse_hex("4646464646464646464646464646464646464646464646464646464646464652"); - auto input = Proto::SigningInput(); - input.set_contract("ONG"); - input.set_method("transfer"); - input.set_owner_private_key(ownerPrivateKey.data(), ownerPrivateKey.size()); - input.set_payer_private_key(payerPrivateKey.data(), payerPrivateKey.size()); - input.set_to_address("Af1n2cZHhMZumNqKgw9sfCNoTWu9de4NDn"); - input.set_amount(1); - input.set_gas_price(500); - input.set_gas_limit(20000); - input.set_nonce(2827104669); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeOntology); - - EXPECT_EQ("00d19d3182a8f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" - "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" - "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" - "00000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140e27e935b87855efa" - "d62bb76b21c7b591f445f867eff86f888ca6ee1870ecd80f73b8ab199a4d757b4c7b9ed46c4ff8cfa8ae" - "faa90b7fb6485e358034448cba752321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e4" - "47125f927b7486ac4140450047b2efb384129a16ec4c707790e9379b978cc7085170071d8d7c5c037d74" - "3b078bd4e21bb4404c0182a32ee05260e22454dffb34dacccf458dfbee6d32db232103d9fd62df332403" - "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", - hex(output.encoded())); -} - -TEST(TWAnySingerOntology, OngWithdraw) { - // tx on polaris test net. - // https://explorer.ont.io/transaction/433cb7ed4dec32d55be0db104aaa7ade4c7dbe0f62ef94f7b17829f7ac7cd75b/testnet - auto ownerPrivateKey = - parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); - auto payerPrivateKey = - parse_hex("4646464646464646464646464646464646464646464646464646464646464652"); - auto input = Proto::SigningInput(); - input.set_contract("ONG"); - input.set_method("withdraw"); - input.set_owner_private_key(ownerPrivateKey.data(), ownerPrivateKey.size()); - input.set_payer_private_key(payerPrivateKey.data(), payerPrivateKey.size()); - input.set_to_address("AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5"); - input.set_amount(1); - input.set_gas_price(500); - input.set_gas_limit(20000); - input.set_nonce(3784713724); - auto data = OngTxBuilder::build(input); - auto rawTx = hex(data); - EXPECT_EQ( - "00d1fc2596e1f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df68b00c6" - "6b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc81400000000000000000000000000000000000000" - "016a7cc814fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc8516a7cc86c0c7472616e7366657246726f" - "6d1400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f" - "6b65000241400ef868766eeafce71b6ff2a4332aa4363980e66c55ef70aea80e3baee1daf02b43ae6d4c7c8a17" - "8b92f523602426eaa4205ab0ae5944b0fdae0abcbabaefbc4c2321031bec1250aa8f78275f99a6663688f31085" - "848d0ed92f1203e447125f927b7486ac4140c49c23092cd9003247a55792211d816010c7d6204c6e07a6e017da" - "70007b25ee2ab3665103f846300cd03512040275b78ae46812d40cd611058decdff5551e1f232103d9fd62df33" - "2403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", - rawTx); -} diff --git a/tests/Ontology/TWCoinTypeTests.cpp b/tests/Ontology/TWCoinTypeTests.cpp deleted file mode 100644 index 30bb745ab50..00000000000 --- a/tests/Ontology/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWOntologyCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOntology)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeOntology, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeOntology, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeOntology)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeOntology)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeOntology), 0); - ASSERT_EQ(TWBlockchainOntology, TWCoinTypeBlockchain(TWCoinTypeOntology)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeOntology)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeOntology)); - assertStringsEqual(symbol, "ONT"); - assertStringsEqual(txUrl, "https://explorer.ont.io/transaction/t123"); - assertStringsEqual(accUrl, "https://explorer.ont.io/address/a12"); - assertStringsEqual(id, "ontology"); - assertStringsEqual(name, "Ontology"); -} diff --git a/tests/Ontology/TransactionTests.cpp b/tests/Ontology/TransactionTests.cpp deleted file mode 100644 index 0c8d4c7b254..00000000000 --- a/tests/Ontology/TransactionTests.cpp +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "PrivateKey.h" - -#include "Ontology/ParamsBuilder.h" -#include "Ontology/Signer.h" -#include "Ontology/Transaction.h" - -#include - -#include -#include - -using namespace TW; -using namespace TW::Ontology; - -TEST(OntologyTransaction, validity) { - std::vector ontContract{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; - auto fromAddress = Address("AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5"); - auto toAddress = Address("APniYDGozkhUh8Tk7pe35aah2HGJ4fJfVd"); - uint64_t amount = 1; - std::list transferParam{fromAddress.data, toAddress.data, amount}; - std::vector args{transferParam}; - auto invokeCode = ParamsBuilder::buildNativeInvokeCode(ontContract, 0x00, "transfer", args); - uint8_t version = 0; - uint8_t txType = 0xd1; - uint32_t nonce = 1552759011; - uint64_t gasPrice = 600; - uint64_t gasLimit = 300000; - auto tx = - Transaction(version, txType, nonce, gasPrice, gasLimit, toAddress.string(), invokeCode); - std::string hexTx = - "00d1e3388d5c5802000000000000e09304000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c6" - "6b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc81457e9d1a61f9aafa798b6c7fbeae35639681d7d" - "f66a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f" - "6e746f6c6f67792e4e61746976652e496e766f6b650000"; - EXPECT_EQ(hexTx, hex(tx.serialize())); - auto signer1 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); - signer1.sign(tx); - hexTx = - "00d1e3388d5c5802000000000000e09304000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c6" - "6b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc81457e9d1a61f9aafa798b6c7fbeae35639681d7d" - "f66a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f" - "6e746f6c6f67792e4e61746976652e496e766f6b6500014140e03a09d85f56d2ceb5817a1f3a430bab9bf0f469" - "da38afe4a5b33de258a06236d8e0a59d25918a49825455c99f91de9caf8071e38a589a530519705af9081eca23" - "21031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac"; - EXPECT_EQ(520, hex(tx.serialize()).length()); - EXPECT_EQ(hexTx.substr(0, 20), hex(tx.serialize()).substr(0, 20)); - auto signer2 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); - signer2.addSign(tx); - auto result = tx.serialize(); - auto verifyPosition1 = - hex(result).find("21031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac"); - auto verifyPosition2 = - hex(result).find("2103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac"); - EXPECT_EQ(450, verifyPosition1); - EXPECT_EQ(654, verifyPosition2); - EXPECT_EQ(724, hex(result).length()); -} \ No newline at end of file diff --git a/tests/Optimism/TWCoinTypeTests.cpp b/tests/Optimism/TWCoinTypeTests.cpp deleted file mode 100644 index 11a0910a505..00000000000 --- a/tests/Optimism/TWCoinTypeTests.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWOptimismCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOptimism)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x6fd99288be9bf71eb002bb31da10a4fb0fbbb3c45ae73693b212f49c9db7df8f")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeOptimism, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x1f932361e31d206b4f6b2478123a9d0f8c761031")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeOptimism, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeOptimism)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeOptimism)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeOptimism), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeOptimism)); - - assertStringsEqual(symbol, "OETH"); - assertStringsEqual(txUrl, "https://optimistic.etherscan.io/tx/0x6fd99288be9bf71eb002bb31da10a4fb0fbbb3c45ae73693b212f49c9db7df8f"); - assertStringsEqual(accUrl, "https://optimistic.etherscan.io/address/0x1f932361e31d206b4f6b2478123a9d0f8c761031"); - assertStringsEqual(id, "optimism"); - assertStringsEqual(name, "Optimistic Ethereum"); -} diff --git a/tests/POANetwork/TWCoinTypeTests.cpp b/tests/POANetwork/TWCoinTypeTests.cpp deleted file mode 100644 index 37ca284bc69..00000000000 --- a/tests/POANetwork/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWPOANetworkCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypePOANetwork)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypePOANetwork, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypePOANetwork, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypePOANetwork)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypePOANetwork)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypePOANetwork), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypePOANetwork)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypePOANetwork)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypePOANetwork)); - assertStringsEqual(symbol, "POA"); - assertStringsEqual(txUrl, "https://blockscout.com/poa/core/tx/t123"); - assertStringsEqual(accUrl, "https://blockscout.com/poa/core/address/a12"); - assertStringsEqual(id, "poa"); - assertStringsEqual(name, "POA Network"); -} diff --git a/tests/Polkadot/AddressTests.cpp b/tests/Polkadot/AddressTests.cpp deleted file mode 100644 index 6cc3fb06368..00000000000 --- a/tests/Polkadot/AddressTests.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "Polkadot/Address.h" -#include "PublicKey.h" -#include "PrivateKey.h" -#include -#include - -using namespace TW; -using namespace TW::Polkadot; - -TEST(PolkadotAddress, Validation) { - // Substrate ed25519 - ASSERT_FALSE(Address::isValid("5FqqU2rytGPhcwQosKRtW1E3ha6BJKAjHgtcodh71dSyXhoZ")); - // Bitcoin - ASSERT_FALSE(Address::isValid("1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA")); - // Kusama ed25519 - ASSERT_FALSE(Address::isValid("FHKAe66mnbk8ke8zVWE9hFVFrJN1mprFPVmD5rrevotkcDZ")); - // Kusama secp256k1 - ASSERT_FALSE(Address::isValid("FxQFyTorsjVsjjMyjdgq8w5vGx8LiA1qhWbRYcFijxKKchx")); - // Kusama sr25519 - ASSERT_FALSE(Address::isValid("EJ5UJ12GShfh7EWrcNZFLiYU79oogdtXFUuDDZzk7Wb2vCe")); - - // Polkadot ed25519 - ASSERT_TRUE(Address::isValid("15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu")); - // Polkadot sr25519 - ASSERT_TRUE(Address::isValid("15AeCjMpcSt3Fwa47jJBd7JzQ395Kr2cuyF5Zp4UBf1g9ony")); -} - -TEST(PolkadotAddress, FromPrivateKey) { - // subkey phrase `chief menu kingdom stereo hope hazard into island bag trick egg route` - auto privateKey = PrivateKey(parse_hex("0x612d82bc053d1b4729057688ecb1ebf62745d817ddd9b595bc822f5f2ba0e41a")); - auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); - ASSERT_EQ(address.string(), "15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu"); -} - -TEST(PolkadotAddress, FromPublicKey) { - auto publicKey = PublicKey(parse_hex("0xbeff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519); - auto address = Address(publicKey); - ASSERT_EQ(address.string(), "15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu"); -} - -TEST(PolkadotAddress, FromString) { - auto address = Address("15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu"); - ASSERT_EQ(address.string(), "15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu"); -} diff --git a/tests/Polkadot/SignerTests.cpp b/tests/Polkadot/SignerTests.cpp deleted file mode 100644 index 5b689e8d5dd..00000000000 --- a/tests/Polkadot/SignerTests.cpp +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Polkadot/Signer.h" -#include "Polkadot/Extrinsic.h" -#include "Polkadot/Address.h" -#include "SS58Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "proto/Polkadot.pb.h" -#include "uint256.h" - -#include -#include - - -namespace TW::Polkadot { - auto privateKey = PrivateKey(parse_hex("0xabf8e5bdbe30c65656c0a3cbd181ff8a56294a69dfedd27982aace4a76909115")); - auto privateKeyIOS = PrivateKey(parse_hex("37932b086586a6675e66e562fe68bd3eeea4177d066619c602fe3efc290ada62")); - auto privateKeyThrow2 = PrivateKey(parse_hex("70a794d4f1019c3ce002f33062f45029c4f930a56b3d20ec477f7668c6bbc37f")); - auto addressThrow2 = "14Ztd3KJDaB9xyJtRkREtSZDdhLSbm7UUKt8Z7AwSv7q85G2"; - auto toPublicKey = PublicKey(parse_hex("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"), TWPublicKeyTypeED25519); - auto genesisHash = parse_hex("91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"); - auto controller1 = "14xKzzU1ZYDnzFj7FgdtDAYSMJNARjDc2gNw4XAFDgr4uXgp"; - -TEST(PolkadotSigner, SignTransfer_9fd062) { - auto toAddress = Address("13ZLCqJNPsRZYEbwjtZZFpWt9GyFzg5WahXCVWKpWdUJqrQ5"); - - auto input = Proto::SigningInput(); - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - auto blockHash = parse_hex("0x5d2143bb808626d63ad7e1cda70fa8697059d670a992e82cd440fbb95ea40351"); - input.set_block_hash(blockHash.data(), blockHash.size()); - input.set_nonce(3); - input.set_spec_version(26); - { - PublicKey publicKey = privateKeyThrow2.getPublicKey(TWPublicKeyTypeED25519); - Address address = Address(publicKey); - EXPECT_EQ(address.string(), addressThrow2); - } - input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(5); - - // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true - auto era = input.mutable_era(); - era->set_block_number(3541050); - era->set_period(64); - - auto balanceCall = input.mutable_balance_call(); - auto transfer = balanceCall->mutable_transfer(); - auto value = store(uint256_t(2000000000)); // 0.2 - transfer->set_to_address(toAddress.string()); - transfer->set_value(value.data(), value.size()); - - auto extrinsic = Extrinsic(input); - auto preimage = extrinsic.encodePayload(); - EXPECT_EQ(hex(preimage), "05007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0300943577a5030c001a0000000500000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c35d2143bb808626d63ad7e1cda70fa8697059d670a992e82cd440fbb95ea40351"); - - auto output = Signer::sign(input); - // https://polkadot.subscan.io/extrinsic/0x9fd06208a6023e489147d8d93f0182b0cb7e45a40165247319b87278e08362d8 - EXPECT_EQ(hex(output.encoded()), "3502849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f7830073e59cef381aedf56d7af076bafff9857ffc1e3bd7d1d7484176ff5b58b73f1211a518e1ed1fd2ea201bd31869c0798bba4ffe753998c409d098b65d25dff801a5030c0005007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0300943577"); -} - -TEST(PolkadotSigner, SignTransferDOT) { - - auto blockHash = parse_hex("0x343a3f4258fd92f5ca6ca5abdf473d86a78b0bcd0dc09c568ca594245cc8c642"); - auto toAddress = SS58Address(toPublicKey, TWSS58AddressTypePolkadot); - - auto input = Proto::SigningInput(); - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - input.set_block_hash(blockHash.data(), blockHash.size()); - - input.set_nonce(0); - input.set_spec_version(17); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(3); - - auto& era = *input.mutable_era(); - era.set_block_number(927699); - era.set_period(8); - - auto balanceCall = input.mutable_balance_call(); - auto& transfer = *balanceCall->mutable_transfer(); - auto value = store(uint256_t(12345)); - transfer.set_to_address(toAddress.string()); - transfer.set_value(value.data(), value.size()); - - auto extrinsic = Extrinsic(input); - auto preimage = extrinsic.encodePayload(); - auto output = Signer::sign(input); - - ASSERT_EQ(hex(preimage), "05008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c032000000110000000300000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3343a3f4258fd92f5ca6ca5abdf473d86a78b0bcd0dc09c568ca594245cc8c642"); - ASSERT_EQ(hex(output.encoded()), "29028488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee003d91a06263956d8ce3ce5c55455baefff299d9cb2bb3f76866b6828ee4083770b6c03b05d7b6eb510ac78d047002c1fe5c6ee4b37c9c5a8b09ea07677f12e50d3200000005008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c0"); -} - -TEST(PolkadotSigner, SignTransfer_72dd5b) { - - auto blockHash = parse_hex("7d5fa17b70251d0806f26156b1b698dfd09e040642fa092595ce0a78e9e84fcd"); - - auto input = Proto::SigningInput(); - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - input.set_block_hash(blockHash.data(), blockHash.size()); - - input.set_nonce(1); - input.set_spec_version(28); - input.set_private_key(privateKeyIOS.bytes.data(), privateKeyIOS.bytes.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(6); - - auto& era = *input.mutable_era(); - era.set_block_number(3910736); - era.set_period(64); - - auto balanceCall = input.mutable_balance_call(); - auto& transfer = *balanceCall->mutable_transfer(); - auto value = store(uint256_t(10000000000)); - transfer.set_to_address("13ZLCqJNPsRZYEbwjtZZFpWt9GyFzg5WahXCVWKpWdUJqrQ5"); - transfer.set_value(value.data(), value.size()); - - auto extrinsic = Extrinsic(input); - auto preimage = extrinsic.encodePayload(); - auto output = Signer::sign(input); - - ASSERT_EQ(hex(preimage), "0500007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b5402050104001c0000000600000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c37d5fa17b70251d0806f26156b1b698dfd09e040642fa092595ce0a78e9e84fcd"); - ASSERT_EQ(hex(output.encoded()), "410284008d96660f14babe708b5e61853c9f5929bc90dd9874485bf4d6dc32d3e6f22eaa0038ec4973ab9773dfcbf170b8d27d36d89b85c3145e038d68914de83cf1f7aca24af64c55ec51ba9f45c5a4d74a9917dee380e9171108921c3e5546e05be15206050104000500007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b5402"); -} - -TEST(PolkadotSigner, SignBond_8da66d) { - auto input = Proto::SigningInput(); - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - auto blockHash = parse_hex("0xf1eee612825f29abd3299b486e401299df2faa55b7ce1e34bf2243bd591905fc"); - input.set_block_hash(blockHash.data(), blockHash.size()); - input.set_nonce(0); - input.set_spec_version(26); - { - PublicKey publicKey = privateKeyThrow2.getPublicKey(TWPublicKeyTypeED25519); - Address address = Address(publicKey); - EXPECT_EQ(address.string(), addressThrow2); - } - input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(5); - - // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true - auto era = input.mutable_era(); - era->set_block_number(3540912); - era->set_period(64); - - auto stakingCall = input.mutable_staking_call(); - auto bond = stakingCall->mutable_bond(); - auto value = store(uint256_t(11000000000)); // 1.1 - bond->set_controller(addressThrow2); // myself - bond->set_value(value.data(), value.size()); - bond->set_reward_destination(Proto::RewardDestination::STASH); - - auto output = Signer::sign(input); - // https://polkadot.subscan.io/extrinsic/0x8da66d3fe0f592cff714ec107289370365117a1abdb72a19ac91181fdcf62bba - ASSERT_EQ(hex(output.encoded()), "3d02849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f783009025843bc49c1c4fbc99dbbd290c92f9879665d55b02f110abfb4800f0e7630877d2cffd853deae7466c22fbc8616a609e1b92615bb365ea8adccba5ef7624050503000007009dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f7830700aea68f0201"); -} - -TEST(PolkadotSigner, SignBondAndNominate_4955314_2) { - - auto key = parse_hex("7f44b19b391a8015ca4c7d94097b3695867a448d1391e7f3243f06987bdb6858"); - auto input = Proto::SigningInput(); - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - input.set_block_hash(genesisHash.data(), genesisHash.size()); - input.set_nonce(4); - input.set_spec_version(30); - input.set_private_key(key.data(), key.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(7); - - auto stakingCall = input.mutable_staking_call(); - auto bondnom = stakingCall->mutable_bond_and_nominate(); - auto value = store(uint256_t(10000000000)); // 1 DOT - bondnom->set_controller("13ZLCqJNPsRZYEbwjtZZFpWt9GyFzg5WahXCVWKpWdUJqrQ5"); - bondnom->set_value(value.data(), value.size()); - bondnom->set_reward_destination(Proto::RewardDestination::STASH); - bondnom->add_nominators("1zugcavYA9yCuYwiEYeMHNJm9gXznYjNfXQjZsZukF1Mpow"); - bondnom->add_nominators("15oKi7HoBQbwwdQc47k71q4sJJWnu5opn1pqoGx4NAEYZSHs"); - - auto output = Signer::sign(input); - // https://polkadot.subscan.io/extrinsic/4955314-2 - ASSERT_EQ(hex(output.encoded()), "6103840036092fac541e0e5feda19e537c679b487566d7101141c203ac8322c27e5f076a00a8b1f859d788f11a958e98b731358f89cf3fdd41a667ea992522e8d4f46915f4c03a1896f2ac54bdc5f16e2ce8a2a3bf233d02aad8192332afd2113ed6688e0d0010001a02080700007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b540201070508002c2a55b5ffdca266bd0207df97565b03255f70783ca1a349be5ed9f44589c36000d44533a4d21fd9d6f5d57c8cd05c61a6f23f9131cec8ae386b6b437db399ec3d"); -} - -TEST(PolkadotSigner, SignNominate_452522) { - auto input = Proto::SigningInput(); - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - auto blockHash = parse_hex("0x211787d016e39007ac054547737a10542620013e73648b3134541d536cb44e2c"); - input.set_block_hash(blockHash.data(), blockHash.size()); - input.set_nonce(1); - input.set_spec_version(26); - input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(5); - - // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true - auto era = input.mutable_era(); - era->set_block_number(3540945); - era->set_period(64); - - auto stakingCall = input.mutable_staking_call(); - auto nominate = stakingCall->mutable_nominate(); - - nominate->add_nominators(controller1); - nominate->add_nominators("1REAJ1k691g5Eqqg9gL7vvZCBG7FCCZ8zgQkZWd4va5ESih"); - - auto output = Signer::sign(input); - // https://polkadot.subscan.io/extrinsic/0x4525224b7d8f3e58de3a54a9fbfd071401c2b737f314c972a2bb087a0ff508a6 - ASSERT_EQ(hex(output.encoded()), "a502849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f78300d73ff0dc456704743f70173a56e6c13e88a6e1dddb38a23552a066e44fb64e2c9d8a5e9a76afb9489b8540365f668bddd34b7d9c8dbdc4600e6316080e55a30315010400070508aee72821ca00e62304e4f0d858122a65b87c8df4f0eae224ae064b951d39f610127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a"); -} - -TEST(PolkadotSigner, SignNominate2) { - auto blockHash = parse_hex("d22a6b2e3e61325050718bd04a14da9efca1f41c9f0a525c375d36106e25af68"); - auto input = Proto::SigningInput(); - - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - input.set_block_hash(blockHash.data(), blockHash.size()); - input.set_nonce(0); - input.set_spec_version(17); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(3); - - auto stakingCall = input.mutable_staking_call(); - auto& nominate = *stakingCall->mutable_nominate(); - // payload size larger than 256, will be hashed - nominate.add_nominators("1zugcabYjgfQdMLC3cAzQ8tJZMo45tMnGpivpAzpxB4CZyK"); - nominate.add_nominators("1REAJ1k691g5Eqqg9gL7vvZCBG7FCCZ8zgQkZWd4va5ESih"); - nominate.add_nominators("1WG3jyNqniQMRZGQUc7QD2kVLT8hkRPGMSqAb5XYQM1UDxN"); - nominate.add_nominators("16QFrtU6kDdBjxY8qEKz5EEfuDkHxqG8pix3wSGKQzRcuWHo"); - nominate.add_nominators("14ShUZUYUR35RBZW6uVVt1zXDxmSQddkeDdXf1JkMA6P721N"); - nominate.add_nominators("15MUBwP6dyVw5CXF9PjSSv7SdXQuDSwjX86v1kBodCSWVR7c"); - - auto output = Signer::sign(input); - - ASSERT_EQ(hex(output.encoded()), "a1048488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee00135bbc68b67fffadaf7e98b6402c4fc60382765f543225083a024b0e0ff8071d4ec4ddd67a65828113cc76f3208765608be010d2fcfdcd47e8fe342872704c000000000705182c2a55b5a116a4c88aff57e8f2b70ba72dda72dda4b78630e16ad0ca69006f18127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a1650c532ed1a8641e8922aa24ade0ff411d03edd9ed1c6b7fe42f1a801cee37ceee9d5d071a418b51c02b456d5f5cefd6231041ad59b0e8379c59c11ba4a2439984e16482c99cfad1436111e321a86d87d0fac203bf64538f888e45d793b5413c08d5de7a5d97bea2c7ddf516d0635bddc43f326ae2f80e2595b49d4a08c4619"); -} - -TEST(PolkadotSigner, SignChill) { - auto blockHash = parse_hex("1d4a1ecc8b1c37bf0ba5d3e0bf14ec5402fbb035eeaf6d8042c07ca5f8c57429"); - auto input = Proto::SigningInput(); - - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - input.set_block_hash(blockHash.data(), blockHash.size()); - input.set_nonce(0); - input.set_spec_version(17); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(3); - - auto stakingCall = input.mutable_staking_call(); - [[maybe_unused]] auto &chill = *stakingCall->mutable_chill(); - auto output = Signer::sign(input); - - ASSERT_EQ(hex(output.encoded()), "9d018488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee0088b5e1cd93ba74b82e329f95e1b22660385970182172b2ae280801fdd1ee5652cf7bf319e5e176ccc299dd8eb1e7fccb0ea7717efaf4aacd7640789dd09c1e070000000706"); -} - -TEST(PolkadotSigner, SignWithdraw) { - auto blockHash = parse_hex("7b4d1d1e2573eabcc90a3e96058eb0d8d21d7a0b636e8030d152d9179a345dda"); - auto input = Proto::SigningInput(); - - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - input.set_block_hash(blockHash.data(), blockHash.size()); - input.set_nonce(0); - input.set_spec_version(17); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(3); - - auto stakingCall = input.mutable_staking_call(); - auto& withdraw = *stakingCall->mutable_withdraw_unbonded(); - withdraw.set_slashing_spans(10); - - auto output = Signer::sign(input); - - ASSERT_EQ(hex(output.encoded()), "ad018488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee002e49bf0dec9bef01dd3bd25419e2147dc983613d0860108f889f9ff2d062c5e3267e309e2dbc35dd2fc2b877b57d86a5f12cbeb8217485be32be3c34d2507d0e00000007030a000000"); -} - -TEST(PolkadotSigner, SignUnbond_070957) { - auto input = Proto::SigningInput(); - - input.set_genesis_hash(genesisHash.data(), genesisHash.size()); - auto blockHash = parse_hex("0x53040c71c6061bd256346b81fcb3545c13b5c34c7cd0c2c25f00aa6e564b16d5"); - input.set_block_hash(blockHash.data(), blockHash.size()); - input.set_nonce(2); - input.set_spec_version(26); - input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); - input.set_network(Proto::Network::POLKADOT); - input.set_transaction_version(5); - - auto era = input.mutable_era(); - era->set_block_number(3540983); - era->set_period(64); - - auto stakingCall = input.mutable_staking_call(); - auto unbond = stakingCall->mutable_unbond(); - auto value = store(uint256_t(4000000000)); - unbond->set_value(value.data(), value.size()); - - auto output = Signer::sign(input); - // https://polkadot.subscan.io/extrinsic/0x070957ab697adbe11f7d72a1314d0a81d272a747d2e6880818073317125f980a - ASSERT_EQ(hex(output.encoded()), "b501849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f783003a762d9dc3f2aba8922c4babf7e6622ca1d74da17ab3f152d8f29b0ffee53c7e5e150915912a9dfd98ef115d272e096543eef9f513207dd606eea97d023a64087503080007020300286bee"); -} - -} // namespace diff --git a/tests/Polkadot/TWCoinTypeTests.cpp b/tests/Polkadot/TWCoinTypeTests.cpp deleted file mode 100644 index 82030aaeeec..00000000000 --- a/tests/Polkadot/TWCoinTypeTests.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWPolkadotCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypePolkadot)); - - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb96f97d8ee508f420e606e1a6dcc74b88844713ddec2bd7cf4e3aa6b1d6beef4")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypePolkadot, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("13hJFqnkqQbmgnGQteGntjMjTdmTBRE8Z93JqxsrpgT7Yjd2")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypePolkadot, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypePolkadot)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypePolkadot)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypePolkadot), 10); - ASSERT_EQ(TWBlockchainPolkadot, TWCoinTypeBlockchain(TWCoinTypePolkadot)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypePolkadot)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypePolkadot)); - assertStringsEqual(symbol, "DOT"); - assertStringsEqual(txUrl, "https://polkadot.subscan.io/extrinsic/0xb96f97d8ee508f420e606e1a6dcc74b88844713ddec2bd7cf4e3aa6b1d6beef4"); - assertStringsEqual(accUrl, "https://polkadot.subscan.io/account/13hJFqnkqQbmgnGQteGntjMjTdmTBRE8Z93JqxsrpgT7Yjd2"); - assertStringsEqual(id, "polkadot"); - assertStringsEqual(name, "Polkadot"); -} diff --git a/tests/Polygon/TWCoinTypeTests.cpp b/tests/Polygon/TWCoinTypeTests.cpp deleted file mode 100644 index a1810ade109..00000000000 --- a/tests/Polygon/TWCoinTypeTests.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWPolygonCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypePolygon)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xe26ed1470d5bf99a53d687843e7acdf7e4ba6620af93b4d672e714de90476e8e")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypePolygon, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x720E1fa107A1Df39Db4E78A3633121ac36Bec132")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypePolygon, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypePolygon)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypePolygon)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypePolygon), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypePolygon)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypePolygon)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypePolygon)); - assertStringsEqual(symbol, "MATIC"); - assertStringsEqual(txUrl, "https://polygonscan.com/tx/0xe26ed1470d5bf99a53d687843e7acdf7e4ba6620af93b4d672e714de90476e8e"); - assertStringsEqual(accUrl, "https://polygonscan.com/address/0x720E1fa107A1Df39Db4E78A3633121ac36Bec132"); - assertStringsEqual(id, "polygon"); - assertStringsEqual(name, "Polygon"); -} diff --git a/tests/PrivateKeyTests.cpp b/tests/PrivateKeyTests.cpp deleted file mode 100644 index f774f7d8eb6..00000000000 --- a/tests/PrivateKeyTests.cpp +++ /dev/null @@ -1,356 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "PrivateKey.h" -#include "PublicKey.h" -#include "HexCoding.h" -#include "Hash.h" - -#include - -using namespace TW; -using namespace std; - - -TEST(PrivateKey, CreateValid) { - Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveSECP256k1)); - auto privateKey = PrivateKey(privKeyData); - EXPECT_EQ(hex(privKeyData), hex(privateKey.bytes)); -} - -string TestInvalid(const Data& privKeyData) { - try { - auto privateKey = PrivateKey(privKeyData); - return hex(privateKey.bytes); - } catch (invalid_argument& ex) { - // expected exception - return string("EXCEPTION: ") + string(ex.what()); - } -} - -TEST(PrivateKey, InvalidShort) { - string res = TestInvalid(parse_hex("deadbeef")); - EXPECT_EQ("EXCEPTION: Invalid private key data", res); -} - -TEST(PrivateKey, InvalidAllZeros) { - string res = TestInvalid(Data(32)); - EXPECT_EQ("EXCEPTION: Invalid private key data", res); -} - -TEST(PrivateKey, InvalidSECP256k1) { - { - auto privKeyData = parse_hex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); - auto valid = PrivateKey::isValid(privKeyData, TWCurveSECP256k1); - EXPECT_EQ(valid, false); - } - { - auto privKeyData = parse_hex("0000000000000000000000000000000000000000000000000000000000000000"); - auto valid = PrivateKey::isValid(privKeyData, TWCurveSECP256k1); - EXPECT_EQ(valid, false); - } -} - -string TestInvalidExtended(const Data& data, const Data& ext, const Data& chainCode) { - try { - auto privateKey = PrivateKey(data, ext, chainCode); - return hex(privateKey.bytes); - } catch (invalid_argument& ex) { - // expected exception - return string("EXCEPTION: ") + string(ex.what()); - } -} - -TEST(PrivateKey, CreateExtendedInvalid) { - { - string res = TestInvalidExtended( - parse_hex("deadbeed"), - parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), - parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4") - ); - EXPECT_EQ("EXCEPTION: Invalid private key or extended key data", res); - } - { - string res = TestInvalidExtended( - parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), - parse_hex("deadbeed"), - parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4") - ); - EXPECT_EQ("EXCEPTION: Invalid private key or extended key data", res); - } - { - string res = TestInvalidExtended( - parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), - parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), - parse_hex("deadbeed") - ); - EXPECT_EQ("EXCEPTION: Invalid private key or extended key data", res); - } -} - -TEST(PrivateKey, Valid) { - Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveSECP256k1)); - EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveED25519)); -} - -TEST(PrivateKey, PublicKey) { - Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = PrivateKey(privKeyData); - { - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - EXPECT_EQ( - "4870d56d074c50e891506d78faa4fb69ca039cc5f131eb491e166b975880e867", - hex(publicKey.bytes) - ); - } - { - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - EXPECT_EQ( - "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1", - hex(publicKey.bytes) - ); - } - { - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); - EXPECT_EQ( - "0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91", - hex(publicKey.bytes) - ); - } - { - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1Extended); - EXPECT_EQ( - "046d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab918b4fe46ccbf56701fb210d67d91c5779468f6b3fdc7a63692b9b62543f47ae", - hex(publicKey.bytes) - ); - } -} - -TEST(PrivateKey, Cleanup) { - Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = new PrivateKey(privKeyData); - auto ptr = privateKey->bytes.data(); - ASSERT_EQ(hex(privKeyData), hex(data(ptr, 32))); - - privateKey->cleanup(); - - // Memory cleaned (filled with 0s). They may be overwritten by something else; we check that it is not equal to original, most of it has changed. - ASSERT_EQ(hex(data(ptr, 32)), "0000000000000000000000000000000000000000000000000000000000000000"); - - delete privateKey; - - // Note: it would be good to check the memory area after deletion of the object, but this is not possible -} - -TEST(PrivateKey, PrivateKeyExtended) { - // Non-extended: both keys are 32 bytes. - auto privateKeyNonext = PrivateKey(parse_hex( - "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5" - )); - EXPECT_EQ("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", hex(privateKeyNonext.bytes)); - auto publicKeyNonext = privateKeyNonext.getPublicKey(TWPublicKeyTypeED25519); - EXPECT_EQ(32, publicKeyNonext.bytes.size()); - - // Extended keys: private key is 3x32 bytes, public key is 64 bytes - auto privateKeyExt = PrivateKey(parse_hex( - "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4" - )); - EXPECT_EQ("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744", hex(privateKeyExt.bytes)); - EXPECT_EQ("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff", hex(privateKeyExt.extensionBytes)); - EXPECT_EQ("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4", hex(privateKeyExt.chainCodeBytes)); - auto publicKeyExt = privateKeyExt.getPublicKey(TWPublicKeyTypeED25519Extended); - EXPECT_EQ(64, publicKeyExt.bytes.size()); - - // Try other constructor for extended key - auto privateKeyExtOne = PrivateKey(parse_hex( - "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4" - )); - EXPECT_EQ("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744", hex(privateKeyExtOne.bytes)); - EXPECT_EQ("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff", hex(privateKeyExtOne.extensionBytes)); - EXPECT_EQ("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4", hex(privateKeyExtOne.chainCodeBytes)); -} - -TEST(PrivateKey, PrivateKeyExtendedError) { - // TWPublicKeyTypeED25519Extended pubkey with non-extended private: error - auto privateKeyNonext = PrivateKey(parse_hex( - "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5" - )); - try { - auto publicKeyError = privateKeyNonext.getPublicKey(TWPublicKeyTypeED25519Extended); - } catch (invalid_argument& ex) { - // expected exception - return; - } - FAIL() << "Should throw Invalid empty key extension"; -} - -TEST(PrivateKey, getSharedKey) { - Data privKeyData = parse_hex("9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0"); - EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveSECP256k1)); - auto privateKey = PrivateKey(privKeyData); - - const Data pubKeyData = parse_hex("02a18a98316b5f52596e75bfa5ca9fa9912edd0c989b86b73d41bb64c9c6adb992"); - EXPECT_TRUE(PublicKey::isValid(pubKeyData, TWPublicKeyTypeSECP256k1)); - PublicKey publicKey(pubKeyData, TWPublicKeyTypeSECP256k1); - EXPECT_TRUE(publicKey.isCompressed()); - - const Data derivedKeyData = privateKey.getSharedKey(publicKey, TWCurveSECP256k1); - - EXPECT_EQ( - "ef2cf705af8714b35c0855030f358f2bee356ff3579cea2607b2025d80133c3a", - hex(derivedKeyData) - ); -} - -/** - * Valid test vector from Wycherproof project - * Source: https://github.com/google/wycheproof/blob/master/testvectors/ecdh_secp256k1_test.json#L31 - */ -TEST(PrivateKey, getSharedKeyWycherproof) { - // Stripped left-padded zeroes from: `00f4b7ff7cccc98813a69fae3df222bfe3f4e28f764bf91b4a10d8096ce446b254` - Data privKeyData = parse_hex("f4b7ff7cccc98813a69fae3df222bfe3f4e28f764bf91b4a10d8096ce446b254"); - EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveSECP256k1)); - auto privateKey = PrivateKey(privKeyData); - - // Decoded from ASN.1 & uncompressed `3056301006072a8648ce3d020106052b8104000a03420004d8096af8a11e0b80037e1ee68246b5dcbb0aeb1cf1244fd767db80f3fa27da2b396812ea1686e7472e9692eaf3e958e50e9500d3b4c77243db1f2acd67ba9cc4` - const Data pubKeyData = parse_hex("02d8096af8a11e0b80037e1ee68246b5dcbb0aeb1cf1244fd767db80f3fa27da2b"); - EXPECT_TRUE(PublicKey::isValid(pubKeyData, TWPublicKeyTypeSECP256k1)); - PublicKey publicKey(pubKeyData, TWPublicKeyTypeSECP256k1); - EXPECT_TRUE(publicKey.isCompressed()); - - const Data derivedKeyData = privateKey.getSharedKey(publicKey, TWCurveSECP256k1); - - // SHA-256 of encoded x-coordinate `02544dfae22af6af939042b1d85b71a1e49e9a5614123c4d6ad0c8af65baf87d65` - EXPECT_EQ( - "81165066322732362ca5d3f0991d7f1f7d0aad7ea533276496785d369e35159a", - hex(derivedKeyData) - ); -} - -TEST(PrivateKey, getSharedKeyBidirectional) { - Data privKeyData1 = parse_hex("9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0"); - EXPECT_TRUE(PrivateKey::isValid(privKeyData1, TWCurveSECP256k1)); - auto privateKey1 = PrivateKey(privKeyData1); - auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeSECP256k1); - - Data privKeyData2 = parse_hex("ef2cf705af8714b35c0855030f358f2bee356ff3579cea2607b2025d80133c3a"); - EXPECT_TRUE(PrivateKey::isValid(privKeyData2, TWCurveSECP256k1)); - auto privateKey2 = PrivateKey(privKeyData2); - auto publicKey2 = privateKey2.getPublicKey(TWPublicKeyTypeSECP256k1); - - const Data derivedKeyData1 = privateKey1.getSharedKey(publicKey2, TWCurveSECP256k1); - const Data derivedKeyData2 = privateKey2.getSharedKey(publicKey1, TWCurveSECP256k1); - - EXPECT_EQ(hex(derivedKeyData1), hex(derivedKeyData2)); -} - -TEST(PrivateKey, getSharedKeyError) { - Data privKeyData = parse_hex("9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0"); - auto privateKey = PrivateKey(privKeyData); - - const Data pubKeyData = parse_hex("02a18a98316b5f52596e75bfa5ca9fa9912edd0c989b86b73d41bb64c9c6adb992"); - PublicKey publicKey(pubKeyData, TWPublicKeyTypeSECP256k1); - - const Data derivedKeyData = privateKey.getSharedKey(publicKey, TWCurveCurve25519); - const Data expected = {}; - - EXPECT_EQ(expected, derivedKeyData); -} - -TEST(PrivateKey, SignSECP256k1) { - Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = PrivateKey(privKeyData); - Data messageData = TW::data("hello"); - Data hash = Hash::keccak256(messageData); - Data actual = privateKey.sign(hash, TWCurveSECP256k1); - - EXPECT_EQ( - "8720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba624d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de901", - hex(actual) - ); -} - -TEST(PrivateKey, SignExtended) { - const auto privateKeyExt = PrivateKey(parse_hex( - "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4" - )); - Data messageData = TW::data("hello"); - Data hash = Hash::keccak256(messageData); - Data actual = privateKeyExt.sign(hash, TWCurveED25519Extended); - - EXPECT_EQ( - "375df53b6a4931dcf41e062b1c64288ed4ff3307f862d5c1b1c71964ce3b14c99422d0fdfeb2807e9900a26d491d5e8a874c24f98eec141ed694d7a433a90f08", - hex(actual) - ); -} - -TEST(PrivateKey, SignSchnorr) { - const auto privateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); - const Data messageData = TW::data("hello schnorr"); - const Data digest = Hash::sha256(messageData); - const auto signature = privateKey.signSchnorr(digest, TWCurveSECP256k1); - EXPECT_EQ(hex(signature), - "b8118ccb99563fe014279c957b0a9d563c1666e00367e9896fe541765246964f64a53052513da4e6dc20fdaf69ef0d95b4ca51c87ad3478986cf053c2dd0b853" - ); -} - -TEST(PrivateKey, SignSchnorrWrongType) { - const auto privateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); - const Data messageData = TW::data("hello schnorr"); - const Data digest = Hash::sha256(messageData); - const auto signature = privateKey.signSchnorr(digest, TWCurveNIST256p1); - EXPECT_EQ(signature.size(), 0); -} - -TEST(PrivateKey, SignNIST256p1) { - Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = PrivateKey(privKeyData); - Data messageData = TW::data("hello"); - Data hash = Hash::keccak256(messageData); - Data actual = privateKey.sign(hash, TWCurveNIST256p1); - - EXPECT_EQ( - "8859e63a0c0cc2fc7f788d7e78406157b288faa6f76f76d37c4cd1534e8d83c468f9fd6ca7dde378df594625dcde98559389569e039282275e3d87c26e36447401", - hex(actual) - ); -} - -int isCanonical(uint8_t by, uint8_t sig[64]) { - return 1; -} - -TEST(PrivateKey, SignCanonicalSECP256k1) { - Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = PrivateKey(privKeyData); - Data messageData = TW::data("hello"); - Data hash = Hash::keccak256(messageData); - Data actual = privateKey.sign(hash, TWCurveSECP256k1, isCanonical); - - EXPECT_EQ( - "208720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba624d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de9", - hex(actual) - ); -} - -TEST(PrivateKey, SignShortDigest) { - Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = PrivateKey(privKeyData); - Data shortDigest = TW::data("12345"); - { - Data actual = privateKey.sign(shortDigest, TWCurveSECP256k1); - EXPECT_EQ(actual.size(), 0); - } - { - Data actual = privateKey.sign(shortDigest, TWCurveNIST256p1); - EXPECT_EQ(actual.size(), 0); - } - { - Data actual = privateKey.sign(shortDigest, TWCurveSECP256k1, isCanonical); - EXPECT_EQ(actual.size(), 0); - } -} diff --git a/tests/PublicKeyTests.cpp b/tests/PublicKeyTests.cpp deleted file mode 100644 index 7f056f3c8f4..00000000000 --- a/tests/PublicKeyTests.cpp +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "PublicKey.h" - -#include "Hash.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "interface/TWTestUtilities.h" - -#include - -using namespace TW; - -TEST(PublicKeyTests, CreateFromPrivateSecp256k1) { - const Data key = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = PrivateKey(key); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - EXPECT_EQ(publicKey.bytes.size(), 33); - EXPECT_EQ(hex(publicKey.bytes), "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); - EXPECT_EQ(publicKey.isCompressed(), true); - EXPECT_TRUE(PublicKey::isValid(publicKey.bytes, TWPublicKeyTypeSECP256k1)); -} - -TEST(PublicKeyTests, CreateFromDataSecp256k1) { - const Data key = parse_hex("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); - PublicKey publicKey(key, TWPublicKeyTypeSECP256k1); - EXPECT_EQ(hex(publicKey.bytes), hex(key)); -} - -TEST(PublicKeyTests, CreateInvalid) { - const Data keyInvalid = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32af"); // too short - try { - PublicKey publicKey(keyInvalid, TWPublicKeyTypeSECP256k1); - } catch (const std::invalid_argument&) { - return; // OK - } - FAIL() << "Missing expected exception"; -} - -TEST(PublicKeyTests, CreateBlake) { - const auto privateKeyHex = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; - const auto publicKeyKeyHex = "b689ab808542e13f3d2ec56fe1efe43a1660dcadc73ce489fde7df98dd8ce5d9"; - { - auto publicKey = PrivateKey(parse_hex(privateKeyHex)).getPublicKey(TWPublicKeyTypeED25519Blake2b); - EXPECT_EQ(hex(publicKey.bytes), publicKeyKeyHex); - EXPECT_EQ(publicKey.bytes.size(), 32); - } - { - const auto publicKey = PublicKey(parse_hex(publicKeyKeyHex), TWPublicKeyTypeED25519Blake2b); - EXPECT_EQ(hex(publicKey.bytes), publicKeyKeyHex); - } -} - -TEST(PublicKeyTests, CompressedExtended) { - const Data key = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = PrivateKey(key); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - EXPECT_EQ(publicKey.type, TWPublicKeyTypeSECP256k1); - EXPECT_EQ(publicKey.bytes.size(), 33); - EXPECT_EQ(publicKey.isCompressed(), true); - EXPECT_TRUE(PublicKey::isValid(publicKey.bytes, TWPublicKeyTypeSECP256k1)); - EXPECT_EQ(hex(publicKey.bytes), std::string("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1")); - - auto extended = publicKey.extended(); - EXPECT_EQ(extended.type, TWPublicKeyTypeSECP256k1Extended); - EXPECT_EQ(extended.bytes.size(), 65); - EXPECT_EQ(extended.isCompressed(), false); - EXPECT_TRUE(PublicKey::isValid(extended.bytes, TWPublicKeyTypeSECP256k1Extended)); - EXPECT_EQ(hex(extended.bytes), std::string("0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91")); - - auto compressed = extended.compressed(); - EXPECT_EQ(compressed.type, TWPublicKeyTypeSECP256k1); - EXPECT_TRUE(compressed == publicKey); - EXPECT_EQ(compressed.bytes.size(), 33); - EXPECT_EQ(compressed.isCompressed(), true); - EXPECT_TRUE(PublicKey::isValid(compressed.bytes, TWPublicKeyTypeSECP256k1)); - EXPECT_EQ(hex(compressed.bytes), std::string("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1")); - - auto extended2 = extended.extended(); - EXPECT_EQ(extended2.type, TWPublicKeyTypeSECP256k1Extended); - EXPECT_EQ(extended2.bytes.size(), 65); - EXPECT_EQ(extended2.isCompressed(), false); - - auto compressed2 = compressed.compressed(); - EXPECT_EQ(compressed2.type, TWPublicKeyTypeSECP256k1); - EXPECT_TRUE(compressed2 == publicKey); - EXPECT_EQ(compressed2.bytes.size(), 33); - EXPECT_EQ(compressed2.isCompressed(), true); -} - -TEST(PublicKeyTests, CompressedExtendedNist) { - const Data key = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = PrivateKey(key); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1); - EXPECT_EQ(publicKey.type, TWPublicKeyTypeNIST256p1); - EXPECT_EQ(publicKey.bytes.size(), 33); - EXPECT_EQ(publicKey.isCompressed(), true); - EXPECT_TRUE(PublicKey::isValid(publicKey.bytes, TWPublicKeyTypeNIST256p1)); - EXPECT_EQ(hex(publicKey.bytes), std::string("026d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab")); - - auto extended = publicKey.extended(); - EXPECT_EQ(extended.type, TWPublicKeyTypeNIST256p1Extended); - EXPECT_EQ(extended.bytes.size(), 65); - EXPECT_EQ(extended.isCompressed(), false); - EXPECT_TRUE(PublicKey::isValid(extended.bytes, TWPublicKeyTypeNIST256p1Extended)); - EXPECT_EQ(hex(extended.bytes), std::string("046d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab918b4fe46ccbf56701fb210d67d91c5779468f6b3fdc7a63692b9b62543f47ae")); - - auto compressed = extended.compressed(); - EXPECT_EQ(compressed.type, TWPublicKeyTypeNIST256p1); - EXPECT_TRUE(compressed == publicKey); - EXPECT_EQ(compressed.bytes.size(), 33); - EXPECT_EQ(compressed.isCompressed(), true); - EXPECT_TRUE(PublicKey::isValid(compressed.bytes, TWPublicKeyTypeNIST256p1)); - EXPECT_EQ(hex(compressed.bytes), std::string("026d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab")); - - auto extended2 = extended.extended(); - EXPECT_EQ(extended2.type, TWPublicKeyTypeNIST256p1Extended); - EXPECT_EQ(extended2.bytes.size(), 65); - EXPECT_EQ(extended2.isCompressed(), false); - - auto compressed2 = compressed.compressed(); - EXPECT_EQ(compressed2.type, TWPublicKeyTypeNIST256p1); - EXPECT_TRUE(compressed2 == publicKey); - EXPECT_EQ(compressed2.bytes.size(), 33); - EXPECT_EQ(compressed2.isCompressed(), true); -} - -TEST(PublicKeyTests, CompressedExtendedED25519) { - const Data key = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); - auto privateKey = PrivateKey(key); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - EXPECT_EQ(publicKey.type, TWPublicKeyTypeED25519); - EXPECT_EQ(publicKey.bytes.size(), 32); - EXPECT_EQ(publicKey.isCompressed(), true); - EXPECT_TRUE(PublicKey::isValid(publicKey.bytes, TWPublicKeyTypeED25519)); - EXPECT_EQ(hex(publicKey.bytes), std::string("4870d56d074c50e891506d78faa4fb69ca039cc5f131eb491e166b975880e867")); - - auto extended = publicKey.extended(); - EXPECT_EQ(extended.type, TWPublicKeyTypeED25519); - EXPECT_TRUE(extended == publicKey); - EXPECT_EQ(extended.bytes.size(), 32); - EXPECT_EQ(extended.isCompressed(), true); - - auto compressed = publicKey.compressed(); - EXPECT_EQ(compressed.type, TWPublicKeyTypeED25519); - EXPECT_TRUE(compressed == publicKey); - EXPECT_EQ(compressed.bytes.size(), 32); - EXPECT_EQ(compressed.isCompressed(), true); -} - -TEST(PublicKeyTests, IsValidWrongType) { - EXPECT_FALSE(PublicKey::isValid(parse_hex("deadbeef"), (enum TWPublicKeyType)99)); -} - -TEST(PublicKeyTests, Verify) { - const auto key = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); - const auto privateKey = PrivateKey(key); - - const char* message = "Hello"; - const Data messageData = TW::data(message); - const Data digest = Hash::sha256(messageData); - - { - const auto signature = privateKey.sign(digest, TWCurveSECP256k1); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - EXPECT_TRUE(publicKey.verify(signature, digest)); - EXPECT_EQ(hex(signature), "0f5d5a9e5fc4b82a625312f3be5d3e8ad017d882de86c72c92fcefa924e894c12071772a14201a3a0debf381b5e8dea39fadb9bcabdc02ee71ab018f55bf717f01"); - } - { - const auto signature = privateKey.sign(digest, TWCurveED25519); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - EXPECT_TRUE(publicKey.verify(signature, digest)); - EXPECT_EQ(hex(signature), "42848abf2641a731e18b8a1fb80eff341a5acebdc56faeccdcbadb960aef775192842fccec344679446daa4d02d264259c8f9aa364164ebe0ebea218581e2e03"); - } - { - const auto signature = privateKey.sign(digest, TWCurveED25519Blake2bNano); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Blake2b); - EXPECT_TRUE(publicKey.verify(signature, digest)); - EXPECT_EQ(hex(signature), "5c1473944cd0234ebc5a91b2966b9e707a33b936dadd149417a2e53b6b3fc97bef17b767b1690708c74d7b4c8fe48703fd44a6ef59d4cc5b9f88ba992db0a003"); - } - { - const auto signature = privateKey.sign(digest, TWCurveNIST256p1); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1Extended); - EXPECT_TRUE(publicKey.verify(signature, digest)); - EXPECT_EQ(hex(signature), "2e4655831f0c60729583595c103bf0d862af6313e4326f03f512682106c792822f5a9cd21e7d4a3316c2d337e5eee649b09c34f7b4407344f0d32e8d33167d8901"); - } -} - -TEST(PublicKeyTests, VerifyEd25519Extended) { - const auto key = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); - const auto privateKey = PrivateKey(key); - - const Data messageData = TW::data("Hello"); - const Data digest = Hash::sha256(messageData); - - try { - privateKey.sign(digest, TWCurveED25519Extended); - } catch (const std::invalid_argument&) { - return; // OK, not implemented - } - FAIL() << "Missing expected exception"; -} - -TEST(PublicKeyTests, VerifySchnorr) { - const auto key = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); - const auto privateKey = PrivateKey(key); - - const Data messageData = TW::data("hello schnorr"); - const Data digest = Hash::sha256(messageData); - - const auto signature = privateKey.signSchnorr(digest, TWCurveSECP256k1); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - EXPECT_TRUE(publicKey.verifySchnorr(signature, digest)); - EXPECT_EQ(hex(signature), "b8118ccb99563fe014279c957b0a9d563c1666e00367e9896fe541765246964f64a53052513da4e6dc20fdaf69ef0d95b4ca51c87ad3478986cf053c2dd0b853"); -} - -TEST(PublicKeyTests, VerifySchnorrWrongType) { - const auto key = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); - const auto privateKey = PrivateKey(key); - - const Data messageData = TW::data("hello schnorr"); - const Data digest = Hash::sha256(messageData); - - const auto signature = privateKey.signSchnorr(digest, TWCurveSECP256k1); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1); - EXPECT_FALSE(publicKey.verifySchnorr(signature, digest)); -} - -TEST(PublicKeyTests, Recover) { - const auto message = parse_hex("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb11432"); - const auto signature = parse_hex("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef80"); - const auto publicKey = PublicKey::recover(signature, message); - EXPECT_EQ(publicKey.type, TWPublicKeyTypeSECP256k1Extended); - EXPECT_EQ(hex(publicKey.bytes), - "0456d8089137b1fd0d890f8c7d4a04d0fd4520a30b19518ee87bd168ea12ed8090329274c4c6c0d9df04515776f2741eeffc30235d596065d718c3973e19711ad0"); -} - -TEST(PublicKeyTests, isValidED25519) { - EXPECT_TRUE(PublicKey::isValid(parse_hex("beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519)); - EXPECT_TRUE(PublicKey(parse_hex("beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_TRUE(PublicKey::isValid(parse_hex("fc8c425a8a94a55ce42f2c24b2fb2ef5ab4a69142d2d97f6c11e0106c84136d5"), TWPublicKeyTypeED25519)); - EXPECT_TRUE(PublicKey(parse_hex("fc8c425a8a94a55ce42f2c24b2fb2ef5ab4a69142d2d97f6c11e0106c84136d5"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_TRUE(PublicKey::isValid(parse_hex("01beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519)); - EXPECT_TRUE(PublicKey(parse_hex("01beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519).isValidED25519()); - // Following 32 bytes are not valid public keys (not on the curve) - EXPECT_TRUE(PublicKey::isValid(parse_hex("8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"), TWPublicKeyTypeED25519)); - EXPECT_FALSE(PublicKey(parse_hex("8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_TRUE(PublicKey::isValid(parse_hex("51fdd5feae59d7dcbf5ebea99c05593ebee302577a5486ceac706ed568aa1e0e"), TWPublicKeyTypeED25519)); - EXPECT_FALSE(PublicKey(parse_hex("51fdd5feae59d7dcbf5ebea99c05593ebee302577a5486ceac706ed568aa1e0e"), TWPublicKeyTypeED25519).isValidED25519()); - // invalid input size/format - EXPECT_FALSE(PublicKey::isValid(parse_hex("1234"), TWPublicKeyTypeED25519)); - EXPECT_FALSE(PublicKey::isValid(parse_hex("beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa5279"), TWPublicKeyTypeED25519)); - EXPECT_FALSE(PublicKey::isValid(parse_hex("02beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519)); - EXPECT_FALSE(PublicKey::isValid(parse_hex("0101beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519)); - EXPECT_FALSE(PublicKey(parse_hex("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"), TWPublicKeyTypeSECP256k1).isValidED25519()); -} diff --git a/tests/Qtum/TWCoinTypeTests.cpp b/tests/Qtum/TWCoinTypeTests.cpp deleted file mode 100644 index bef8e814b59..00000000000 --- a/tests/Qtum/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWQtumCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeQtum)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeQtum, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeQtum, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeQtum)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeQtum)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeQtum), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeQtum)); - ASSERT_EQ(0x32, TWCoinTypeP2shPrefix(TWCoinTypeQtum)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeQtum)); - assertStringsEqual(symbol, "QTUM"); - assertStringsEqual(txUrl, "https://qtum.info/tx/t123"); - assertStringsEqual(accUrl, "https://qtum.info/address/a12"); - assertStringsEqual(id, "qtum"); - assertStringsEqual(name, "Qtum"); -} diff --git a/tests/Ravencoin/TWCoinTypeTests.cpp b/tests/Ravencoin/TWCoinTypeTests.cpp deleted file mode 100644 index f639dfe6bba..00000000000 --- a/tests/Ravencoin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWRavencoinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeRavencoin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeRavencoin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeRavencoin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeRavencoin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeRavencoin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeRavencoin), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeRavencoin)); - ASSERT_EQ(0x7a, TWCoinTypeP2shPrefix(TWCoinTypeRavencoin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeRavencoin)); - assertStringsEqual(symbol, "RVN"); - assertStringsEqual(txUrl, "https://ravencoin.network/tx/t123"); - assertStringsEqual(accUrl, "https://ravencoin.network/address/a12"); - assertStringsEqual(id, "ravencoin"); - assertStringsEqual(name, "Ravencoin"); -} diff --git a/tests/Ripple/AddressTests.cpp b/tests/Ripple/AddressTests.cpp deleted file mode 100644 index f73660b0090..00000000000 --- a/tests/Ripple/AddressTests.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ripple/Address.h" -#include "Ripple/XAddress.h" -#include "HexCoding.h" -#include "PrivateKey.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::Ripple; - -TEST(RippleAddress, FromPublicKey) { - const auto publicKey = PublicKey(parse_hex("0303E20EC6B4A39A629815AE02C0A1393B9225E3B890CAE45B59F42FA29BE9668D"), TWPublicKeyTypeSECP256k1); - const auto address = Address(publicKey); - ASSERT_EQ(string("rnBFvgZphmN39GWzUJeUitaP22Fr9be75H"), address.string()); -} - -TEST(RippleAddress, FromString) { - string classic = "rnBFvgZphmN39GWzUJeUitaP22Fr9be75H"; - const auto address = Address(classic); - - ASSERT_EQ(address.string(), classic); -} - -TEST(RippleXAddress, FromPublicKey) { - const auto publicKey = PublicKey(parse_hex("0303E20EC6B4A39A629815AE02C0A1393B9225E3B890CAE45B59F42FA29BE9668D"), TWPublicKeyTypeSECP256k1); - const auto address = XAddress(publicKey, 12345); - ASSERT_EQ(string("X76UnYEMbQfEs3mUqgtjp4zFy9exgThRj7XVZ6UxsdrBptF"), address.string()); -} - -TEST(RippleXAddress, FromString) { - string xAddress = "X76UnYEMbQfEs3mUqgtjp4zFy9exgThRj7XVZ6UxsdrBptF"; - string xAddress2 = "X76UnYEMbQfEs3mUqgtjp4zFy9exgTsM93nriVZAPufrpE3"; - const auto address = XAddress(xAddress); - const auto address2 = XAddress(xAddress2); - - ASSERT_EQ(address.tag, 12345); - ASSERT_EQ(address.string(), xAddress); - - ASSERT_EQ(address2.tag, 0); - ASSERT_EQ(address2.string(), xAddress2); -} - -TEST(RippleAddress, isValid) { - string classicAddress = "r36yxStAh7qgTQNHTzjZvXybCTzUFhrfav"; - string bitcoinAddress = "1Ma2DrB78K7jmAwaomqZNRMCvgQrNjE2QC"; - string xAddress = "XVfvixWZQKkcenFRYApCjpTUyJ4BePTe3jJv7beatUZvQYh"; - - ASSERT_TRUE(Address::isValid(classicAddress)); - ASSERT_TRUE(XAddress::isValid(xAddress)); - ASSERT_FALSE(Address::isValid(bitcoinAddress)); - ASSERT_FALSE(XAddress::isValid(bitcoinAddress)); -} diff --git a/tests/Ripple/TWAnySignerTests.cpp b/tests/Ripple/TWAnySignerTests.cpp deleted file mode 100644 index cbfc4f28823..00000000000 --- a/tests/Ripple/TWAnySignerTests.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "proto/Ripple.pb.h" -#include - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; -using namespace TW::Ripple; - -TEST(TWAnySignerRipple, Sign) { - auto key = parse_hex("ba005cd605d8a02e3d5dfd04234cef3a3ee4f76bfbad2722d1fb5af8e12e6764"); - Proto::SigningInput input; - - input.set_amount(29000000); - input.set_fee(200000); - input.set_sequence(1); - input.set_account("rDpysuumkweqeC7XdNgYNtzL5GxbdsmrtF"); - input.set_destination("rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"); - input.set_private_key(key.data(), key.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeXRP); - - EXPECT_EQ(hex(output.encoded()), "12000022800000002400000001614000000001ba8140684000000000030d407321026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6744630440220067f20b3eebfc7107dd0bcc72337a236ac3be042c0469f2341d76694a17d4bb9022048393d7ee7dcb729783b33f5038939ddce1bb8337e66d752974626854556bbb681148400b6b6d08d5d495653d73eda6804c249a5148883148132e4e20aecf29090ac428a9c43f230a829220d"); -} diff --git a/tests/Ripple/TWCoinTypeTests.cpp b/tests/Ripple/TWCoinTypeTests.cpp deleted file mode 100644 index 5f4249ca2e1..00000000000 --- a/tests/Ripple/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWXRPCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeXRP)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("E26AB8F3372D2FC02DEC1FD5674ADAB762D684BFFDBBDF5D674E9D7CF4A47054")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeXRP, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("rfkH7EuS1XcSkB9pocy1R6T8F4CsNYixYU")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeXRP, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeXRP)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeXRP)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeXRP), 6); - ASSERT_EQ(TWBlockchainRipple, TWCoinTypeBlockchain(TWCoinTypeXRP)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeXRP)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeXRP)); - assertStringsEqual(symbol, "XRP"); - assertStringsEqual(txUrl, "https://bithomp.com/explorer/E26AB8F3372D2FC02DEC1FD5674ADAB762D684BFFDBBDF5D674E9D7CF4A47054"); - assertStringsEqual(accUrl, "https://bithomp.com/explorer/rfkH7EuS1XcSkB9pocy1R6T8F4CsNYixYU"); - assertStringsEqual(id, "ripple"); - assertStringsEqual(name, "XRP"); -} diff --git a/tests/Ripple/TWRippleAddressTests.cpp b/tests/Ripple/TWRippleAddressTests.cpp deleted file mode 100644 index 7fd89c52c43..00000000000 --- a/tests/Ripple/TWRippleAddressTests.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" - -#include - -#include - -TEST(TWRipple, ExtendedKeys) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic( - STRING("ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal").get(), - STRING("TREZOR").get() - )); - - auto xpub = WRAPS(TWHDWalletGetExtendedPublicKey(wallet.get(), TWPurposeBIP44, TWCoinTypeXRP, TWHDVersionXPUB)); - auto xprv = WRAPS(TWHDWalletGetExtendedPrivateKey(wallet.get(), TWPurposeBIP44, TWCoinTypeXRP, TWHDVersionXPRV)); - - assertStringsEqual(xpub, "xpub6D9oDY4gqFBtsFEonh5GTDiUm6nmij373YWzmYdshcnM4AFzdhUf55iZD33vNU2ZqfQJU5wiCJUgisMt2RHKDzhi1PbZfh5Y2NiiYJAQqUn"); - assertStringsEqual(xprv, "xprv9zASp2XnzsdbemALgfYG65mkD4xHKGKFgKbPyAEG9HFNBMvr6AAQXHQ5MmqM66EnbJfe9TvYMy1bucz7hSQjG43NVizRZwJJYfLmeKo4nVB"); -} diff --git a/tests/Ripple/TransactionTests.cpp b/tests/Ripple/TransactionTests.cpp deleted file mode 100644 index 4162ebbfe2a..00000000000 --- a/tests/Ripple/TransactionTests.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Ripple/Address.h" -#include "Ripple/Transaction.h" -#include "Ripple/BinaryCoding.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::Ripple; - -TEST(RippleTransaction, serializeAmount) { - /// From https://github.com/trezor/trezor-core/blob/master/tests/test_apps.ripple.serializer.py - auto data0 = Transaction::serializeAmount(0); - auto data1 = Transaction::serializeAmount(1); - auto data2 = Transaction::serializeAmount(93493429243); - auto data3 = Transaction::serializeAmount(25000000); - auto data4 = Transaction::serializeAmount(100000000000); - /// more than max supply - auto data5 = Transaction::serializeAmount(200000000000000000); - /// negative value - auto data6 = Transaction::serializeAmount(-1); - - ASSERT_EQ(hex(data0), "4000000000000000"); - ASSERT_EQ(hex(data1), "4000000000000001"); - ASSERT_EQ(hex(data2), "40000015c4a483fb"); - ASSERT_EQ(hex(data3), "40000000017d7840"); - ASSERT_EQ(hex(data4), "400000174876e800"); - ASSERT_EQ(hex(data5), "42c68af0bb140000"); - ASSERT_EQ(hex(data6), ""); -} - -TEST(RippleTransaction, serialize) { - /// From https://github.com/trezor/trezor-core/blob/master/tests/test_apps.ripple.serializer.py - auto account = Address("r9TeThyi5xiuUUrFjtPKZiHcDxs7K9H6Rb"); - auto destination = "r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C"; - auto tx1 = Transaction( - /* amount */25000000, - /* fee */10, - /* flags */0, - /* sequence */2, - /* last_ledger_sequence */0, - /* account */account, - /* destination */destination, - /* destination_tag*/0 - ); - auto serialized1 = tx1.serialize(); - ASSERT_EQ(hex(serialized1), "120000220000000024000000026140000000017d784068400000000000000a81145ccb151f6e9d603f394ae778acf10d3bece874f68314e851bbbe79e328e43d68f43445368133df5fba5a"); - - auto tx2 = Transaction( - /* amount */200000, - /* fee */15, - /* flags */0, - /* sequence */144, - /* last_ledger_sequence */0, - /* account */Address("rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e"), - /* destination */"rw71Qs1UYQrSQ9hSgRohqNNQcyjCCfffkQ", - /* destination_tag*/0 - ); - auto serialized2 = tx2.serialize(); - ASSERT_EQ(hex(serialized2), "12000022000000002400000090614000000000030d4068400000000000000f8114aa1bd19d9e87be8069fdbf6843653c43837c03c6831467fe6ec28e0464dd24fb2d62a492aac697cfad02"); - - auto tx3 = Transaction( - /* amount */25000000, - /* fee */12, - /* flags */0, - /* sequence */1, - /* last_ledger_sequence */0, - /* account */Address("r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C"), - /* destination */"rBqSFEFg2B6GBMobtxnU1eLA1zbNC9NDGM", - /* destination_tag*/4146942154 - ); - auto serialized3 = tx3.serialize(); - ASSERT_EQ(hex(serialized3), "120000220000000024000000012ef72d50ca6140000000017d784068400000000000000c8114e851bbbe79e328e43d68f43445368133df5fba5a831476dac5e814cd4aa74142c3ab45e69a900e637aa2"); - - auto tx4 = Transaction( - /* amount */25000000, - /* fee */12, - /* flags */0, - /* sequence */1, - /* last_ledger_sequence */0, - /* account */Address("r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C"), - /* destination */"XVhidoXkozM5DTZFdDnJ5nYC8FPrTuJiyGh1VxSGS6RNJJ5", - /* ignore destination_tag*/12345 - ); - auto serialized4 = tx4.serialize(); - ASSERT_EQ(hex(serialized4), hex(serialized3)); -} - -TEST(RippleTransaction, preImage) { - auto account = Address("r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"); - auto destination = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; - auto tx1 = Transaction( - /* amount */1000, - /* fee */10, - /* flags */2147483648, - /* sequence */1, - /* last_ledger_sequence */0, - /* account */account, - /* destination */destination, - /* destination_tag*/0 - ); - tx1.pub_key = parse_hex("ed5f5ac8b98974a3ca843326d9b88cebd0560177b973ee0b149f782cfaa06dc66a"); - auto unsignedTx = tx1.getPreImage(); - - ASSERT_EQ(hex(unsignedTx), - /* prefix */ "53545800" - /* tx type */ "120000" - /* flags */ "2280000000" - /* sequence */ "2400000001" - /* amount */ "6140000000000003e8" - /* fee */ "68400000000000000a" - /* pub key */ "7321ed5f5ac8b98974a3ca843326d9b88cebd0560177b973ee0b149f782cfaa06dc66a" - /* account */ "81145b812c9d57731e27a2da8b1830195f88ef32a3b6" - /* destination */ "8314b5f762798a53d543a014caf8b297cff8f2f937e8" - ); - ASSERT_EQ(unsignedTx.size(), 114); -} diff --git a/tests/Ronin/TWCoinTypeTests.cpp b/tests/Ronin/TWCoinTypeTests.cpp deleted file mode 100644 index b36c6610026..00000000000 --- a/tests/Ronin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWRoninCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeRonin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xc09835aaf9c1cacea8ce322865583c791d3a4dfbd9a3b72f79539db88d3697ab")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeRonin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xdc05ecd5fbdb64058d94f3182d66f44342b9adcb")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeRonin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeRonin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeRonin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeRonin), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeRonin)); - - assertStringsEqual(symbol, "RON"); - assertStringsEqual(txUrl, "https://explorer.roninchain.com/tx/0xc09835aaf9c1cacea8ce322865583c791d3a4dfbd9a3b72f79539db88d3697ab"); - assertStringsEqual(accUrl, "https://explorer.roninchain.com/address/0xdc05ecd5fbdb64058d94f3182d66f44342b9adcb"); - assertStringsEqual(id, "ronin"); - assertStringsEqual(name, "Ronin"); -} diff --git a/tests/Solana/AddressTests.cpp b/tests/Solana/AddressTests.cpp deleted file mode 100644 index 5af714c46c6..00000000000 --- a/tests/Solana/AddressTests.cpp +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Solana/Address.h" -#include "Solana/Program.h" -#include "Base58.h" -#include "PrivateKey.h" -#include "HexCoding.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::Solana; - -TEST(SolanaAddress, FromPublicKey) { - const auto addressString = "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"; - const auto publicKey = PublicKey(Base58::bitcoin.decode(addressString), TWPublicKeyTypeED25519); - const auto address = Address(publicKey); - ASSERT_EQ(addressString, address.string()); -} - -TEST(SolanaAddress, FromString) { - string addressString = "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"; - const auto address = Address(addressString); - ASSERT_EQ(address.string(), addressString); -} - -TEST(SolanaAddress, isValid) { - ASSERT_TRUE(Address::isValid("2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST")); - ASSERT_FALSE(Address::isValid( - "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdSl")); // Contains invalid base-58 character - ASSERT_FALSE( - Address::isValid("2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpd")); // Is invalid length -} - -TEST(SolanaAddress, isValidOnCurve) { - EXPECT_TRUE(PublicKey(Base58::bitcoin.decode("HzqnaMjWFbK2io6WgV2Z5uBguCBU21RMUS16wsDUHkon"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_TRUE(PublicKey(Base58::bitcoin.decode("68io7dTfyeWua1wD1YcCMka4y5iiChceaFRCBjqCM5PK"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_TRUE(PublicKey(Base58::bitcoin.decode("Dra34QLFCjxnk8tUNcBwxs6pgb5spF4oseQYF2xn7ABZ"), TWPublicKeyTypeED25519).isValidED25519()); - // negative case - EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"), TWPublicKeyTypeED25519).isValidED25519()); - EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("AbygL37RheNZv327cMvZPqKYLLkZ6wqWYexRxgNiZyeP"), TWPublicKeyTypeED25519).isValidED25519()); -} - -TEST(SolanaAddress, defaultTokenAddress) { - const Address serumToken = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - EXPECT_EQ(Address("HBYC51YrGFAZ8rM7Sj8e9uqKggpSrDYrinQDZzvMtqQp").defaultTokenAddress(serumToken).string(), - "6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"); - EXPECT_EQ(Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V").defaultTokenAddress(serumToken).string(), - "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - EXPECT_EQ(Address("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ").defaultTokenAddress(serumToken).string(), - "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); -} \ No newline at end of file diff --git a/tests/Solana/SignerTests.cpp b/tests/Solana/SignerTests.cpp deleted file mode 100644 index f7eb93825e8..00000000000 --- a/tests/Solana/SignerTests.cpp +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Solana/Signer.h" -#include "Solana/Transaction.h" -#include "Solana/Program.h" -#include "HexCoding.h" -#include "PublicKey.h" - -#include - -using namespace TW; -using namespace TW::Solana; - -TEST(SolanaSigner, CompiledInstruction) { - const auto privateKey0 = - PrivateKey(Base58::bitcoin.decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); - const auto publicKey0 = privateKey0.getPublicKey(TWPublicKeyTypeED25519); - const auto address0 = Address(publicKey0); - ASSERT_EQ(Data(publicKey0.bytes.begin(), publicKey0.bytes.end()), - Base58::bitcoin.decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); - const auto privateKey1 = - PrivateKey(Base58::bitcoin.decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); - const auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeED25519); - const auto address1 = Address(publicKey1); - ASSERT_EQ(Data(publicKey1.bytes.begin(), publicKey1.bytes.end()), - Base58::bitcoin.decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); - Address programId("11111111111111111111111111111111"); - - std::vector
addresses = {address0, address1, programId}; - - std::vector instrAddresses = { - AccountMeta(address1, false, false), - AccountMeta(address0, false, false), - AccountMeta(programId, false, false), - AccountMeta(address1, false, false), - AccountMeta(address0, false, false), - }; - Data data = {0, 1, 2, 4}; - Instruction instruction(programId, instrAddresses, data); - - auto compiledInstruction = CompiledInstruction(instruction, addresses); - - EXPECT_EQ(compiledInstruction.programIdIndex, 2); - ASSERT_EQ(compiledInstruction.accounts.size(), 5); - EXPECT_EQ(compiledInstruction.accounts[0], 1); - EXPECT_EQ(compiledInstruction.accounts[1], 0); - EXPECT_EQ(compiledInstruction.accounts[2], 2); - EXPECT_EQ(compiledInstruction.accounts[3], 1); - EXPECT_EQ(compiledInstruction.accounts[4], 0); - ASSERT_EQ(compiledInstruction.data.size(), 4); -} - -TEST(SolanaSigner, CompiledInstructionFindAccount) { - Address address1 = Address(parse_hex("0102030405060708090a0102030405060708090a0102030405060708090a0101")); - Address address2 = Address(parse_hex("0102030405060708090a0102030405060708090a0102030405060708090a0102")); - Address address3 = Address(parse_hex("0102030405060708090a0102030405060708090a0102030405060708090a0103")); - Address programId("11111111111111111111111111111111"); - Instruction instruction(programId, std::vector{ - AccountMeta(address1, true, false), - AccountMeta(address2, false, false), - }, Data{1, 2, 3, 4}); - std::vector
addresses = { - address1, - address2, - programId, - }; - CompiledInstruction compiledInstruction = CompiledInstruction(instruction, addresses); - ASSERT_EQ(compiledInstruction.findAccount(address1), 0); - ASSERT_EQ(compiledInstruction.findAccount(address2), 1); - ASSERT_EQ(compiledInstruction.findAccount(programId), 2); - // negative case - try { - compiledInstruction.findAccount(address3); - FAIL() << "Missing expected exception"; - } catch (...) { - // ok - } -} - -TEST(SolanaSigner, SingleSignTransaction) { - const auto privateKey = - PrivateKey(Base58::bitcoin.decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::bitcoin.decode("7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q")); - - const auto from = Address(publicKey); - auto to = Address("EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); - - std::vector signerKeys; - signerKeys.push_back(privateKey); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - Signature expectedSignature( - "5T6uZBHnHFd8uWErDBTFRVkbKuhbcm94K5MJ2beTYDruzqv4FjS7EMKvC94ZfxNAiWUXZ6bZxS3WXUbhJwYNPWn"); - expectedSignatures.push_back(expectedSignature); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "3p2kzZ1DvquqC6LApPuxpTg5CCDVPqJFokGSnGhnBHrta4uq7S2EyehV1XNUVXp51D69GxGzQZU" - "jikfDzbWBG2aFtG3gHT1QfLzyFKHM4HQtMQMNXqay1NAeiiYZjNhx9UvMX4uAQZ4Q6rx6m2AYfQ" - "7aoMUrejq298q1wBFdtS9XVB5QTiStnzC7zs97FUEK2T4XapjF1519EyFBViTfHpGpnf5bfizDz" - "sW9kYUtRDW1UC2LgHr7npgq5W9TBmHf9hSmRgM9XXucjXLqubNWE7HUMhbKjuBqkirRM"; - ASSERT_EQ(transaction.serialize(), expectedString); - - const auto additionalPrivateKey = - PrivateKey(Base58::bitcoin.decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); - signerKeys.push_back(additionalPrivateKey); - try { - Signer::sign(signerKeys, transaction); - FAIL() << "publicKey not found in message.accountKeys"; - } catch (std::invalid_argument const& err) { - EXPECT_EQ(err.what(), std::string("publicKey not found in message.accountKeys")); - } -} - -TEST(SolanaSigner, SignTransactionToSelf) { - const auto privateKey = - PrivateKey(Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::bitcoin.decode("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu")); - - const auto from = Address(publicKey); - auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); - - std::vector signerKeys; - signerKeys.push_back(privateKey); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - Signature expectedSignature( - "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); - expectedSignatures.push_back(expectedSignature); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqe" - "EFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8n" - "UkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, MultipleSignTransaction) { - const auto privateKey0 = - PrivateKey(Base58::bitcoin.decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); - const auto publicKey0 = privateKey0.getPublicKey(TWPublicKeyTypeED25519); - const auto address0 = Address(publicKey0); - ASSERT_EQ(Data(publicKey0.bytes.begin(), publicKey0.bytes.end()), - Base58::bitcoin.decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); - const auto privateKey1 = - PrivateKey(Base58::bitcoin.decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); - const auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeED25519); - const auto address1 = Address(publicKey1); - ASSERT_EQ(Data(publicKey1.bytes.begin(), publicKey1.bytes.end()), - Base58::bitcoin.decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); - - Data data = {0, 0, 0, 0}; - Address programId("11111111111111111111111111111111"); - std::vector instrAddresses = { - AccountMeta(address0, true, false), - AccountMeta(address1, false, false), - }; - Instruction instruction(programId, instrAddresses, data); - std::vector instructions = {instruction}; - - MessageHeader header = {2, 0, 1}; - std::vector
accountKeys = {address0, address1, programId}; - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - Message message; - message.header = header; - message.accountKeys = accountKeys; - message.recentBlockhash = recentBlockhash; - message.instructions = instructions; - message.compileInstructions(); - - auto transaction = Transaction(message); - - std::vector signerKeys; - // Sign order should not matter - signerKeys.push_back(privateKey1); - signerKeys.push_back(privateKey0); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - Signature expectedSignature0( - "37beWPhNMfWUz75Tb24TX3PCS89FZscbCgwwLpFnzVfZYPqDpAWruvqzc9eeQYft35H23Vm9Tv1dPwEKWT3vAVPb"); - expectedSignatures.push_back(expectedSignature0); - Signature expectedSignature1( - "5NxQshVaAXtQ8YVdcBtCanT62KbxnRfhubjGndFvetgn9AiaoLVZvRGutR5D7FJebRxq8bd6nQXn59LFzavEUrdQ"); - expectedSignatures.push_back(expectedSignature1); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "oL2CmkcP9xf2DiU7eo6hh3JdHnX3NGjunheXYo6SjVchzc8LtFJpPs4jccWUd7oPZUPQNTcR7Ee" - "Hn259ror9A7aXgJdP4djhntoD8irF1kuBZCj7pubtoWfiAKzagSL4hChQsTSe7e9jaGtoXu58mP" - "HCMKTz55TLjhdmCj7ixoWRowWEzkrF49MxXnurb4yf6ASru1XdHPFn3DdzkRHgypYwvRM6ci8p2" - "7trQvXFukhWX6qG6JkxqsWYSzACcAAGGWfAxSi63Yx1RxkxGUzyxy5f2thQhWZ6Nx6pR1im65yV" - "YMYPXj94kgtHxXw9h5V4p7xSAwRpmhw4jewYyQVX4jmnfro3gFNdX9AqpqMs4uGHA4rZM"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignUpdateBlockhash) { - const auto privateKey = - PrivateKey(Base58::bitcoin.decode("G4VSzrknPBWZ1z2YwUnWTxD1td7wmqR5jMPEJRN6wm8S")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::bitcoin.decode("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5")); - - const auto from = Address(publicKey); - auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); - - std::vector signerKeys; - signerKeys.push_back(privateKey); - Signer::sign(signerKeys, transaction); - - Solana::Hash newBlockhash("GgBaCs3NCBuZN12kCJgAW63ydqohFkHEdfdEXBPzLHq"); - Signer::signUpdateBlockhash(signerKeys, transaction, newBlockhash); - - std::vector expectedSignatures; - Signature expectedSignature( - "5AFhXjvGdENXCAe9MPvUA2qjoL4XtZwZKG7kK2HmZf1ibpxjx5kzogHZjN39uYB9J33UFJN15KhSggBZhzyNQmta"); - expectedSignatures.push_back(expectedSignature); - ASSERT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - "62ABadDCoPfGGRnhLoBhfcPekMHyN5ee8DgTY8wD4iwKDjyFAsNbsaahTcqMWxmwa61q9iAGCQB" - "v1bETcYzWsTwLKMVGLoEpwqA84mPjqHyr5sQD5dcghyQiQ1ckYNub9K7s8FspVwwowK8gJG69xe" - "DEaqi7G1zrChBVbQYTmVUwJETyDmP1Vs8QU3CaxBs8qwcxoziU52KWLBpRj9o38QVBdxJtJ7hig" - "hgPKJubfqUfTWdN94PzqEfyPqwoCpFD39nvBn8C5xe1caPKivicg6U7Lzm9s8RYTLCEB"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignRawMessage) { - const auto privateKey = - PrivateKey(Base58::bitcoin.decode("GjXseuD8JavBjKMdd6GEsPYZPV7tMMa46GS2JRS5tHRq")); - const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), - Base58::bitcoin.decode("3BocAWPm1oNXN5qkAV4QeDUmAPpkTcN1rrmCMWAfsXJY")); - - auto rawMessageData = - "01000203207be13c43c4528592eaf3fd34e064c641c5be3cb6691877d7ade94dff36734108eaea30723c33b525" - "07bc54024910612f885e4c80c10b99a047fd42c0acbace00000000000000000000000000000000000000000000" - "000000000000000000000404040404040404040404040404040404040404040404040404040404040404010202" - "00010c020000002a00000000000000"; - - std::vector signerKeys; - signerKeys.push_back(privateKey); - Data rawTransaction = Signer::signRawMessage(signerKeys, parse_hex(rawMessageData)); - - auto expectedHex = - "016e7f8349977b482bccf0bfc202ad917295803831e59ccb865b97d657464791ebfe3336879b84b9f165e464a3" - "4751fe30d54b01f3c9f33f969aafe1e85951b10901000203207be13c43c4528592eaf3fd34e064c641c5be3cb6" - "691877d7ade94dff36734108eaea30723c33b52507bc54024910612f885e4c80c10b99a047fd42c0acbace0000" - "000000000000000000000000000000000000000000000000000000000000040404040404040404040404040404" - "040404040404040404040404040404040401020200010c020000002a00000000000000"; - ASSERT_EQ(hex(rawTransaction), expectedHex); -} - -TEST(SolanaSigner, SignDelegateStakeV2) { - const auto privateKeySigner = - PrivateKey(Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - ASSERT_EQ(signer.string(), "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - - auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); - - auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = Transaction(message); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - Signature expectedSignature("58iogHzSJZmvTxi71W8k2yZXSPVfGAgtgqrk1RaBtfVFewU9yiJCkvSF1Hhjyax5DuexzR7ryWZDAWKQ73pyqvMs"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = "j24mVM9Zgu5vDZhPLGGuCRXQnP9djNtxdHh4txN3S7dwJsNNL5fbhzGpPgSUAcLGoMVCfF9TuqTYfpfJnb4sJFe1ahM8yPL5HwuKL6py5AZJFi8SWx9fvaVB699dCPo1GT3JoEBLPCZ9o2jQtnwzLkzTYJnKv2axqhKWFE2sz6TBA5J39eZcjMFUYgyxz6Q5S4MWqYQCb8UET2NAEZoKcfy7j8N25WXL6Gj4j3hBZjpHQQNaGaNEprEqyma3ZuVhpGiCALSsuzVLX3wZVo4icXwe952deMFA4tH3BK1jcSQCgfmcKDJ9nd7bdrnUUs4BoMdF1uDZB5LxE2UH8QiqtYvaUcorF4SJ3gPxM5ykbyPsNK1cSYZF9NMpW2GofyC17eELwnHQTQB2kqphxJZu7BahvkwiDPPeeydiXAkBspJ3nc3PCBujv6WJw22ZHw5j6zAP8ZGnCW44pqtWD5qifF9tTKhySKdANNiWifs3tSCCPQqjfJXu14drNinR6VG8rJxS1qgmRYiRQUa7m1vtoaZFRN5qKUeAfoFKkAVaNnMdwgsNqNH4dqBodTCJFs1LkYwhgRZdZGbwXTn1j7vpR3DSnv4g72i2H556srzK53jdUmdv6yfxt516XDSshqZtHnKZ1tudxKjBXwsqT3imDiZFVka9wKWUAYMCi4XZ79CY6Xpsd9c18U2e9TCngQmgkTATFgrqysfraokNffgqWxvsPMugksbvbPjJs3iCzByvphkC9p7hCf6LwbeF8XnVB91EAgRDA4VLE1f9wkcq5zjy879YWJ4r516h3PQszTz1EaJXNAXdbk5Em7eyuuabGP1Q3nijFTL2yhMDsXpgrjAuEAABNxFMd4J1JRMaic615mHrhwociksrsfQK"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignDelegateStakeV1) { - const auto privateKeySigner = - PrivateKey(Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - ASSERT_EQ(signer.string(), "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - - auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); - - auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = Transaction(message); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - Signature expectedSignature("gDPbnakbktrASmnUwKGpmftvQRbcyAvxyAyVXq3oVLfAdTPDqY8hhLPHTgidEZGWcmiaXnEyKg2GQLkkAh3JYr3"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = "TKPiN35HzeD3zdwxDFvnkgoqud7CZsda15JkBwM4nDpr623rM7MZsH6QvMMyKpiz7MeRNTrfyHkRLQSBT9Tbg2mgTdfrbhhqeF3Suu5ECphqn8DFYPoMnFzeg5u9gaqevfjhuizzeo2YDJF8aVGy1pez8gMbp5vHz1SuvQUgfcvFctggUMwNiJorSmmp3N6TzQSd38CZrA8ZLhaJjuwDwVMjmj18rGTV1gkX19L7byTFrus2vNvPeUa2AawwUnFpYMPgvCKkHTrpnjvypjoLof9yMUFQ5M1S3Ntv53KJyXwXq6ejJnBDtisnDcdMDNSZp3VeKz6XCr8XVM5xNVh3LX12V4kc3ueqkokYJLP1JmuhA3nNZA1G5KTNno93HUoBkEa1x5h3haoCSgmQC97LoJbJM6B6C2NbaDj2J6iiTaVQdin4He4Jpj575WDhNTqsLjzFUHPUHQF1CRnuss8UpVyMsa4kdVqCDQGeh5DKbkikgcB8GKPBuC91DRxGEqgoygNsu5nnQy4o3YAJnBBK6HsKxpdjbYD8wCUdLw8muhjpEqeBTPShEaogm9zfehidiCcnxbeoX3gmW8oH9gpWoX7GrkJgF6Wn7iWohmrzqzAjoBz8hpeY5nkkhHrf9iswVGMpakdLGy3YxkGJVpsW8KJACwEKXGLq8SVLtXSUHG8EP16zfYHxKjkCSs8PkdFsA5esxsxppPTVZivuEPqJ5og55aNmugdNDrAFYWdcH1Q4rm7BXN6oHECdz2yY4HFVWh9u592oqozt2gQKu3vmhcNFzzQe1xgs6zKSv38kSGTnipd7Hx2VL3qNAR6XBRiwAi226qSTzxi6R82p7cMB7TMy6fk5AZ3sXDSXFNJ9S5SSU1V63ruw75QMtVio"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignCreateTokenAccount) { - const auto privateKeySigner = - PrivateKey(Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - Solana::Hash recentBlockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - - auto message = Message::createTokenCreateAccount(signer, TokenInstruction::CreateTokenAccount, signer, token, tokenAddress, recentBlockhash); - auto transaction = Transaction(message); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - Signature expectedSignature("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - // test data obtained from spl-token create-account - "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVnH2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtCjAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzjB2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2TN3xXwu1"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignCreateTokenAccountForOther) { - const auto privateKeySigner = - PrivateKey(parse_hex("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto otherMainAddress = Address("3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft"); - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto tokenAddress = Address("67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3"); - Solana::Hash recentBlockhash("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); - - auto message = Message::createTokenCreateAccount(signer, TokenInstruction::CreateTokenAccount, otherMainAddress, token, tokenAddress, recentBlockhash); - auto transaction = Transaction(message); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - auto expectedString = - // https://explorer.solana.com/tx/3E6UFVamHCm6Bgk8gXdZex7R7tJAVxqJm6t9ephAKu1PjcfZrD7CJqMwKu6RrvWSUESbZFqzdUyLXuxAFaawPHvJ - "4BsrHedHuForcKDhLdnLYDXgtQgQEj3EQVDtEhqa7o6ukFjW3shpTWv6PeKQdMp6af4ASjD4xQeZvXxLK5WUjguVMUf3xdJn7RnFeM7hdDJ56RDBM5PRJbRJVHjz6FJ7SVNTvr9y3gVYQtWx7NfKRxiyEAfq9JG7nqxSWaW6raMr9t35aVcdAVuXE9iXj3rzhVfCS69vVzy5KcFEK3mvDYG6L12V2CfviCydmeCvPw5r3zBUrZSQv7Ti4XFNBrPbk28gcqQwsBknBqasHxHqD9VUyPmBTuUyXq75QN8rhqN55NjxKBUw37tEUS1jKVpWnTeLFq1eRAMdXvjftNuQ5Bmm8Zc12PGWj9vdorBaYyvZXexJST5xNjR4SCkXvXZoRScETck95chv3VBn54jP8DpB4GGUmATFKSxpdtnNV64i1SQXW13KJwswthJvAaDiqevQLKLkvrTEAdb4BxEfPkFjDVti6P58rTZCMg5CTVLqdmWwpTSW5V"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaSigner, SignTransferToken) { - const auto privateKeySigner = - PrivateKey(Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); - const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); - auto signer = Address(publicKeySigner); - EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto senderTokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - uint64_t amount = 4000; - uint8_t decimals = 6; - Solana::Hash recentBlockhash("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); - - auto message = Message::createTokenTransfer(signer, TokenInstruction::TokenTransfer, token, - senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); - auto transaction = Transaction(message); - - std::vector signerKeys; - signerKeys.push_back(privateKeySigner); - Signer::sign(signerKeys, transaction); - - std::vector expectedSignatures; - Signature expectedSignature("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); - expectedSignatures.push_back(expectedSignature); - EXPECT_EQ(transaction.signatures, expectedSignatures); - - auto expectedString = - // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg - // test data obtained from spl-token transfer - "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jRe2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvPiPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMqDV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnotcrH7pUa94xCVvCPPaomF"; - EXPECT_EQ(transaction.serialize(), expectedString); -} diff --git a/tests/Solana/TWAnySignerTests.cpp b/tests/Solana/TWAnySignerTests.cpp deleted file mode 100644 index ae130918f77..00000000000 --- a/tests/Solana/TWAnySignerTests.cpp +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Base58.h" -#include "HexCoding.h" -#include "proto/Solana.pb.h" -#include "Solana/Address.h" -#include "Solana/Program.h" -#include "PrivateKey.h" -#include "../interface/TWTestUtilities.h" -#include - -#include - -using namespace TW; -using namespace TW::Solana; - -const auto expectedString1 = - "3p2kzZ1DvquqC6LApPuxpTg5CCDVPqJFokGSnGhnBHrta4uq7S2EyehV1XNUVXp51D69GxGzQZU" - "jikfDzbWBG2aFtG3gHT1QfLzyFKHM4HQtMQMNXqay1NAeiiYZjNhx9UvMX4uAQZ4Q6rx6m2AYfQ" - "7aoMUrejq298q1wBFdtS9XVB5QTiStnzC7zs97FUEK2T4XapjF1519EyFBViTfHpGpnf5bfizDz" - "sW9kYUtRDW1UC2LgHr7npgq5W9TBmHf9hSmRgM9XXucjXLqubNWE7HUMhbKjuBqkirRM"; - -TEST(TWAnySignerSolana, SignTransfer) { - auto privateKey = Base58::bitcoin.decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); - auto input = Proto::SigningInput(); - - auto& message = *input.mutable_transfer_transaction(); - message.set_recipient("EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd"); - message.set_value((uint64_t)42L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - ASSERT_EQ(output.encoded(), expectedString1); -} - -TEST(TWAnySignerSolana, SignTransferToSelf) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Proto::SigningInput(); - - auto& message = *input.mutable_transfer_transaction(); - message.set_recipient("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - message.set_value((uint64_t)42L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqe" - "EFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8n" - "UkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDelegateStakeTransaction_noStakeAccount) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_delegate_stake_transaction(); - message.set_validator_pubkey("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - message.set_value((uint64_t)42L); - message.set_stake_account(""); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "j24mVM9Zgu5vDZhPLGGuCRXQnP9djNtxdHh4txN3S7dwJsNNL5fbhzGpPgSUAcLGoMVCfF9TuqTYfpfJnb4sJFe1ahM8yPL5HwuKL6py5AZJFi8SWx9fvaVB699dCPo1GT3JoEBLPCZ9o2jQtnwzLkzTYJnKv2axqhKWFE2sz6TBA5J39eZcjMFUYgyxz6Q5S4MWqYQCb8UET2NAEZoKcfy7j8N25WXL6Gj4j3hBZjpHQQNaGaNEprEqyma3ZuVhpGiCALSsuzVLX3wZVo4icXwe952deMFA4tH3BK1jcSQCgfmcKDJ9nd7bdrnUUs4BoMdF1uDZB5LxE2UH8QiqtYvaUcorF4SJ3gPxM5ykbyPsNK1cSYZF9NMpW2GofyC17eELwnHQTQB2kqphxJZu7BahvkwiDPPeeydiXAkBspJ3nc3PCBujv6WJw22ZHw5j6zAP8ZGnCW44pqtWD5qifF9tTKhySKdANNiWifs3tSCCPQqjfJXu14drNinR6VG8rJxS1qgmRYiRQUa7m1vtoaZFRN5qKUeAfoFKkAVaNnMdwgsNqNH4dqBodTCJFs1LkYwhgRZdZGbwXTn1j7vpR3DSnv4g72i2H556srzK53jdUmdv6yfxt516XDSshqZtHnKZ1tudxKjBXwsqT3imDiZFVka9wKWUAYMCi4XZ79CY6Xpsd9c18U2e9TCngQmgkTATFgrqysfraokNffgqWxvsPMugksbvbPjJs3iCzByvphkC9p7hCf6LwbeF8XnVB91EAgRDA4VLE1f9wkcq5zjy879YWJ4r516h3PQszTz1EaJXNAXdbk5Em7eyuuabGP1Q3nijFTL2yhMDsXpgrjAuEAABNxFMd4J1JRMaic615mHrhwociksrsfQK"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDelegateStakeTransaction_withAccount) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_delegate_stake_transaction(); - message.set_validator_pubkey("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - message.set_stake_account("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); - message.set_value((uint64_t)42L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "TKPiN35HzeD3zdwxDFvnkgoqud7CZsda15JkBwM4nDpr623rM7MZsH6QvMMyKpiz7MeRNTrfyHkRLQSBT9Tbg2mgTdfrbhhqeF3Suu5ECphqn8DFYPoMnFzeg5u9gaqevfjhuizzeo2YDJF8aVGy1pez8gMbp5vHz1SuvQUgfcvFctggUMwNiJorSmmp3N6TzQSd38CZrA8ZLhaJjuwDwVMjmj18rGTV1gkX19L7byTFrus2vNvPeUa2AawwUnFpYMPgvCKkHTrpnjvypjoLof9yMUFQ5M1S3Ntv53KJyXwXq6ejJnBDtisnDcdMDNSZp3VeKz6XCr8XVM5xNVh3LX12V4kc3ueqkokYJLP1JmuhA3nNZA1G5KTNno93HUoBkEa1x5h3haoCSgmQC97LoJbJM6B6C2NbaDj2J6iiTaVQdin4He4Jpj575WDhNTqsLjzFUHPUHQF1CRnuss8UpVyMsa4kdVqCDQGeh5DKbkikgcB8GKPBuC91DRxGEqgoygNsu5nnQy4o3YAJnBBK6HsKxpdjbYD8wCUdLw8muhjpEqeBTPShEaogm9zfehidiCcnxbeoX3gmW8oH9gpWoX7GrkJgF6Wn7iWohmrzqzAjoBz8hpeY5nkkhHrf9iswVGMpakdLGy3YxkGJVpsW8KJACwEKXGLq8SVLtXSUHG8EP16zfYHxKjkCSs8PkdFsA5esxsxppPTVZivuEPqJ5og55aNmugdNDrAFYWdcH1Q4rm7BXN6oHECdz2yY4HFVWh9u592oqozt2gQKu3vmhcNFzzQe1xgs6zKSv38kSGTnipd7Hx2VL3qNAR6XBRiwAi226qSTzxi6R82p7cMB7TMy6fk5AZ3sXDSXFNJ9S5SSU1V63ruw75QMtVio"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDeactivateStakeTransaction) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_deactivate_stake_transaction(); - message.set_stake_account("6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "6x3fSstNz4GpPxmT5jHXwyD62uyJMKaPWeBDNNcwXZA9NJ3E7KavCXPNUd8ZYTX5VpkfHKGszkwzM6AdAp4giLD29jvWdNYjkV1Nvb42xFwGD6ryMPZzXkJijaRTrA7SvPTDSRU2haGVmorqkywAXLQUCw47NmBUfLTb5gDcKoBeaAsahckv1eCE746thJVTg2dQNvUTULKF6xckUg7kwFkcUuRe4HCcRgrKcNAUKLR2rEM3brVQkUyAaAtMMtc3gVDXxxpbtW5Fa9wGaEnh31FdRo4z5YBzAUaz7vcrvzF2j81KCPTVnYyTmeJzCzJafzCVCtw"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDeactivateAllStakeTransaction) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_deactivate_all_stake_transaction(); - *message.add_stake_accounts() = "CJQStmfyoHbosX1GfVn64yWrNJAo214q2aqxwS6FGh4k"; - *message.add_stake_accounts() = "6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"; - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "U9azMJWRfDhypoDeQLYWyBYFZCwRNZy8sbrVX9awKK84zNGbSQfYTTJ3ZyzjNUVbU5npbw2MsWfmZGHZRvpfN7G7o3sVePyFRXrmLxrGZzGycFv25Zff4zPxDarbsugbCBgzVGpgwu8x7MdkwBAVHVtNsgMcHgArEAjEmk7YEGpZ15rjo39bCRvmuprWLqSv2SK1RyTZPpTPXVevAbA4i9vvcY8eUbwW29SZCoyGaagLU5EBV9vckMjzGa7gq2yMR6rbq8tDdWaXapYs8RavU49WN94yg4wdE4fzYq8DjqXHq3MuUBLxeYDKJnvj84ioeM4eR1EwjBNrGyz5GHTRuhbNg1nc57SpKsSMVSZW5Ra3tUk84YZXYFHxzeQ9Tv4o"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignWithdrawStakeTransaction) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_withdraw_transaction(); - message.set_stake_account("6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"); - message.set_value((uint64_t)42L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "gxr4o1trVP8DGG8UC21AA964YqAPFA3rBCF9MwmBQpn5fDtcujM9wp1gzT466MxWGR8wMciS6dSL771q29eURrEEuvhJzRaFDGPLgVB3UL4gd4T2amPQkR4Dzq5drKEtPJRBR86KVVc2kjDsbWNpdL8S7pZqW3VUijAbm9TS8ezG8NExSCkhxExKhUjXWWguEL4qXra7s2JZfhtmvuJneWnEY3isUVfC9knWtGNwpNFvRvzbH2sgHzwtSsD7mkYrBJoazLCwT8r9yypxycHL41XcGtH425MA16kVSunvvBfzG9PzBTS65YJBs64tzttasCU9uEphkwgmfrmoEC8iKt8xD47Ra79RyXd95yURsaxvpb1tVAH8kMNtj8iV1Pfm"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignWithdrawAllStakeTransaction) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_withdraw_all_transaction(); - message.add_stake_accounts(); - message.add_stake_accounts(); - message.mutable_stake_accounts(0)->set_stake_account("CJQStmfyoHbosX1GfVn64yWrNJAo214q2aqxwS6FGh4k"); - message.mutable_stake_accounts(0)->set_value((uint64_t)42L); - message.mutable_stake_accounts(1)->set_stake_account("6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"); - message.mutable_stake_accounts(1)->set_value((uint64_t)67L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "cvBNusjtHkR74EfWsvFPEe2Mydcr7eoLeY2wJw2ZMZYViotbb63Adai7UD1PW9uLusoVHGLeJC5cPgVBC4F693P9tPAxLs9yiZj1ZJQ4DgnYbeXafqzjdWje1Ly5FgpDUJaaU2RnLCG51CcrmiTJ4KB5fwai6egZaNjbiqo1DEC1wJz4FgKug2aKQWLdeCiH9WhCuvqfhNV6mEE4qRCkU8uS2gfSqBd1AdrczvoDEbKQszosrwmawxqmvTE5EWaFzMb48x9nLqxvpQCvGQu1nX6FxZJjv2swekA7wGLEAA4uSdFLTHNrYSi8pn8hVYGwESEzth9oiPkJCvW7Y2KvGALeERUZn8knHiz2eqaaT72Ajp9UogMdZtiuFHufveLXpBLWUERchhB7eU1magYcPNHcZuEE4uQv5kZJhHAqYCGU6dyUFLVA9Edus7o6fTktYVCjoGb"; - EXPECT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignDeactivateStakeTransaction_1) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_deactivate_stake_transaction(); - message.set_stake_account("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); - - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "AhfB77PTGTKBfbGPGuEz2khbBy8m8Kou1zqZST9dP7PLJNSeEze5NJuCh5qecPLa3S8xAQ6mTULmnAWiW81ib87nhy" - "wFtx5nKiUvmhdXsvKCSX6NNtNXdRz5yZi3UEop4obco85SY2czS6n4SJwmtDedHLtg9urqdZVth7AUM8KAtrRsksyv" - "ZRYXh64Z8QGyNY7ekj31ae11avGiSDNWYZZHqx7VPWRsKeatGyGk5zPmnRdL8ABMQgJ1Te7wAWwVnNn5QcoAxDuPw6" - "uDctP8Q5S4TieRVatCnukQFj5BTJisez3E2ZJPWhVrMh4K3wEFkPHA7dR"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignWithdrawStakeTransaction_1) { - auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); - auto input = Solana::Proto::SigningInput(); - - auto& message = *input.mutable_withdraw_transaction(); - message.set_stake_account("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); - message.set_value((uint64_t)42L); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_recent_blockhash("11111111111111111111111111111111"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = "NL7WgagucfLd6AkTtcKe1dqd47xxzF356Q7tEhPrz1LRzZiAmokAaUkpwJ7X71Pmz97zZf9gZQU5BNswdcdpqUL8n1jwn4CoZMaPJhX5LF43Sj817cgreSG14TEWfKertpVpTtc5zY7vkDM7t9wjYhkaqgYz76HQtqAqRHnHF2Qr9EEfLj4zYRerWtyfS3EVyVUaasPxJ5vkcaonEfpGc6uWecaFr2A3YbzEBQpWXjMaXLqmMDtNS8rTNZmwvToa71ddFZKDgaHDcc6Lkg8qriZ3aQbUqL1TbeYp2mk9dWTKY62L1YFE2DyZV5P2qz5feywcMZ9JW6X1wBmiHFCseC42QbnbTibr1VdqLbGx7UWn5tHWk5jCN2aatEPfbFDZ"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateTokenAccount1) { - auto privateKeyData = Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_token_account_transaction(); - message.set_main_address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_token_address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVnH2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtCjAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzjB2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2TN3xXwu1"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateTokenAccount2) { - auto privateKeyData = parse_hex("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_token_account_transaction(); - message.set_main_address("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("HxaCmxrXgzkzXYvDFTToENtf9rVKk7cbiuSUqnqNheHq"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/5KtPn1LGuxhFiwjxErkxTb7XxtLVYUBe6Cn33ej7ATNVyorrkk3UAFJWDBUmzP8CZjmkocCxiMAdYnvrKoGpMsJx - "EoJGDRFZdnjmx7rgwYSuDGTMTUdxCBeh8RggrQDzGht9bwzLPpCWkCrN4iQJqg3R6JxP7z2QZuf7dGCZcjMVBmmisYE8waRsohcvygRwmGr6nefbaujR5avm2x3EUvoTGyy8cMZJxX7URx45qQJyCgqFLNFCQzD1Kej3xCEPAJqCdGZgmqkryw2E2nkpGKXgRmbyEg2rFgd5kpvjG6jSLLYzGomxVnaKK2XyMQbcedkTMYJ8Ara71iWPRFUziWfgivZcA1qsQp92Fpao3FSsRprhoQz9u1VyAnh8zEM9jCKiE5s4dwCknqCJYeYsbMLn1be2vNP9bMQfu1jjGSHmbb9WR3E2vakTUEUByASXqSAJZuXYE5scopEzB28rC8nrC31ArLMZng5wWym3QbqEv2Syd6RHoEeoXR6vA5LPqvJKyvtH82p4hc4XbD18128aNrFG3GTD2P"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateTokenAccountForOther) { - auto privateKeyData = parse_hex("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_token_account_transaction(); - message.set_main_address("3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft"); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_token_address("67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3"); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/3E6UFVamHCm6Bgk8gXdZex7R7tJAVxqJm6t9ephAKu1PjcfZrD7CJqMwKu6RrvWSUESbZFqzdUyLXuxAFaawPHvJ - "4BsrHedHuForcKDhLdnLYDXgtQgQEj3EQVDtEhqa7o6ukFjW3shpTWv6PeKQdMp6af4ASjD4xQeZvXxLK5WUjguVMUf3xdJn7RnFeM7hdDJ56RDBM5PRJbRJVHjz6FJ7SVNTvr9y3gVYQtWx7NfKRxiyEAfq9JG7nqxSWaW6raMr9t35aVcdAVuXE9iXj3rzhVfCS69vVzy5KcFEK3mvDYG6L12V2CfviCydmeCvPw5r3zBUrZSQv7Ti4XFNBrPbk28gcqQwsBknBqasHxHqD9VUyPmBTuUyXq75QN8rhqN55NjxKBUw37tEUS1jKVpWnTeLFq1eRAMdXvjftNuQ5Bmm8Zc12PGWj9vdorBaYyvZXexJST5xNjR4SCkXvXZoRScETck95chv3VBn54jP8DpB4GGUmATFKSxpdtnNV64i1SQXW13KJwswthJvAaDiqevQLKLkvrTEAdb4BxEfPkFjDVti6P58rTZCMg5CTVLqdmWwpTSW5V"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignTokenTransfer1) { - auto privateKeyData = Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_token_transfer_transaction(); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_sender_token_address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - message.set_recipient_token_address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - message.set_amount(4000); // 0.004 - message.set_decimals(6); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg - "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jRe2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvPiPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMqDV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnotcrH7pUa94xCVvCPPaomF"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignTokenTransfer2) { - auto privateKeyData = Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_token_transfer_transaction(); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_sender_token_address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - message.set_recipient_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); - message.set_amount(6100); // 0.0061 - message.set_decimals(6); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("zMEbroNLJ4vfDTdQyA72rk35c7nPo4K38efHLujbSuz"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - // https://explorer.solana.com/tx/2pMvzparE16evNgNhiexBfj15eurQgqFJXemYkuGasWV8RfT5tQseadqXA2VXbgGZPM1MpLcGwfkKKqvYvrKTmnR - "LCtawaKHmvh9WEjYPFFMDQXsdKMQbVyK4Q3aRRfLCouqw6GE4p31PRPFoQqtazTziEj3ex3iLgnCspz1MN4SUE9d33g3HiiA6oCS6wGMvB2i3ojtmJzndCiLoDmuZgiuGouVSeS2MAEUoS3CRjdnbNKbRwgKn8YsDe1bZ57ueipfBLJfiE7xr8ji678uAv8FcMgo8Mq88SBGxVCUhjMS2VGQZhRUHHzDmvnzxhbbUzsLDfApzjHExkUm7ws3cQ2i1cSpQNCQWJd6rcDv1sYwDAavPS571Ny3CUq4cZxABh45Gj88LkRpzBMRdoebrh9hPy8ZRnu7PocBVjZytCgdF4CuhzdYNsmdcuU2WN5CEmv5zQ7pBrFdLZ8bBifP"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignCreateAndTransferToken) { - auto privateKeyData = Base58::bitcoin.decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); - ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); - - auto input = Solana::Proto::SigningInput(); - auto& message = *input.mutable_create_and_transfer_token_transaction(); - message.set_recipient_main_address("71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd"); - message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - message.set_recipient_token_address("EF6L8yJT1SoRoDCkAZfSVmaweqMzfhxZiptKi7Tgj5XY"); - message.set_sender_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); - message.set_amount(2900); - message.set_decimals(6); - input.set_private_key(privateKeyData.data(), privateKeyData.size()); - input.set_recent_blockhash("DMmDdJP41M9mw8Z4586VSvxqGCrqPy5uciF6HsKUVDja"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeSolana); - - auto expectedString = - //https://explorer.solana.com/tx/449VaYo48LrkMJF6XVKt9sJwVQN6Seqrmh9erDCLtiuj6BgFG3wpF5TwjNkxgJ7qzNa6NTj3TFsU3h9hKszfkA7w - "3Y2MVz2VVi7aEyC9q1awwdk1ModDBPHRSacKmTYnSgkmbbJeZ62Fub1bVPSHaTy4LUcQpzCQYhHAKtTKXUDYijEeLsMAUqPBEMAq1w8zCdqDpdXy6M4PuwNtYVV1WgqeiEsiMWpPp4BGWKfcziwFbmYueUGituacJq4wTnt92fho8mFi49XW64gEG4iNGScDtJkY7Geq8PKiLh1E9JMJoceiHxKbmxzCmmLTxEHdhySYHcDUSXnXWogZskeZNBMtR9dNjEMkCzEjrxRpBtJPtUNshciY45mDPNmw4j3xyLCBTRikyfFLc5g11r3UgyVD4YokoPRvrEXsgt6W3yjBshropBm6mY2eJYvfY2eZz4Yq8kLcUatCHVKtjcb1mP9Ww57KisJ9bRhipC8sodFaMYhZARMEa4a1u9eH4MyNUATRGNXarwQSBY46PWS3nKP6QBK7Dw7Ppp9MmYkdPcXKaLScbyLF3jKu6dHWMkHw3WdXSsM1wwXjXnWF9LxdwaEVcDmySWybj6aKD9QCWTU5kdncqJU56f7SYNRTN289WdUFGNDmSh56tj2v1"; - ASSERT_EQ(output.encoded(), expectedString); -} - -TEST(TWAnySignerSolana, SignJSON) { - auto json = STRING(R"({"recentBlockhash":"11111111111111111111111111111111","transferTransaction":{"recipient":"EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd","value":"42"}})"); - Data keyData = Base58::bitcoin.decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); - EXPECT_EQ(hex(keyData), "8778cc93c6596387e751d2dc693bbd93e434bd233bc5b68a826c56131821cb63"); - auto key = WRAPD(TWDataCreateWithBytes(keyData.data(), keyData.size())); - - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeSolana)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeSolana)); - assertStringsEqual(result, expectedString1); -} diff --git a/tests/Solana/TWCoinTypeTests.cpp b/tests/Solana/TWCoinTypeTests.cpp deleted file mode 100644 index 5bd9998488a..00000000000 --- a/tests/Solana/TWCoinTypeTests.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - -TEST(TWSolanaCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeSolana)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("5LmxrEKGchhMuYfw6Qut6CbsvE9pVfb8YvwZKvWssSesDVjHioBCmWKSJQh1WhvcM6CpemhpHNmEMA2a36rzwTa8")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeSolana, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("Bxp8yhH9zNwxyE4UqxP7a7hgJ5xTZfxNNft7YJJ2VRjT")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeSolana, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeSolana)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeSolana)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeSolana), 9); - ASSERT_EQ(TWBlockchainSolana, TWCoinTypeBlockchain(TWCoinTypeSolana)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeSolana)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeSolana)); - assertStringsEqual(symbol, "SOL"); - assertStringsEqual(txUrl, "https://solscan.io/tx/5LmxrEKGchhMuYfw6Qut6CbsvE9pVfb8YvwZKvWssSesDVjHioBCmWKSJQh1WhvcM6CpemhpHNmEMA2a36rzwTa8"); - assertStringsEqual(accUrl, "https://solscan.io/account/Bxp8yhH9zNwxyE4UqxP7a7hgJ5xTZfxNNft7YJJ2VRjT"); - assertStringsEqual(id, "solana"); - assertStringsEqual(name, "Solana"); -} diff --git a/tests/Solana/TWSolanaAddressTests.cpp b/tests/Solana/TWSolanaAddressTests.cpp deleted file mode 100644 index 0ce32caf5db..00000000000 --- a/tests/Solana/TWSolanaAddressTests.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" - -#include -#include -#include - -#include - -TEST(TWSolanaAddress, HDWallet) { - auto mnemonic = - "shoot island position soft burden budget tooth cruel issue economy destroy above"; - auto passphrase = ""; - - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(STRING(mnemonic).get(), STRING(passphrase).get())); - - auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKey(wallet.get(), TWCoinTypeSolana, WRAPS(TWCoinTypeDerivationPath(TWCoinTypeSolana)).get())); - auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519(privateKey.get())); - auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeSolana)); - auto addressStr = WRAPS(TWAnyAddressDescription(address.get())); - - assertStringsEqual(addressStr, "2bUBiBNZyD29gP1oV6de7nxowMLoDBtopMMTGgMvjG5m"); -} - -TEST(TWSolanaProgram, defaultTokenAddress) { - const char* serumToken = "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"; - auto solanaAddress = WRAP(TWSolanaAddress, TWSolanaAddressCreateWithString(WRAPS(TWStringCreateWithUTF8Bytes("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V")).get())); - auto address1 = WRAPS(TWSolanaAddressDefaultTokenAddress(solanaAddress.get(), WRAPS(TWStringCreateWithUTF8Bytes(serumToken)).get())); - EXPECT_EQ(std::string(TWStringUTF8Bytes(address1.get())), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); -} diff --git a/tests/Solana/TransactionTests.cpp b/tests/Solana/TransactionTests.cpp deleted file mode 100644 index 3d1254bd3ff..00000000000 --- a/tests/Solana/TransactionTests.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Solana/Address.h" -#include "Solana/Transaction.h" -#include "Solana/Program.h" -#include "HexCoding.h" -#include "PublicKey.h" - -#include "BinaryCoding.h" - -#include - -using namespace TW; -using namespace TW::Solana; - -TEST(SolanaTransaction, TransferMessageData) { - auto from = Address("6eoo7i1khGhVm8tLBMAdq4ax2FxkKP4G7mCcfHyr3STN"); - auto to = Address("56B334QvCDMSirsmtEJGfanZm8GqeQarrSjdAb2MbeNM"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); - - auto expectedHex = - "0100010353f9d600fe925083bb399907ea648d23a6a081fc7e9059202fd725f7edd281dd3cc1ff9ba3c7a876c8" - "082df2f8a36ea9342ce3819dd4b6fa72d4a18e04a5363a00000000000000000000000000000000000000000000" - "000000000000000000000000000000000000000000000000000000000000000000000000000000000000010202" - "00010c020000002a00000000000000"; - ASSERT_EQ(hex(transaction.messageData()), expectedHex); -} - -TEST(SolanaTransaction, TransferSerializeTransaction) { - auto from = Address("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5"); - auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); - Signature signature( - "46SRiQGvtPb1iivDfnuC3dW1GzXkfQPTjdUyvFqF2sdPvFrsfx94fys2xpNKR6UiAj7RgKWdJG6mEfe85up6i1JT"); - transaction.signatures.clear(); - transaction.signatures.push_back(signature); - - auto expectedString = - "5SiHeYyuDgjHxWHbYXSSPfmYc8s7EYZ8bdZ7j15z9Bj1yyZA3Bia9uWkRdXVkuqifXiiQj6fVKy" - "7UkCL5kvv6iKrfjWTZ3szMVssTFxgJ7p8UJ7Mgg2uhHejVJvbzbiHHLbNVuJFs6kBxddnJ2yjWU" - "Cp2dYJgjmphfA8hRHHdPH4Rv6znxEhD8q9XY4nByRPL7oMCo32oxeJn5rGbUZdCkapRUXG7zU9w" - "hv6KjBktcUQZRCahhowGJT4UM5yCNCsUcqY9yan7UxqPyJgaFPuq4duqWJtQ39bTQ36X"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, TransferTransactionPayToSelf) { - auto from = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto transaction = Transaction(from, to, 42, recentBlockhash); - Signature signature( - "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); - transaction.signatures.clear(); - transaction.signatures.push_back(signature); - - auto expectedString = - "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqe" - "EFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8n" - "UkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, StakeSerializeTransactionV2) { - auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); - auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = Transaction(message); - Signature signature( - "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); - transaction.signatures.clear(); - transaction.signatures.push_back(signature); - - auto expectedString = "W1EAswaWK7mF4r9eZ2hHBZnfPnqLuNPiYkEMzFbwQsgSQu6XbSTL9AN92iyMbAMxPoRpt9ipUyztrmszAnm688N3k7uhiKn2osm9nxi6YkGLfu31jHTSu7mn3RtmenV3qopfPDAM92cXrzUhKGWtQ6cATeQh8i8ZfHpmjyuik7Eg3SQ4sa2543CmcozzjmTTWThThuLdvFCZJzBeRBFWLujqjbs5mA66XVtiDwsEqYByznoo4BN45XUHxnZebmPfo4hi5sf27UkhzPHik371BGxbVDexQp4y5nCEHy8ybfNCvMPLr2SEBiWSifwPkmwYN3hGCkBpqLoHCCiRcyJuRHW8hSDFR4JPQ3Xe3FGfpgbayaawZigUnFuPGSpoGrURZRoLCzc6V4ApqcJbmzFhg5zJz2yTX5GvQSYWLFnTKbPYcgBNpdyMLJTivonrKtgkfdymZVKjDwnHUApC7WD4L9mqzTf1dzR61Fxhu3Rdh8ECiVEDgB1wkWZWkTKEdANmtaYLKCMUs3n4VhuZbSFLEiTg7yRWM2pjBgiBB4qywbF7SE75UtzSFCaDnn27mKkxRBqZEGEgfpEoK2AxjsiCZEZxfLeyZFbwWe7xasmNiXr6CnAQhwsmxJk79h7SYmaje76JLxHVX5gbQmLfn5bc1xthS3YhteSovQ8xYq1jiHCfsXRwbxKrNA4kVMiSa6spoU9AhFL8cDAZjAqoU4YRwBihZVhXSFCRnYAK8FabzEv1M44EeHX1sfMG8T1U7y3DEjom7jv6rqZfLumWpbXDTqanB7zTbTjGyDcBBf21edjpZzBZ7osS5fTVYJ5mZBSvjjhuGkUgZZWgYozAKvdyyrJH6UdcPvNm2XgMRYJxqyCin1zhCeQ25vK1H8Jj"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, StakeSerializeTransactionV1) { - auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); - auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); - auto programId = Address("Stake11111111111111111111111111111111111111"); - Solana::Hash recentBlockhash("11111111111111111111111111111111"); - auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); - auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); - auto transaction = Transaction(message); - Signature signature( - "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); - transaction.signatures.clear(); - transaction.signatures.push_back(signature); - - auto expectedString = "W1EAswaWK7mF4r9eZ2hHBZnfPnqLuNPiYkEMzFbwQsgSQu6XbSTL9AN92iyMbAMxPoRpt9ipUyztrmszAnm688N3k7uhiKn2osm9nxi6YkGLfu31jHTSu7mn3RtmenV3qopfPDAM7jtGoYQFb7eFVbujUb6tbeQ9UqLJq1sJ7uMZ4wqecmQPouDmJnpmJk4CHMzLnPNTwyGmGio6sYAS3xKZ7DFXvjwGPuD8PyYHSfdPro1p3jy9igPZNAbQ6fgK7LL3sERKCUdvPy7k14xgHbtsVy2mu54LY5c8F9sFst2uzQiTsXRTdjPFAyCVwB5pccNVotCrJ6Q2aKSC2D2knVH7LgWzSBMSreJG75xyATneu922wSzz7QJDieqhDtdePtSbPtoCdtPNmDfdaeDbHxVAxMios9F7RSRmH2dq86NfWDvF8TuEbYY7gPnygz6jGvwfqSSoSnY8TnUhhceC7wJSMc8Hcf1kyfi8dqKm7rF57YjnrQoMmL5bWqJLKoJtdfFu24ceQN21k38U2tUMWJaBASWukgTJUbNSCemNPZt4P3cNbeB3L1wBj4GEYXVTbTFYKME5JscU5RsnkMJZZ1PgxSi63HT4hwQLok4c18UdJgzMFu1njpZj3Sw76mwV3ea7ruHnP4yyM3YhUGbNjpx5fAcnvdLcXChdsgeUpJhutME6V86Rk2EEskoJeD3qNWi3hvfQx172hZRHyKyr29Ts1uLQxcMJq7oeQUxvTfXxSe6cBuPJUDFkAET3qpS7rWM7rvQQ8rDLQF5QvcJnrYTq12pVgw28WXdgi45811a7DWHGuwHRj5FJdLQAHkKe4EXVeTCdbYHREVwuyTJgAvb8SXjRE5a9n3qpRDr7iEd5UDZKB5HgvMsMYWh5"; - ASSERT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, CreateTokenAccountTransaction) { - auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - Solana::Hash recentBlockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - auto message = Message::createTokenCreateAccount(signer, TokenInstruction::CreateTokenAccount, signer, token, tokenAddress, recentBlockhash); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numCreditOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numCreditOnlyUnsignedAccounts, 5); - ASSERT_EQ(message.accountKeys.size(), 7); - EXPECT_EQ(message.accountKeys[0].string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - EXPECT_EQ(message.accountKeys[1].string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - EXPECT_EQ(message.accountKeys[2].string(), "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - EXPECT_EQ(message.accountKeys[3].string(), "11111111111111111111111111111111"); - EXPECT_EQ(message.accountKeys[4].string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - EXPECT_EQ(message.accountKeys[5].string(), "SysvarRent111111111111111111111111111111111"); - EXPECT_EQ(message.accountKeys[6].string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - EXPECT_EQ(Base58::bitcoin.encode(message.recentBlockhash.bytes), "9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); - ASSERT_EQ(message.instructions.size(), 1); - EXPECT_EQ(message.instructions[0].programId.string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); - ASSERT_EQ(message.instructions[0].accounts.size(), 7); - EXPECT_EQ(message.instructions[0].accounts[0].account.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - EXPECT_EQ(message.instructions[0].accounts[1].account.string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - EXPECT_EQ(message.instructions[0].accounts[2].account.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - EXPECT_EQ(message.instructions[0].accounts[3].account.string(), "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - EXPECT_EQ(message.instructions[0].accounts[4].account.string(), "11111111111111111111111111111111"); - EXPECT_EQ(message.instructions[0].accounts[5].account.string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - EXPECT_EQ(message.instructions[0].accounts[6].account.string(), "SysvarRent111111111111111111111111111111111"); - auto transaction = Transaction(message); - transaction.signatures.clear(); - Signature signature("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); - transaction.signatures.push_back(signature); - - auto expectedString = - // test data obtained from spl-token create-account - "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVnH2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtCjAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzjB2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2TN3xXwu1"; - EXPECT_EQ(transaction.serialize(), expectedString); -} - -TEST(SolanaTransaction, TransferTokenTransaction) { - auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); - auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); - auto senderTokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); - auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); - uint64_t amount = 4000; - uint8_t decimals = 6; - Solana::Hash recentBlockhash("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); - auto message = Message::createTokenTransfer(signer, TokenInstruction::TokenTransfer, token, senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); - EXPECT_EQ(message.header.numRequiredSignatures, 1); - EXPECT_EQ(message.header.numCreditOnlySignedAccounts, 0); - EXPECT_EQ(message.header.numCreditOnlyUnsignedAccounts, 2); - ASSERT_EQ(message.accountKeys.size(), 5); - ASSERT_EQ(message.instructions.size(), 1); - EXPECT_EQ(message.instructions[0].programId.string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); - ASSERT_EQ(message.instructions[0].accounts.size(), 4); - auto transaction = Transaction(message); - transaction.signatures.clear(); - Signature signature("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); - transaction.signatures.push_back(signature); - - auto expectedString = - // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg - // test data obtained from spl-token transfer - "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jRe2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvPiPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMqDV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnotcrH7pUa94xCVvCPPaomF"; - EXPECT_EQ(transaction.serialize(), expectedString); -} diff --git a/tests/Stellar/AddressTests.cpp b/tests/Stellar/AddressTests.cpp deleted file mode 100644 index 9fc5dc06b19..00000000000 --- a/tests/Stellar/AddressTests.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Stellar/Address.h" -#include "Bitcoin/Address.h" -#include "HexCoding.h" -#include "PrivateKey.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::Stellar; - -TEST(StellarAddress, FromPublicKey) { - const auto publicKey = PublicKey(parse_hex("0103E20EC6B4A39A629815AE02C0A1393B9225E3B890CAE45B59F42FA29BE9668D"), TWPublicKeyTypeED25519); - const auto address = Address(publicKey); - auto str = hex(address.bytes); - ASSERT_EQ(string("GAB6EDWGWSRZUYUYCWXAFQFBHE5ZEJPDXCIMVZC3LH2C7IU35FTI2NOQ"), address.string()); -} - -TEST(StellarAddress, FromString) { - string stellarAddress = "GAB6EDWGWSRZUYUYCWXAFQFBHE5ZEJPDXCIMVZC3LH2C7IU35FTI2NOQ"; - const auto address = Address(stellarAddress); - ASSERT_EQ(address.string(), stellarAddress); -} - -TEST(StellarAddress, isValid) { - string stellarAddress = "GABQHYQOY22KHGTCTAK24AWAUE4TXERF4O4JBSXELNM7IL5CTPUWM3SC"; - string bitcoinAddress = "1Ma2DrB78K7jmAwaomqZNRMCvgQrNjE2QC"; - - ASSERT_TRUE(Address::isValid(stellarAddress)); - ASSERT_FALSE(Address::isValid(bitcoinAddress)); -} diff --git a/tests/Stellar/TWAnySignerTests.cpp b/tests/Stellar/TWAnySignerTests.cpp deleted file mode 100644 index bbe59a37f43..00000000000 --- a/tests/Stellar/TWAnySignerTests.cpp +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" - -#include "HexCoding.h" -#include "PrivateKey.h" -#include "Stellar/Address.h" -#include "proto/Stellar.pb.h" -#include -#include -#include - -using namespace TW; -using namespace TW::Stellar; - -TEST(TWAnySingerStellar, Sign_Payment) { - auto key = parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722"); - Proto::SigningInput input; - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); - input.set_fee(1000); - input.set_sequence(2); - input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); - input.mutable_op_payment()->set_amount(10000000); - input.set_private_key(key.data(), key.size()); - auto& memoText = *input.mutable_memo_text(); - memoText.set_text("Hello, world!"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeStellar); - - EXPECT_EQ(output.signature(), "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAEAAAANSGVsbG8sIHdvcmxkIQAAAAAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAEBQQldEkYJ6rMvOHilkwFCYyroGGUvrNeWVqr/sn3iFFqgz91XxgUT0ou7bMSPRgPROfBYDfQCFfFxbcDPrrCwB"); -} - -TEST(TWAnySingerStellar, Sign_Payment_66b5) { - auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); - PrivateKey privKey = PrivateKey(key); - PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); - Address addr = Address(pubKey); - EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); - - Proto::SigningInput input; - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); - input.set_fee(1000); - input.set_sequence(144098454883270657); - input.mutable_op_payment()->set_destination("GA3ISGYIE2ZTH3UAKEKBVHBPKUSL3LT4UQ6C5CUGP2IM5F467O267KI7"); - input.mutable_op_payment()->set_amount(1000000); - input.set_private_key(key.data(), key.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeStellar); - - // curl "https://horizon.stellar.org/transactions/66b5bca4b4293bdd85a6a559b08918482774b76bcc170b4533411f1d6422ce24" - EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAD6AH/8MgAAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAANokbCCazM+6AURQanC9VJL2ufKQ8LoqGfpDOl577te8AAAAAAAAAAAAPQkAAAAAAAAAAAXfTkXUAAABAM9Nhzr8iWKzqnHknrxSVoa4b2qzbTzgyE2+WWxg6XHH50xiFfmvtRKVhzp0Jg8PfhatOb6KNheKRWEw4OvqEDw=="); -} - -TEST(TWAnySingerStellar, Sign_Payment_Asset_ea50) { - auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); - PrivateKey privKey = PrivateKey(key); - PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); - Address addr = Address(pubKey); - EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); - - Proto::SigningInput input; - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); - input.set_fee(1000); - input.set_sequence(144098454883270661); - input.mutable_op_payment()->set_destination("GA3ISGYIE2ZTH3UAKEKBVHBPKUSL3LT4UQ6C5CUGP2IM5F467O267KI7"); - input.mutable_op_payment()->mutable_asset()->set_issuer("GA6HCMBLTZS5VYYBCATRBRZ3BZJMAFUDKYYF6AH6MVCMGWMRDNSWJPIH"); - input.mutable_op_payment()->mutable_asset()->set_alphanum4("MOBI"); - input.mutable_op_payment()->set_amount(12000000); - input.set_private_key(key.data(), key.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeStellar); - - // curl "https://horizon.stellar.org/transactions/ea50884cd1288d2d5420065995d13d750d812258e0e79280c4033a434e625c99 - EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAD6AH/8MgAAAAFAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAANokbCCazM+6AURQanC9VJL2ufKQ8LoqGfpDOl577te8AAAABTU9CSQAAAAA8cTArnmXa4wEQJxDHOw5SwBaDVjBfAP5lRMNZkRtlZAAAAAAAtxsAAAAAAAAAAAF305F1AAAAQEuWZZvKZuF6SMuSGIyfLqx5sn5O55+Kd489uP4g9jZH4UE7zZ4ME0+74I0BU8YDsYOmmxcfp/vdwTd+n3oGCQw="); -} - -TEST(TWAnySingerStellar, Sign_Change_Trust_ad9c) { - auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); - PrivateKey privKey = PrivateKey(key); - PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); - Address addr = Address(pubKey); - EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); - - Proto::SigningInput input; - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); - input.set_fee(10000); - input.set_sequence(144098454883270659); - input.mutable_op_change_trust()->mutable_asset()->set_issuer("GA6HCMBLTZS5VYYBCATRBRZ3BZJMAFUDKYYF6AH6MVCMGWMRDNSWJPIH"); - input.mutable_op_change_trust()->mutable_asset()->set_alphanum4("MOBI"); - input.mutable_op_change_trust()->set_valid_before(1613336576); - input.set_private_key(key.data(), key.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeStellar); - - // curl "https://horizon.stellar.org/transactions/ad9cd0f3d636096b6502ccae07adbcf2cd3c0da5393fc2b07813dbe90ecc0d7b" - EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAnEAH/8MgAAAADAAAAAQAAAAAAAAAAAAAAAGApkAAAAAAAAAAAAQAAAAAAAAAGAAAAAU1PQkkAAAAAPHEwK55l2uMBECcQxzsOUsAWg1YwXwD+ZUTDWZEbZWR//////////wAAAAAAAAABd9ORdQAAAEAnfyXyaNQX5Bq3AEQVBIaYd+cLib+y2sNY7DF/NYVSE51dZ6swGGElz094ObsPefmVmeRrkGsSc/fF5pmth+wJ"); -} - -TEST(TWAnySingerStellar, Sign_Change_Trust_2) { - auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); - PrivateKey privKey = PrivateKey(key); - PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); - Address addr = Address(pubKey); - EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); - - Proto::SigningInput input; - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); - input.set_fee(10000); - input.set_sequence(144098454883270659); - input.mutable_op_change_trust()->mutable_asset()->set_issuer("GDUKMGUGDZQK6YHYA5Z6AY2G4XDSZPSZ3SW5UN3ARVMO6QSRDWP5YLEX"); - input.mutable_op_change_trust()->mutable_asset()->set_alphanum4("USD"); - input.mutable_op_change_trust()->set_valid_before(1613336576); - input.set_private_key(key.data(), key.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeStellar); - - EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAnEAH/8MgAAAADAAAAAQAAAAAAAAAAAAAAAGApkAAAAAAAAAAAAQAAAAAAAAAGAAAAAVVTRAAAAAAA6KYahh5gr2D4B3PgY0blxyy+Wdyt2jdgjVjvQlEdn9x//////////wAAAAAAAAABd9ORdQAAAEDMZtN05ZsZB4OKOZSFkQvuRqDIvMME3PYMTAGJPQlO6Ee0nOtaRn2q0uf0IhETSSfqcsK5asAZzNj07tG0SPwM"); -} diff --git a/tests/Stellar/TWCoinTypeTests.cpp b/tests/Stellar/TWCoinTypeTests.cpp deleted file mode 100644 index 12164f9b2d6..00000000000 --- a/tests/Stellar/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWStellarCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeStellar)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("d9aeabfa9d24df8c5755125f8af243b74cd3ff878656cfa72c566a8824bf6e84")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeStellar, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("GCILJZQ3CKBKBUJWW4TAM6Q37LJA5MQX6GMSFSQN75BPLWIZ33OPRG52")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeStellar, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeStellar)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeStellar)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeStellar), 7); - ASSERT_EQ(TWBlockchainStellar, TWCoinTypeBlockchain(TWCoinTypeStellar)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeStellar)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeStellar)); - assertStringsEqual(symbol, "XLM"); - assertStringsEqual(txUrl, "https://blockchair.com/stellar/transaction/d9aeabfa9d24df8c5755125f8af243b74cd3ff878656cfa72c566a8824bf6e84"); - assertStringsEqual(accUrl, "https://blockchair.com/stellar/account/GCILJZQ3CKBKBUJWW4TAM6Q37LJA5MQX6GMSFSQN75BPLWIZ33OPRG52"); - assertStringsEqual(id, "stellar"); - assertStringsEqual(name, "Stellar"); -} diff --git a/tests/Stellar/TransactionTests.cpp b/tests/Stellar/TransactionTests.cpp deleted file mode 100644 index d9324c4c339..00000000000 --- a/tests/Stellar/TransactionTests.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" - -#include "Stellar/Address.h" -#include "Stellar/Signer.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include -#include -#include -#include "BinaryCoding.h" - -#include - -using namespace std; -using namespace TW; -using namespace TW::Stellar; - -TEST(StellarTransaction, sign) { - auto words = STRING("indicate rival expand cave giant same grocery burden ugly rose tuna blood"); - auto passphrase = STRING(""); - - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); - auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeStellar)); - auto input = TW::Stellar::Proto::SigningInput(); - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); - input.set_fee(1000); - input.set_sequence(2); - input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); - input.mutable_op_payment()->set_amount(10000000); - input.set_private_key(privateKey.get()->impl.bytes.data(), privateKey.get()->impl.bytes.size()); - - const auto signer = TW::Stellar::Signer(input); - - const auto signature = signer.sign(); - ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAxYC2MXoOs5v3/NT6PBn9q0uJu6u/YQle5FBa9uzteq4AAAAAAAAAAACYloAAAAAAAAAAARnfXKIAAABAocQZwTnVvGMQlpdGacWvgenxN5ku8YB8yhEGrDfEV48yDqcj6QaePAitDj/N2gxfYD9Q2pJ+ZpkQMsZZG4ACAg=="); -} - -TEST(StellarTransaction, signWithMemoText) { - auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); - auto input = Proto::SigningInput(); - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); - input.set_fee(1000); - input.set_sequence(2); - auto memoText = Proto::MemoText(); - memoText.set_text("Hello, world!"); - *input.mutable_memo_text() = memoText; - input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); - input.mutable_op_payment()->set_amount(10000000); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - const auto signer = Signer(input); - - const auto signature = signer.sign(); - ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAEAAAANSGVsbG8sIHdvcmxkIQAAAAAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAEBQQldEkYJ6rMvOHilkwFCYyroGGUvrNeWVqr/sn3iFFqgz91XxgUT0ou7bMSPRgPROfBYDfQCFfFxbcDPrrCwB"); -} - -TEST(StellarTransaction, signWithMemoHash) { - auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); - auto input = Proto::SigningInput(); - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); - input.set_fee(1000); - input.set_sequence(2); - auto memoHash = Proto::MemoHash(); - auto fromHex = parse_hex("315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"); - memoHash.set_hash(fromHex.data(), fromHex.size()); - *input.mutable_memo_hash() = memoHash; - input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); - input.mutable_op_payment()->set_amount(10000000); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - const auto signer = Signer(input); - - const auto signature = signer.sign(); - ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAMxX1vbdtB4xDuKwAZOSgFkYSsfznfIaTRb/JTHWJTt0wAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAECIyh1BG+hER5W+dgHDKe49X6VEYRWIjajM4Ufq3DUG/yw7Xv1MMF4eax3U0TRi7Qwj2fio/DRD3+/Ljtvip2MD"); -} - -TEST(StellarTransaction, signWithMemoReturn) { - auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); - auto input = Proto::SigningInput(); - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); - input.set_fee(1000); - input.set_sequence(2); - auto memoHash = Proto::MemoHash(); - auto fromHex = parse_hex("315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"); - memoHash.set_hash(fromHex.data(), fromHex.size()); - *input.mutable_memo_return_hash() = memoHash; - input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); - input.mutable_op_payment()->set_amount(10000000); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - const auto signer = Signer(input); - - const auto signature = signer.sign(); - ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAQxX1vbdtB4xDuKwAZOSgFkYSsfznfIaTRb/JTHWJTt0wAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAEBd77iui04quoaoWMfeJO06nRfn3Z9bptbAj7Ol44j3ApU8c9dJwVhJbQ7La4mKgIkYviEhGx3AIulFYCkokb8M"); -} - -TEST(StellarTransaction, signWithMemoID) { - auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); - auto input = Proto::SigningInput(); - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); - input.set_fee(1000); - input.set_sequence(2); - auto memoId = Proto::MemoId(); - memoId.set_id(1234567890); - *input.mutable_memo_id() = memoId; - input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); - input.mutable_op_payment()->set_amount(10000000); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - const auto signer = Signer(input); - - const auto signature = signer.sign(); - ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAIAAAAASZYC0gAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAEAOJ8wwCizQPf6JmkCsCNZolQeqet2qN7fgLUUQlwx3TNzM0+/GJ6Qc2faTybjKy111rE60IlnfaPeMl/nyxKIB"); -} - -TEST(StellarTransaction, signAcreateAccount) { - auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); - auto input = Proto::SigningInput(); - input.set_passphrase(TWStellarPassphrase_Stellar); - input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); - input.set_fee(1000); - input.set_sequence(2); - auto memoId = Proto::MemoId(); - memoId.set_id(1234567890); - *input.mutable_memo_id() = memoId; - input.mutable_op_create_account()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); - input.mutable_op_create_account()->set_amount(10000000); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - const auto signer = Signer(input); - - const auto signature = signer.sign(); - ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAIAAAAASZYC0gAAAAEAAAAAAAAAAAAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAmJaAAAAAAAAAAAEZ31yiAAAAQNgqNDqbe0X60gyH+1xf2Tv2RndFiJmyfbrvVjsTfjZAVRrS2zE9hHlqPQKpZkGKEFka7+1ElOS+/m/1JDnauQg="); -} diff --git a/tests/THORChain/SignerTests.cpp b/tests/THORChain/SignerTests.cpp deleted file mode 100644 index 5cb58be271d..00000000000 --- a/tests/THORChain/SignerTests.cpp +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "proto/Cosmos.pb.h" -#include "THORChain/Signer.h" -#include "HexCoding.h" - -#include -#include - -using namespace TW; - -TEST(THORChainSigner, SignTx) { - auto input = Cosmos::Proto::SigningInput(); - input.set_memo("memo1234"); - - auto msg = input.add_messages(); - auto& message = *msg->mutable_send_coins_message(); - message.set_from_address("thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r"); - message.set_to_address("thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"); - auto amountOfTx = message.add_amounts(); - amountOfTx->set_denom("rune"); - amountOfTx->set_amount(50000000); - - auto& fee = *input.mutable_fee(); - fee.set_gas(2000000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("rune"); - amountOfFee->set_amount(200); - - std::string json; - google::protobuf::util::MessageToJsonString(input, &json); - - EXPECT_EQ(R"({"fee":{"amounts":[{"denom":"rune","amount":"200"}],"gas":"2000000"},"memo":"memo1234","messages":[{"sendCoinsMessage":{"fromAddress":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","toAddress":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn","amounts":[{"denom":"rune","amount":"50000000"}]}}]})", json); - - auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); - input.set_private_key(privateKey.data(), privateKey.size()); - - auto output = THORChain::Signer::sign(input); - - EXPECT_EQ(R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"rune"}],"gas":"2000000"},"memo":"memo1234","msg":[{"type":"thorchain/MsgSend","value":{"amount":[{"amount":"50000000","denom":"rune"}],"from_address":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","to_address":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"12AaNC0v51Rhz8rBf7V7rpI6oksREWrjzba3RK1v1NNlqZq62sG0aXWvStp9zZXe07Pp2FviFBAx+uqWsO30NQ=="}]}})", output.json()); - EXPECT_EQ(hex(output.signature()), "d7601a342d2fe75461cfcac17fb57bae923aa24b11116ae3cdb6b744ad6fd4d365a99abadac1b46975af4ada7dcd95ded3b3e9d85be2141031faea96b0edf435"); -} - -TEST(THORChainSigner, SignJson) { - auto inputJson = R"({"fee":{"amounts":[{"denom":"rune","amount":"200"}],"gas":"2000000"},"memo":"memo1234","messages":[{"sendCoinsMessage":{"fromAddress":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","toAddress":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn","amounts":[{"denom":"rune","amount":"50000000"}]}}]})"; - auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); - - auto outputJson = THORChain::Signer::signJSON(inputJson, privateKey); - - EXPECT_EQ(R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"rune"}],"gas":"2000000"},"memo":"memo1234","msg":[{"type":"thorchain/MsgSend","value":{"amount":[{"amount":"50000000","denom":"rune"}],"from_address":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","to_address":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"12AaNC0v51Rhz8rBf7V7rpI6oksREWrjzba3RK1v1NNlqZq62sG0aXWvStp9zZXe07Pp2FviFBAx+uqWsO30NQ=="}]}})", outputJson); -} diff --git a/tests/THORChain/TWAnyAddressTests.cpp b/tests/THORChain/TWAnyAddressTests.cpp deleted file mode 100644 index 28e1618885e..00000000000 --- a/tests/THORChain/TWAnyAddressTests.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include "HexCoding.h" - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; - -TEST(THORChainAnyAddress, IsValid) { - EXPECT_TRUE(TWAnyAddressIsValid(STRING("thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r").get(), TWCoinTypeTHORChain)); - EXPECT_TRUE(TWAnyAddressIsValid(STRING("thor1c8jd7ad9pcw4k3wkuqlkz4auv95mldr2kyhc65").get(), TWCoinTypeTHORChain)); - EXPECT_FALSE(TWAnyAddressIsValid(STRING("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02").get(), TWCoinTypeTHORChain)); -} - -TEST(THORChainAnyAddress, Create) { - auto string = STRING("thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r"); - auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeTHORChain)); - auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); - EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); - auto keyHash = WRAPD(TWAnyAddressData(addr.get())); - assertHexEqual(keyHash, "1522e767db6eb19708b0038029bfbd607bc9bd0e"); -} diff --git a/tests/THORChain/TWAnySignerTests.cpp b/tests/THORChain/TWAnySignerTests.cpp deleted file mode 100644 index 0f065497660..00000000000 --- a/tests/THORChain/TWAnySignerTests.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2017-2021 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include -#include -#include "Cosmos/Address.h" -#include "proto/Cosmos.pb.h" -#include "HexCoding.h" - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; - -TEST(THORChainTWAnySigner, SignTx) { - auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); - Cosmos::Proto::SigningInput input; - input.set_account_number(593); - input.set_chain_id("thorchain"); - input.set_sequence(3); - input.set_private_key(privateKey.data(), privateKey.size()); - input.set_memo(""); - - auto fromAddress = "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r"; - auto toAddress = "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"; - - auto msg = input.add_messages(); - auto& message = *msg->mutable_send_coins_message(); - message.set_from_address(fromAddress); - message.set_to_address(toAddress); - auto amountOfTx = message.add_amounts(); - amountOfTx->set_denom("rune"); - amountOfTx->set_amount(10000000); - - auto& fee = *input.mutable_fee(); - fee.set_gas(200000); - auto amountOfFee = fee.add_amounts(); - amountOfFee->set_denom("rune"); - amountOfFee->set_amount(2000000); - - Cosmos::Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeTHORChain); - - // https://viewblock.io/thorchain/tx/FD0445AFFC4ED9ACCB7B5D3ADE361DAE4596EA096340F1360F1020381EA454AF - ASSERT_EQ(output.json(), R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"2000000","denom":"rune"}],"gas":"200000"},"memo":"","msg":[{"type":"thorchain/MsgSend","value":{"amount":[{"amount":"10000000","denom":"rune"}],"from_address":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","to_address":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"qgpMX3WNq4DsNBnYtdmBD4ejiailK4uI/m3/YVqCSNF8AtkUOTmP48ztqCbpkWTFvw1/9S8/ivsFxOcK6AI0jA=="}]}})"); -} diff --git a/tests/THORChain/TWCoinTypeTests.cpp b/tests/THORChain/TWCoinTypeTests.cpp deleted file mode 100644 index a80328106c6..00000000000 --- a/tests/THORChain/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWTHORChainCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTHORChain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("ADF0899E58C177E2391F22D04E9C5E1C35BB0F75B42B363A0761687907FD9476")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTHORChain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("thor196yf4pq80hjrmz7nnh0ar0ypqg02r0w4dq4mzu")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTHORChain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTHORChain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTHORChain)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTHORChain), 8); - ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeTHORChain)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTHORChain)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTHORChain)); - assertStringsEqual(symbol, "RUNE"); - assertStringsEqual(txUrl, "https://viewblock.io/thorchain/tx/ADF0899E58C177E2391F22D04E9C5E1C35BB0F75B42B363A0761687907FD9476"); - assertStringsEqual(accUrl, "https://viewblock.io/thorchain/address/thor196yf4pq80hjrmz7nnh0ar0ypqg02r0w4dq4mzu"); - assertStringsEqual(id, "thorchain"); - assertStringsEqual(name, "THORChain"); -} diff --git a/tests/Terra/TWCoinTypeTests.cpp b/tests/Terra/TWCoinTypeTests.cpp deleted file mode 100644 index 4c0a23d8c94..00000000000 --- a/tests/Terra/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWTerraCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTerra)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("D28D8AFC7CE89F2A22FA2DBF78D2C0A36E549BB830C4D9FA7459E3F723CA7182")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTerra, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("terra16t3gx5rqvz6ru37yzn3shuu20erv4ngmfr59zf")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTerra, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTerra)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTerra)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTerra), 6); - ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeTerra)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTerra)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTerra)); - assertStringsEqual(symbol, "LUNA"); - assertStringsEqual(txUrl, "https://finder.terra.money/tx/tx/D28D8AFC7CE89F2A22FA2DBF78D2C0A36E549BB830C4D9FA7459E3F723CA7182"); - assertStringsEqual(accUrl, "https://finder.terra.money/tx/address/terra16t3gx5rqvz6ru37yzn3shuu20erv4ngmfr59zf"); - assertStringsEqual(id, "terra"); - assertStringsEqual(name, "Terra"); -} diff --git a/tests/Tezos/AddressTests.cpp b/tests/Tezos/AddressTests.cpp deleted file mode 100644 index 9a9e5c090cf..00000000000 --- a/tests/Tezos/AddressTests.cpp +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Tezos/Address.h" -#include "HDWallet.h" -#include "HexCoding.h" -#include "PrivateKey.h" -#include "Tezos/Forging.h" - -#include - -#include -#include -#include - -using namespace TW; -using namespace TW::Tezos; - -TEST(TezosAddress, forge_tz1) { - auto input = Address("tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3Don"); - auto expected = "00cfa4aae60f5d9389752d41e320da224d43287fe2"; - - ASSERT_EQ(input.forge(), parse_hex(expected)); -} - -TEST(TezosAddress, forge_tz2) { - auto input = Address("tz2Rh3NYeLxrqTuvaZJmaMiVMqCajeXMWtYo"); - auto expected = "01be99dd914e38388ec80432818b517759e3524f16"; - - ASSERT_EQ(input.forge(), parse_hex(expected)); -} - -TEST(TezosAddress, forge_tz3) { - auto input = Address("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9"); - auto expected = "02358cbffa97149631cfb999fa47f0035fb1ea8636"; - - ASSERT_EQ(input.forge(), parse_hex(expected)); -} - -TEST(TezosAddress, isInvalid) { - std::array invalidAddresses { - "NmH7tmeJUmHcncBDvpr7aJNEBk7rp5zYsB1qt", // Invalid prefix, valid checksum - "tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3AAAA", // Valid prefix, invalid checksum - "1tzeZwq8b5cvE2bPKokatLkVMzkxz24zAAAAA" // Invalid prefix, invalid checksum - }; - - for (auto& address : invalidAddresses) { - ASSERT_FALSE(Address::isValid(address)); - } -} - -TEST(TezosAddress, isValid) { - std::array validAddresses { - "tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt", - "tz2PdGc7U5tiyqPgTSgqCDct94qd6ovQwP6u", - "tz3VEZ4k6a4Wx42iyev6i2aVAptTRLEAivNN" - }; - - for (auto& address : validAddresses) { - ASSERT_TRUE(Address::isValid(address)); - } -} - -TEST(TezosAddress, string) { - auto addressString = "tz1d1qQL3mYVuiH4JPFvuikEpFwaDm85oabM"; - auto address = Address(addressString); - ASSERT_EQ(address.string(), addressString); -} - -TEST(TezosAddress, deriveOriginatedAddress) { - auto operationHash = "oo7VeTEPjEusPKnsHtKcGYbYa7i4RWpcEhUVo3Suugbbs6K62Ro"; - auto operationIndex = 0; - auto expected = "KT1WrtjtAYQSrUVvSNJPTZTebiUWoopQL5hw"; - - ASSERT_EQ(Address::deriveOriginatedAddress(operationHash, operationIndex), expected); -} - -TEST(TezosAddress, PublicKeyInit) { - Data bytes {1, 254, 21, 124, 200, 1, 23, 39, 147, 108, 89, 47, 133, 108, 144, 113, 211, 156, 244, 172, 218, 223, 166, 215, 100, 53, 228, 97, 156, 157, 197, 111, 99,}; - const auto publicKey = PublicKey(bytes, TWPublicKeyTypeED25519); - auto address = Address(publicKey); - - auto expected = "tz1cG2jx3W4bZFeVGBjsTxUAG8tdpTXtE8PT"; - ASSERT_EQ(address.string(), expected); -} diff --git a/tests/Tezos/ForgingTests.cpp b/tests/Tezos/ForgingTests.cpp deleted file mode 100644 index 187569f6021..00000000000 --- a/tests/Tezos/ForgingTests.cpp +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Tezos/BinaryCoding.h" -#include "Tezos/Address.h" -#include "HDWallet.h" -#include "HexCoding.h" -#include "PublicKey.h" -#include "PrivateKey.h" -#include "Tezos/Forging.h" -#include "proto/Tezos.pb.h" - -#include - -#include -#include -#include - -using namespace TW; -using namespace TW::Tezos; - -TEST(Forging, ForgeBoolTrue) { - auto expected = "ff"; - - auto output = forgeBool(true); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeBoolFalse) { - auto expected = "00"; - - auto output = forgeBool(false); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeZarithZero) { - auto expected = "00"; - - auto output = forgeZarith(0); - - ASSERT_EQ(hex(output), hex(parse_hex(expected))); -} - -TEST(Forging, ForgeZarithTen) { - auto expected = "0a"; - - auto output = forgeZarith(10); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeZarithTwenty) { - auto expected = "14"; - - auto output = forgeZarith(20); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeZarithOneHundredFifty) { - auto expected = "9601"; - - auto output = forgeZarith(150); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgeZarithLarge) { - auto expected = "bbd08001"; - - auto output = forgeZarith(2107451); - - ASSERT_EQ(hex(output), expected); -} - -TEST(Forging, forge_tz1) { - auto expected = "00cfa4aae60f5d9389752d41e320da224d43287fe2"; - - auto output = forgePublicKeyHash("tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3Don"); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, forge_tz2) { - auto expected = "01be99dd914e38388ec80432818b517759e3524f16"; - - auto output = forgePublicKeyHash("tz2Rh3NYeLxrqTuvaZJmaMiVMqCajeXMWtYo"); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, forge_tz3) { - auto expected = "02358cbffa97149631cfb999fa47f0035fb1ea8636"; - - auto output = forgePublicKeyHash("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9"); - - ASSERT_EQ(output, parse_hex(expected)); -} - -TEST(Forging, ForgePublicKey) { - auto expected = "00311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95"; - - auto privateKey = PrivateKey(parse_hex("c6377a4cc490dc913fc3f0d9cf67d293a32df4547c46cb7e9e33c3b7b97c64d8")); - auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); - auto output = forgePublicKey(publicKey); - - ASSERT_EQ(hex(output), expected); -} - - -TEST(TezosTransaction, forgeTransaction) { - auto transactionOperationData = new TW::Tezos::Proto::TransactionOperationData(); - transactionOperationData -> set_amount(1); - transactionOperationData -> set_destination("tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt"); - - auto transactionOperation = TW::Tezos::Proto::Operation(); - transactionOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - transactionOperation.set_fee(1272); - transactionOperation.set_counter(30738); - transactionOperation.set_gas_limit(10100); - transactionOperation.set_storage_limit(257); - transactionOperation.set_kind(TW::Tezos::Proto::Operation::TRANSACTION); - transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); - - auto expected = "6c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e81020100008fb5cea62d147c696afd9a93dbce962f4c8a9c9100"; - auto serialized = forgeOperation(transactionOperation); - - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); -} - -TEST(TezosTransaction, forgeReveal) { - PublicKey publicKey = parsePublicKey("edpku9ZF6UUAEo1AL3NWy1oxHLL6AfQcGYwA5hFKrEKVHMT3Xx889A"); - - auto revealOperationData = new TW::Tezos::Proto::RevealOperationData(); - revealOperationData -> set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); - - auto revealOperation = TW::Tezos::Proto::Operation(); - revealOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - revealOperation.set_fee(1272); - revealOperation.set_counter(30738); - revealOperation.set_gas_limit(10100); - revealOperation.set_storage_limit(257); - revealOperation.set_kind(TW::Tezos::Proto::Operation::REVEAL); - revealOperation.set_allocated_reveal_operation_data(revealOperationData); - - auto expected = "6b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e"; - auto serialized = forgeOperation(revealOperation); - - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); -} - -TEST(TezosTransaction, forgeDelegate) { - auto delegateOperationData = new TW::Tezos::Proto::DelegationOperationData(); - delegateOperationData->set_delegate("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - - auto delegateOperation = TW::Tezos::Proto::Operation(); - delegateOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - delegateOperation.set_fee(1272); - delegateOperation.set_counter(30738); - delegateOperation.set_gas_limit(10100); - delegateOperation.set_storage_limit(257); - delegateOperation.set_kind(TW::Tezos::Proto::Operation::DELEGATION); - delegateOperation.set_allocated_delegation_operation_data(delegateOperationData); - - auto expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e8102ff003e47f837f0467b4acde406ed5842f35e2414b1a8"; - auto serialized = forgeOperation(delegateOperation); - - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); -} - -TEST(TezosTransaction, forgeUndelegate) { - auto delegateOperationData = new TW::Tezos::Proto::DelegationOperationData(); - delegateOperationData->set_delegate(""); - - auto delegateOperation = TW::Tezos::Proto::Operation(); - delegateOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - delegateOperation.set_fee(1272); - delegateOperation.set_counter(30738); - delegateOperation.set_gas_limit(10100); - delegateOperation.set_storage_limit(257); - delegateOperation.set_kind(TW::Tezos::Proto::Operation::DELEGATION); - delegateOperation.set_allocated_delegation_operation_data(delegateOperationData); - - auto expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200"; - auto serialized = forgeOperation(delegateOperation); - - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); -} \ No newline at end of file diff --git a/tests/Tezos/PublicKeyTests.cpp b/tests/Tezos/PublicKeyTests.cpp deleted file mode 100644 index baac0151631..00000000000 --- a/tests/Tezos/PublicKeyTests.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Tezos/BinaryCoding.h" -#include "Tezos/Forging.h" -#include "PublicKey.h" -#include "Data.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Tezos; - -TEST(TezosPublicKey, forge) { - auto input = parsePublicKey("edpkuAfEJCEatRgFpRGg3gn3FdWniLXBoubARreRwuVZPWufkgDBvR"); - auto expected = "00451bde832454ba73e6e0de313fcf5d1565ec51080edc73bb19287b8e0ab2122b"; - auto serialized = forgePublicKey(input); - ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); -} - -TEST(TezosPublicKey, parse) { - auto input = "edpkuAfEJCEatRgFpRGg3gn3FdWniLXBoubARreRwuVZPWufkgDBvR"; - auto bytes = Data({1, 69, 27, 222, 131, 36, 84, 186, 115, 230, 224, 222, 49, 63, 207, 93, 21, 101, 236, 81, 8, 14, 220, 115, 187, 25, 40, 123, 142, 10, 178, 18, 43}); - auto output = parsePublicKey(input); - auto expected = PublicKey(bytes, TWPublicKeyTypeED25519); - ASSERT_EQ(output, expected); -} diff --git a/tests/Tezos/SignerTests.cpp b/tests/Tezos/SignerTests.cpp deleted file mode 100644 index 987d3a573c8..00000000000 --- a/tests/Tezos/SignerTests.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Tezos/BinaryCoding.h" -#include "Tezos/OperationList.h" -#include "Tezos/Signer.h" -#include "PrivateKey.h" -#include "Base58.h" -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Tezos; - -TEST(TezosSigner, SignString) { - Data bytesToSign = parse_hex("ffaa"); - Data expectedSignature = parse_hex("eaab7f4066217b072b79609a9f76cdfadd93f8dde41763887e131c02324f18c8e41b1009e334baf87f9d2e917bf4c0e73165622e5522409a0c5817234a48cc02"); - Data expected = Data(); - append(expected, bytesToSign); - append(expected, expectedSignature); - - auto key = PrivateKey(parse_hex("0x2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f")); - auto signedBytes = Signer().signData(key, bytesToSign); - - ASSERT_EQ(signedBytes, expected); -} - -TEST(TezosSigner, SignOperationList) { - auto branch = "BLDnkhhVgwdBAtmDNQc5HtEMsrxq8L3t7NQbjUbbdTdw5Ug1Mpe"; - auto op_list = Tezos::OperationList(branch); - - auto transactionOperationData = new Proto::TransactionOperationData(); - transactionOperationData->set_amount(11100000); - transactionOperationData->set_destination("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); - - auto transactionOperation = TW::Tezos::Proto::Operation(); - transactionOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - transactionOperation.set_fee(1283); - transactionOperation.set_counter(1878); - transactionOperation.set_gas_limit(10307); - transactionOperation.set_storage_limit(0); - transactionOperation.set_kind(Proto::Operation::TRANSACTION); - transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); - - op_list.addOperation(transactionOperation); - - PublicKey publicKey = parsePublicKey("edpkuNb9N2UHtGeSc2BZCBHN8ETx7E4DwkSfz5Hw3m3tF3dLZTU8qp"); - auto revealOperationData = new TW::Tezos::Proto::RevealOperationData(); - revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); - - auto revealOperation = Proto::Operation(); - revealOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - revealOperation.set_fee(1268); - revealOperation.set_counter(1876); - revealOperation.set_gas_limit(10100); - revealOperation.set_storage_limit(0); - revealOperation.set_kind(Proto::Operation::REVEAL); - revealOperation.set_allocated_reveal_operation_data(revealOperationData); - - op_list.addOperation(revealOperation); - - auto delegateOperationData = new Tezos::Proto::DelegationOperationData(); - delegateOperationData->set_delegate("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); - - auto delegateOperation = Proto::Operation(); - delegateOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); - delegateOperation.set_fee(1257); - delegateOperation.set_counter(1879); - delegateOperation.set_gas_limit(10100); - delegateOperation.set_storage_limit(0); - delegateOperation.set_kind(Proto::Operation::DELEGATION); - delegateOperation.set_allocated_delegation_operation_data(delegateOperationData); - - op_list.addOperation(delegateOperation); - - auto decodedPrivateKey = Base58::bitcoin.decodeCheck("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - auto key = PrivateKey(Data(decodedPrivateKey.begin() + 4, decodedPrivateKey.end())); - - std::string expectedForgedBytesToSign = hex(op_list.forge(key)); - std::string expectedSignature = "871693145f2dc72861ff6816e7ac3ce93c57611ac09a4c657a5a35270fa57153334c14cd8cae94ee228b6ef52f0e3f10948721e666318bc54b6c455404b11e03"; - std::string expectedSignedBytes = expectedForgedBytesToSign + expectedSignature; - - auto signedBytes = Signer().signOperationList(key, op_list); - auto signedBytesHex = hex(signedBytes.begin(), signedBytes.end()); - - ASSERT_EQ(hex(signedBytes.begin(), signedBytes.end()), expectedSignedBytes); -} diff --git a/tests/Tezos/TWAnySignerTests.cpp b/tests/Tezos/TWAnySignerTests.cpp deleted file mode 100644 index 4f2627ed2d7..00000000000 --- a/tests/Tezos/TWAnySignerTests.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "proto/Tezos.pb.h" -#include "../interface/TWTestUtilities.h" -#include - -#include - -using namespace TW; -using namespace TW::Tezos; - -TEST(TWAnySignerTezos, Sign) { - auto key = parse_hex("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f"); - auto revealKey = parse_hex("311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95"); - - Proto::SigningInput input; - input.set_private_key(key.data(), key.size()); - auto& operations = *input.mutable_operation_list(); - operations.set_branch("BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"); - - auto& reveal = *operations.add_operations(); - auto& revealData = *reveal.mutable_reveal_operation_data(); - revealData.set_public_key(revealKey.data(), revealKey.size()); - reveal.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - reveal.set_fee(1272); - reveal.set_counter(30738); - reveal.set_gas_limit(10100); - reveal.set_storage_limit(257); - reveal.set_kind(Proto::Operation::REVEAL); - - auto& transaction = *operations.add_operations(); - auto& txData = *transaction.mutable_transaction_operation_data(); - txData.set_amount(1); - txData.set_destination("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - transaction.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); - transaction.set_fee(1272); - transaction.set_counter(30739); - transaction.set_gas_limit(10100); - transaction.set_storage_limit(257); - transaction.set_kind(Proto::Operation::TRANSACTION); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeTezos); - - EXPECT_EQ(hex(output.encoded()), "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff956c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80993f001f44e810201000081faa75f741ef614b0e35fcc8c90dfa3b0b95721000217034271b815e5f0c0a881342838ce49d7b48cdf507c72b1568c69a10db70c98774cdad1a74df760763e25f760ff13afcbbf3a1f2c833a0beeb9576a579c05"); -} - -TEST(TWAnySignerTezos, SignJSON) { - auto json = STRING(R"({"operationList": {"branch": "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp","operations": [{"source": "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW","fee": 1272,"counter": 30738,"gasLimit": 10100,"storageLimit": 257,"kind": 107,"revealOperationData": {"publicKey": "QpqYbIBypAofOj4qtaWBm7Gy+2mZPFAEg3gVudxVkj4="}},{"source": "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW","fee": 1272,"counter": 30739,"gasLimit": 10100,"storageLimit": 257,"kind": 108,"transactionOperationData": {"destination": "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW","amount": 1}}]}})"); - auto key = DATA("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f"); - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeTezos)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeTezos)); - assertStringsEqual(result, "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e6c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80993f001f44e810201000081faa75f741ef614b0e35fcc8c90dfa3b0b957210001b86398d5b9be737dca8e4106ea18d70e69b75e92f892fb283546a99152b8d7794b919c0fbf1c31de386069a60014491c0e7505adef5781cead1cfe6608030b"); -} diff --git a/tests/Tezos/TWCoinTypeTests.cpp b/tests/Tezos/TWCoinTypeTests.cpp deleted file mode 100644 index a8d44065d59..00000000000 --- a/tests/Tezos/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWTezosCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTezos)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("onk3Z6V4StyfiXTPSHwZFvTKVAaws37cHmZacmULPr3VbVHpKrg")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTezos, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("tz1SiPXX4MYGNJNDsRc7n8hkvUqFzg8xqF9m")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTezos, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTezos)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTezos)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTezos), 6); - ASSERT_EQ(TWBlockchainTezos, TWCoinTypeBlockchain(TWCoinTypeTezos)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTezos)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTezos)); - assertStringsEqual(symbol, "XTZ"); - assertStringsEqual(txUrl, "https://tzstats.com/onk3Z6V4StyfiXTPSHwZFvTKVAaws37cHmZacmULPr3VbVHpKrg"); - assertStringsEqual(accUrl, "https://tzstats.com/tz1SiPXX4MYGNJNDsRc7n8hkvUqFzg8xqF9m"); - assertStringsEqual(id, "tezos"); - assertStringsEqual(name, "Tezos"); -} diff --git a/tests/Theta/TWAnySignerTests.cpp b/tests/Theta/TWAnySignerTests.cpp deleted file mode 100644 index 92d748c6ebc..00000000000 --- a/tests/Theta/TWAnySignerTests.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "uint256.h" -#include "proto/Theta.pb.h" -#include - -#include "../interface/TWTestUtilities.h" -#include - -using namespace TW; -using namespace TW::Theta; - -TEST(TWAnySignerTheta, Sign) { - auto privateKey = parse_hex("93a90ea508331dfdf27fb79757d4250b4e84954927ba0073cd67454ac432c737"); - - Proto::SigningInput input; - input.set_chain_id("privatenet"); - input.set_to_address("0x9F1233798E905E173560071255140b4A8aBd3Ec6"); - auto amount = store(uint256_t(10)); - input.set_theta_amount(amount.data(), amount.size()); - auto tfuelAmount = store(uint256_t(20)); - input.set_tfuel_amount(tfuelAmount.data(), tfuelAmount.size()); - auto fee = store(uint256_t(1000000000000)); - input.set_fee(fee.data(), fee.size()); - input.set_sequence(1); - input.set_private_key(privateKey.data(), privateKey.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeTheta); - - ASSERT_EQ(hex(output.encoded()), "02f887c78085e8d4a51000f863f861942e833968e5bb786ae419c4d13189fb081cc43babc70a85e8d4a5101401b8415190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8fe9134ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501d9d8949f1233798e905e173560071255140b4a8abd3ec6c20a14"); -} diff --git a/tests/Theta/TWCoinTypeTests.cpp b/tests/Theta/TWCoinTypeTests.cpp deleted file mode 100644 index 55ed4e35b38..00000000000 --- a/tests/Theta/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWThetaCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTheta)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTheta, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTheta, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTheta)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTheta)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTheta), 18); - ASSERT_EQ(TWBlockchainTheta, TWCoinTypeBlockchain(TWCoinTypeTheta)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTheta)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTheta)); - assertStringsEqual(symbol, "THETA"); - assertStringsEqual(txUrl, "https://explorer.thetatoken.org/txs/t123"); - assertStringsEqual(accUrl, "https://explorer.thetatoken.org/account/a12"); - assertStringsEqual(id, "theta"); - assertStringsEqual(name, "Theta"); -} diff --git a/tests/Theta/TransactionTests.cpp b/tests/Theta/TransactionTests.cpp deleted file mode 100644 index 19699afcc78..00000000000 --- a/tests/Theta/TransactionTests.cpp +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Theta/Transaction.h" - -#include "HexCoding.h" - -#include - -using namespace TW; -using namespace TW::Theta; - -TEST(ThetaTransaction, Encode) { - const auto from = Ethereum::Address("0x2E833968E5bB786Ae419c4d13189fB081Cc43bab"); - const auto to = Ethereum::Address("0x9F1233798E905E173560071255140b4A8aBd3Ec6"); - auto transaction = Transaction(from, to, 10, 20, 1); - ASSERT_EQ(hex(transaction.encode()), - "02f843c78085e8d4a51000e0df942e833968e5bb786ae419c4d13189fb081cc43babc70a85e8d4a51014" - "0180d9d8949f1233798e905e173560071255140b4a8abd3ec6c20a14"); -} - -TEST(ThetaTransaction, EncodeWithSignature) { - const auto from = Ethereum::Address("0x2E833968E5bB786Ae419c4d13189fB081Cc43bab"); - const auto to = Ethereum::Address("0x9F1233798E905E173560071255140b4A8aBd3Ec6"); - auto transaction = Transaction(from, to, 10, 20, 1); - transaction.setSignature( - from, parse_hex("5190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8fe9134" - "ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501")); - ASSERT_EQ(hex(transaction.encode()), - "02f887c78085e8d4a51000f863f861942e833968e5bb786ae419c4d13189fb081cc43babc70a85e8d4a5" - "101401b8415190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8fe9134" - "ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501d9d8949f1233798e905e17356007" - "1255140b4a8abd3ec6c20a14"); -} diff --git a/tests/ThunderToken/TWCoinTypeTests.cpp b/tests/ThunderToken/TWCoinTypeTests.cpp deleted file mode 100644 index 3b31872c8c3..00000000000 --- a/tests/ThunderToken/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWThunderTokenCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeThunderToken)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeThunderToken, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeThunderToken, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeThunderToken)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeThunderToken)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeThunderToken), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeThunderToken)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeThunderToken)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeThunderToken)); - assertStringsEqual(symbol, "TT"); - assertStringsEqual(txUrl, "https://scan.thundercore.com/transactions/t123"); - assertStringsEqual(accUrl, "https://scan.thundercore.com/address/a12"); - assertStringsEqual(id, "thundertoken"); - assertStringsEqual(name, "Thunder Token"); -} diff --git a/tests/TomoChain/TWCoinTypeTests.cpp b/tests/TomoChain/TWCoinTypeTests.cpp deleted file mode 100644 index 0cb99362b04..00000000000 --- a/tests/TomoChain/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWTomoChainCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTomoChain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x35a8d3ab06c94d5b7d27221b7c9a24ba3f1710dd0fcfd75c5d59b3a885fd709b")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTomoChain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x86cCbD9bfb371c355202086882bC644A7D0b024B")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTomoChain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTomoChain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTomoChain)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTomoChain), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeTomoChain)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTomoChain)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTomoChain)); - assertStringsEqual(symbol, "TOMO"); - assertStringsEqual(txUrl, "https://tomoscan.io/tx/0x35a8d3ab06c94d5b7d27221b7c9a24ba3f1710dd0fcfd75c5d59b3a885fd709b"); - assertStringsEqual(accUrl, "https://tomoscan.io/address/0x86cCbD9bfb371c355202086882bC644A7D0b024B"); - assertStringsEqual(id, "tomochain"); - assertStringsEqual(name, "TomoChain"); -} diff --git a/tests/Tron/TWAnySignerTests.cpp b/tests/Tron/TWAnySignerTests.cpp deleted file mode 100644 index 0dc27c18583..00000000000 --- a/tests/Tron/TWAnySignerTests.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "proto/Tron.pb.h" -#include - -#include "../interface/TWTestUtilities.h" -#include - -namespace TW::Tron { - -TEST(TWAnySignerTron, SignTransferAsset) { - auto input = Proto::SigningInput(); - auto& transaction = *input.mutable_transaction(); - - auto& transfer = *transaction.mutable_transfer_asset(); - transfer.set_owner_address("TJRyWwFs9wTFGZg3JbrVriFbNfCug5tDeC"); - transfer.set_to_address("THTR75o8xXAgCTQqpiot2AFRAjvW1tSbVV"); - transfer.set_amount(4); - transfer.set_asset_name("1000959"); - - transaction.set_timestamp(1539295479000); - transaction.set_expiration(1541890116000 + 10 * 60 * 60 * 1000); - - auto& blockHeader = *transaction.mutable_block_header(); - blockHeader.set_timestamp(1541890116000); - const auto txTrieRoot = parse_hex("845ab51bf63c2c21ee71a4dc0ac3781619f07a7cd05e1e0bd8ba828979332ffa"); - blockHeader.set_tx_trie_root(txTrieRoot.data(), txTrieRoot.size()); - const auto parentHash = parse_hex("00000000003cb800a7e69e9144e3d16f0cf33f33a95c7ce274097822c67243c1"); - blockHeader.set_parent_hash(parentHash.data(), parentHash.size()); - blockHeader.set_number(3979265); - const auto witnessAddress = parse_hex("41b487cdc02de90f15ac89a68c82f44cbfe3d915ea"); - blockHeader.set_witness_address(witnessAddress.data(), witnessAddress.size()); - blockHeader.set_version(3); - - const auto privateKey = parse_hex("2d8f68944bdbfbc0769542fba8fc2d2a3de67393334471624364c7006da2aa54"); - input.set_private_key(privateKey.data(), privateKey.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeTron); - - ASSERT_EQ(hex(output.signature()), "77f5eabde31e739d34a66914540f1756981dc7d782c9656f5e14e53b59a15371603a183aa12124adeee7991bf55acc8e488a6ca04fb393b1a8ac16610eeafdfc00"); -} - -} diff --git a/tests/Tron/TWCoinTypeTests.cpp b/tests/Tron/TWCoinTypeTests.cpp deleted file mode 100644 index e496477156e..00000000000 --- a/tests/Tron/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWTronCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTron)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTron, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTron, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTron)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTron)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTron), 6); - ASSERT_EQ(TWBlockchainTron, TWCoinTypeBlockchain(TWCoinTypeTron)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTron)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTron)); - assertStringsEqual(symbol, "TRX"); - assertStringsEqual(txUrl, "https://tronscan.org/#/transaction/t123"); - assertStringsEqual(accUrl, "https://tronscan.org/#/address/a12"); - assertStringsEqual(id, "tron"); - assertStringsEqual(name, "Tron"); -} diff --git a/tests/VeChain/TWAnySignerTests.cpp b/tests/VeChain/TWAnySignerTests.cpp deleted file mode 100644 index 289c725d42b..00000000000 --- a/tests/VeChain/TWAnySignerTests.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "proto/VeChain.pb.h" -#include "../interface/TWTestUtilities.h" -#include -#include - -using namespace TW; -using namespace TW::VeChain; - -TEST(TWAnySignerVeChain, Sign) { - auto input = Proto::SigningInput(); - - input.set_chain_tag(1); - input.set_block_ref(1); - input.set_expiration(1); - input.set_gas_price_coef(0); - input.set_gas(21000); - input.set_nonce(1); - - auto key = parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646"); - input.set_private_key(key.data(), key.size()); - - auto& clause = *input.add_clauses(); - auto amount = parse_hex("31303030"); // 1000 - clause.set_to("0x3535353535353535353535353535353535353535"); - clause.set_value(amount.data(), amount.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeVeChain); - - ASSERT_EQ(hex(output.encoded()), "f86a010101dcdb943535353535353535353535353535353535353535843130303080808252088001c0b841bf8edf9600e645b5abd677cb52f585e7f655d1361075d511b37f707a9f31da6702d28739933b264527a1d05b046f5b74044b88c30c3f5a09d616bd7a4af4901601"); -} diff --git a/tests/VeChain/TWCoinTypeTests.cpp b/tests/VeChain/TWCoinTypeTests.cpp deleted file mode 100644 index cf41c6cba08..00000000000 --- a/tests/VeChain/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWVeChainCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeVeChain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xa424053be0063555aee73a595ca69968c2e4d90d36f280753e503b92b11a655d")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeVeChain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x8a0a035a33173601bfbec8b6ae7c4a6557a55103")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeVeChain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeVeChain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeVeChain)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeVeChain), 18); - ASSERT_EQ(TWBlockchainVechain, TWCoinTypeBlockchain(TWCoinTypeVeChain)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeVeChain)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeVeChain)); - assertStringsEqual(symbol, "VET"); - assertStringsEqual(txUrl, "https://explore.vechain.org/transactions/0xa424053be0063555aee73a595ca69968c2e4d90d36f280753e503b92b11a655d"); - assertStringsEqual(accUrl, "https://explore.vechain.org/accounts/0x8a0a035a33173601bfbec8b6ae7c4a6557a55103"); - assertStringsEqual(id, "vechain"); - assertStringsEqual(name, "VeChain"); -} diff --git a/tests/Viacoin/TWCoinTypeTests.cpp b/tests/Viacoin/TWCoinTypeTests.cpp deleted file mode 100644 index 15ead27fdc1..00000000000 --- a/tests/Viacoin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWViacoinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeViacoin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeViacoin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeViacoin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeViacoin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeViacoin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeViacoin), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeViacoin)); - ASSERT_EQ(0x21, TWCoinTypeP2shPrefix(TWCoinTypeViacoin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeViacoin)); - assertStringsEqual(symbol, "VIA"); - assertStringsEqual(txUrl, "https://explorer.viacoin.org/tx/t123"); - assertStringsEqual(accUrl, "https://explorer.viacoin.org/address/a12"); - assertStringsEqual(id, "viacoin"); - assertStringsEqual(name, "Viacoin"); -} diff --git a/tests/Wanchain/TWCoinTypeTests.cpp b/tests/Wanchain/TWCoinTypeTests.cpp deleted file mode 100644 index f3ba2ce8ee7..00000000000 --- a/tests/Wanchain/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWWanchainCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeWanchain)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x180ea96a3218b82b9b35d796823266d8a425c182507adfe5eeffc96e6a14d856")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeWanchain, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x69B492D57bb777e97aa7044D0575228434e2E8B1")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeWanchain, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeWanchain)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeWanchain)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeWanchain), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeWanchain)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeWanchain)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeWanchain)); - assertStringsEqual(symbol, "WAN"); - assertStringsEqual(txUrl, "https://www.wanscan.org/tx/0x180ea96a3218b82b9b35d796823266d8a425c182507adfe5eeffc96e6a14d856"); - assertStringsEqual(accUrl, "https://www.wanscan.org/address/0x69B492D57bb777e97aa7044D0575228434e2E8B1"); - assertStringsEqual(id, "wanchain"); - assertStringsEqual(name, "Wanchain"); -} diff --git a/tests/Waves/AddressTests.cpp b/tests/Waves/AddressTests.cpp deleted file mode 100644 index b5a8afe9232..00000000000 --- a/tests/Waves/AddressTests.cpp +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "PrivateKey.h" -#include "Waves/Address.h" - -#include -#include -#include - -using namespace std; -using namespace TW; -using namespace TW::Waves; - -TEST(WavesAddress, SecureHash) { - const auto secureHash = - hex(Address::secureHash(parse_hex("0157c7fefc0c6acc54e9e4354a81ac1f038e01745731"))); - - ASSERT_EQ(secureHash, "a7978a753c6496866dc75ba3abcaaec796f2380037a1fa7c46cbf9762ee380df"); -} - -TEST(WavesAddress, FromPrivateKey) { - const auto privateKey = - PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); - const auto publicKeyEd25519 = privateKey.getPublicKey(TWPublicKeyTypeED25519); - ASSERT_EQ(hex(Data(publicKeyEd25519.bytes.begin(), publicKeyEd25519.bytes.end())), - "ff84c4bfc095df25b01e48807715856d95af93d88c5b57f30cb0ce567ca4ced6"); - const auto publicKeyCurve25519 = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); - ASSERT_EQ(hex(Data(publicKeyCurve25519.bytes.begin(), publicKeyCurve25519.bytes.end())), - "559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"); - const auto address = Address(publicKeyCurve25519); - - ASSERT_EQ(address.string(), "3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds"); -} - -TEST(WavesAddress, FromPublicKey) { - const auto publicKey = - PublicKey(parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"), - TWPublicKeyTypeCURVE25519); - const auto address = Address(publicKey); - - ASSERT_EQ(address.string(), "3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds"); -} - -TEST(WavesAddress, Invalid) { - ASSERT_FALSE(Address::isValid(std::string("abc"))); - ASSERT_FALSE(Address::isValid(std::string("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"))); - ASSERT_FALSE(Address::isValid(std::string("3PLANf4MgtNN5v5k4NNnyx2m4zKJiw1tF9v"))); - ASSERT_FALSE(Address::isValid(std::string("3PLANf4MgtNN5v6k4NNnyx2m4zKJiw1tF8v"))); -} - -TEST(WavesAddress, Valid) { - ASSERT_TRUE(Address::isValid(std::string("3PLANf4MgtNN5v6k4NNnyx2m4zKJiw1tF9v"))); - ASSERT_TRUE(Address::isValid(std::string("3PDjjLFDR5aWkKgufika7KSLnGmAe8ueDpC"))); - ASSERT_TRUE(Address::isValid(std::string("3PLjucTjqEfmgBF7fs2CER3fHQapCtknPeW"))); - ASSERT_TRUE(Address::isValid(std::string("3PB9ffP1YKQer3e7t283gPCLyjEfK8xrGp7"))); -} - -TEST(WavesAddress, InitWithString) { - const auto address = Address("3PQupTC1yRiHneotFt79LF2pkN6GrGMwEy3"); - ASSERT_EQ(address.string(), "3PQupTC1yRiHneotFt79LF2pkN6GrGMwEy3"); -} - -TEST(WavesAddress, InitWithInvalidString) { - EXPECT_THROW(Address("3PQupTC1yRiHneotFt79LF2pkN6GrGMwEy2"), invalid_argument); -} - -TEST(WavesAddress, Derive) { - const auto mnemonic = - "water process satisfy repeat flag avoid town badge sketch surge split between cabin sugar " - "ill special axis adjust pull useful craft peace flee physical"; - const auto wallet = HDWallet(mnemonic, ""); - const auto address1 = TW::deriveAddress( - TWCoinTypeWaves, wallet.getKey(TWCoinTypeWaves, DerivationPath("m/44'/5741564'/0'/0'/0'"))); - const auto address2 = TW::deriveAddress( - TWCoinTypeWaves, wallet.getKey(TWCoinTypeWaves, DerivationPath("m/44'/5741564'/0'/0'/1'"))); - - ASSERT_EQ(address1, "3PQupTC1yRiHneotFt79LF2pkN6GrGMwEy3"); - ASSERT_EQ(address2, "3PEXw52bkS9XuLhttWoKyykZjXqEY8zeLxf"); -} \ No newline at end of file diff --git a/tests/Waves/LeaseTests.cpp b/tests/Waves/LeaseTests.cpp deleted file mode 100644 index 41de50d1980..00000000000 --- a/tests/Waves/LeaseTests.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "Waves/Address.h" -#include "proto/Waves.pb.h" -#include "Waves/Transaction.h" - -#include -#include - -using json = nlohmann::json; - -using namespace std; -using namespace TW; -using namespace TW::Waves; - -TEST(WavesLease, serialize) { - const auto privateKey = - PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); - auto input = Proto::SigningInput(); - input.set_timestamp(int64_t(1526646497465)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto& message = *input.mutable_lease_message(); - message.set_amount(int64_t(100000000)); - message.set_fee(int64_t(100000)); - message.set_to("3P9DEDP5VbyXQyKtXDUt2crRPn5B7gs6ujc"); - auto tx1 = Transaction( - input, - /* pub_key */ - parse_hex("425f57a8cb5439e4e912e66376f7041565d029ae4437dae1a3ebe15649d43461")); - auto serialized1 = tx1.serializeToSign(); - ASSERT_EQ(hex(serialized1), "080200425f57a8cb5439e4e912e66376f7041565d029ae4437dae1a3ebe15649d4346101574" - "fdfcd1bfb19114bd2ac369e32013c70c6d03a4627879cbf0000000005f5e100000000000001" - "86a0000001637338e0b9"); -} - -TEST(WavesLease, CancelSerialize) { - const auto privateKey = - PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); - auto input = Proto::SigningInput(); - input.set_timestamp(int64_t(1568831000826)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto& message = *input.mutable_cancel_lease_message(); - message.set_fee(int64_t(100000)); - message.set_lease_id("44re3UEDw1QwPFP8dKzfuGHVMNBejUW9NbhxG6b4KJ1T"); - auto tx1 = Transaction( - input, - /* pub_key */ - parse_hex("425f57a8cb5439e4e912e66376f7041565d029ae4437dae1a3ebe15649d43461")); - auto serialized1 = tx1.serializeToSign(); - ASSERT_EQ(hex(serialized1), "090257425f57a8cb5439e4e912e66376f7041565d029ae4437dae1a3ebe15649d" - "4346100000000000186a00000016d459d50fa2d8fee08efc97f79bcd97a4d977c" - "76183580d723909af2b50e72b02f1e36707e"); -} - -TEST(WavesLease, jsonSerialize) { - const auto privateKey = PrivateKey(parse_hex( - "9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); - const auto publicKeyCurve25519 = - privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); - auto input = Proto::SigningInput(); - input.set_timestamp(int64_t(1568973547102)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto& message = *input.mutable_lease_message(); - message.set_amount(int64_t(100000)); - message.set_fee(int64_t(100000)); - message.set_to("3P9DEDP5VbyXQyKtXDUt2crRPn5B7gs6ujc"); - auto tx1 = Transaction(input, - /* pub_key */ - parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d")); - - auto signature = Signer::sign(privateKey, tx1); - auto json = tx1.buildJson(signature); - - ASSERT_EQ(json["type"], TransactionType::lease); - ASSERT_EQ(json["version"], TransactionVersion::V2); - ASSERT_EQ(json["fee"], int64_t(100000)); - ASSERT_EQ(json["senderPublicKey"], - "6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU"); - ASSERT_EQ(json["timestamp"], int64_t(1568973547102)); - ASSERT_EQ(json["proofs"].dump(), - "[\"4opce9e99827upK3m3D3NicnvBqbMLtAJ4Jc8ksTLiScqBgjdqzr9JyXG" - "C1NAGZUbkqJvix9bNrBokrxtGruwmu3\"]"); - ASSERT_EQ(json["recipient"], "3P9DEDP5VbyXQyKtXDUt2crRPn5B7gs6ujc"); - ASSERT_EQ(json["amount"], int64_t(100000)); - ASSERT_EQ(json.dump(), - "{\"amount\":100000,\"fee\":100000,\"proofs\":[" - "\"4opce9e99827upK3m3D3NicnvBqbMLtAJ4Jc8ksTLiScqBgjdqzr9JyXGC1NAGZUbkqJ" - "vix9bNrBokrxtGruwmu3\"],\"recipient\":" - "\"3P9DEDP5VbyXQyKtXDUt2crRPn5B7gs6ujc\",\"senderPublicKey\":" - "\"6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU\",\"timestamp\":" - "1568973547102,\"type\":8,\"version\":2}"); -} - -TEST(WavesLease, jsonCancelSerialize) { - const auto privateKey = PrivateKey(parse_hex( - "9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); - const auto publicKeyCurve25519 = - privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); - auto input = Proto::SigningInput(); - input.set_timestamp(int64_t(1568973547102)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto& message = *input.mutable_cancel_lease_message(); - message.set_lease_id("DKhmXrCsBwf6WVhGh8bYVBnjtAXGpk2K4Yd3CW4u1huG"); - message.set_fee(int64_t(100000)); - auto tx1 = Transaction(input, - /* pub_key */ - parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d")); - auto signature = Signer::sign(privateKey, tx1); - auto json = tx1.buildJson(signature); - - ASSERT_EQ(json["type"], TransactionType::cancelLease); - ASSERT_EQ(json["version"], TransactionVersion::V2); - ASSERT_EQ(json["fee"], int64_t(100000)); - ASSERT_EQ(json["senderPublicKey"], - "6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU"); - ASSERT_EQ(json["leaseId"], "DKhmXrCsBwf6WVhGh8bYVBnjtAXGpk2K4Yd3CW4u1huG"); - ASSERT_EQ(json["chainId"], 87); - ASSERT_EQ(json["timestamp"], int64_t(1568973547102)); - ASSERT_EQ(json["proofs"].dump(), - "[\"Mwhh7kdbhPv9vtnPh6pjEcHTFJ5h5JtAziwFpqH8Ykw1yWYie4Nquh" - "eYtAWPbRowgpDVBxvG1rTrv82LnFdByQY\"]"); - ASSERT_EQ(json.dump(), - "{\"chainId\":87,\"fee\":100000,\"leaseId\":\"DKhmXrCsBwf6WVhGh8bYVBnjtAXGpk2K4Yd3CW4u1huG\"," - "\"proofs\":[\"Mwhh7kdbhPv9vtnPh6pjEcHTFJ5h5JtAziwFpqH8Ykw1yWYie4NquheYtAWP" - "bRowgpDVBxvG1rTrv82LnFdByQY\"],\"senderPublicKey\":" - "\"6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU\",\"timestamp\":" - "1568973547102,\"type\":9,\"version\":2}"); -} - - diff --git a/tests/Waves/SignerTests.cpp b/tests/Waves/SignerTests.cpp deleted file mode 100644 index bf45d2db69e..00000000000 --- a/tests/Waves/SignerTests.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "PublicKey.h" -#include "Waves/Signer.h" -#include "Waves/Transaction.h" - -#include -#include - -using namespace TW; -using namespace TW::Waves; - -TEST(WavesSigner, SignTransaction) { - const auto privateKey = - PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); - const auto publicKeyCurve25519 = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); - ASSERT_EQ(hex(Data(publicKeyCurve25519.bytes.begin(), publicKeyCurve25519.bytes.end())), - "559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"); - // 3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds - const auto address = Address(publicKeyCurve25519); - auto input = Proto::SigningInput(); - input.set_timestamp(int64_t(1526641218066)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto& message = *input.mutable_transfer_message(); - message.set_amount(int64_t(100000000)); - message.set_asset(Transaction::WAVES); - message.set_fee(int64_t(100000000)); - message.set_fee_asset(Transaction::WAVES); - message.set_to(address.string()); - message.set_attachment("falafel"); - auto tx1 = Transaction( - input, - /* pub_key */ - parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d")); - - auto signature = Signer::sign(privateKey, tx1); - - EXPECT_EQ(hex(tx1.serializeToSign()), - "0402559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d00000000016372e8" - "52120000000005f5e1000000000005f5e10001570acc4110b78a6d38b34d879b5bba38806202ecf1732f" - "8542000766616c6166656c"); - EXPECT_EQ(hex(signature), "af7989256f496e103ce95096b3f52196dd9132e044905fe486da3b829b5e403bcba9" - "5ab7e650a4a33948c2d05cfca2dce4d4df747e26402974490fb4c49fbe8f"); - - ASSERT_TRUE(publicKeyCurve25519.verify(signature, tx1.serializeToSign())); -} - -TEST(WavesSigner, curve25519_pk_to_ed25519) { - const auto publicKeyCurve25519 = - parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"); - auto r = Data(); - r.resize(32); - curve25519_pk_to_ed25519(r.data(), publicKeyCurve25519.data()); - EXPECT_EQ(hex(r), "ff84c4bfc095df25b01e48807715856d95af93d88c5b57f30cb0ce567ca4ce56"); -} diff --git a/tests/Waves/TWAnySignerTests.cpp b/tests/Waves/TWAnySignerTests.cpp deleted file mode 100644 index 9bb037ba987..00000000000 --- a/tests/Waves/TWAnySignerTests.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "Base58.h" -#include "HexCoding.h" -#include "proto/Waves.pb.h" -#include "../interface/TWTestUtilities.h" -#include -#include - -using namespace TW; -using namespace TW::Waves; - -TEST(TWAnySignerWaves, Sign) { - auto input = Proto::SigningInput(); - const auto privateKey = Base58::bitcoin.decode("83mqJpmgB5Mko1567sVAdqZxVKsT6jccXt3eFSi4G1zE"); - - input.set_timestamp(int64_t(1559146613)); - input.set_private_key(privateKey.data(), privateKey.size()); - auto& message = *input.mutable_transfer_message(); - message.set_amount(int64_t(100000000)); - message.set_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); - message.set_fee(int64_t(100000)); - message.set_fee_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); - message.set_to("3PPCZQkvdMJpmx7Zrz1cnYsPe9Bt1XT2Ckx"); - message.set_attachment("hello"); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeWaves); - - ASSERT_EQ(hex(output.signature()), "5d6a77b1fd9b53d9735cd2543ba94215664f2b07d6c7befb081221fcd49f5b6ad6b9ac108582e8d3e74943bdf35fd80d985edf4b4de1fb1c5c427e84d0879f8f"); -} diff --git a/tests/Waves/TWCoinTypeTests.cpp b/tests/Waves/TWCoinTypeTests.cpp deleted file mode 100644 index b9043540b04..00000000000 --- a/tests/Waves/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWWavesCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeWaves)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeWaves, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeWaves, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeWaves)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeWaves)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeWaves), 8); - ASSERT_EQ(TWBlockchainWaves, TWCoinTypeBlockchain(TWCoinTypeWaves)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeWaves)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeWaves)); - assertStringsEqual(symbol, "WAVES"); - assertStringsEqual(txUrl, "https://wavesexplorer.com/tx/t123"); - assertStringsEqual(accUrl, "https://wavesexplorer.com/address/a12"); - assertStringsEqual(id, "waves"); - assertStringsEqual(name, "Waves"); -} diff --git a/tests/Waves/TransactionTests.cpp b/tests/Waves/TransactionTests.cpp deleted file mode 100644 index bfb7998d99f..00000000000 --- a/tests/Waves/TransactionTests.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "PrivateKey.h" -#include "PublicKey.h" -#include "Waves/Address.h" -#include "proto/Waves.pb.h" -#include "Waves/Transaction.h" - -#include -#include - -using json = nlohmann::json; - -using namespace std; -using namespace TW; -using namespace TW::Waves; - -TEST(WavesTransaction, serialize) { - const auto privateKey = - PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); - auto input = Proto::SigningInput(); - input.set_timestamp(int64_t(1526641218066)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto& message = *input.mutable_transfer_message(); - message.set_amount(int64_t(100000000)); - message.set_asset(""); - message.set_fee(int64_t(100000000)); - message.set_fee_asset(Transaction::WAVES); - message.set_to("3PLgzJXQiN77G7KgnR1WVa8jBYhF2dmWndx"); - message.set_attachment("falafel"); - auto tx1 = Transaction( - input, - /* pub_key */ - parse_hex("d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef22")); - auto serialized1 = tx1.serializeToSign(); - ASSERT_EQ(hex(serialized1), "0402d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef" - "2200000000016372e852120000000005f5e1000000000005f5e1000157cdc9381c" - "071beb5abd27738d5cd36cf75f3cbfdd69e8e6bb000766616c6166656c"); - - - auto input2 = Proto::SigningInput(); - input2.set_timestamp(int64_t(1)); - input2.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto& message2 = *input2.mutable_transfer_message(); - message2.set_amount(int64_t(1)); - message2.set_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); - message2.set_fee(int64_t(1)); - message2.set_fee_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); - message2.set_to("3PLgzJXQiN77G7KgnR1WVa8jBYhF2dmWndx"); - message2.set_attachment(""); - - auto tx2 = Transaction( - input2, - /* pub_key */ - parse_hex("d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef22")); - auto serialized2 = tx2.serializeToSign(); - ASSERT_EQ(hex(serialized2), - "0402d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef2201bae8ddc9955fa6" - "f69f8e7b155efcdb97bc3bb3a95db4c4604408cec245cd187201bae8ddc9955fa6f69f8e7b155efcdb97" - "bc3bb3a95db4c4604408cec245cd18720000000000000001000000000000000100000000000000010157" - "cdc9381c071beb5abd27738d5cd36cf75f3cbfdd69e8e6bb0000"); -} - -TEST(WavesTransaction, failedSerialize) { - // 141 bytes attachment - const auto privateKey = - PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); - auto input = Proto::SigningInput(); - input.set_timestamp(int64_t(1526641218066)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto& message = *input.mutable_transfer_message(); - message.set_amount(int64_t(100000000)); - message.set_asset(""); - message.set_fee(int64_t(100000000)); - message.set_fee_asset(""); - message.set_to("3PLgzJXQiN77G7KgnR1WVa8jBYhF2dmWndx"); - message.set_attachment("falafelfalafelfalafelfalafelfalafelfalafelfalafel" - "falafelfalafelfalafelfalafelfalafelfalafelfalafel" - "falafelfalafelfalafelfalafelfalafelfalafel"); - auto tx1 = Transaction( - input, - /* pub_key */ - parse_hex("d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef22")); - EXPECT_THROW(tx1.serializeToSign(), invalid_argument); -} - -TEST(WavesTransaction, jsonSerialize) { - - const auto privateKey = - PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); - const auto publicKeyCurve25519 = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); - ASSERT_EQ(hex(Data(publicKeyCurve25519.bytes.begin(), publicKeyCurve25519.bytes.end())), - "559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"); - const auto address = Address(publicKeyCurve25519); - - auto input = Proto::SigningInput(); - input.set_timestamp(int64_t(1526641218066)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto& message = *input.mutable_transfer_message(); - message.set_amount(int64_t(10000000)); - message.set_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); - message.set_fee(int64_t(100000000)); - message.set_fee_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD82zq"); - message.set_to(address.string()); - message.set_attachment("falafel"); - auto tx1 = Transaction( - input, - /* pub_key */ - parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d")); - - - auto signature = Signer::sign(privateKey, tx1); - - auto json = tx1.buildJson(signature); - - ASSERT_EQ(json["type"], TransactionType::transfer); - ASSERT_EQ(json["version"], TransactionVersion::V2); - ASSERT_EQ(json["fee"], int64_t(100000000)); - ASSERT_EQ(json["senderPublicKey"], "6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU"); - ASSERT_EQ(json["timestamp"], int64_t(1526641218066)); - ASSERT_EQ(json["proofs"].dump(), "[\"5ynN2NUiFHkQzw9bK8R7dZcNfTWMAtcWRJsrMvFFM6dUT3fSnPCCX7CTajNU8bJCB" - "H69vU1mnwfx4zpDtF1SkzKg\"]"); - ASSERT_EQ(json["recipient"], "3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds"); - ASSERT_EQ(json["assetId"], "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); - ASSERT_EQ(json["feeAssetId"], "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD82zq"); - ASSERT_EQ(json["amount"], int64_t(10000000)); - ASSERT_EQ(json["attachment"], "4t2Xazb2SX"); - ASSERT_EQ(json.dump(), "{\"amount\":10000000,\"assetId\":\"DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq\",\"attachment\":\"4t2Xazb2SX\",\"fee\":100000000,\"feeAssetId\":\"DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD82zq\",\"proofs\":[\"5ynN2NUiFHkQzw9bK8R7dZcNfTWMAtcWRJsrMvFFM6dUT3fSnPCCX7CTajNU8bJCBH69vU1mnwfx4zpDtF1SkzKg\"],\"recipient\":\"3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds\",\"senderPublicKey\":\"6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU\",\"timestamp\":1526641218066,\"type\":4,\"version\":2}"); -} diff --git a/tests/Zcash/TWCoinTypeTests.cpp b/tests/Zcash/TWCoinTypeTests.cpp deleted file mode 100644 index 956f05639d3..00000000000 --- a/tests/Zcash/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWZcashCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeZcash)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("f2438a93039faf08d39bd3df1f7b5f19a2c29ffe8753127e2956ab4461adab35")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeZcash, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("t1Yfrf1dssDLmaMBsq2LFKWPbS5vH3nGpa2")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeZcash, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeZcash)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeZcash)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeZcash), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeZcash)); - ASSERT_EQ(0xbd, TWCoinTypeP2shPrefix(TWCoinTypeZcash)); - ASSERT_EQ(0x1c, TWCoinTypeStaticPrefix(TWCoinTypeZcash)); - assertStringsEqual(symbol, "ZEC"); - assertStringsEqual(txUrl, "https://blockchair.com/zcash/transaction/f2438a93039faf08d39bd3df1f7b5f19a2c29ffe8753127e2956ab4461adab35"); - assertStringsEqual(accUrl, "https://blockchair.com/zcash/address/t1Yfrf1dssDLmaMBsq2LFKWPbS5vH3nGpa2"); - assertStringsEqual(id, "zcash"); - assertStringsEqual(name, "Zcash"); -} diff --git a/tests/Zcoin/TWCoinTypeTests.cpp b/tests/Zcoin/TWCoinTypeTests.cpp deleted file mode 100644 index c11de5e831f..00000000000 --- a/tests/Zcoin/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWZcoinCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeZcoin)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("09a60d58b3d17519a42a8eca60750c33b710ca8f3ca71994192e05c248a2a111")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeZcoin, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a8ULhhDgfdSiXJhSZVdhb8EuDc6R3ogsaM")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeZcoin, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeZcoin)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeZcoin)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeZcoin), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeZcoin)); - ASSERT_EQ(0x7, TWCoinTypeP2shPrefix(TWCoinTypeZcoin)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeZcoin)); - assertStringsEqual(symbol, "FIRO"); - assertStringsEqual(txUrl, "https://explorer.firo.org/tx/09a60d58b3d17519a42a8eca60750c33b710ca8f3ca71994192e05c248a2a111"); - assertStringsEqual(accUrl, "https://explorer.firo.org/address/a8ULhhDgfdSiXJhSZVdhb8EuDc6R3ogsaM"); - assertStringsEqual(id, "zcoin"); - assertStringsEqual(name, "Firo"); -} diff --git a/tests/Zelcash/TWCoinTypeTests.cpp b/tests/Zelcash/TWCoinTypeTests.cpp deleted file mode 100644 index 50bfe25002f..00000000000 --- a/tests/Zelcash/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWZelcashCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeZelcash)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeZelcash, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeZelcash, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeZelcash)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeZelcash)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeZelcash), 8); - ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeZelcash)); - ASSERT_EQ(0xbd, TWCoinTypeP2shPrefix(TWCoinTypeZelcash)); - ASSERT_EQ(0x1c, TWCoinTypeStaticPrefix(TWCoinTypeZelcash)); - assertStringsEqual(symbol, "FLUX"); - assertStringsEqual(txUrl, "https://explorer.runonflux.io/tx/t123"); - assertStringsEqual(accUrl, "https://explorer.runonflux.io/address/a12"); - assertStringsEqual(id, "zelcash"); - assertStringsEqual(name, "Flux"); -} diff --git a/tests/Zilliqa/AddressTests.cpp b/tests/Zilliqa/AddressTests.cpp deleted file mode 100644 index e4f0a49e269..00000000000 --- a/tests/Zilliqa/AddressTests.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "PrivateKey.h" -#include "Zilliqa/Address.h" -#include "Zilliqa/AddressChecksum.h" - -#include - -#include - -using namespace TW; -using namespace TW::Zilliqa; - -TEST(ZilliqaAddress, FromPrivateKey) { - const auto privateKey = - PrivateKey(parse_hex("3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6")); - const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); - const auto address = Address(publicKey); - auto expectedAddress = "zil1j8xae6lggm8y63m3y2r7aefu797ze7mhzulnqg"; - - ASSERT_EQ(address.getHrp(), stringForHRP(TWHRPZilliqa)); - ASSERT_EQ(address.string(), expectedAddress); -} - -TEST(ZilliqaAddress, Validation) { - ASSERT_FALSE(Zilliqa::Address::isValid("0x91cddcebe846ce4d47712287eee53cf17c2cfb7")); - ASSERT_FALSE(Zilliqa::Address::isValid("")); - ASSERT_FALSE(Zilliqa::Address::isValid("0x")); - ASSERT_FALSE(Zilliqa::Address::isValid("91cddcebe846ce4d47712287eee53cf17c2cfb7")); - - ASSERT_TRUE(Zilliqa::Address::isValid("zil1fwh4ltdguhde9s7nysnp33d5wye6uqpugufkz7")); -} - -TEST(ZilliqaAddress, Checksum) { - ASSERT_EQ( - checksum(parse_hex("4BAF5FADA8E5DB92C3D3242618C5B47133AE003C")), - "4BAF5faDA8e5Db92C3d3242618c5B47133AE003C" - ); - ASSERT_EQ( - checksum(parse_hex("448261915A80CDE9BDE7C7A791685200D3A0BF4E")), - "448261915a80cdE9BDE7C7a791685200D3A0bf4E" - ); - ASSERT_EQ( - checksum(parse_hex("0xDED02FD979FC2E55C0243BD2F52DF022C40ADA1E")), - "Ded02fD979fC2e55c0243bd2F52df022c40ADa1E" - ); - ASSERT_EQ( - checksum(parse_hex("0x13F06E60297BEA6A3C402F6F64C416A6B31E586E")), - "13F06E60297bea6A3c402F6f64c416A6b31e586e" - ); - ASSERT_EQ( - checksum(parse_hex("0x1A90C25307C3CC71958A83FA213A2362D859CF33")), - "1a90C25307C3Cc71958A83fa213A2362D859CF33" - ); -} diff --git a/tests/Zilliqa/SignatureTests.cpp b/tests/Zilliqa/SignatureTests.cpp deleted file mode 100644 index 386e655cfbf..00000000000 --- a/tests/Zilliqa/SignatureTests.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "../interface/TWTestUtilities.h" -#include "HexCoding.h" -#include "Data.h" -#include -#include - -#include - -using namespace TW; - -TEST(ZilliqaSignature, Signing) { - auto keyData = WRAPD(TWDataCreateWithHexString(STRING("0xafeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5").get())); - auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(keyData.get())); - auto pubKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), true)); - - auto message = "hello schnorr"; - auto messageData = WRAPD(TWDataCreateWithBytes((uint8_t *)message, strnlen(message, 13))); - auto signatureData = WRAPD(TWPrivateKeySignSchnorr(privateKey.get(), messageData.get(), TWCurveSECP256k1)); - auto signature = data(TWDataBytes(signatureData.get()), TWDataSize(signatureData.get())); - - ASSERT_TRUE(TWPublicKeyVerifySchnorr(pubKey.get(), signatureData.get(), messageData.get())); - EXPECT_EQ(hex(signature), "d166b1ae7892c5ef541461dc12a50214d0681b63d8037cda29a3fe6af8bb973e4ea94624d85bc0010bdc1b38d05198328fae21254adc2bf5feaf2804d54dba55"); -} diff --git a/tests/Zilliqa/SignerTests.cpp b/tests/Zilliqa/SignerTests.cpp deleted file mode 100644 index 10f72933b2a..00000000000 --- a/tests/Zilliqa/SignerTests.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "PrivateKey.h" -#include "Zilliqa/Address.h" -#include "Zilliqa/Signer.h" -#include "proto/Zilliqa.pb.h" -#include "uint256.h" - -#include - -using namespace TW; -using namespace TW::Zilliqa; - -TEST(ZilliqaSigner, PreImage) { - auto privateKey = PrivateKey(parse_hex("0E891B9DFF485000C7D1DC22ECF3A583CC50328684321D61947A86E57CF6C638")); - auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - ASSERT_EQ(hex(pubKey.bytes), "034ae47910d58b9bde819c3cffa8de4441955508db00aa2540db8e6bf6e99abc1b"); - - auto amount = uint256_t(15000000000000); - auto gasPrice = uint256_t(1000000000); - auto amountData = store(amount); - auto gasData = store(gasPrice); - auto toAddress = Address(parse_hex("0x9Ca91EB535Fb92Fda5094110FDaEB752eDb9B039")); - - auto input = Proto::SigningInput(); - auto& tx = *input.mutable_transaction(); - auto& transfer = *tx.mutable_transfer(); - transfer.set_amount(amountData.data(), amountData.size()); - - input.set_version(65537); - input.set_nonce(4); - input.set_to(toAddress.string()); - input.set_gas_price(gasData.data(), gasData.size()); - input.set_gas_limit(uint64_t(1)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - Address address; - auto preImage = Signer::getPreImage(input, address); - auto signature = Signer::sign(input).signature(); - - ASSERT_EQ(hex(preImage.begin(), preImage.end()), "0881800410041a149ca91eb535fb92fda5094110fdaeb752edb9b03922230a21034ae47910d58b9bde819c3cffa8de4441955508db00aa2540db8e6bf6e99abc1b2a120a10000000000000000000000da475abf00032120a100000000000000000000000003b9aca003801"); - - ASSERT_TRUE(pubKey.verifySchnorr(Data(signature.begin(), signature.end()), preImage)); -} - -TEST(ZilliqaSigner, Signing) { - auto privateKey = PrivateKey(parse_hex("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748")); - auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - - // 1 ZIL - auto amount = uint256_t(1000000000000); - auto gasPrice = uint256_t(1000000000); - auto amountData = store(amount); - auto gasData = store(gasPrice); - auto toAddress = Address(parse_hex("0x7FCcaCf066a5F26Ee3AFfc2ED1FA9810Deaa632C")); - - auto input = Proto::SigningInput(); - auto& tx = *input.mutable_transaction(); - auto& transfer = *tx.mutable_transfer(); - transfer.set_amount(amountData.data(), amountData.size()); - - input.set_version(65537); - input.set_nonce(2); - input.set_to(toAddress.string()); - input.set_gas_price(gasData.data(), gasData.size()); - input.set_gas_limit(uint64_t(1)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto output = Signer::sign(input); - - ASSERT_EQ(hex(output.signature().begin(), output.signature().end()), "001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268"); - ASSERT_EQ(output.json(), R"({"amount":"1000000000000","code":"","data":"","gasLimit":"1","gasPrice":"1000000000","nonce":2,"pubKey":"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c","signature":"001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268","toAddr":"7FCcaCf066a5F26Ee3AFfc2ED1FA9810Deaa632C","version":65537})"); -} - -TEST(ZilliqaSigner, SigningData) { - // https://viewblock.io/zilliqa/tx/0x6228b3d7e69fc3481b84fd00e892cec359a41654f58948ff7b1b932396b00ad9 - auto privateKey = PrivateKey(parse_hex("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748")); - auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); - - // 10 ZIL - auto amount = uint256_t(10000000000000); - auto gasPrice = uint256_t(2000000000); - auto amountData = store(amount); - auto gasData = store(gasPrice); - - std::string json = "{\"_tag\":\"DelegateStake\",\"params\":[{\"type\":\"ByStr20\",\"value\":\"0x122219cCeAb410901e96c3A0e55E46231480341b\",\"vname\":\"ssnaddr\"}]}"; - auto jsonData = Data(json.begin(), json.end()); - - auto input = Proto::SigningInput(); - auto& tx = *input.mutable_transaction(); - auto& raw = *tx.mutable_raw_transaction(); - raw.set_amount(amountData.data(), amountData.size()); - raw.set_data(jsonData.data(), jsonData.size()); - - input.set_version(65537); - input.set_nonce(56); - input.set_to("zil1g029nmzsf36r99vupp4s43lhs40fsscx3jjpuy"); - input.set_gas_price(gasData.data(), gasData.size()); - input.set_gas_limit(uint64_t(5000)); - input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); - - auto output = Signer::sign(input); - ASSERT_EQ(output.json(), "{\"amount\":\"10000000000000\",\"code\":\"\",\"data\":\"{\\\"_tag\\\":\\\"DelegateStake\\\",\\\"params\\\":[{\\\"type\\\":\\\"ByStr20\\\",\\\"value\\\":\\\"0x122219cCeAb410901e96c3A0e55E46231480341b\\\",\\\"vname\\\":\\\"ssnaddr\\\"}]}\",\"gasLimit\":\"5000\",\"gasPrice\":\"2000000000\",\"nonce\":56,\"pubKey\":\"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c\",\"signature\":\"437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d\",\"toAddr\":\"43D459eC504C7432959c086B0ac7F7855E984306\",\"version\":65537}"); - ASSERT_EQ(hex(output.signature().begin(), output.signature().end()), "437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d"); -} diff --git a/tests/Zilliqa/TWAnySignerTests.cpp b/tests/Zilliqa/TWAnySignerTests.cpp deleted file mode 100644 index e9cbe99aa3f..00000000000 --- a/tests/Zilliqa/TWAnySignerTests.cpp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "HexCoding.h" -#include "uint256.h" -#include "proto/Zilliqa.pb.h" -#include "../interface/TWTestUtilities.h" -#include -#include - -using namespace TW; -using namespace TW::Zilliqa; - -TEST(TWAnySignerZilliqa, Sign) { - auto input = Proto::SigningInput(); - auto& tx = *input.mutable_transaction(); - auto& transfer = *tx.mutable_transfer(); - auto key = parse_hex("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748"); - auto amount = store(uint256_t(1000000000000)); - auto gasPrice = store(uint256_t(1000000000)); - - input.set_version(65537); - input.set_nonce(2); - input.set_to("zil10lx2eurx5hexaca0lshdr75czr025cevqu83uz"); - input.set_gas_price(gasPrice.data(), gasPrice.size()); - input.set_gas_limit(1); - input.set_private_key(key.data(), key.size()); - transfer.set_amount(amount.data(), amount.size()); - - Proto::SigningOutput output; - ANY_SIGN(input, TWCoinTypeZilliqa); - - EXPECT_EQ(hex(output.signature()), "001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268"); - EXPECT_EQ(hex(output.json()), "7b22616d6f756e74223a2231303030303030303030303030222c22636f6465223a22222c2264617461223a22222c226761734c696d6974223a2231222c226761735072696365223a2231303030303030303030222c226e6f6e6365223a322c227075624b6579223a22303366623330623139366365336539373635393365636332646132323064636139636465613863383464323337333737303034326139333062383932616330663563222c227369676e6174757265223a223030316661346466303863313161346137396539366536393339396565343865656563633738323331613738623033353561386361373833633737633133393433366533373933346665636332323532656438646163303065323335653232643138343130343631666238393636383563343237303634323733386564323638222c22746f41646472223a2237464363614366303636613546323645653341466663324544314641393831304465616136333243222c2276657273696f6e223a36353533377d"); -} - -TEST(TWAnySignerZilliqa, SignJSON) { - auto json = STRING(R"({"version":65537,"nonce":"2","to":"zil10lx2eurx5hexaca0lshdr75czr025cevqu83uz","gasPrice":"O5rKAA==","gasLimit":"1","transaction":{"transfer":{"amount":"6NSlEAA="}}})"); - auto key = DATA("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748"); - auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeZilliqa)); - - ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeZilliqa)); - assertStringsEqual(result, "7b22616d6f756e74223a2231303030303030303030303030222c22636f6465223a22222c2264617461223a22222c226761734c696d6974223a2231222c226761735072696365223a2231303030303030303030222c226e6f6e6365223a322c227075624b6579223a22303366623330623139366365336539373635393365636332646132323064636139636465613863383464323337333737303034326139333062383932616330663563222c227369676e6174757265223a223030316661346466303863313161346137396539366536393339396565343865656563633738323331613738623033353561386361373833633737633133393433366533373933346665636332323532656438646163303065323335653232643138343130343631666238393636383563343237303634323733386564323638222c22746f41646472223a2237464363614366303636613546323645653341466663324544314641393831304465616136333243222c2276657273696f6e223a36353533377d"); -} diff --git a/tests/Zilliqa/TWCoinTypeTests.cpp b/tests/Zilliqa/TWCoinTypeTests.cpp deleted file mode 100644 index 0b1d57ea02b..00000000000 --- a/tests/Zilliqa/TWCoinTypeTests.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. -// -// This is a GENERATED FILE, changes made here MAY BE LOST. -// Generated one-time (codegen/bin/cointests) -// - -#include "../interface/TWTestUtilities.h" -#include -#include - - -TEST(TWZilliqaCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeZilliqa)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeZilliqa, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeZilliqa, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeZilliqa)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeZilliqa)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeZilliqa), 12); - ASSERT_EQ(TWBlockchainZilliqa, TWCoinTypeBlockchain(TWCoinTypeZilliqa)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeZilliqa)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeZilliqa)); - assertStringsEqual(symbol, "ZIL"); - assertStringsEqual(txUrl, "https://viewblock.io/zilliqa/tx/t123"); - assertStringsEqual(accUrl, "https://viewblock.io/zilliqa/address/a12"); - assertStringsEqual(id, "zilliqa"); - assertStringsEqual(name, "Zilliqa"); -} diff --git a/tests/chains/Aeternity/AddressTests.cpp b/tests/chains/Aeternity/AddressTests.cpp new file mode 100644 index 00000000000..c08866c05e7 --- /dev/null +++ b/tests/chains/Aeternity/AddressTests.cpp @@ -0,0 +1,24 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include + +namespace TW::Aeternity::tests { + +TEST(AeternityAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148"), TWPublicKeyTypeED25519); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); +} + +TEST(AeternityAddress, FromString) { + auto address = Address("ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); + ASSERT_EQ(address.string(), "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); +} + +} // namespace TW::Aeternity::tests diff --git a/tests/chains/Aeternity/SignerTests.cpp b/tests/chains/Aeternity/SignerTests.cpp new file mode 100644 index 00000000000..55f9246fd3b --- /dev/null +++ b/tests/chains/Aeternity/SignerTests.cpp @@ -0,0 +1,85 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Aeternity/Signer.h" +#include "Aeternity/Transaction.h" +#include "HexCoding.h" + +#include "Aeternity/Address.h" +#include +#include "uint256.h" + +namespace TW::Aeternity::tests { + +TEST(AeternitySigner, Sign) { + std::string sender_id = "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"; + std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; + uint256_t amount = 10; + uint256_t fee = 20000000000000; + std::string payload = "Hello World"; + uint64_t ttl = 82757; + uint64_t nonce = 49; + + auto transaction = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); + auto privateKey = PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); + + auto result = Signer::sign(privateKey, transaction); + EXPECT_EQ(result.signature(), "sg_VW42qDPP3MMNFAStYaumjZz7mC7BZYpbNa15E57ejqUe7JdQFWCiX65eLNUpGMpt8tSpfgCfkYzcaFppqx7W75CrcWdC8"); + EXPECT_EQ(result.encoded(), "tx_+KkLAfhCuEDZ2XDV5OuHv1iuLn66sFLBUwnzp1K8JW1Zz+fEgmuEh6HEvNu0R112M3IYkVzvTSnT0pJ3TWhVOumgJ+IWwW8HuGH4XwwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKMKhhIwnOVAAIMBQ0Uxi0hlbGxvIFdvcmxkDZqNSg=="); +} + +TEST(AeternitySigner, SignTxWithZeroTtl) { + std::string sender_id = "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"; + std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; + uint256_t amount = 10; + uint256_t fee = 20000000000000; + std::string payload = "Hello World"; + uint64_t ttl = 0; + uint64_t nonce = 49; + + auto transaction = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); + auto privateKey = PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); + + auto result = Signer::sign(privateKey, transaction); + EXPECT_EQ(result.signature(), "sg_7qJK868bqEZ5ciC2P3WCKYfhayvKTHvPsz3bdPgpfF3Ky7yNg9f8k22A3gxjjSm9afa6JmP8TJpF4GJkFh2k7gGaog9KS"); + EXPECT_EQ(result.encoded(), "tx_+KYLAfhCuEA0OgWhpq/VfS6ksMS+Df4ewZxIITEhjaaMOiyT0aRuAEe6b5+d2cQtzoyz58NNr+N4MFowctrGXrCrrkhNIywLuF74XAwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKMKhhIwnOVAAAAxi0hlbGxvIFdvcmxkjoDNvQ=="); +} + +TEST(AeternitySigner, SignTxWithZeroAmount) { + std::string sender_id = "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"; + std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; + uint256_t amount = 0; + uint256_t fee = 20000000000000; + std::string payload = "Zero amount test"; + uint64_t ttl = 113579; + uint64_t nonce = 7; + + auto transaction = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); + auto privateKey = PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); + + auto result = Signer::sign(privateKey, transaction); + EXPECT_EQ(result.signature(), "sg_ShWvujPnyKBT1Ng2X5k6XSchVK8Bq7LYEisPMH11DUoPkXZcooBzqw81j9j5JewoFFpT9xEhUptj1azcLA21ogURYh4Lz"); + EXPECT_EQ(result.encoded(), "tx_+K4LAfhCuEDEbeoiVYmJCXm91KNfZXOvZMoT9x/sZja09EXZmErFBxm52b1IVoM4806Zr+TsliAYzUyKfUUFo3jGfXEPdZ8PuGb4ZAwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKMAhhIwnOVAAIMBu6sHkFplcm8gYW1vdW50IHRlc3S5L3Vn"); +} + +TEST(AeternitySigner, SignTxWithZeroNonce) { + std::string sender_id = "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"; + std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; + uint256_t amount = 3369980000000000000; + uint256_t fee = 20000000000000; + std::string payload = "Zero nonce test"; + uint64_t ttl = 113579; + uint64_t nonce = 0; + + auto transaction = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); + auto privateKey = PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646")); + + auto result = Signer::sign(privateKey, transaction); + EXPECT_EQ(result.signature(), "sg_MaJc4ptSUhq5kH6mArszDAvu4f7PejyuhmgM6U8GEr8bRUTaSFbdFPx4C6FEYA5v5Lgwu9EToaWnHgR2xkqZ9JjHnaBpA"); + EXPECT_EQ(result.encoded(), "tx_+LULAfhCuECdQsgcE8bp+9CANdasxkt5gxfjBSI1ztyPl1aNJbm+MwUvE7Lu/qvAkHijfe+Eui2zrqhZRYc5mblRa+oLOIIEuG34awwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKOILsSS9IArwACGEjCc5UAAgwG7qwCPWmVybyBub25jZSB0ZXN0piWfFA=="); +} + +} // namespace TW::Aeternity::tests diff --git a/tests/Aeternity/TWAeternityAddressTests.cpp b/tests/chains/Aeternity/TWAeternityAddressTests.cpp similarity index 96% rename from tests/Aeternity/TWAeternityAddressTests.cpp rename to tests/chains/Aeternity/TWAeternityAddressTests.cpp index 51376f5cf7a..b801091634c 100644 --- a/tests/Aeternity/TWAeternityAddressTests.cpp +++ b/tests/chains/Aeternity/TWAeternityAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/Aeternity/TWAnySignerTests.cpp b/tests/chains/Aeternity/TWAnySignerTests.cpp new file mode 100644 index 00000000000..9a1f716447f --- /dev/null +++ b/tests/chains/Aeternity/TWAnySignerTests.cpp @@ -0,0 +1,38 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "uint256.h" +#include "proto/Aeternity.pb.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::Aeternity::tests { + +TEST(TWAnySignerAeternity, Sign) { + auto privateKey = parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + + Proto::SigningInput input; + input.set_from_address("ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); + input.set_to_address("ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"); + auto amount = store(uint256_t(10)); + input.set_amount(amount.data(), amount.size()); + auto fee = store(uint256_t(20000000000000)); + input.set_fee(fee.data(), fee.size()); + input.set_payload("Hello World"); + input.set_ttl(82757); + input.set_nonce(49); + input.set_private_key(privateKey.data(), privateKey.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeAeternity); + + ASSERT_EQ(output.encoded(), "tx_+KkLAfhCuEDZ2XDV5OuHv1iuLn66sFLBUwnzp1K8JW1Zz+fEgmuEh6HEvNu0R112M3IYkVzvTSnT0pJ3TWhVOumgJ+IWwW8HuGH4XwwBoQHuk6T2b40WuBm7m+uf/M383BQS6H/uajJMKpmh4OZxSKEBHxOjsIvwAUAGYqaLadh194A87EwIZH9u1dhMeJe9UKMKhhIwnOVAAIMBQ0Uxi0hlbGxvIFdvcmxkDZqNSg=="); +} + +} // namespace TW::Aeternity::tests diff --git a/tests/chains/Aeternity/TWCoinTypeTests.cpp b/tests/chains/Aeternity/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..7e9f16603ca --- /dev/null +++ b/tests/chains/Aeternity/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWAeternityCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeAeternity)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeAeternity, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeAeternity, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeAeternity)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeAeternity)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeAeternity), 18); + ASSERT_EQ(TWBlockchainAeternity, TWCoinTypeBlockchain(TWCoinTypeAeternity)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAeternity)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAeternity)); + assertStringsEqual(symbol, "AE"); + assertStringsEqual(txUrl, "https://explorer.aepps.com/transactions/t123"); + assertStringsEqual(accUrl, "https://explorer.aepps.com/account/transactions/a12"); + assertStringsEqual(id, "aeternity"); + assertStringsEqual(name, "Aeternity"); +} diff --git a/tests/chains/Aeternity/TransactionTests.cpp b/tests/chains/Aeternity/TransactionTests.cpp new file mode 100644 index 00000000000..fa50abb449f --- /dev/null +++ b/tests/chains/Aeternity/TransactionTests.cpp @@ -0,0 +1,63 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "TestUtilities.h" +#include +#include + +namespace TW::Aeternity::tests { + +TEST(AeternityTransaction, EncodeRlp) { + std::string sender_id = "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi"; + std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; + uint64_t amount = 10; + uint64_t fee = 2e13; + std::string payload = "Hello World"; + uint64_t ttl = 82757; + uint64_t nonce = 49; + + auto tx = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); + auto encodedTx = tx.encode(); + auto encodedTxHex = TW::hex(encodedTx); + + ASSERT_EQ(encodedTxHex, "f85f0c01a101cea7ade470c9f99d9d4e400880a86f1d49bb444b62f11a9ebb64bbcfeb73fef3a1011f13a3b08bf001400662a68b69d875f7803cec4c08647f6ed5d84c7897bd50a30a8612309ce5400083014345318b48656c6c6f20576f726c64"); +} + +TEST(AeternityTransaction, EncodeRlpWithZeroAmount) { + std::string sender_id = "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi"; + std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; + uint64_t amount = 0; + uint64_t fee = 2e13; + std::string payload = "Hello World"; + uint64_t ttl = 82757; + uint64_t nonce = 49; + + auto tx = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); + auto encodedTx = tx.encode(); + auto encodedTxHex = TW::hex(encodedTx); + + ASSERT_EQ(encodedTxHex, "f85f0c01a101cea7ade470c9f99d9d4e400880a86f1d49bb444b62f11a9ebb64bbcfeb73fef3a1011f13a3b08bf001400662a68b69d875f7803cec4c08647f6ed5d84c7897bd50a3008612309ce5400083014345318b48656c6c6f20576f726c64"); +} + +TEST(AeternityTransaction, EncodeRlpWithZeroTtl) { + std::string sender_id = "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi"; + std::string recipient_id = "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v"; + uint64_t amount = 10; + uint64_t fee = 2e13; + std::string payload = "Hello World"; + uint64_t ttl = 0; + uint64_t nonce = 49; + + auto tx = Transaction(sender_id, recipient_id, amount, fee, payload, ttl, nonce); + auto encodedTx = tx.encode(); + auto encodedTxHex = TW::hex(encodedTx); + + ASSERT_EQ(encodedTxHex, "f85c0c01a101cea7ade470c9f99d9d4e400880a86f1d49bb444b62f11a9ebb64bbcfeb73fef3a1011f13a3b08bf001400662a68b69d875f7803cec4c08647f6ed5d84c7897bd50a30a8612309ce5400000318b48656c6c6f20576f726c64"); +} + +} // namespace TW::Aeternity::tests diff --git a/tests/chains/Aion/AddressTests.cpp b/tests/chains/Aion/AddressTests.cpp new file mode 100644 index 00000000000..05f37edb64e --- /dev/null +++ b/tests/chains/Aion/AddressTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Aion/Address.h" +#include "HexCoding.h" + +#include + +using namespace TW; + +namespace TW::Aion::tests { + +TEST(AionAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("01a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7"), TWPublicKeyTypeED25519); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "0xa0d2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"); +} + +TEST(AionAddress, FromString) { + std::string aionAddress = "0xa0d2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"; + const auto address = Address(aionAddress); + ASSERT_EQ(address.string(), aionAddress); +} + +TEST(AionAddress, isValid) { + std::string validAddress = "0xa0d2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"; + std::string invalidAddress = "0xzzd2312facea71b740679c926d040c9056a65a4bfa2ddd18ec160064f82909e7"; + + ASSERT_TRUE(Address::isValid(validAddress)); + ASSERT_FALSE(Address::isValid(invalidAddress)); +} + +} // namespace TW::Aion::tests diff --git a/tests/chains/Aion/RLPTests.cpp b/tests/chains/Aion/RLPTests.cpp new file mode 100644 index 00000000000..86df3146ace --- /dev/null +++ b/tests/chains/Aion/RLPTests.cpp @@ -0,0 +1,27 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Aion/RLP.h" +#include "HexCoding.h" +#include + +namespace TW::Aion::tests { + +using boost::multiprecision::uint128_t; + +TEST(AionRLP, EncodeLong) { + EXPECT_EQ(hex(RLP::encodeLong(uint128_t(1))), "01"); + EXPECT_EQ(hex(RLP::encodeLong(uint128_t(21000))), "825208"); + EXPECT_EQ(hex(RLP::encodeLong(uint128_t(1000000))), "830f4240"); + EXPECT_EQ(hex(RLP::encodeLong(uint128_t(20000000000))), "8800000004a817c800"); + EXPECT_EQ(hex(RLP::encodeLong(uint128_t(9007199254740991))), "88001fffffffffffff"); + EXPECT_EQ(hex(RLP::encodeLong(uint128_t(9007199254740990))), "88001ffffffffffffe"); + EXPECT_EQ(hex(RLP::encodeLong(uint128_t(4294967296L))), "880000000100000000"); + EXPECT_EQ(hex(RLP::encodeLong(uint128_t(4295000060L))), "880000000100007ffc"); + EXPECT_EQ(hex(RLP::encodeLong(uint128_t(72057594037927935L))), "8800ffffffffffffff"); +} + +} // namespace TW::Aion::tests diff --git a/tests/chains/Aion/SignerTests.cpp b/tests/chains/Aion/SignerTests.cpp new file mode 100644 index 00000000000..34d44504db7 --- /dev/null +++ b/tests/chains/Aion/SignerTests.cpp @@ -0,0 +1,41 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Aion/Signer.h" +#include "Aion/Transaction.h" +#include "HexCoding.h" + +#include + +namespace TW::Aion::tests { + +TEST(AionSigner, Sign) { + auto address = Aion::Address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); + auto transaction = Transaction(9, 20000000000, 21000, address, 10000, 155157377101, {}); + + auto privateKey = PrivateKey(parse_hex("db33ffdf82c7ba903daf68d961d3c23c20471a8ce6b408e52d579fd8add80cc9")); + Signer::sign(privateKey, transaction); + + EXPECT_EQ(hex(transaction.signature), "a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); + + // Raw transaction + EXPECT_EQ(hex(transaction.encode()), "f89b09a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108085242019b04d8252088800000004a817c80001b860a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); +} + +TEST(AionSigner, SignWithData) { + auto address = Aion::Address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); + auto transaction = Transaction(9, 20000000000, 21000, address, 10000, 155157377101, parse_hex("41494f4e0000")); + + auto privateKey = PrivateKey(parse_hex("db33ffdf82c7ba903daf68d961d3c23c20471a8ce6b408e52d579fd8add80cc9")); + Signer::sign(privateKey, transaction); + + EXPECT_EQ(hex(transaction.signature), "a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a736fc2642c2d62900204779aa274dba3b8712eff7a8464aa78ea52b09ece20679fe3f5edf94c84a7e0c5f93213be891bc279af927086f455167f5bc73d3046c0d"); + + // Raw transaction + EXPECT_EQ(hex(transaction.encode()), "f8a109a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108641494f4e000085242019b04d8252088800000004a817c80001b860a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a736fc2642c2d62900204779aa274dba3b8712eff7a8464aa78ea52b09ece20679fe3f5edf94c84a7e0c5f93213be891bc279af927086f455167f5bc73d3046c0d"); +} + +} // namespace TW::Aion::tests diff --git a/tests/chains/Aion/TWAnySignerTests.cpp b/tests/chains/Aion/TWAnySignerTests.cpp new file mode 100644 index 00000000000..5b7f3417014 --- /dev/null +++ b/tests/chains/Aion/TWAnySignerTests.cpp @@ -0,0 +1,39 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "uint256.h" +#include "proto/Aion.pb.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::Aion::tests { + +TEST(TWAnySignerAion, Sign) { + auto privateKey = parse_hex("db33ffdf82c7ba903daf68d961d3c23c20471a8ce6b408e52d579fd8add80cc9"); + + Proto::SigningInput input; + input.set_to_address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); + auto amount = store(uint256_t(10000)); + input.set_amount(amount.data(), amount.size()); + auto gasPrice = store(uint256_t(20000000000)); + input.set_gas_price(gasPrice.data(), gasPrice.size()); + auto gasLimit = store(uint256_t(21000)); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + auto nonce = store(uint256_t(9)); + input.set_nonce(nonce.data(), nonce.size()); + input.set_timestamp(155157377101); + input.set_private_key(privateKey.data(), privateKey.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeAion); + + ASSERT_EQ(hex(output.encoded()), "f89b09a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108085242019b04d8252088800000004a817c80001b860a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); +} + +} // namespace TW::Aion::tests diff --git a/tests/chains/Aion/TWCoinTypeTests.cpp b/tests/chains/Aion/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..9d217c0654f --- /dev/null +++ b/tests/chains/Aion/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWAionCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeAion)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeAion, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeAion, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeAion)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeAion)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeAion), 18); + ASSERT_EQ(TWBlockchainAion, TWCoinTypeBlockchain(TWCoinTypeAion)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAion)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAion)); + assertStringsEqual(symbol, "AION"); + assertStringsEqual(txUrl, "https://mainnet.aion.network/#/transaction/t123"); + assertStringsEqual(accUrl, "https://mainnet.aion.network/#/account/a12"); + assertStringsEqual(id, "aion"); + assertStringsEqual(name, "Aion"); +} diff --git a/tests/chains/Aion/TransactionTests.cpp b/tests/chains/Aion/TransactionTests.cpp new file mode 100644 index 00000000000..9b9ff818758 --- /dev/null +++ b/tests/chains/Aion/TransactionTests.cpp @@ -0,0 +1,26 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Aion/Transaction.h" +#include "HexCoding.h" +#include + +namespace TW::Aion::tests { + +TEST(AionTransaction, Encode) { + auto address = Aion::Address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); + auto transaction = Transaction(9, 20000000000, 21000, address, 10000, 155157377101, {}); + ASSERT_EQ(hex(transaction.encode()), "f83909a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108085242019b04d8252088800000004a817c80001"); +} + +TEST(AionTransaction, EncodeWithSignature) { + auto address = Aion::Address("0xa082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e"); + auto transaction = Transaction(9, 20000000000, 21000, address, 10000, 155157377101, {}); + transaction.signature = parse_hex("a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); + ASSERT_EQ(hex(transaction.encode()), "f89b09a0a082c3de528b7807dc27ad66debb16d4cfe4054209398cee619dd95955063d1e8227108085242019b04d8252088800000004a817c80001b860a775daa30b33fda3091768f0561c8042ee23cb48a6a3e5d7e8248b13d04a48a7d3d3386742c2716031b79950cef5fcb49c079a5cab095c8b08915e126b9741389924ba2d5c00036a3b39c2a8562fa0800f1a13a566ce6e027274ce63a41dec07"); +} + +} // namespace TW::Aion::tests diff --git a/tests/chains/Algorand/AddressTests.cpp b/tests/chains/Algorand/AddressTests.cpp new file mode 100644 index 00000000000..6edff289604 --- /dev/null +++ b/tests/chains/Algorand/AddressTests.cpp @@ -0,0 +1,48 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Algorand/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include +#include + +using namespace TW; + +namespace TW::Algorand::tests { + +TEST(AlgorandAddress, Validation) { + // empty address + ASSERT_FALSE(Address::isValid("")); + // invalid checksum + ASSERT_FALSE(Address::isValid("JBCQYJ2FREG667NAN7BFKH4RFIKPT7CYDQJNW3SNN5Z7F7ILFLKQ346TS3")); + // wrong length + ASSERT_FALSE(Address::isValid("JBCQYJ2FREG667NAN7BFKH4RFIKPT7CYDQJNW3SNN5Z7F7ILFLKQ346TSU ")); + // Stellar address + ASSERT_FALSE(Address::isValid("GABQHYQOY22KHGTCTAK24AWAUE4TXERF4O4JBSXELNM7IL5CTPUWM3SC")); + + ASSERT_TRUE(Address::isValid("HXIWBVQGOO6ZWE5NYJO22XMYRUGZ6TGNX2K2EERPT3ZIWPHE5CLJGB2GEA")); +} + +TEST(AlgorandAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("526d96fffdbfe787b2f00586298538f9a019e97f6587964dc61aae9ad1d7fa23")); + auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(address.string(), "JBCQYJ2FREG667NAN7BFKH4RFIKPT7CYDQJNW3SNN5Z7F7ILFLKQ346TSU"); +} + +TEST(AlgorandAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("c2b423afa8b0095e5ae105668b91b2132db4dadbf38acfc64908d3476a00191f"), TWPublicKeyTypeED25519); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "YK2CHL5IWAEV4WXBAVTIXENSCMW3JWW36OFM7RSJBDJUO2QADEP5QYVO5I"); +} + +TEST(AlgorandAddress, FromString) { + auto address = Address("PITDOF57RHOVLT37KM7DCXDCETLDL3OA5CBAN7LQ44Z36LGFC27IJ2IQ64"); + ASSERT_EQ(address.string(), "PITDOF57RHOVLT37KM7DCXDCETLDL3OA5CBAN7LQ44Z36LGFC27IJ2IQ64"); +} + +} // namespace TW::Algorand::tests diff --git a/tests/chains/Algorand/SignerTests.cpp b/tests/chains/Algorand/SignerTests.cpp new file mode 100644 index 00000000000..d3efef06418 --- /dev/null +++ b/tests/chains/Algorand/SignerTests.cpp @@ -0,0 +1,197 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Algorand/Address.h" +#include "Algorand/BinaryCoding.h" +#include "Algorand/Signer.h" +#include "Base64.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include +#include + +using namespace TW; + +namespace TW::Algorand::tests { + +TEST(AlgorandSigner, EncodeNumbers) { + auto tests = { + std::make_tuple(100ull, "64"), + std::make_tuple(200ull, "ccc8"), + std::make_tuple(55536ull, "cdd8f0"), + std::make_tuple(3294967296ull, "cec4653600"), + std::make_tuple(14294967296ull, "cf00000003540be400"), + }; + + for (auto& test : tests) { + Data data; + encodeNumber(std::get<0>(test), data); + ASSERT_EQ(hex(data), std::get<1>(test)); + } +} + +TEST(AlgorandSigner, EncodeStrings) { + auto tests = { + std::make_tuple("algo", "a4616c676f"), + std::make_tuple("It's like JSON. but fast and small.", "d92349742773206c696b65204a534f4e2e20627574206661737420616e6420736d616c6c2e"), + std::make_tuple( + "MessagePack is an efficient binary serialization format. It lets you exchange data among multiple languages like JSON. But it's faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one extra byte in addition to the strings themselves.", + "da011f4d6573736167655061636b20697320616e20656666696369656e742062696e6172792073657269616c697a6174696f6e20666f726d61742e204974206c65747320796f752065786368616e6765206461746120616d6f6e67206d756c7469706c65206c616e677561676573206c696b65204a534f4e2e2042757420697427732066617374657220616e6420736d616c6c65722e20536d616c6c20696e7465676572732061726520656e636f64656420696e746f20612073696e676c6520627974652c20616e64207479706963616c2073686f727420737472696e67732072657175697265206f6e6c79206f6e65206578747261206279746520696e206164646974696f6e20746f2074686520737472696e6773207468656d73656c7665732e")}; + + for (auto& test : tests) { + Data data; + Algorand::encodeString(std::get<0>(test), data); + ASSERT_EQ(hex(data), std::get<1>(test)); + } +} + +TEST(AlgorandSigner, EncodeBytes) { + auto rawtx = "010000000001029294c2b3bd4d25483c4c12432df01a856a38cc0cb48da1a7dd590b7d893392a90000000000ffffffffded892ea55bf1c6ccc495d3493767d7c24497f612b9edc9ab8d30eb671ea76750000000000ffffffff021027000000000000160014b96bacd6f729ef8ac1dd30d159433c0917ba8d3db00f00000000000016001476cd9d430de6db162fc3db509920255ff6d2bdb002483045022100eb8675ff6775e9c399dddba9f178002b745872e541617d690cbce7c933adb87602205de8074c173696de65d4c644a84ea1337c9e9928c7052fddcf9d99e35815e2f20121032858d3a5f9825408ea3959800c5daf22e7a91e459ef168df45071266501d28e102473044022025f1cf362a9c09bd351769f1918ab9f0a6c3f6c4682f29fdbfc08354554ea37b02203f62345b3da4d7a29f58c7c741682be4108a0fb2013980332cc3e081aad7423f01210237d83670da2d3947a58752dab95d59b592c78f2e734d1c14dbf75b29bbe4116100000000"; + Data data; + encodeBytes(parse_hex(rawtx), data); + ASSERT_EQ(hex(data), "c50173010000000001029294c2b3bd4d25483c4c12432df01a856a38cc0cb48da1a7dd590b7d893392a90000000000ffffffffded892ea55bf1c6ccc495d3493767d7c24497f612b9edc9ab8d30eb671ea76750000000000ffffffff021027000000000000160014b96bacd6f729ef8ac1dd30d159433c0917ba8d3db00f00000000000016001476cd9d430de6db162fc3db509920255ff6d2bdb002483045022100eb8675ff6775e9c399dddba9f178002b745872e541617d690cbce7c933adb87602205de8074c173696de65d4c644a84ea1337c9e9928c7052fddcf9d99e35815e2f20121032858d3a5f9825408ea3959800c5daf22e7a91e459ef168df45071266501d28e102473044022025f1cf362a9c09bd351769f1918ab9f0a6c3f6c4682f29fdbfc08354554ea37b02203f62345b3da4d7a29f58c7c741682be4108a0fb2013980332cc3e081aad7423f01210237d83670da2d3947a58752dab95d59b592c78f2e734d1c14dbf75b29bbe4116100000000"); +} + +TEST(AlgorandSigner, Sign) { + auto key = PrivateKey(parse_hex("c9d3cc16fecabe2747eab86b81528c6ed8b65efc1d6906d86aabc27187a1fe7c")); + auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); + auto from = Address(publicKey); + auto to = Address("UCE2U2JC4O4ZR6W763GUQCG57HQCDZEUJY4J5I6VYY4HQZUJDF7AKZO5GM"); + Data note; + std::string genesisId = "mainnet-v1.0"; + auto genesisHash = Base64::decode("wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="); + auto transaction = Transfer( + /* from */ from, + /* to */ to, + /* fee */ 488931, + /* amount */ 847, + /* first round */ 51, + /* last round */ 61, + /* note */ note, + /* type */ "pay", + /* genesis id*/ genesisId, + /* genesis hash*/ genesisHash); + + auto serialized = transaction.serialize(); + auto signature = Signer::sign(key, transaction); + auto result = transaction.BaseTransaction::serialize(signature); + + ASSERT_EQ(hex(serialized), "89a3616d74cd034fa3666565ce000775e3a2667633a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c763da3726376c420a089aa6922e3b998fadff6cd4808ddf9e021e4944e389ea3d5c638786689197ea3736e64c42074b000b6368551a6066d713e2866002e8dab34b69ede09a72e85a39bbb1f7928a474797065a3706179"); + ASSERT_EQ(hex(signature), "de73363dbdeda0682adca06f6268a16a6ec47253c94d5692dc1c49a84a05847812cf66d7c4cf07c7e2f50f143ec365d405e30b35117b264a994626054d2af604"); + ASSERT_EQ(hex(result), "82a3736967c440de73363dbdeda0682adca06f6268a16a6ec47253c94d5692dc1c49a84a05847812cf66d7c4cf07c7e2f50f143ec365d405e30b35117b264a994626054d2af604a374786e89a3616d74cd034fa3666565ce000775e3a2667633a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c763da3726376c420a089aa6922e3b998fadff6cd4808ddf9e021e4944e389ea3d5c638786689197ea3736e64c42074b000b6368551a6066d713e2866002e8dab34b69ede09a72e85a39bbb1f7928a474797065a3706179"); +} + +TEST(AlgorandSigner, SignAsset) { + // https://testnet.algoexplorer.io/tx/NJ62HYO2LC222AVLIN2GW5LKIWKLGC7NZLIQ3DUL2RDVRYO2UW7A + auto key = PrivateKey(parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04")); + auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); + auto from = Address(publicKey); + auto to = Address("GJIWJSX2EU5RC32LKTDDXWLA2YICBHKE35RV2ZPASXZYKWUWXFLKNFSS4U"); + Data note; + std::string genesisId = "testnet-v1.0"; + auto genesisHash = Base64::decode("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="); + auto transaction = AssetTransfer( + /* from */ from, + /* to */ to, + /* fee */ 2340, + /* amount */ 1000000, + /* asset id */ 13379146, + /* first round */ 15775683, + /* last round */ 15776683, + /* note */ note, + /* type */ "axfer", + /* genesis id*/ genesisId, + /* genesis hash*/ genesisHash); + + auto serialized = transaction.serialize(); + auto signature = Signer::sign(key, transaction); + auto result = transaction.BaseTransaction::serialize(signature); + + ASSERT_EQ(hex(serialized), "8aa461616d74ce000f4240a461726376c420325164cafa253b116f4b54c63bd960d610209d44df635d65e095f3855a96b956a3666565cd0924a26676ce00f0b7c3a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bbaba3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); + ASSERT_EQ(hex(signature), "412720eff99a17280a437bdb8eeba7404b855d6433fffd5dde7f7966c1f9ae531a1af39e18b8a58b4a6c6acb709cca92f8a18c36d8328be9520c915311027005"); + ASSERT_EQ(hex(result), "82a3736967c440412720eff99a17280a437bdb8eeba7404b855d6433fffd5dde7f7966c1f9ae531a1af39e18b8a58b4a6c6acb709cca92f8a18c36d8328be9520c915311027005a374786e8aa461616d74ce000f4240a461726376c420325164cafa253b116f4b54c63bd960d610209d44df635d65e095f3855a96b956a3666565cd0924a26676ce00f0b7c3a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bbaba3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); +} + +TEST(AlgorandSigner, SignAssetOptIn) { + // https://testnet.algoexplorer.io/tx/47LE2QS4B5N6IFHXOUN2MJUTCOQCHNY6AB3AJYECK4IM2VYKJDKQ + auto key = PrivateKey(parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04")); + auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); + auto address = Address(publicKey); + Data note; + std::string genesisId = "testnet-v1.0"; + auto genesisHash = Base64::decode("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="); + auto transaction = OptInAssetTransaction( + /* from */ address, + /* fee */ 2340, + /* asset id */ 13379146, + /* first round */ 15775553, + /* last round */ 15776553, + /* note */ note, + /* type */ "axfer", + /* genesis id*/ genesisId, + /* genesis hash*/ genesisHash); + + auto serialized = transaction.serialize(); + auto signature = Signer::sign(key, transaction); + auto result = transaction.BaseTransaction::serialize(signature); + + ASSERT_EQ(hex(serialized), "89a461726376c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a3666565cd0924a26676ce00f0b741a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bb29a3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); + ASSERT_EQ(hex(signature), "f3a29d9a40271c00b542b38ab2ccb4967015ae6609368d4b8eb2f5e2b5348577cf9e0f62b0777ccb2d8d9b943b15c24c0cf1db312cb01a3c198d9d9c6c5bb00b"); + ASSERT_EQ(hex(result), "82a3736967c440f3a29d9a40271c00b542b38ab2ccb4967015ae6609368d4b8eb2f5e2b5348577cf9e0f62b0777ccb2d8d9b943b15c24c0cf1db312cb01a3c198d9d9c6c5bb00ba374786e89a461726376c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a3666565cd0924a26676ce00f0b741a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bb29a3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); +} + +TEST(AlgorandSigner, ProtoSignerOptIn) { + // https://testnet.algoexplorer.io/tx/47LE2QS4B5N6IFHXOUN2MJUTCOQCHNY6AB3AJYECK4IM2VYKJDKQ + auto optIn = new Proto::AssetOptIn(); + optIn->set_asset_id(13379146); + + auto privateKey = parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04"); + + auto input = Proto::SigningInput(); + auto genesisHash = Base64::decode("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="); + std::string str(genesisHash.begin(), genesisHash.end()); + input.set_allocated_asset_opt_in(optIn); + input.set_genesis_hash(str); + input.set_genesis_id("testnet-v1.0"); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_first_round(15775553); + input.set_last_round(15776553); + input.set_fee(2340); + + auto result = Signer::sign(input); + auto encoded = result.encoded(); + + ASSERT_EQ(hex(encoded), "82a3736967c440f3a29d9a40271c00b542b38ab2ccb4967015ae6609368d4b8eb2f5e2b5348577cf9e0f62b0777ccb2d8d9b943b15c24c0cf1db312cb01a3c198d9d9c6c5bb00ba374786e89a461726376c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a3666565cd0924a26676ce00f0b741a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bb29a3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); +} + +TEST(AlgorandSigner, ProtoSignerAssetTransaction) { + // https://testnet.algoexplorer.io/tx/NJ62HYO2LC222AVLIN2GW5LKIWKLGC7NZLIQ3DUL2RDVRYO2UW7A + auto transaction = new Proto::AssetTransfer(); + transaction->set_asset_id(13379146); + transaction->set_amount(1000000); + transaction->set_to_address("GJIWJSX2EU5RC32LKTDDXWLA2YICBHKE35RV2ZPASXZYKWUWXFLKNFSS4U"); + + auto privateKey = parse_hex("5a6a3cfe5ff4cc44c19381d15a0d16de2a76ee5c9b9d83b232e38cb5a2c84b04"); + + auto input = Proto::SigningInput(); + auto genesisHash = Base64::decode("SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI="); + std::string str(genesisHash.begin(), genesisHash.end()); + input.set_allocated_asset_transfer(transaction); + input.set_genesis_hash(str); + input.set_genesis_id("testnet-v1.0"); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_first_round(15775683); + input.set_last_round(15776683); + input.set_fee(2340); + + auto result = Signer::sign(input); + auto encoded = result.encoded(); + + ASSERT_EQ(hex(encoded), "82a3736967c440412720eff99a17280a437bdb8eeba7404b855d6433fffd5dde7f7966c1f9ae531a1af39e18b8a58b4a6c6acb709cca92f8a18c36d8328be9520c915311027005a374786e8aa461616d74ce000f4240a461726376c420325164cafa253b116f4b54c63bd960d610209d44df635d65e095f3855a96b956a3666565cd0924a26676ce00f0b7c3a367656eac746573746e65742d76312e30a26768c4204863b518a4b3c84ec810f22d4f1081cb0f71f059a7ac20dec62f7f70e5093a22a26c76ce00f0bbaba3736e64c42082872d60c338cb928006070e02ec0942addcb79e7fbd01c76458aea526899bd3a474797065a56178666572a478616964ce00cc264a"); +} + +} // namespace TW::Algorand::tests diff --git a/tests/chains/Algorand/TWAnySignerTests.cpp b/tests/chains/Algorand/TWAnySignerTests.cpp new file mode 100644 index 00000000000..3890bafcfeb --- /dev/null +++ b/tests/chains/Algorand/TWAnySignerTests.cpp @@ -0,0 +1,52 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base64.h" +#include "HexCoding.h" +#include "proto/Algorand.pb.h" +#include + +#include "TestUtilities.h" +#include + +using namespace TW; + +namespace TW::Algorand::tests { + +TEST(TWAnySignerAlgorand, Sign) { + auto privateKey = parse_hex("d5b43d706ef0cb641081d45a2ec213b5d8281f439f2425d1af54e2afdaabf55b"); + auto note = parse_hex("68656c6c6f"); + auto genesisHash = Base64::decode("wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8="); + + Proto::SigningInput input; + auto& transaction = *input.mutable_transfer(); + transaction.set_to_address("CRLADAHJZEW2GFY2UPEHENLOGCUOU74WYSTUXQLVLJUJFHEUZOHYZNWYR4"); + transaction.set_amount(1000000000000ull); + input.set_first_round(1937767ull); + input.set_last_round(1938767ull); + input.set_fee(263000ull); + input.set_genesis_id("mainnet-v1.0"); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_note(note.data(), note.size()); + input.set_private_key(privateKey.data(), privateKey.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeAlgorand); + + ASSERT_EQ(hex(output.encoded()), "82a3736967c440baa00062adcdcb5875e4435cdc6885d26bfe5308ab17983c0fda790b7103051fcb111554e5badfc0ac7edf7e1223a434342a9eeed5cdb047690827325051560ba374786e8aa3616d74cf000000e8d4a51000a3666565ce00040358a26676ce001d9167a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c76ce001d954fa46e6f7465c40568656c6c6fa3726376c42014560180e9c92da3171aa3c872356e30a8ea7f96c4a74bc1755a68929c94cb8fa3736e64c42061bf060efc02e2887dfffc8ed85268c8c091c013eedf315bc50794d02a8791ada474797065a3706179"); +} + +TEST(TWAnySignerAlgorand, SignJSON) { + auto json = STRING(R"({"genesisId":"mainnet-v1.0","genesisHash":"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=","note":"aGVsbG8=","firstRound":"1937767","lastRound":"1938767","fee":"263000","transfer":{"toAddress":"CRLADAHJZEW2GFY2UPEHENLOGCUOU74WYSTUXQLVLJUJFHEUZOHYZNWYR4","amount":"1000000000000"}})"); + auto key = DATA("d5b43d706ef0cb641081d45a2ec213b5d8281f439f2425d1af54e2afdaabf55b"); + + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeAlgorand)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeAlgorand)); + assertStringsEqual(result, "82a3736967c440baa00062adcdcb5875e4435cdc6885d26bfe5308ab17983c0fda790b7103051fcb111554e5badfc0ac7edf7e1223a434342a9eeed5cdb047690827325051560ba374786e8aa3616d74cf000000e8d4a51000a3666565ce00040358a26676ce001d9167a367656eac6d61696e6e65742d76312e30a26768c420c061c4d8fc1dbdded2d7604be4568e3f6d041987ac37bde4b620b5ab39248adfa26c76ce001d954fa46e6f7465c40568656c6c6fa3726376c42014560180e9c92da3171aa3c872356e30a8ea7f96c4a74bc1755a68929c94cb8fa3736e64c42061bf060efc02e2887dfffc8ed85268c8c091c013eedf315bc50794d02a8791ada474797065a3706179"); +} + +} // namespace TW::Algorand::tests diff --git a/tests/chains/Algorand/TWCoinTypeTests.cpp b/tests/chains/Algorand/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..26d33c3670f --- /dev/null +++ b/tests/chains/Algorand/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWAlgorandCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeAlgorand)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("CR7POXFTYDLC7TV3IXHA7AZKWABUJC52BACLHJQNXAKZJGRPQY3A")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeAlgorand, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("J4AEINCSSLDA7LNBNWM4ZXFCTLTOZT5LG3F5BLMFPJYGFWVCMU37EZI2AM")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeAlgorand, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeAlgorand)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeAlgorand)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeAlgorand), 6); + ASSERT_EQ(TWBlockchainAlgorand, TWCoinTypeBlockchain(TWCoinTypeAlgorand)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAlgorand)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAlgorand)); + assertStringsEqual(symbol, "ALGO"); + assertStringsEqual(txUrl, "https://algoexplorer.io/tx/CR7POXFTYDLC7TV3IXHA7AZKWABUJC52BACLHJQNXAKZJGRPQY3A"); + assertStringsEqual(accUrl, "https://algoexplorer.io/address/J4AEINCSSLDA7LNBNWM4ZXFCTLTOZT5LG3F5BLMFPJYGFWVCMU37EZI2AM"); + assertStringsEqual(id, "algorand"); + assertStringsEqual(name, "Algorand"); +} diff --git a/tests/chains/Aptos/AddressTests.cpp b/tests/chains/Aptos/AddressTests.cpp new file mode 100644 index 00000000000..92f156d077e --- /dev/null +++ b/tests/chains/Aptos/AddressTests.cpp @@ -0,0 +1,54 @@ +// Copyright © 2017-2022 Trust Wallet. +// Author: Clement Doumergue +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Aptos/Address.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include +#include + +namespace TW::Aptos::tests { + +TEST(AptosAddress, Valid) { + ASSERT_TRUE(Address::isValid("0x1")); + ASSERT_TRUE(Address::isValid(gAddressOne.string())); + ASSERT_TRUE(Address::isValid(gAddressZero.string())); + ASSERT_TRUE(Address::isValid("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); + ASSERT_TRUE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); + ASSERT_TRUE(Address::isValid("19aadeca9388e009d136245b9a67423f3eee242b03142849eb4f81a4a409e59c")); +} + +TEST(AptosAddress, Invalid) { + ASSERT_FALSE(Address::isValid("Seff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b")); // Invalid hex character + ASSERT_FALSE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175bb")); // Invalid length: too long + ASSERT_FALSE(Address::isValid("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175")); // Invalid length: too short +} + +TEST(AptosAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("088baa019f081d6eab8dff5c447f9ce2f83c1babf3d03686299eaf6a1e89156e")); + auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(address.string(), "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); +} + +TEST(AptosAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("ad0e293a56c9fc648d1872a00521d97e6b65724519a2676c2c47cb95d131cf5a"), TWPublicKeyTypeED25519); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "0xe9c4d0b6fe32a5cc8ebd1e9ad5b54a0276a57f2d081dcb5e30342319963626c3"); +} + +TEST(AptosAddress, FromString) { + auto address = Address("eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); + ASSERT_EQ(address.string(), "0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); +} + +TEST(AptosAddress, ShortString) { + ASSERT_EQ(gAddressOne.string(), "0x0000000000000000000000000000000000000000000000000000000000000001"); + ASSERT_EQ(gAddressOne.shortString(), "1"); +} + +} // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/MoveTypesTests.cpp b/tests/chains/Aptos/MoveTypesTests.cpp new file mode 100644 index 00000000000..45c7ec321e7 --- /dev/null +++ b/tests/chains/Aptos/MoveTypesTests.cpp @@ -0,0 +1,60 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include + +namespace TW::Aptos::tests { + +TEST(AptosMoveTypes, ModuleId) { + ModuleId module(gAddressOne, "coin"); + ASSERT_EQ(module.address(), gAddressOne); + ASSERT_EQ(module.name(), "coin"); + ASSERT_EQ(hex(module.accessVector()), "00000000000000000000000000000000000000000000000000000000000000000104636f696e"); + ASSERT_EQ(module.string(), "0x0000000000000000000000000000000000000000000000000000000000000001::coin"); + ASSERT_EQ(module.shortString(), "0x1::coin"); +} + +/* +TEST(AptosMoveTypes, StructTag) { + auto functorTest = [](T value, const std::string expectedHex) { + TypeTag t{.tags = value}; + StructTag st(gAddressOne, "abc", "abc", std::vector{{t}}); + ASSERT_EQ(st.moduleID().name(), "abc"); + ASSERT_EQ(st.moduleID().address(), gAddressOne); + ASSERT_EQ(hex(st.serialize()), expectedHex); + }; + functorTest(Bool{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630100"); + functorTest(U8{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630101"); + functorTest(U64{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630102"); + functorTest(U128{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630103"); + functorTest(TAddress{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630104"); + functorTest(TSigner{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630105"); + functorTest(Vector{.tags = std::vector{{TypeTag{.tags = U8{}}}}}, "0100000000000000000000000000000000000000000000000000000000000000010361626303616263010601"); + StructTag stInner(gAddressOne, "foo", "bar", std::vector{{U8{}}}); + functorTest(TStructTag{stInner}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630107000000000000000000000000000000000000000000000000000000000000000103666f6f036261720101"); +} +*/ +TEST(AptosMoveTypes, TypeTagDisplay) { + auto functorTest = [](const TypeTag &value, const std::string& expected) { + ASSERT_EQ(TypeTagToString(value), expected); + }; + functorTest(TypeTag{.tags = Bool{}}, "bool"); + functorTest(TypeTag{.tags = U8{}}, "u8"); + functorTest(TypeTag{.tags = U64{}}, "u64"); + functorTest(TypeTag{.tags = U128{}}, "u128"); + functorTest(TypeTag{.tags = TAddress{}}, "address"); + functorTest(TypeTag{.tags = TSigner{}}, "signer"); + TypeTag t{.tags = TypeTag::TypeTagVariant(Vector{.tags = {{U8{}}}})}; + functorTest(t, "vector"); + StructTag st(gAddressOne, "foo", "bar", std::vector{{U8{}}}); + TypeTag anotherT{.tags = TypeTag::TypeTagVariant(st)}; + functorTest(anotherT, "0x1::foo::bar"); + functorTest(gTransferTag, "0x1::aptos_coin::AptosCoin"); +} + +} // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/SignerTests.cpp b/tests/chains/Aptos/SignerTests.cpp new file mode 100644 index 00000000000..c47c05bc3e5 --- /dev/null +++ b/tests/chains/Aptos/SignerTests.cpp @@ -0,0 +1,379 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Aptos/Address.h" +#include "Aptos/Signer.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "TestUtilities.h" +#include + +#include + +namespace TW::Aptos::tests { + +TEST(AptosSigner, ClaimNftTxSign) { + // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x60b51e15140ec0b7650334e948fb447ce3cb13ae63492260461ebfa9d02e85c4?network=testnet + Proto::SigningInput input; + input.set_sender("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(19); + auto& tf = *input.mutable_nft_message()->mutable_claim_nft(); + tf.set_sender("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); + tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); + tf.set_collectionname("Topaz Troopers"); + tf.set_name("Topaz Trooper #20068"); + tf.set_property_version(0); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(2); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002"); + ASSERT_EQ(hex(result.authenticator().signature()), "ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3013000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c636c61696d5f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40ede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::claim_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "19", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xede1ffb5f8f663741c2ca9597af44af81c98f7a910261bb4125f758fd0c0ebbf5bacb34f1196ad45153177729eb6d478676b364ab747da17602713f65ca2dd0a", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, NftOfferTxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x514e473618bd3cb89a2b110b7c473db9a2e10532f98eb42d02d86fb31c00525d?network=testnet + Proto::SigningInput input; + input.set_sender("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); + input.set_sequence_number(1); + auto& tf = *input.mutable_nft_message()->mutable_offer_nft(); + tf.set_receiver("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); + tf.set_collectionname("Topaz Troopers"); + tf.set_name("Topaz Trooper #20068"); + tf.set_property_version(0); + tf.set_amount(1); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(2); + auto privateKey = PrivateKey(parse_hex("7bebb6d543d17f6fe4e685cfab239fa37896edd594ff859f1df32f244fb707e2")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada0000000002"); + ASSERT_EQ(hex(result.authenticator().signature()), "af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f"); + ASSERT_EQ(hex(result.encoded()), "783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee01000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572730c6f666665725f73637269707400062007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000080100000000000000fe4d3200000000006400000000000000c2276ada00000000020020d1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a411340af5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0", "1"], + "function": "0x3::token_transfers::offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "sequence_number": "1", + "signature": { + "public_key": "0xd1d99b67e37b483161a0fa369c46f34a3be4863c20e20fc7cdc669c0826a4113", + "signature": "0xaf5c7357a83c69e3f425beb23eaf232f8bb36dea3b7cad4a7ab8d735cee999c8ec5285005adf69dc85a6c34b042dd0308fe92b76dad5d6ac88c7b9259902c10f", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, CancelNftOfferTxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0x0b8c64e6847c368e4c6bd2cce0e9eab378971b0ef2e3bc40cbd292910a80201d?network=testnet + Proto::SigningInput input; + input.set_sender("0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(21); + auto& tf = *input.mutable_nft_message()->mutable_cancel_offer_nft(); + tf.set_receiver("0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee"); + tf.set_creator("0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac"); + tf.set_collectionname("Topaz Troopers"); + tf.set_name("Topaz Trooper #20068"); + tf.set_property_version(0); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(2); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada0000000002"); + ASSERT_EQ(hex(result.authenticator().signature()), "826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3015000000000000000200000000000000000000000000000000000000000000000000000000000000030f746f6b656e5f7472616e73666572731363616e63656c5f6f666665725f736372697074000520783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee209125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac0f0e546f70617a2054726f6f706572731514546f70617a2054726f6f70657220233230303638080000000000000000fe4d3200000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": [ + "0x783135e8b00430253a22ba041d860c373d7a1501ccf7ac2d1ad37a8ed2775aee", + "0x9125e4054d884fdc7296b66e12c0d63a7baa0d88c77e8e784987c0a967c670ac", + "Topaz Troopers", "Topaz Trooper #20068", "0"], + "function": "0x3::token_transfers::cancel_offer_script", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "21", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x826722d374e276f618123e77da3ac024c89a3f97db9e09e19aa8ed06c3cdfc57d4a21c7890137f9a7c0447cc303447ba10ca5b1908e889071e0a68f48c0f260a", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, TxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(99); + auto& tf = *input.mutable_transfer(); + tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + tf.set_amount(1000); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(33); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"); + ASSERT_EQ(hex(result.authenticator().signature()), "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::aptos_account::transfer", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "99", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, CreateAccount) { + // Successfully broadcasted: https://explorer.aptoslabs.com/txn/0x477141736de6b0936a6c3734e4d6fd018c7d21f1f28f99028ef0bc6881168602?network=Devnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(0); + auto& tf = *input.mutable_create_account(); + tf.set_auth_key("0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(33); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada0000000021"); + ASSERT_EQ(hex(result.authenticator().signature()), "fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3000000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e740e6372656174655f6163636f756e740001203aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30efe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40fcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x3aa1672641a4e17b3d913b4c0301e805755a80b12756fc729c5878f12344d30e"], + "function": "0x1::aptos_account::create_account", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "0", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xfcba3dfbec76721454ef414955f09f159660a13886b4edd8c579e3c779c29073afe7b25efa3fef9b21c2efb1cf16b4247fc0e5c8f63fdcd1c8d87f5d59f44501", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, BlindSign) { + // successfully broadcasted https://explorer.aptoslabs.com/txn/0xd95857a9e644528708778a3a0a6e13986751944fca30eaac98853c1655de0422?network=Devnet + // encoded submission with: + // curl --location --request POST 'https://fullnode.devnet.aptoslabs.com/v1/transactions/encode_submission' \ + //--header 'Content-Type: application/json' \ + //--header 'Cookie: AWSALB=0zI2zWypvEr0I3sGM6vnyHSxYO1D0aaMXfyA/2VwhA291aJJ80Yz67Fur50sXPFBI8dKKID4p8DShj1KkEXPY/NGAylpOj1EG2M2Qjuu1B38Q5C+dZW2CHT+IAZ5; AWSALBCORS=0zI2zWypvEr0I3sGM6vnyHSxYO1D0aaMXfyA/2VwhA291aJJ80Yz67Fur50sXPFBI8dKKID4p8DShj1KkEXPY/NGAylpOj1EG2M2Qjuu1B38Q5C+dZW2CHT+IAZ5' \ + //--data-raw '{ + // "expiration_timestamp_secs": "3664390082", + // "gas_unit_price": "100", + // "max_gas_amount": "3296766", + // "payload": { + // "function": "0x4633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b298837::pool::swap_y_to_x", + // "type_arguments": [ + // "0xdeae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e01::devnet_coins::DevnetUSDT", + // "0x1::aptos_coin::AptosCoin" + // ], + // "arguments": [ + // "100000000", + // "0" + // ], + // "type": "entry_function_payload" + // }, + // "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + // "sequence_number": "2" + //}' + Proto::SigningInput input; + input.set_any_encoded("0xb5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada0000000021"); + ASSERT_EQ(hex(result.authenticator().signature()), "9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); + ASSERT_EQ(hex(result.encoded()), "b5e97db07fa0bd0e5598aa3643a9bc6f6693bddc1a9fec9e674a461eaa00b19307968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f300200000000000000024633134869a61c41ad42eaca028d71c5b8b4109ffd69e1aa99c35a621b29883704706f6f6c0b737761705f795f746f5f780207deae46f81671e76f444e2ce5a299d9e1ea06a8fa26e81dfd49aa7fa5a5a60e010c6465766e65745f636f696e730a4465766e657455534454000700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e00020800e1f50500000000080000000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c409e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b"); + nlohmann::json expectedJson = R"( + { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x9e81026fdd43986f4d5588afdab875cd18b64dc15b3489fcc00ed46fc361915b27e23e0cefe6d23698ee76a562915fe85e99185dbc1dd29ba720f7fad144af0b", + "type": "ed25519_signature" + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, TokenRegisterTxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xe591252daed785641bfbbcf72a5d17864568cf32e04c0cc9129f3a13834d0e8e?network=testnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(23); + auto& tf = *input.mutable_register_token(); + tf.mutable_function()->set_account_address("0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379"); + tf.mutable_function()->set_module("move_coin"); + tf.mutable_function()->set_name("MoveCoin"); + input.set_max_gas_amount(2000000); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(2); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada0000000002"); + ASSERT_EQ(hex(result.authenticator().signature()), "e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3017000000000000000200000000000000000000000000000000000000000000000000000000000000010c6d616e616765645f636f696e0872656769737465720107e4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379096d6f76655f636f696e084d6f7665436f696e000080841e00000000006400000000000000c2276ada00000000020020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c40e230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "2000000", + "payload": { + "arguments": [], + "function": "0x1::managed_coin::register", + "type": "entry_function_payload", + "type_arguments": ["0xe4497a32bf4a9fd5601b27661aa0b933a923191bf403bd08669ab2468d43b379::move_coin::MoveCoin"] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "23", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0xe230b49f552fb85356dbec9df13f0dc56228eb7a9c29a8af3a99f4ae95b86c72bdcaa4ff1e9beb0bd81c298b967b9d97449856ec8bc672a08e2efef345c37100", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +TEST(AptosSigner, TokenTxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb5b383a5c7f99b2edb3bed9533f8169a89051b149d65876a82f4c0b9bf78a15b?network=Devnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(24); + auto& tf = *input.mutable_token_transfer(); + tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + tf.set_amount(100000); + tf.mutable_function()->set_account_address("0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9"); + tf.mutable_function()->set_module("coins"); + tf.mutable_function()->set_name("BTC"); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(32); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto result = Signer::sign(input); + ASSERT_EQ(hex(result.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada0000000020"); + ASSERT_EQ(hex(result.authenticator().signature()), "7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a"); + ASSERT_EQ(hex(result.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30180000000000000002000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010743417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b905636f696e730342544300022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008a086010000000000fe4d3200000000006400000000000000c2276ada00000000200020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c407643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","100000"], + "function": "0x1::coin::transfer", + "type": "entry_function_payload", + "type_arguments": ["0x43417434fd869edee76cca2a4d2301e528a1551b1d719b75c350c3c97d15b8b9::coins::BTC"] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "24", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x7643ec8aae6198bd13ca6ea2962265859cba5a228e7d181131f6c022700dd02a7a04dc0345ad99a0289e5ab80b130b3864e6404079980bc226f1a13aee7d280a", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(result.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +} // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/TWAnySignerTests.cpp b/tests/chains/Aptos/TWAnySignerTests.cpp new file mode 100644 index 00000000000..e97b0977d9f --- /dev/null +++ b/tests/chains/Aptos/TWAnySignerTests.cpp @@ -0,0 +1,63 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Aptos/Address.h" +#include "Aptos/Signer.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include +#include +#include "TestUtilities.h" + +#include + +namespace TW::Aptos::tests { + +TEST(TWAnySignerAptos, TxSign) { + // Successfully broadcasted https://explorer.aptoslabs.com/txn/0xb4d62afd3862116e060dd6ad9848ccb50c2bc177799819f1d29c059ae2042467?network=devnet + Proto::SigningInput input; + input.set_sender("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + input.set_sequence_number(99); + auto& tf = *input.mutable_transfer(); + tf.set_to("0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); + tf.set_amount(1000); + input.set_max_gas_amount(3296766); + input.set_gas_unit_price(100); + input.set_expiration_timestamp_secs(3664390082); + input.set_chain_id(33); + auto privateKey = PrivateKey(parse_hex("5d996aa76b3212142792d9130796cd2e11e3c445a93118c08414df4f66bc60ec")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeAptos); + ASSERT_EQ(hex(output.raw_txn()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada0000000021"); + ASSERT_EQ(hex(output.authenticator().signature()), "5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); + ASSERT_EQ(hex(output.encoded()), "07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3063000000000000000200000000000000000000000000000000000000000000000000000000000000010d6170746f735f6163636f756e74087472616e7366657200022007968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f3008e803000000000000fe4d3200000000006400000000000000c2276ada00000000210020ea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c405707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01"); + nlohmann::json expectedJson = R"( + { + "expiration_timestamp_secs": "3664390082", + "gas_unit_price": "100", + "max_gas_amount": "3296766", + "payload": { + "arguments": ["0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30","1000"], + "function": "0x1::aptos_account::transfer", + "type": "entry_function_payload", + "type_arguments": [] + }, + "sender": "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30", + "sequence_number": "99", + "signature": { + "public_key": "0xea526ba1710343d953461ff68641f1b7df5f23b9042ffa2d2a798d3adb3f3d6c", + "signature": "0x5707246db31e2335edc4316a7a656a11691d1d1647f6e864d1ab12f43428aaaf806cf02120d0b608cdd89c5c904af7b137432aacdd60cc53f9fad7bd33578e01", + "type": "ed25519_signature" + } + } + )"_json; + nlohmann::json parsedJson = nlohmann::json::parse(output.json()); + assertJSONEqual(expectedJson, parsedJson); +} + +} // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/TWAptosAddressTests.cpp b/tests/chains/Aptos/TWAptosAddressTests.cpp new file mode 100644 index 00000000000..e03407f5c56 --- /dev/null +++ b/tests/chains/Aptos/TWAptosAddressTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "TestUtilities.h" +#include +#include + +#include + +using namespace TW; + +namespace TW::Aptos::tests { + +TEST(TWAptosAddress, HDWallet) { + auto mnemonic = + "shoot island position soft burden budget tooth cruel issue economy destroy above"; + auto passphrase = ""; + + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(STRING(mnemonic).get(), STRING(passphrase).get())); + + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKey(wallet.get(), TWCoinTypeAptos, WRAPS(TWCoinTypeDerivationPath(TWCoinTypeAptos)).get())); + + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519(privateKey.get())); + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeAptos)); + auto addressStr = WRAPS(TWAnyAddressDescription(address.get())); + + assertStringsEqual(addressStr, "0x07968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30"); +} + +} // namespace TW::Aptos::tests diff --git a/tests/chains/Aptos/TWCoinTypeTests.cpp b/tests/chains/Aptos/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..af10a478945 --- /dev/null +++ b/tests/chains/Aptos/TWCoinTypeTests.cpp @@ -0,0 +1,35 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWAptosCoinType, TWCoinType) { + const auto coin = TWCoinTypeAptos; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("91424546")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "aptos"); + assertStringsEqual(name, "Aptos"); + assertStringsEqual(symbol, "APT"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 8); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainAptos); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(txUrl, "https://explorer.aptoslabs.com/txn/91424546"); + assertStringsEqual(accUrl, "https://explorer.aptoslabs.com/account/0x6af7d07b8a541913dfa87a9f99628faa255c70241ef9ebd9b82a7e715ee13108"); +} diff --git a/tests/chains/Aptos/TransactionPayloadTests.cpp b/tests/chains/Aptos/TransactionPayloadTests.cpp new file mode 100644 index 00000000000..29fd5e3712a --- /dev/null +++ b/tests/chains/Aptos/TransactionPayloadTests.cpp @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include + +namespace TW::Aptos::tests { + +TEST(AptosTransactionPayload, PayLoadBasis) { + ModuleId module(gAddressOne, "coin"); + std::uint64_t amount{1000}; + Address to("0xeeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b"); + BCS::Serializer serializer; + serializer << to; + std::vector args; + args.emplace_back(serializer.bytes); + serializer.clear(); + serializer << amount; + args.emplace_back(serializer.bytes); + TransactionPayload payload = EntryFunction(module, "transfer", {gTransferTag}, args); + ASSERT_EQ(std::get(payload).module().name(), "coin"); + ASSERT_EQ(std::get(payload).module().shortString(), "0x1::coin"); + serializer.clear(); + serializer << payload; + ASSERT_EQ(hex(serializer.bytes), "02000000000000000000000000000000000000000000000000000000000000000104636f696e087472616e73666572010700000000000000000000000000000000000000000000000000000000000000010a6170746f735f636f696e094170746f73436f696e000220eeff357ea5c1a4e7bc11b2b17ff2dc2dcca69750bfef1e1ebcaccf8c8018175b08e803000000000000"); +} + +} // namespace TW::Aptos::tests diff --git a/tests/chains/Arbitrum/TWCoinTypeTests.cpp b/tests/chains/Arbitrum/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..bab96f946ff --- /dev/null +++ b/tests/chains/Arbitrum/TWCoinTypeTests.cpp @@ -0,0 +1,30 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWArbitrumCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeArbitrum)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xa1e319be22c08155e5904aa211fb87df5f4ade48de79c6578b1cf3dfda9e498c")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeArbitrum, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xecf9ffa7f51e1194f89c25ad8c484f6bfd04e1ac")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeArbitrum, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeArbitrum)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeArbitrum)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeArbitrum), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeArbitrum)); + + assertStringsEqual(symbol, "ETH"); + assertStringsEqual(txUrl, "https://arbiscan.io/tx/0xa1e319be22c08155e5904aa211fb87df5f4ade48de79c6578b1cf3dfda9e498c"); + assertStringsEqual(accUrl, "https://arbiscan.io/address/0xecf9ffa7f51e1194f89c25ad8c484f6bfd04e1ac"); + assertStringsEqual(id, "arbitrum"); + assertStringsEqual(name, "Arbitrum"); +} diff --git a/tests/chains/Aurora/TWCoinTypeTests.cpp b/tests/chains/Aurora/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..b612ce508ce --- /dev/null +++ b/tests/chains/Aurora/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWAuroraCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeAurora)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x99deebdb70f8027037abb3d3d0f3c7523daee857d85e9056d2671593ff2f2f28")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeAurora, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x8707cdE20dd43E3dB1F74c28fcd509ef38B0bA51")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeAurora, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeAurora)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeAurora)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeAurora), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeAurora)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAurora)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAurora)); + assertStringsEqual(symbol, "ETH"); + assertStringsEqual(txUrl, "https://aurorascan.dev/tx/0x99deebdb70f8027037abb3d3d0f3c7523daee857d85e9056d2671593ff2f2f28"); + assertStringsEqual(accUrl, "https://aurorascan.dev/address/0x8707cdE20dd43E3dB1F74c28fcd509ef38B0bA51"); + assertStringsEqual(id, "aurora"); + assertStringsEqual(name, "Aurora"); +} diff --git a/tests/chains/Avalanche/TWCoinTypeTests.cpp b/tests/chains/Avalanche/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..8bf54001455 --- /dev/null +++ b/tests/chains/Avalanche/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWAvalancheCoinType, TWCoinTypeCChain) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeAvalancheCChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x9243890b844219accefd8798271052f5a056453ec18984a56e81c92921330d54")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeAvalancheCChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xa664325f36Ec33E66323fe2620AF3f2294b2Ef3A")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeAvalancheCChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeAvalancheCChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeAvalancheCChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeAvalancheCChain), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeAvalancheCChain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeAvalancheCChain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeAvalancheCChain)); + assertStringsEqual(symbol, "AVAX"); + assertStringsEqual(txUrl, "https://snowtrace.io/tx/0x9243890b844219accefd8798271052f5a056453ec18984a56e81c92921330d54"); + assertStringsEqual(accUrl, "https://snowtrace.io/address/0xa664325f36Ec33E66323fe2620AF3f2294b2Ef3A"); + assertStringsEqual(id, "avalanchec"); + assertStringsEqual(name, "Avalanche C-Chain"); +} diff --git a/tests/chains/BandChain/TWCoinTypeTests.cpp b/tests/chains/BandChain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..f6ad4c690c0 --- /dev/null +++ b/tests/chains/BandChain/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWBandChainCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBandChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("473264551D3063A9EC64EC251C61BE92DDDFCF6CC46D026D1E574D83D5447173")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBandChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("band12nmsm9khdsv0tywge43q3zwj8kkj3hvup9rltp")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBandChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBandChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBandChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBandChain), 6); + ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeBandChain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeBandChain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBandChain)); + assertStringsEqual(symbol, "BAND"); + assertStringsEqual(txUrl, "https://scan-wenchang-testnet2.bandchain.org//tx/473264551D3063A9EC64EC251C61BE92DDDFCF6CC46D026D1E574D83D5447173"); + assertStringsEqual(accUrl, "https://scan-wenchang-testnet2.bandchain.org//account/band12nmsm9khdsv0tywge43q3zwj8kkj3hvup9rltp"); + assertStringsEqual(id, "band"); + assertStringsEqual(name, "BandChain"); +} diff --git a/tests/Binance/SignerTests.cpp b/tests/chains/Binance/SignerTests.cpp similarity index 100% rename from tests/Binance/SignerTests.cpp rename to tests/chains/Binance/SignerTests.cpp diff --git a/tests/chains/Binance/TWAnySignerTests.cpp b/tests/chains/Binance/TWAnySignerTests.cpp new file mode 100644 index 00000000000..84193da844a --- /dev/null +++ b/tests/chains/Binance/TWAnySignerTests.cpp @@ -0,0 +1,93 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Binance/Address.h" +#include "proto/Binance.pb.h" +#include +#include "Coin.h" + +#include "TestUtilities.h" +#include +#include + +namespace TW::Binance { + +Proto::SigningOutput SignTest() { + auto input = Proto::SigningInput(); + input.set_chain_id("Binance-Chain-Tigris"); + input.set_account_number(0); + input.set_sequence(0); + input.set_source(0); + + auto key = parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"); + input.set_private_key(key.data(), key.size()); + + auto& order = *input.mutable_send_order(); + + Address fromAddress; + EXPECT_TRUE(Address::decode("bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", fromAddress)); + EXPECT_EQ(hex(fromAddress.getKeyHash()), "40c2979694bbc961023d1d27be6fc4d21a9febe6"); + auto fromKeyhash = fromAddress.getKeyHash(); + Address toAddress; + EXPECT_TRUE(Address::decode("bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5", toAddress)); + EXPECT_EQ(hex(toAddress.getKeyHash()), "bffe47abfaede50419c577f1074fee6dd1535cd1"); + auto toKeyhash = toAddress.getKeyHash(); + + { + auto inputOrder = order.add_inputs(); + inputOrder->set_address(fromKeyhash.data(), fromKeyhash.size()); + auto inputCoin = inputOrder->add_coins(); + inputCoin->set_denom("BNB"); + inputCoin->set_amount(1); + } + + { + auto output = order.add_outputs(); + output->set_address(toKeyhash.data(), toKeyhash.size()); + auto outputCoin = output->add_coins(); + outputCoin->set_denom("BNB"); + outputCoin->set_amount(1); + } + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeBinance); + return output; +} + +TEST(TWAnySignerBinance, Sign) { + Proto::SigningOutput output = SignTest(); + ASSERT_EQ(hex(output.encoded()), "b801f0625dee0a462a2c87fa0a1f0a1440c2979694bbc961023d1d27be6fc4d21a9febe612070a03424e421001121f0a14bffe47abfaede50419c577f1074fee6dd1535cd112070a03424e421001126a0a26eb5ae98721026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e50212409073e581e1ea4fdf11242fe30a732f96d20799c638354bcf7a242161ac015b9321fbbed93e85b0ef9b5de58fba74dff54ecb1e379ef26e1023be8996003f4899"); +} + +TEST(TWAnySignerBinance, SignJSON) { + auto json = STRING(R"({"chainId":"Binance-Chain-Tigris","accountNumber":"13186","source":"2","memo":"Testing","sendOrder":{"inputs":[{"address":"EuZU7e+eUIuDNzaph9Bp2lqJrts=","coins":[{"denom":"BNB","amount":"1345227"}]}],"outputs":[{"address":"M7vzB7mBRvE9IGk8+UbC13pMryg=","coins":[{"denom":"BNB","amount":"1345227"}]}]}})"); + auto key = DATA("f947b3554a1c2fa70e1caa9de53fbda353348d1e856c593848f3a29737d31f11"); + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeBinance)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeBinance)); + assertStringsEqual(result, "ca01f0625dee0a4a2a2c87fa0a210a1412e654edef9e508b833736a987d069da5a89aedb12090a03424e4210cb8d5212210a1433bbf307b98146f13d20693cf946c2d77a4caf2812090a03424e4210cb8d52126d0a26eb5ae9872102e58176f271a9796b4288908be85094a2ac978e25535fd59a37b58626e3a84d9e1240015b4beb3d6ef366a7a92fd283f66b8f0d8cdb6b152a9189146b27f84f507f047e248517cf691a36ebc2b7f3b7f64e27585ce1c40f1928d119c31af428efcf3e1882671a0754657374696e672002"); +} + +TEST(TWAnySignerBinance, MultithreadedSigning) { + auto f = [](int n) { + for (int i = 0; i < n; i++) { + Proto::SigningOutput output = SignTest(); + ASSERT_EQ(hex(output.encoded()), "b801f0625dee0a462a2c87fa0a1f0a1440c2979694bbc961023d1d27be6fc4d21a9febe612070a03424e421001121f0a14bffe47abfaede50419c577f1074fee6dd1535cd112070a03424e421001126a0a26eb5ae98721026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e50212409073e581e1ea4fdf11242fe30a732f96d20799c638354bcf7a242161ac015b9321fbbed93e85b0ef9b5de58fba74dff54ecb1e379ef26e1023be8996003f4899"); + } + }; + + // Ensure multiple threads cause no asserts + std::thread th1(f, 1000); + std::thread th2(f, 1000); + std::thread th3(f, 1000); + + th1.join(); + th2.join(); + th3.join(); +} + +} // namespace TW::Binance diff --git a/tests/chains/Binance/TWCoinTypeTests.cpp b/tests/chains/Binance/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..2059c040c6b --- /dev/null +++ b/tests/chains/Binance/TWCoinTypeTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWBinanceCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBinance)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("A93625C9F9ABEA1A8E31585B30BBB16C34FAE0D172EB5B6B2F834AF077BF06BB")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBinance, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("bnb1u7jm0cll5h3224y0tapwn6gf6pr49ytewx4gsz")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBinance, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBinance)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBinance)); + const auto chainId = WRAPS(TWCoinTypeChainId(TWCoinTypeBinance)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBinance), 8); + ASSERT_EQ(TWBlockchainBinance, TWCoinTypeBlockchain(TWCoinTypeBinance)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeBinance)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBinance)); + assertStringsEqual(symbol, "BNB"); + assertStringsEqual(txUrl, "https://explorer.binance.org/tx/A93625C9F9ABEA1A8E31585B30BBB16C34FAE0D172EB5B6B2F834AF077BF06BB"); + assertStringsEqual(accUrl, "https://explorer.binance.org/address/bnb1u7jm0cll5h3224y0tapwn6gf6pr49ytewx4gsz"); + assertStringsEqual(id, "binance"); + assertStringsEqual(name, "BNB Beacon Chain"); + assertStringsEqual(chainId, "Binance-Chain-Tigris"); +} diff --git a/tests/chains/BinanceSmartChain/SignerTests.cpp b/tests/chains/BinanceSmartChain/SignerTests.cpp new file mode 100644 index 00000000000..6303410de22 --- /dev/null +++ b/tests/chains/BinanceSmartChain/SignerTests.cpp @@ -0,0 +1,79 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "Ethereum/Signer.h" +#include "Ethereum/Transaction.h" +#include "Ethereum/Address.h" +#include "Ethereum/ABI.h" +#include "proto/Ethereum.pb.h" +#include "HexCoding.h" +#include "uint256.h" +#include "TestUtilities.h" + +#include + +namespace TW::Binance { + +TEST(BinanceSmartChain, SignNativeTransfer) { + // https://explorer.binance.org/smart-testnet/tx/0x6da28164f7b3bc255d749c3ae562e2a742be54c12bf1858b014cc2fe5700684e + + auto toAddress = parse_hex("0x31BE00EB1fc8e14A696DBC72f746ec3e95f49683"); + auto transaction = Ethereum::TransactionNonTyped::buildNativeTransfer( + /* nonce: */ 0, + /* gasPrice: */ 20000000000, + /* gasLimit: */ 21000, + /* to: */ toAddress, + /* amount: */ 10000000000000000 // 0.01 + ); + + // addr: 0xB9F5771C27664bF2282D98E09D7F50cEc7cB01a7 mnemonic: isolate dismiss ... cruel note + auto privateKey = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); + uint256_t chainID = 97; + auto signature = Ethereum::Signer::sign(privateKey, chainID, transaction); + + auto encoded = transaction->encoded(signature, chainID); + ASSERT_EQ(hex(encoded), "f86c808504a817c8008252089431be00eb1fc8e14a696dbc72f746ec3e95f49683872386f26fc100008081e5a057806b486844c5d0b7b5ce34b289f4e8776aa1fe24a3311cef5053995c51050ca07697aa0695de27da817625df0e7e4c64b0ab22d9df30aec92299a7b380be8db7"); +} + +TEST(BinanceSmartChain, SignTokenTransfer) { + auto toAddress = parse_hex("0x31BE00EB1fc8e14A696DBC72f746ec3e95f49683"); + auto func = Ethereum::ABI::Function("transfer", std::vector>{ + std::make_shared(toAddress), + std::make_shared(uint256_t(10000000000000000)) + }); + Data payloadFunction; + func.encode(payloadFunction); + EXPECT_EQ(hex(payloadFunction), "a9059cbb00000000000000000000000031be00eb1fc8e14a696dbc72f746ec3e95f49683000000000000000000000000000000000000000000000000002386f26fc10000"); + + auto input = Ethereum::Proto::SigningInput(); + auto chainId = store(uint256_t(97)); + auto nonce = store(uint256_t(30)); + auto gasPrice = store(uint256_t(20000000000)); + auto gasLimit = store(uint256_t(1000000)); + auto tokenContractAddress = "0xed24fc36d5ee211ea25a80239fb8c4cfd80f12ee"; + auto dummyAmount = store(uint256_t(0)); + // addr: 0xB9F5771C27664bF2282D98E09D7F50cEc7cB01a7 mnemonic: isolate dismiss ... cruel note + auto privateKey = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_to_address(tokenContractAddress); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); + transfer.set_data(payloadFunction.data(), payloadFunction.size()); + + const std::string expected = "f8ab1e8504a817c800830f424094ed24fc36d5ee211ea25a80239fb8c4cfd80f12ee80b844a9059cbb00000000000000000000000031be00eb1fc8e14a696dbc72f746ec3e95f49683000000000000000000000000000000000000000000000000002386f26fc1000081e6a0aa9d5e9a947e96f728fe5d3e6467000cd31a693c00270c33ec64b4abddc29516a00bf1d5646139b2bcca1ad64e6e79f45b7d1255de603b5a3765cbd9544ae148d0"; + + Ethereum::Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSmartChain); + + EXPECT_EQ(hex(output.encoded()), expected); +} + +} // namespace TW::Binance diff --git a/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp b/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..f7769d5c198 --- /dev/null +++ b/tests/chains/BinanceSmartChain/TWAnyAddressTests.cpp @@ -0,0 +1,29 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(TWBinanceSmartChain, Address) { + + auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA("727f677b390c151caf9c206fd77f77918f56904b5504243db9b21e51182c4c06").get())); + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), false)); + auto string = "0xf3d468DBb386aaD46E92FF222adDdf872C8CC064"; + + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeSmartChain)); + auto expected = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(STRING(string).get(), TWCoinTypeSmartChain)); + + auto addressString = WRAPS(TWAnyAddressDescription(address.get())); + auto expectedString = WRAPS(TWAnyAddressDescription(expected.get())); + + assertStringsEqual(addressString, string); + assertStringsEqual(expectedString, string); +} diff --git a/tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp b/tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..f48a6b1e56e --- /dev/null +++ b/tests/chains/BinanceSmartChain/TWCoinTypeTests.cpp @@ -0,0 +1,44 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWBinanceSmartChainCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeSmartChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb9ae2e808fe8e57171f303ad8f6e3fd17d949b0bfc7b4db6e8e30a71cc517d7e")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeSmartChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x35552c16704d214347f29Fa77f77DA6d75d7C752")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeSmartChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeSmartChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeSmartChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeSmartChain), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeSmartChain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeSmartChain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeSmartChain)); + ASSERT_EQ(20000714, TWCoinTypeSmartChain); + assertStringsEqual(symbol, "BNB"); + assertStringsEqual(txUrl, "https://bscscan.com/tx/0xb9ae2e808fe8e57171f303ad8f6e3fd17d949b0bfc7b4db6e8e30a71cc517d7e"); + assertStringsEqual(accUrl, "https://bscscan.com/address/0x35552c16704d214347f29Fa77f77DA6d75d7C752"); + assertStringsEqual(id, "smartchain"); + assertStringsEqual(name, "BNB Smart Chain"); +} + +TEST(TWBinanceSmartChainLegacyCoinType, TWCoinType) { + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeSmartChainLegacy)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeSmartChainLegacy)); + + ASSERT_EQ(10000714, TWCoinTypeSmartChainLegacy); + assertStringsEqual(id, "bsc"); + assertStringsEqual(name, "Smart Chain Legacy"); +} diff --git a/tests/Bitcoin/BitcoinAddressTests.cpp b/tests/chains/Bitcoin/BitcoinAddressTests.cpp similarity index 91% rename from tests/Bitcoin/BitcoinAddressTests.cpp rename to tests/chains/Bitcoin/BitcoinAddressTests.cpp index 6b8dcba3fc5..dd769f6a29e 100644 --- a/tests/Bitcoin/BitcoinAddressTests.cpp +++ b/tests/chains/Bitcoin/BitcoinAddressTests.cpp @@ -5,16 +5,17 @@ // file LICENSE at the root of the source code distribution tree. #include "Bitcoin/Address.h" -#include -#include "PublicKey.h" #include "Bitcoin/Script.h" #include "HexCoding.h" +#include "PublicKey.h" +#include -#include #include +#include using namespace TW; -using namespace TW::Bitcoin; + +namespace TW::Bitcoin::tests { const char* TestPubKey1 = "039d645d2ce630c2a9a6dbe0cbd0a8fcb7b70241cb8a48424f25593290af2494b9"; const char* TestP2phkAddr1 = "12dNaXQtN5Asn2YFwT1cvciCrJa525fAe4"; @@ -50,20 +51,21 @@ TEST(BitcoinAddress, P2SH_CreateFromString) { TEST(BitcoinAddress, P2WPKH_Nested_P2SH) { // P2SH address cannot be created directly from pubkey, script is built const auto publicKey = PublicKey(parse_hex(TestPubKey1), TWPublicKeyTypeSECP256k1); - + const auto pubKeyHash = publicKey.hash({}); EXPECT_EQ(hex(pubKeyHash), "11d91ce1cc681f95583da3f4a6841c174be950c7"); - - const auto script = Script::buildPayToWitnessProgram(pubKeyHash); - EXPECT_EQ(hex(script.bytes), "0014" "11d91ce1cc681f95583da3f4a6841c174be950c7"); - + + const auto script = Script::buildPayToV0WitnessProgram(pubKeyHash); + EXPECT_EQ(hex(script.bytes), "0014" + "11d91ce1cc681f95583da3f4a6841c174be950c7"); + const auto scriptHash = Hash::sha256ripemd(script.bytes.data(), script.bytes.size()); EXPECT_EQ(hex(scriptHash), "ee1e69460b59027d9df0a79ca2c92aa382a25fb7"); - + Data addressData = {TWCoinTypeP2shPrefix(TWCoinTypeBitcoin)}; TW::append(addressData, scriptHash); EXPECT_EQ(hex(addressData), TestP2shData1); - + const auto address = Address(addressData); EXPECT_EQ(address.string(), TestP2shAddr1); EXPECT_EQ(hex(address.bytes), TestP2shData1); @@ -74,3 +76,5 @@ TEST(BitcoinAddress, P2SH_CreateFromData) { EXPECT_EQ(address.string(), TestP2shAddr1); EXPECT_EQ(hex(address.bytes), TestP2shData1); } + +} // namespace TW::Bitcoin::tests diff --git a/tests/Bitcoin/BitcoinScriptTests.cpp b/tests/chains/Bitcoin/BitcoinScriptTests.cpp similarity index 92% rename from tests/Bitcoin/BitcoinScriptTests.cpp rename to tests/chains/Bitcoin/BitcoinScriptTests.cpp index 56109ac2f47..66c50b9701e 100644 --- a/tests/Bitcoin/BitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/BitcoinScriptTests.cpp @@ -6,13 +6,13 @@ #include "Bitcoin/Script.h" #include "Bitcoin/SignatureBuilder.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin::tests { const Script PayToScriptHash(parse_hex("a914" "4733f37cf4db86fbc2efed2500b4f4e49f312023" "87")); const Script PayToWitnessScriptHash(parse_hex("0020" "ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3db")); @@ -61,7 +61,7 @@ TEST(BitcoinScript, PayToScriptHash) { EXPECT_EQ(hex(script.bytes), hex(PayToScriptHash.bytes)); EXPECT_EQ(PayToScriptHash.isPayToScriptHash(), true); - EXPECT_EQ(PayToScriptHash.bytes.size(), 23); + EXPECT_EQ(PayToScriptHash.bytes.size(), 23ul); EXPECT_EQ(PayToWitnessScriptHash.isPayToScriptHash(), false); EXPECT_EQ(PayToWitnessPublicKeyHash.isPayToScriptHash(), false); @@ -85,7 +85,7 @@ TEST(BitcoinScript, PayToWitnessScriptHash) { EXPECT_EQ(hex(script.bytes), hex(PayToWitnessScriptHash.bytes)); EXPECT_EQ(PayToWitnessScriptHash.isPayToWitnessScriptHash(), true); - EXPECT_EQ(PayToWitnessScriptHash.bytes.size(), 34); + EXPECT_EQ(PayToWitnessScriptHash.bytes.size(), 34ul); EXPECT_EQ(PayToScriptHash.isPayToWitnessScriptHash(), false); EXPECT_EQ(PayToWitnessPublicKeyHash.isPayToWitnessScriptHash(), false); @@ -109,7 +109,7 @@ TEST(BitcoinScript, PayToWitnessPublicKeyHash) { EXPECT_EQ(hex(script.bytes), hex(PayToWitnessPublicKeyHash.bytes)); EXPECT_EQ(PayToWitnessPublicKeyHash.isPayToWitnessPublicKeyHash(), true); - EXPECT_EQ(PayToWitnessPublicKeyHash.bytes.size(), 22); + EXPECT_EQ(PayToWitnessPublicKeyHash.bytes.size(), 22ul); EXPECT_EQ(PayToScriptHash.isPayToWitnessPublicKeyHash(), false); EXPECT_EQ(PayToWitnessScriptHash.isPayToWitnessPublicKeyHash(), false); @@ -160,21 +160,21 @@ TEST(BitcoinScript, GetScriptOp) { { size_t index = 0; uint8_t opcode; Data operand; EXPECT_EQ(Script(parse_hex("4f")).getScriptOp(index, opcode, operand), true); - EXPECT_EQ(index, 1); + EXPECT_EQ(index, 1ul); EXPECT_EQ(opcode, 0x4f); EXPECT_EQ(hex(operand), ""); } { size_t index = 0; uint8_t opcode; Data operand; EXPECT_EQ(Script(parse_hex("05" "0102030405")).getScriptOp(index, opcode, operand), true); - EXPECT_EQ(index, 6); + EXPECT_EQ(index, 6ul); EXPECT_EQ(opcode, 0x05); EXPECT_EQ(hex(operand), "0102030405"); } { // OP_PUSHDATA1 size_t index = 0; uint8_t opcode; Data operand; EXPECT_EQ(Script(parse_hex("4c" "05" "0102030405")).getScriptOp(index, opcode, operand), true); - EXPECT_EQ(index, 7); + EXPECT_EQ(index, 7ul); EXPECT_EQ(opcode, 0x4c); EXPECT_EQ(hex(operand), "0102030405"); } @@ -189,7 +189,7 @@ TEST(BitcoinScript, GetScriptOp) { { // OP_PUSHDATA2 size_t index = 0; uint8_t opcode; Data operand; EXPECT_EQ(Script(parse_hex("4d" "0500" "0102030405")).getScriptOp(index, opcode, operand), true); - EXPECT_EQ(index, 8); + EXPECT_EQ(index, 8ul); EXPECT_EQ(opcode, 0x4d); EXPECT_EQ(hex(operand), "0102030405"); } @@ -204,7 +204,7 @@ TEST(BitcoinScript, GetScriptOp) { { // OP_PUSHDATA4 size_t index = 0; uint8_t opcode; Data operand; EXPECT_EQ(Script(parse_hex("4e" "05000000" "0102030405")).getScriptOp(index, opcode, operand), true); - EXPECT_EQ(index, 10); + EXPECT_EQ(index, 10ul); EXPECT_EQ(opcode, 0x4e); EXPECT_EQ(hex(operand), "0102030405"); } @@ -239,7 +239,7 @@ TEST(BitcoinScript, MatchMultiSig) { // valid one key EXPECT_EQ(Script(parse_hex("51" "21" "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" "51" "ae")).matchMultisig(keys, required), true); EXPECT_EQ(required, 1); - ASSERT_EQ(keys.size(), 1); + ASSERT_EQ(keys.size(), 1ul); EXPECT_EQ(hex(keys[0]), "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432"); EXPECT_EQ(Script(parse_hex("51" "21" "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" "21" "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1" "51" "ae")).matchMultisig(keys, required), false); @@ -248,7 +248,7 @@ TEST(BitcoinScript, MatchMultiSig) { // valid two keys EXPECT_EQ(Script(parse_hex("52" "21" "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" "21" "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1" "52" "ae")).matchMultisig(keys, required), true); EXPECT_EQ(required, 2); - ASSERT_EQ(keys.size(), 2); + ASSERT_EQ(keys.size(), 2ul); EXPECT_EQ(hex(keys[0]), "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432"); EXPECT_EQ(hex(keys[1]), "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); @@ -261,7 +261,7 @@ TEST(BitcoinScript, MatchMultiSig) { // valid one key, OP_PUSHDATA1 EXPECT_EQ(Script(parse_hex("514c" "21" "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" "51" "ae")).matchMultisig(keys, required), true); EXPECT_EQ(required, 1); - ASSERT_EQ(keys.size(), 1); + ASSERT_EQ(keys.size(), 1ul); EXPECT_EQ(hex(keys[0]), "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432"); // OP_PUSHDATA2 @@ -273,7 +273,7 @@ TEST(BitcoinScript, MatchMultiSig) { // valid one key, OP_PUSHDATA2 EXPECT_EQ(Script(parse_hex("514d" "2100" "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" "51" "ae")).matchMultisig(keys, required), true); EXPECT_EQ(required, 1); - ASSERT_EQ(keys.size(), 1); + ASSERT_EQ(keys.size(), 1ul); EXPECT_EQ(hex(keys[0]), "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432"); // OP_PUSHDATA4 @@ -286,7 +286,7 @@ TEST(BitcoinScript, MatchMultiSig) { // valid one key, OP_PUSHDATA2 EXPECT_EQ(Script(parse_hex("514e" "21000000" "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" "51" "ae")).matchMultisig(keys, required), true); EXPECT_EQ(required, 1); - ASSERT_EQ(keys.size(), 1); + ASSERT_EQ(keys.size(), 1ul); EXPECT_EQ(hex(keys[0]), "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432"); // valid three keys, mixed @@ -296,12 +296,31 @@ TEST(BitcoinScript, MatchMultiSig) { "4e" "21000000" "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" "53" "ae")).matchMultisig(keys, required), true); EXPECT_EQ(required, 3); - ASSERT_EQ(keys.size(), 3); + ASSERT_EQ(keys.size(), 3ul); EXPECT_EQ(hex(keys[0]), "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432"); EXPECT_EQ(hex(keys[1]), "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); EXPECT_EQ(hex(keys[2]), "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432"); } +TEST(BitcoinScript, OpReturn) { + { + Data data = parse_hex("00010203"); + Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(hex(script.bytes), "6a0400010203"); + } + { + Data data = parse_hex("535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a"); + Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(hex(script.bytes), "6a3b535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a"); + } + { + // too long, truncated + Data data = Data(70); + Script script = Script::buildOpReturnScript(data); + EXPECT_EQ(hex(script.bytes), "6a4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + } +} + TEST(BitcoinTransactionSigner, PushAllEmpty) { { std::vector input = {}; @@ -346,4 +365,5 @@ TEST(BitcoinTransactionSigner, PushAllEmpty) { Data res = SignatureBuilder::pushAll(input); EXPECT_EQ(hex(res), hex(expected)); } -} \ No newline at end of file +} +} // namespace TW::Bitcoin::tests diff --git a/tests/Bitcoin/FeeCalculatorTests.cpp b/tests/chains/Bitcoin/FeeCalculatorTests.cpp similarity index 89% rename from tests/Bitcoin/FeeCalculatorTests.cpp rename to tests/chains/Bitcoin/FeeCalculatorTests.cpp index 755ea13efa0..93ea4013e77 100644 --- a/tests/Bitcoin/FeeCalculatorTests.cpp +++ b/tests/chains/Bitcoin/FeeCalculatorTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -8,9 +8,7 @@ #include -using namespace TW; -using namespace TW::Bitcoin; - +namespace TW::Bitcoin { TEST(BitcoinFeeCalculator, ConstantFeeCalculator) { const auto feeCalculator = ConstantFeeCalculator(33); @@ -32,7 +30,7 @@ TEST(BitcoinFeeCalculator, LinearFeeCalculator) { } TEST(BitcoinFeeCalculator, BitcoinCalculate) { - FeeCalculator& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); + const FeeCalculator& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); EXPECT_EQ(feeCalculator.calculate(1, 2, 1), 174); EXPECT_EQ(feeCalculator.calculate(1, 1, 1), 143); EXPECT_EQ(feeCalculator.calculate(0, 2, 1), 72); @@ -42,7 +40,7 @@ TEST(BitcoinFeeCalculator, BitcoinCalculate) { } TEST(BitcoinFeeCalculator, SegwitCalculate) { - FeeCalculator& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); + const FeeCalculator& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); EXPECT_EQ(feeCalculator.calculate(1, 2, 1), 174); EXPECT_EQ(feeCalculator.calculate(1, 1, 1), 143); EXPECT_EQ(feeCalculator.calculate(0, 2, 1), 72); @@ -69,9 +67,11 @@ TEST(BitcoinFeeCalculator, DefaultCalculateSingleInput) { } TEST(BitcoinFeeCalculator, DecredCalculate) { - FeeCalculator& feeCalculator = getFeeCalculator(TWCoinTypeDecred); + const FeeCalculator& feeCalculator = getFeeCalculator(TWCoinTypeDecred); EXPECT_EQ(feeCalculator.calculate(1, 2, 1), 254); EXPECT_EQ(feeCalculator.calculate(0, 0, 1), 12); EXPECT_EQ(feeCalculator.calculate(1, 2, 10), 2540); EXPECT_EQ(feeCalculator.calculateSingleInput(1), 166); } + +} // namespace TW::Bitcoin diff --git a/tests/Bitcoin/InputSelectorTests.cpp b/tests/chains/Bitcoin/InputSelectorTests.cpp similarity index 96% rename from tests/Bitcoin/InputSelectorTests.cpp rename to tests/chains/Bitcoin/InputSelectorTests.cpp index ad773a72465..1f66fedeec5 100644 --- a/tests/Bitcoin/InputSelectorTests.cpp +++ b/tests/chains/Bitcoin/InputSelectorTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,17 +7,11 @@ #include #include "TxComparisonHelper.h" -#include "Bitcoin/OutPoint.h" -#include "Bitcoin/Script.h" #include "Bitcoin/InputSelector.h" -#include "proto/Bitcoin.pb.h" #include -#include - -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { TEST(BitcoinInputSelector, SelectUnspents1) { auto utxos = buildTestUTXOs({4000, 2000, 6000, 1000, 11000, 12000}); @@ -193,7 +187,7 @@ TEST(BitcoinInputSelector, SelectThreeNoDust) { // 100'000 would fit with dust; instead two UTXOs are selected not to leave dust EXPECT_TRUE(verifySelectedUTXOs(selected, {75'000, 100'000})); - + EXPECT_EQ(feeCalculator.calculate(1, 2, 1), 174); const auto dustLimit = 102; @@ -432,7 +426,7 @@ TEST(BitcoinInputSelector, ManyUtxos_900) { valueSum += val; } const uint64_t requestedAmount = valueSum / 8; - EXPECT_EQ(requestedAmount, 5'068'125); + EXPECT_EQ(requestedAmount, 5'068'125ul); auto utxos = buildTestUTXOs(values); auto selector = InputSelector(utxos); @@ -446,8 +440,8 @@ TEST(BitcoinInputSelector, ManyUtxos_900) { subset.push_back(val); subsetSum += val; } - EXPECT_EQ(subset.size(), 59); - EXPECT_EQ(subsetSum, 5'138'900); + EXPECT_EQ(subset.size(), 59ul); + EXPECT_EQ(subsetSum, 5'138'900ul); EXPECT_TRUE(verifySelectedUTXOs(selected, subset)); } @@ -462,7 +456,7 @@ TEST(BitcoinInputSelector, ManyUtxos_5000_simple) { valueSum += val; } const uint64_t requestedAmount = valueSum / 20; - EXPECT_EQ(requestedAmount, 62'512'500); + EXPECT_EQ(requestedAmount, 62'512'500ul); auto utxos = buildTestUTXOs(values); auto selector = InputSelector(utxos); @@ -471,13 +465,13 @@ TEST(BitcoinInputSelector, ManyUtxos_5000_simple) { // expected result: 1205 utxos, with the smaller amounts (except the very small dust ones) std::vector subset; uint64_t subsetSum = 0; - for (int i = 10; i < 1205+10; ++i) { + for (int i = 10; i < 1205 + 10; ++i) { const uint64_t val = (i + 1) * 100; subset.push_back(val); subsetSum += val; } - EXPECT_EQ(subset.size(), 1205); - EXPECT_EQ(subsetSum, 73'866'500); + EXPECT_EQ(subset.size(), 1205ul); + EXPECT_EQ(subsetSum, 73'866'500ul); EXPECT_TRUE(verifySelectedUTXOs(selected, subset)); } @@ -485,11 +479,9 @@ TEST(BitcoinInputSelector, ManyUtxos_MaxAmount_5000) { const auto n = 5000; const auto byteFee = 10; std::vector values; - uint64_t valueSum = 0; for (int i = 0; i < n; ++i) { const uint64_t val = (i + 1) * 100; values.push_back(val); - valueSum += val; } auto utxos = buildTestUTXOs(values); @@ -499,12 +491,14 @@ TEST(BitcoinInputSelector, ManyUtxos_MaxAmount_5000) { // expected result: 4990 utxos (none of which is dust) std::vector subset; uint64_t subsetSum = 0; - for (int i = 10; i < 4990+10; ++i) { + for (int i = 10; i < 4990 + 10; ++i) { const uint64_t val = (i + 1) * 100; subset.push_back(val); subsetSum += val; } - EXPECT_EQ(subset.size(), 4990); - EXPECT_EQ(subsetSum, 1'250'244'500); + EXPECT_EQ(subset.size(), 4990ul); + EXPECT_EQ(subsetSum, 1'250'244'500ul); EXPECT_TRUE(verifySelectedUTXOs(selected, subset)); } + +} // namespace TW::Bitcoin diff --git a/tests/chains/Bitcoin/MessageSignerTests.cpp b/tests/chains/Bitcoin/MessageSignerTests.cpp new file mode 100644 index 00000000000..df617721300 --- /dev/null +++ b/tests/chains/Bitcoin/MessageSignerTests.cpp @@ -0,0 +1,167 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Bitcoin/MessageSigner.h" +#include +#include "Bitcoin/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "Base64.h" +#include "Coin.h" +#include "Data.h" +#include "TestUtilities.h" + +#include +#include + +#include +#include + +namespace TW::Bitcoin::MessageSignerTests { + +const auto gPrivateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + +TEST(BitcoinMessageSigner, VerifyMessage) { + EXPECT_TRUE(MessageSigner::verifyMessage( + "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", + "test signature", + "H+3L5IbSVcejp4S2VwLXCxLEMQAWDvKbE8lQyq0ocdvyM1aoEudkzN/S/qLI3vnNOFY6V13BXWSFrPr3OjGa5Dk=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN", + "This is an example of a signed message.", + "G39Qf0XrZHICWbz3r5gOkcgTRw3vM4leGjiR3refr/K1OezcKmmXaLn4zc8ji2rjbBUIMrIhH/jc5Z2qEEz7qVk=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "1H8X4u6CVZRTLLNbUQTKAnc5vCkqWMpwfF", + "compressed key", + "IKUI9v2xbHogJe8HKXI2M5KEhMKaW6fjNxtyEy27Mf+3/e1ht4jZoc85e4F8stPsxt4Xcg8Yr42S28O6L/Qx9fE=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + "test signature", + "ILH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + "another text", + "H7vrF2C+TlFiHyegAw3QLv6SK0myuEEXUOgfx0+Qio1YVDuSa6p/OHpoQVlUt3F8QJdbdZN9M1h/fYEAnEz16V0=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "1E4T9JZ3mq6cdgiRJEWzHqDXb9t322fE6d", + "test signature", + "HLH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo=" + )); +} + +TEST(BitcoinMessageSigner, SignAndVerify) { + const auto pubKey = gPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(pubKey.bytes), "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); + const auto address = Address(pubKey, TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); + EXPECT_EQ(address, "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X"); + + { + const auto msg = "test signature"; + const auto signature = MessageSigner::signMessage(gPrivateKey, address, msg); + EXPECT_EQ(signature, "ILH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo="); + + EXPECT_TRUE(MessageSigner::verifyMessage(address, msg, signature)); + } + { + const auto msg = "another text"; + const auto signature = MessageSigner::signMessage(gPrivateKey, address, msg); + EXPECT_EQ(signature, "H7vrF2C+TlFiHyegAw3QLv6SK0myuEEXUOgfx0+Qio1YVDuSa6p/OHpoQVlUt3F8QJdbdZN9M1h/fYEAnEz16V0="); + + EXPECT_TRUE(MessageSigner::verifyMessage(address, msg, signature)); + } + + // uncompressed + const auto pubKeyUncomp = gPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + const auto keyHash = pubKeyUncomp.hash(Data{TW::p2pkhPrefix(TWCoinTypeBitcoin)}, Hash::HasherSha256ripemd); + const auto addressUncomp = Address(keyHash).string(); + EXPECT_EQ(addressUncomp, "1E4T9JZ3mq6cdgiRJEWzHqDXb9t322fE6d"); + { + const auto msg = "test signature"; + const auto signature = MessageSigner::signMessage(gPrivateKey, addressUncomp, msg, false); + EXPECT_EQ(signature, "HLH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo="); + + EXPECT_TRUE(MessageSigner::verifyMessage(addressUncomp, msg, signature)); + } +} + +TEST(BitcoinMessageSigner, SignNegative) { + const auto address = Address(gPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1), TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); + EXPECT_EQ(address, "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X"); + const auto msg = "test signature"; + // Use invalid address + EXPECT_EXCEPTION(MessageSigner::signMessage(gPrivateKey, "__THIS_IS_NOT_A_VALID_ADDRESS__", msg), "Address is not valid (legacy) address"); + // Use invalid address, not legacy + EXPECT_EXCEPTION(MessageSigner::signMessage(gPrivateKey, "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8", msg), "Address is not valid (legacy) address"); + // Use valid, but not matching key + EXPECT_EXCEPTION(MessageSigner::signMessage(gPrivateKey, "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", msg), "Address does not match key"); +} + +TEST(BitcoinMessageSigner, VerifyNegative) { + const auto sig = parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae439"); + // Baseline positive + EXPECT_TRUE(MessageSigner::verifyMessage( + "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", + "test signature", + sig + )); + + // Provide non-matching address + EXPECT_FALSE(MessageSigner::verifyMessage( + "1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN", + "test signature", + sig + )); + // Signature too short + EXPECT_EXCEPTION(MessageSigner::verifyMessage( + "1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN", + "test signature", + parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae4") + ), "signature too short"); + // Invalid address + EXPECT_EXCEPTION(MessageSigner::verifyMessage( + "__THIS_IS_NOT_A_VALID_ADDRESS__", + "test signature", + parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae4") + ), "Input address invalid"); + EXPECT_EXCEPTION(MessageSigner::verifyMessage( + "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8", + "test signature", + parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae4") + ), "Input address invalid"); +} + +TEST(BitcoinMessageSigner, MessageToHash) { + EXPECT_EQ(hex(MessageSigner::messageToHash("Hello, world!")), "02d6c0643e40b0db549cbbd7eb47dcab71a59d7017199ebde6b272f28fbbf95f"); + EXPECT_EQ(hex(MessageSigner::messageToHash("test signature")), "8e81cc5bca9862d8b7f22be1f7cb762b49121cf4e1611c27906a041f9a9eb21f"); +} + +TEST(TWBitcoinMessageSigner, VerifyMessage) { + EXPECT_TRUE(TWBitcoinMessageSignerVerifyMessage( + STRING("1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr").get(), + STRING("test signature").get(), + STRING("H+3L5IbSVcejp4S2VwLXCxLEMQAWDvKbE8lQyq0ocdvyM1aoEudkzN/S/qLI3vnNOFY6V13BXWSFrPr3OjGa5Dk=").get() + )); +} + +TEST(TWBitcoinMessageSigner, SignAndVerify) { + const auto privKeyData = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + const auto address = STRING("19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X"); + const auto message = STRING("test signature"); + + const auto signature = WRAPS(TWBitcoinMessageSignerSignMessage(privateKey.get(), address.get(), message.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "ILH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo="); + + EXPECT_TRUE(TWBitcoinMessageSignerVerifyMessage(address.get(), message.get(), signature.get())); +} + +} // namespace TW::Bitcoin diff --git a/tests/chains/Bitcoin/SegwitAddressTests.cpp b/tests/chains/Bitcoin/SegwitAddressTests.cpp new file mode 100644 index 00000000000..3180eef8337 --- /dev/null +++ b/tests/chains/Bitcoin/SegwitAddressTests.cpp @@ -0,0 +1,253 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Bech32.h" +#include "Bitcoin/SegwitAddress.h" +#include "HDWallet.h" +#include "HexCoding.h" + +#include +#include +#include +#include + +using namespace TW; +namespace TW::Bitcoin::tests { + +static const std::string valid_checksum[] = { + "A12UEL5L", + "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", + "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", + "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", + "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", +}; + +static const std::string invalid_checksum[] = { + " 1nwldj5", + "\x7f""1axkwrx", + "an124characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx0123456789012345678901234567890123456789", + "pzry9x0s0muk", + "1pzry9x0s0muk", + "x1b4n0q5v", + "li1dgmt3", + "de1lg7wt\xff", +}; + +struct valid_address_data { + std::string address; + std::string scriptPubKeyHex; +}; + +struct invalid_address_data { + std::string hrp; + int version; + size_t program_length; +}; + +static const std::vector valid_address = { + /// test vectors from BIP173 + {"BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", "0014751e76e8199196d454941c45d1b3a323f1433bd6"}, + {"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", "00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262"}, + //{"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"}, + //{"BC1SW50QA3JX3S", "6002751e"}, + //{"bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", "5210751e76e8199196d454941c45d1b3a323"}, + {"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"}, + {"bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8", "00140cb9f5c6b62c03249367bc20a90dd2425e6926af"}, + {"bc1qm9jzmujvdqjj6y28hptk859zs3yyv78hpqqjfj", "0014d9642df24c68252d1147b85763d0a284484678f7"}, + {"bc1ptmsk7c2yut2xah4pgflpygh2s7fh0cpfkrza9cjj29awapv53mrslgd5cf", "51205ee16f6144e2d46edea1427e1222ea879377e029b0c5d2e252517aee85948ec7"}, // Taproot + + /// test vectors from BIP350 + {"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", "5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"}, + {"BC1SW50QGDZ25J", "6002751e"}, + {"bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", "5210751e76e8199196d454941c45d1b3a323"}, + {"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", "0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"}, + {"tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", "5120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433"}, + {"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", "512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"}, +}; + +static const std::vector invalid_address = { + /// test vectors from BIP73 + //"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", // Invalid human-readable part + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", // Invalid checksum + "BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", // Invalid witness version + "bc1rw5uspcuh", // Invalid program length + "bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", // Invalid program length + "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", // Invalid program length for witness version 0 (per BIP141) + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", // Mixed case + "bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", // zero padding of more than 4 bits + "tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", // Non-zero padding in 8-to-5 conversion + "bc1gmk9yu", // Empty data section + + /// test vectors from BIP350 + //"tc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq5zuyut", // Invalid human-readable part + "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqh2y7hd", // Invalid checksum (Bech32 instead of Bech32m) + "tb1z0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqglt7rf", // Invalid checksum (Bech32 instead of Bech32m) + "BC1S0XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ54WELL", // Invalid checksum (Bech32 instead of Bech32m) + "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kemeawh", // Invalid checksum (Bech32m instead of Bech32) + "tb1q0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq24jc47", // Invalid checksum (Bech32m instead of Bech32) + "bc1p38j9r5y49hruaue7wxjce0updqjuyyx0kh56v8s25huc6995vvpql3jow4", // Invalid character in checksum + "BC130XLXVLHEMJA6C4DQV22UAPCTQUPFHLXM9H8Z3K2E72Q4K9HCZ7VQ7ZWS8R", // Invalid witness version + "bc1pw5dgrnzv", // Invalid program length (1 byte) + "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav253zgeav", // Invalid program length (41 bytes) + "BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", // Invalid program length for witness version 0 (per BIP141) + "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq47Zagq", // Mixed case + "bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v07qwwzcrf", // zero padding of more than 4 bits + "tb1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vpggkg4j", // Non-zero padding in 8-to-5 conversion + "bc1gmk9yu", // Empty data section + + "an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", // overall max length exceeded + "bc1q9zpgru", // 1 byte data (only version byte) + "BC1SW50QA3JX3S", // version = 16 + "bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", // version = 2 +}; + +static const invalid_address_data invalid_address_enc[] = { + {"BC", 0, 20}, + {"bc", 0, 21}, + {"bc", 17, 32}, + {"bc", 1, 1}, + {"bc", 16, 41}, +}; + +static Data segwit_scriptpubkey(int witver, const Data& witprog) { + Data ret; + ret.push_back(witver ? (0x50 + (witver & 0x1f)) : 0); + ret.push_back(witprog.size()); + ret.insert(ret.end(), witprog.begin(), witprog.end()); + return ret; +} + +bool case_insensitive_equal(const std::string& s1, const std::string& s2) { + std::string s1l = s1; + std::transform(s1l.begin(), s1l.end(), s1l.begin(), [](unsigned char c){ return std::tolower(c); }); + std::string s2l = s2; + std::transform(s2l.begin(), s2l.end(), s2l.begin(), [](unsigned char c){ return std::tolower(c); }); + return s1l == s2l; +} + +TEST(SegwitAddress, ValidChecksum) { + for (auto i = 0ul; i < sizeof(valid_checksum) / sizeof(valid_checksum[0]); ++i) { + auto dec = Bech32::decode(valid_checksum[i]); + ASSERT_FALSE(std::get<0>(dec).empty()); + + auto recode = Bech32::encode(std::get<0>(dec), std::get<1>(dec), Bech32::ChecksumVariant::Bech32); + ASSERT_FALSE(recode.empty()); + + ASSERT_TRUE(case_insensitive_equal(recode, valid_checksum[i])); + } +} + +TEST(SegwitAddress, InvalidChecksum) { + for (auto i = 0ul; i < sizeof(invalid_checksum) / sizeof(invalid_checksum[0]); ++i) { + auto dec = Bech32::decode(invalid_checksum[i]); + EXPECT_TRUE(std::get<0>(dec).empty() && std::get<1>(dec).empty()); + } +} + +TEST(SegwitAddress, ValidAddress) { + for (auto& td: valid_address) { + auto dec = SegwitAddress::decode(td.address); + EXPECT_TRUE(std::get<2>(dec)) << "Valid address could not be decoded " << td.address; + EXPECT_TRUE(std::get<0>(dec).witnessProgram.size() > 0) << "Empty decoded address data for " << td.address; + EXPECT_EQ(std::get<1>(dec).length(), 2ul); // hrp + + // recode + std::string recode = std::get<0>(dec).string(); + EXPECT_FALSE(recode.empty()) << "Recode failed for " << td.address; + EXPECT_TRUE(case_insensitive_equal(td.address, recode)); + + Data spk = segwit_scriptpubkey(std::get<0>(dec).witnessVersion, std::get<0>(dec).witnessProgram); + EXPECT_EQ(hex(spk), td.scriptPubKeyHex); + } +} + +TEST(SegwitAddress, InvalidAddress) { + for (auto i = 0ul; i < invalid_address.size(); ++i) { + auto dec = SegwitAddress::decode(invalid_address[i]); + EXPECT_FALSE(std::get<2>(dec)) << "Invalid address reported as valid: " << invalid_address[i]; + } +} + +TEST(SegwitAddress, InvalidAddressEncoding) { + for (auto i = 0ul; i < sizeof(invalid_address_enc) / sizeof(invalid_address_enc[0]); ++i) { + auto address = SegwitAddress(invalid_address_enc[i].hrp, invalid_address_enc[i].version, Data(invalid_address_enc[i].program_length, 0)); + std::string code = address.string(); + EXPECT_TRUE(code.empty()); + } +} + +TEST(SegwitAddress, LegacyAddress) { + auto result = SegwitAddress::decode("TLWEciM1CjP5fJqM2r9wymAidkkYtTU5k3"); + EXPECT_FALSE(std::get<2>(result)); +} + +TEST(SegwitAddress, fromRaw) { + { + auto addr = SegwitAddress::fromRaw("bc", parse_hex("000e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e16")); + EXPECT_TRUE(addr.second); + EXPECT_EQ(addr.first.string(), "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"); + } + { + // empty data + auto addr = SegwitAddress::fromRaw("bc", Data()); + EXPECT_FALSE(addr.second); + } +} + +TEST(SegwitAddress, Equals) { + const auto dec1 = SegwitAddress::decode("bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8"); + EXPECT_TRUE(std::get<2>(dec1)); + const auto addr1 = std::get<0>(dec1); + const auto dec2 = SegwitAddress::decode("bc1qm9jzmujvdqjj6y28hptk859zs3yyv78hpqqjfj"); + EXPECT_TRUE(std::get<2>(dec2)); + const auto addr2 = std::get<0>(dec2); + + ASSERT_TRUE(addr1 == addr1); + ASSERT_FALSE(addr1 == addr2); +} + +TEST(SegwitAddress, TestnetAddress) { + const auto mnemonic1 = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; + const auto passphrase = ""; + const auto coin = TWCoinTypeBitcoin; + HDWallet wallet = HDWallet(mnemonic1, passphrase); + + // default + { + const auto privKey = wallet.getKey(coin, TWDerivationDefault); + const auto pubKey = privKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(pubKey.bytes), "02df9ef2a7a5552765178b181e1e1afdefc7849985c7dfe9647706dd4fa40df6ac"); + EXPECT_EQ(SegwitAddress(pubKey, "bc").string(), "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + } + + // testnet: different derivation path and hrp + { + const auto privKey = wallet.getKey(coin, TW::DerivationPath("m/84'/1'/0'/0/0")); + const auto pubKey = privKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(pubKey.bytes), "03eb1a535b59f03894b99319f850c784cf4164f4de07620695c5cf0dc5c1ab2a54"); + EXPECT_EQ(SegwitAddress::createTestnetFromPublicKey(pubKey).string(), "tb1qq8p994ak933c39d2jaj8n4sg598tnkhnyk5sg5"); + // alternative with custom hrp + EXPECT_EQ(SegwitAddress(pubKey, "tb").string(), "tb1qq8p994ak933c39d2jaj8n4sg598tnkhnyk5sg5"); + } + + EXPECT_TRUE(SegwitAddress::isValid("tb1qq8p994ak933c39d2jaj8n4sg598tnkhnyk5sg5")); +} + +TEST(SegwitAddress, SegwitDerivationHDWallet) { + const auto mnemonic1 = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; + const auto passphrase = ""; + const auto coin = TWCoinTypeBitcoin; + HDWallet wallet = HDWallet(mnemonic1, passphrase); + + // addresses with different derivations + EXPECT_EQ(wallet.deriveAddress(coin), "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + EXPECT_EQ(wallet.deriveAddress(coin, TWDerivationDefault), "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + EXPECT_EQ(wallet.deriveAddress(coin, TWDerivationBitcoinSegwit), "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + EXPECT_EQ(wallet.deriveAddress(coin, TWDerivationBitcoinLegacy), "1GVb4mfQrvymPLz7zeZ3LnQ8sFv3NedZXe"); + EXPECT_EQ(wallet.deriveAddress(coin, TWDerivationBitcoinTestnet), "tb1qq8p994ak933c39d2jaj8n4sg598tnkhnyk5sg5"); +} + +} // namespace TW::Bitcoin::tests diff --git a/tests/Bitcoin/TWBitcoinAddressTests.cpp b/tests/chains/Bitcoin/TWBitcoinAddressTests.cpp similarity index 98% rename from tests/Bitcoin/TWBitcoinAddressTests.cpp rename to tests/chains/Bitcoin/TWBitcoinAddressTests.cpp index c2e81b6d33a..13c96dc5a5c 100644 --- a/tests/Bitcoin/TWBitcoinAddressTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Bitcoin/TWBitcoinScriptTests.cpp b/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp similarity index 92% rename from tests/Bitcoin/TWBitcoinScriptTests.cpp rename to tests/chains/Bitcoin/TWBitcoinScriptTests.cpp index 0d8cae2c41a..2f9a3e1ad29 100644 --- a/tests/Bitcoin/TWBitcoinScriptTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinScriptTests.cpp @@ -4,34 +4,38 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include #include +namespace TW::Bitcoin::TWScriptTests { + +// clang-format off const auto PayToScriptHash = WRAP(TWBitcoinScript, TWBitcoinScriptCreateWithData(DATA("a914" "4733f37cf4db86fbc2efed2500b4f4e49f312023" "87").get())); const auto PayToWitnessScriptHash = WRAP(TWBitcoinScript, TWBitcoinScriptCreateWithData(DATA("0020" "ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3db").get())); const auto PayToWitnessPublicKeyHash = WRAP(TWBitcoinScript, TWBitcoinScriptCreateWithData(DATA("0014" "79091972186c449eb1ded22b78e40d009bdf0089").get())); const auto PayToPublicKeySecp256k1 = WRAP(TWBitcoinScript, TWBitcoinScriptCreateWithData(DATA("21" "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" "ac").get())); const auto PayToPublicKeyHash = WRAP(TWBitcoinScript, TWBitcoinScriptCreateWithData(DATA("76a914" "79091972186c449eb1ded22b78e40d009bdf0089" "88ac").get())); +// clang-format on TEST(TWBitcoinScript, Create) { auto data = DATA("a9144733f37cf4db86fbc2efed2500b4f4e49f31202387"); { auto script = WRAP(TWBitcoinScript, TWBitcoinScriptCreateWithData(data.get())); ASSERT_TRUE(script.get() != nullptr); - ASSERT_EQ(TWBitcoinScriptSize(script.get()), 23); + ASSERT_EQ(TWBitcoinScriptSize(script.get()), 23ul); } { auto script = WRAP(TWBitcoinScript, TWBitcoinScriptCreateWithBytes(TWDataBytes(data.get()), TWDataSize(data.get()))); ASSERT_TRUE(script.get() != nullptr); - ASSERT_EQ(TWBitcoinScriptSize(script.get()), 23); + ASSERT_EQ(TWBitcoinScriptSize(script.get()), 23ul); auto scriptCopy = WRAP(TWBitcoinScript, TWBitcoinScriptCreateCopy(script.get())); ASSERT_TRUE(scriptCopy.get() != nullptr); - ASSERT_EQ(TWBitcoinScriptSize(scriptCopy.get()), 23); + ASSERT_EQ(TWBitcoinScriptSize(scriptCopy.get()), 23ul); } } @@ -117,7 +121,9 @@ TEST(TWBitcoinScript, BuildPayToPublicKey) { const auto script = WRAP(TWBitcoinScript, TWBitcoinScriptBuildPayToPublicKey(pubkey.get())); ASSERT_TRUE(script.get() != nullptr); const auto hex = WRAPS(TWStringCreateWithHexData(WRAPD(TWBitcoinScriptData(script.get())).get())); - ASSERT_STREQ(TWStringUTF8Bytes(hex.get()), "21" "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" "ac"); + ASSERT_STREQ(TWStringUTF8Bytes(hex.get()), "21" + "03c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432" + "ac"); } TEST(TWBitcoinScript, BuildPayToWitnessPubkeyHash) { @@ -125,7 +131,8 @@ TEST(TWBitcoinScript, BuildPayToWitnessPubkeyHash) { const auto script = WRAP(TWBitcoinScript, TWBitcoinScriptBuildPayToWitnessPubkeyHash(hash.get())); ASSERT_TRUE(script.get() != nullptr); const auto hex = WRAPS(TWStringCreateWithHexData(WRAPD(TWBitcoinScriptData(script.get())).get())); - ASSERT_STREQ(TWStringUTF8Bytes(hex.get()), "0014" "79091972186c449eb1ded22b78e40d009bdf0089"); + ASSERT_STREQ(TWStringUTF8Bytes(hex.get()), "0014" + "79091972186c449eb1ded22b78e40d009bdf0089"); } TEST(TWBitcoinScript, BuildPayToWitnessScriptHash) { @@ -133,7 +140,8 @@ TEST(TWBitcoinScript, BuildPayToWitnessScriptHash) { const auto script = WRAP(TWBitcoinScript, TWBitcoinScriptBuildPayToWitnessScriptHash(hash.get())); ASSERT_TRUE(script.get() != nullptr); const auto hex = WRAPS(TWStringCreateWithHexData(WRAPD(TWBitcoinScriptData(script.get())).get())); - ASSERT_STREQ(TWStringUTF8Bytes(hex.get()), "0020" "ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3db"); + ASSERT_STREQ(TWStringUTF8Bytes(hex.get()), "0020" + "ff25429251b5a84f452230a3c75fd886b7fc5a7865ce4a7bb7a9d7c5be6da3db"); } TEST(TWBitcoinScript, ScriptHash) { @@ -216,6 +224,7 @@ TEST(TWBitcoinSigHashType, HashTypeForCoin) { EXPECT_EQ(TWBitcoinScriptHashTypeForCoin(TWCoinTypeLitecoin), (uint32_t)TWBitcoinSigHashTypeAll); EXPECT_EQ(TWBitcoinScriptHashTypeForCoin(TWCoinTypeZcash), (uint32_t)TWBitcoinSigHashTypeAll); EXPECT_EQ(TWBitcoinScriptHashTypeForCoin(TWCoinTypeBitcoinCash), (uint32_t)TWBitcoinSigHashTypeAll | (uint32_t)TWBitcoinSigHashTypeFork); + EXPECT_EQ(TWBitcoinScriptHashTypeForCoin(TWCoinTypeECash), (uint32_t)TWBitcoinSigHashTypeAll | (uint32_t)TWBitcoinSigHashTypeFork); EXPECT_EQ(TWBitcoinScriptHashTypeForCoin(TWCoinTypeBitcoinGold), (uint32_t)TWBitcoinSigHashTypeAll | (uint32_t)TWBitcoinSigHashTypeForkBTG); } @@ -232,3 +241,5 @@ TEST(TWBitcoinSigHashType, IsNone) { EXPECT_FALSE(TWBitcoinSigHashTypeIsNone(TWBitcoinSigHashTypeAll)); EXPECT_FALSE(TWBitcoinSigHashTypeIsNone(TWBitcoinSigHashTypeFork)); } + +} // namespace TW::Bitcoin::TWScriptTests diff --git a/tests/Bitcoin/TWBitcoinSigningTests.cpp b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp similarity index 84% rename from tests/Bitcoin/TWBitcoinSigningTests.cpp rename to tests/chains/Bitcoin/TWBitcoinSigningTests.cpp index c2ac12d4869..f2c26b2691c 100644 --- a/tests/Bitcoin/TWBitcoinSigningTests.cpp +++ b/tests/chains/Bitcoin/TWBitcoinSigningTests.cpp @@ -1,37 +1,34 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. +#include "Base58.h" #include "Bitcoin/Address.h" #include "Bitcoin/OutPoint.h" #include "Bitcoin/Script.h" +#include "Bitcoin/SegwitAddress.h" +#include "Bitcoin/SigHashType.h" #include "Bitcoin/Transaction.h" #include "Bitcoin/TransactionBuilder.h" #include "Bitcoin/TransactionSigner.h" -#include "Bitcoin/SigHashType.h" -#include "Base58.h" #include "Hash.h" #include "HexCoding.h" #include "PrivateKey.h" -#include "proto/Bitcoin.pb.h" #include "TxComparisonHelper.h" -#include "../interface/TWTestUtilities.h" +#include "proto/Bitcoin.pb.h" -#include #include #include -#include -#include #include -#include #include +#include -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { +// clang-format off SigningInput buildInputP2PKH(bool omitKey = false) { auto hash0 = parse_hex("fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f"); auto hash1 = parse_hex("ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a"); @@ -73,7 +70,8 @@ SigningInput buildInputP2PKH(bool omitKey = false) { input.utxos.push_back(utxo0); UTXO utxo1; - utxo1.script = Script(parse_hex("0014" "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); + utxo1.script = Script(parse_hex("0014" + "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); utxo1.amount = 600'000'000; utxo1.outPoint = OutPoint(hash1, 1, UINT32_MAX); input.utxos.push_back(utxo1); @@ -146,7 +144,7 @@ TEST(BitcoinSigning, EncodeP2WPKH) { Data unsignedData; unsignedTx.encode(unsignedData, Transaction::SegwitFormatMode::Segwit); - ASSERT_EQ(unsignedData.size(), 164); + ASSERT_EQ(unsignedData.size(), 164ul); ASSERT_EQ(hex(unsignedData), "01000000" // version "0001" // marker & flag @@ -201,17 +199,17 @@ TEST(BitcoinSigning, SignP2WPKH_Bip143) { input.utxos.push_back(utxo0); UTXO utxo1; - auto utxo1Script = Script::buildPayToWitnessProgram(utxoPubkeyHash1); + auto utxo1Script = Script::buildPayToV0WitnessProgram(utxoPubkeyHash1); utxo1.script = utxo1Script; utxo1.amount = 600000000; // 0x23C34600 0046c323 utxo1.outPoint = OutPoint(hash1, 1, UINT32_MAX); - input.utxos.push_back(utxo1); + input.utxos.push_back(utxo1); // Set plan to force both UTXOs and exact output amounts TransactionPlan plan; plan.amount = amount; plan.availableAmount = 600000000 + 1000000; - plan.fee = 265210000; // very large, the amounts specified (in1, out0, out1) are not consistent/realistic + plan.fee = 265210000; // very large, the amounts specified (in1, out0, out1) are not consistent/realistic plan.change = 223450000; // 0x0d519390 plan.branchId = {0}; plan.utxos.push_back(utxo0); @@ -274,7 +272,8 @@ SigningInput buildInputP2WPKH(int64_t amount, TWBitcoinSigHashType hashType, int assert(hex(utxoPubkeyHash1) == "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1"); input.privateKeys.push_back(utxoKey1); - auto scriptPub1 = Script(parse_hex("0014" "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); + auto scriptPub1 = Script(parse_hex("0014" + "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); Data scriptHash; scriptPub1.matchPayToWitnessPublicKeyHash(scriptHash); auto scriptHashHex = hex(scriptHash); @@ -290,7 +289,8 @@ SigningInput buildInputP2WPKH(int64_t amount, TWBitcoinSigHashType hashType, int input.utxos.push_back(utxo0); UTXO utxo1; - utxo1.script = Script(parse_hex("0014" "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); + utxo1.script = Script(parse_hex("0014" + "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); utxo1.amount = utxo1Amount; utxo1.outPoint = OutPoint(hash1, 1, UINT32_MAX); input.utxos.push_back(utxo1); @@ -316,7 +316,7 @@ TEST(BitcoinSigning, SignP2WPKH) { Data serialized; signedTx.encode(serialized); EXPECT_EQ(getEncodedTxSize(signedTx), (EncodedTxSize{195, 192, 193})); - EXPECT_EQ(serialized.size(), 192); + EXPECT_EQ(serialized.size(), 192ul); EXPECT_TRUE(validateEstimatedSize(signedTx, -1, 1)); ASSERT_EQ(hex(serialized), // printed using prettyPrintTransaction "01000000" // version @@ -330,11 +330,11 @@ TEST(BitcoinSigning, SignP2WPKH) { { // Non-segwit encoded, for comparison - Data serialized; - signedTx.encode(serialized, Transaction::SegwitFormatMode::NonSegwit); + Data serialized_; + signedTx.encode(serialized_, Transaction::SegwitFormatMode::NonSegwit); EXPECT_EQ(getEncodedTxSize(signedTx), (EncodedTxSize{195, 192, 193})); - EXPECT_EQ(serialized.size(), 192); - ASSERT_EQ(hex(serialized), // printed using prettyPrintTransaction + EXPECT_EQ(serialized_.size(), 192ul); + ASSERT_EQ(hex(serialized_), // printed using prettyPrintTransaction "01000000" // version "01" // inputs "fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f" "00000000" "49" "483045022100c327babdd370f0fc5b24cf920736446bf7d9c5660e4a5f7df432386fd652fe280220269c4fc3690c1c248e50c8bf2435c20b4ef00f308b403575f4437f862a91c53a01" "ffffffff" @@ -500,7 +500,7 @@ SigningInput buildInputP2WSH(enum TWBitcoinSigHashType hashType, bool omitScript auto hash0 = parse_hex("0001000000000000000000000000000000000000000000000000000000000000"); utxo0.outPoint = OutPoint(hash0, 0, UINT32_MAX); input.utxos.push_back(utxo0); - + return input; } @@ -624,7 +624,7 @@ TEST(BitcoinSigning, SignP2WSH_HashAnyoneCanPay) { Data serialized; signedTx.encode(serialized); - EXPECT_EQ(serialized.size(), 231); + EXPECT_EQ(serialized.size(), 231ul); EXPECT_EQ(getEncodedTxSize(signedTx), (EncodedTxSize{231, 119, 147})); EXPECT_TRUE(validateEstimatedSize(signedTx, -1, 1)); ASSERT_EQ(hex(serialized), // printed using prettyPrintTransaction @@ -976,7 +976,7 @@ TEST(BitcoinSigning, SignP2SH_P2WSH) { "47" "3044022044e3b59b06931d46f857c82fa1d53d89b116a40a581527eac35c5eb5b7f0785302207d0f8b5d063ffc6749fb4e133db7916162b540c70dee40ec0b21e142d8843b3a00" "cf" "56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae" "00000000" // nLockTime - ; + ; Data serialized; signedTx.encode(serialized); @@ -1106,7 +1106,7 @@ TEST(BitcoinSigning, Plan_10input_MaxAmount) { input.toAddress = "bc1qauwlpmzamwlf9tah6z4w0t8sunh6pnyyjgk0ne"; input.changeAddress = ownAddress; - // Plan. + // Plan. // Estimated size: witness size: 10 * (1 + 1 + 72 + 1 + 33) + 2 = 1082; base 451; raw 451 + 1082 = 1533; vsize 451 + 1082/4 --> 722 // Actual size: witness size: 1078; base 451; raw 451 + 1078 = 1529; vsize 451 + 1078/4 --> 721 auto plan = TransactionBuilder::plan(input); @@ -1128,7 +1128,7 @@ TEST(BitcoinSigning, Plan_10input_MaxAmount) { EXPECT_EQ(getEncodedTxSize(signedTx), (EncodedTxSize{1529, 451, 721})); EXPECT_TRUE(validateEstimatedSize(signedTx, -1, 1)); - ASSERT_EQ(serialized.size(), 1529); + ASSERT_EQ(serialized.size(), 1529ul); } TEST(BitcoinSigning, Sign_LitecoinReal_a85f) { @@ -1295,7 +1295,7 @@ TEST(BitcoinSigning, Sign_ManyUtxos_400) { input.utxos.push_back(utxo); utxoSum += utxo.amount; } - EXPECT_EQ(utxoSum, 1'202'000); + EXPECT_EQ(utxoSum, 1'202'000ul); input.coinType = TWCoinTypeBitcoin; input.hashType = hashTypeForCoin(TWCoinTypeBitcoin); @@ -1316,8 +1316,8 @@ TEST(BitcoinSigning, Sign_ManyUtxos_400) { subset.push_back(val); subsetSum += val; } - EXPECT_EQ(subset.size(), 66); - EXPECT_EQ(subsetSum, 308'550); + EXPECT_EQ(subset.size(), 66ul); + EXPECT_EQ(subsetSum, 308'550ul); EXPECT_TRUE(verifyPlan(plan, subset, 300'000, 4'561)); // Extend input with keys, reuse plan, Sign @@ -1334,7 +1334,7 @@ TEST(BitcoinSigning, Sign_ManyUtxos_400) { Data serialized; signedTx.encode(serialized); - EXPECT_EQ(serialized.size(), 9871); + EXPECT_EQ(serialized.size(), 9871ul); } TEST(BitcoinSigning, Sign_ManyUtxos_2000) { @@ -1364,7 +1364,7 @@ TEST(BitcoinSigning, Sign_ManyUtxos_2000) { input.utxos.push_back(utxo); utxoSum += utxo.amount; } - EXPECT_EQ(utxoSum, 22'010'000); + EXPECT_EQ(utxoSum, 22'010'000ul); input.coinType = TWCoinTypeBitcoin; input.hashType = hashTypeForCoin(TWCoinTypeBitcoin); @@ -1385,8 +1385,8 @@ TEST(BitcoinSigning, Sign_ManyUtxos_2000) { subset.push_back(val); subsetSum += val; } - EXPECT_EQ(subset.size(), 601); - EXPECT_EQ(subsetSum, 2'410'010); + EXPECT_EQ(subset.size(), 601ul); + EXPECT_EQ(subsetSum, 2'410'010ul); EXPECT_TRUE(verifyPlan(plan, subset, 2'000'000, 40'943)); // Extend input with keys, reuse plan, Sign @@ -1403,7 +1403,7 @@ TEST(BitcoinSigning, Sign_ManyUtxos_2000) { Data serialized; signedTx.encode(serialized); - EXPECT_EQ(serialized.size(), 89'339); + EXPECT_EQ(serialized.size(), 89'339ul); } TEST(BitcoinSigning, EncodeThreeOutput) { @@ -1433,7 +1433,7 @@ TEST(BitcoinSigning, EncodeThreeOutput) { Data unsignedData; unsignedTx.encode(unsignedData, Transaction::SegwitFormatMode::Segwit); - EXPECT_EQ(unsignedData.size(), 147); + EXPECT_EQ(unsignedData.size(), 147ul); EXPECT_EQ(hex(unsignedData), // printed using prettyPrintTransaction "01000000" // version "0001" // marker & flag @@ -1454,7 +1454,7 @@ TEST(BitcoinSigning, EncodeThreeOutput) { auto pubkey = PrivateKey(privkey).getPublicKey(TWPublicKeyTypeSECP256k1); EXPECT_EQ(hex(pubkey.bytes), "036739829f2cfec79cfe6aaf1c22ecb7d4867dfd8ab4deb7121b36a00ab646caed"); - auto utxo0Script = Script::lockScriptForAddress(ownAddress, coin); // buildPayToWitnessProgram() + auto utxo0Script = Script::lockScriptForAddress(ownAddress, coin); // buildPayToV0WitnessProgram() Data keyHashIn0; EXPECT_TRUE(utxo0Script.matchPayToWitnessPublicKeyHash(keyHashIn0)); EXPECT_EQ(hex(keyHashIn0), "5c74be45eb45a3459050667529022d9df8a1ecff"); @@ -1464,19 +1464,19 @@ TEST(BitcoinSigning, EncodeThreeOutput) { auto hashType = TWBitcoinSigHashType::TWBitcoinSigHashTypeAll; Data sighash = unsignedTx.getSignatureHash(redeemScript0, unsignedTx.inputs[0].previousOutput.index, - hashType, utxo0Amount, static_cast(unsignedTx.version)); - auto sig = privkey.signAsDER(sighash, TWCurveSECP256k1); + hashType, utxo0Amount, static_cast(unsignedTx._version)); + auto sig = privkey.signAsDER(sighash); ASSERT_FALSE(sig.empty()); sig.push_back(hashType); EXPECT_EQ(hex(sig), "30450221008d88197a37ffcb51ecacc7e826aa588cb1068a107a82373c4b54ec42318a395c02204abbf5408504614d8f943d67e7873506c575e85a5e1bd92a02cd345e5192a82701"); - + // add witness stack unsignedTx.inputs[0].scriptWitness.push_back(sig); unsignedTx.inputs[0].scriptWitness.push_back(pubkey.bytes); unsignedData.clear(); unsignedTx.encode(unsignedData, Transaction::SegwitFormatMode::Segwit); - EXPECT_EQ(unsignedData.size(), 254); + EXPECT_EQ(unsignedData.size(), 254ul); // https://blockchair.com/litecoin/transaction/9e3fe98565a904d2da5ec1b3ba9d2b3376dfc074f43d113ce1caac01bf51b34c EXPECT_EQ(hex(unsignedData), // printed using prettyPrintTransaction "01000000" // version @@ -1547,5 +1547,214 @@ TEST(BitcoinSigning, RedeemExtendedPubkeyUTXO) { Data encoded; signedTx.encode(encoded); - EXPECT_EQ(encoded.size(), 402); + EXPECT_EQ(encoded.size(), 402ul); +} + +TEST(BitcoinSigning, SignP2TR_5df51e) { + const auto privateKey = "13fcaabaf9e71ffaf915e242ec58a743d55f102cf836968e5bd4881135e0c52c"; + const auto ownAddress = "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8"; + const auto toAddress = "bc1ptmsk7c2yut2xah4pgflpygh2s7fh0cpfkrza9cjj29awapv53mrslgd5cf"; // Taproot + const auto coin = TWCoinTypeBitcoin; + + // Setup input + SigningInput input; + input.hashType = hashTypeForCoin(coin); + input.amount = 1100; + input.useMaxAmount = false; + input.byteFee = 1; + input.toAddress = toAddress; + input.changeAddress = ownAddress; + input.coinType = coin; + + auto utxoKey0 = PrivateKey(parse_hex(privateKey)); + auto pubKey0 = utxoKey0.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(pubKey0.bytes), "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee"); + EXPECT_EQ(SegwitAddress(pubKey0, "bc").string(), ownAddress); + auto utxoPubkeyHash = Hash::ripemd(Hash::sha256(pubKey0.bytes)); + EXPECT_EQ(hex(utxoPubkeyHash), "0cb9f5c6b62c03249367bc20a90dd2425e6926af"); + input.privateKeys.push_back(utxoKey0); + + auto redeemScript = Script::lockScriptForAddress(input.toAddress, coin); + EXPECT_EQ(hex(redeemScript.bytes), "51205ee16f6144e2d46edea1427e1222ea879377e029b0c5d2e252517aee85948ec7"); + auto scriptHash = Hash::ripemd(Hash::sha256(redeemScript.bytes)); + EXPECT_EQ(hex(scriptHash), "e0a5001e7b394a1a6b2978cdcab272241280bf46"); + input.scripts[hex(scriptHash)] = redeemScript; + + UTXO utxo0; + auto utxo0Script = Script::lockScriptForAddress(ownAddress, coin); + EXPECT_EQ(hex(utxo0Script.bytes), "00140cb9f5c6b62c03249367bc20a90dd2425e6926af"); + utxo0.script = utxo0Script; + utxo0.amount = 49429; + auto hash0 = parse_hex("c24bd72e3eaea797bd5c879480a0db90980297bc7085efda97df2bf7d31413fb"); + std::reverse(hash0.begin(), hash0.end()); + utxo0.outPoint = OutPoint(hash0, 1, UINT32_MAX); + input.utxos.push_back(utxo0); + + { + // test plan (but do not reuse plan result) + auto plan = TransactionBuilder::plan(input); + EXPECT_TRUE(verifyPlan(plan, {49429}, 1100, 153)); + } + + // Sign + auto result = TransactionSigner::sign(input); + + ASSERT_TRUE(result) << std::to_string(result.error()); + auto signedTx = result.payload(); + + Data serialized; + signedTx.encode(serialized); + EXPECT_EQ(getEncodedTxSize(signedTx), (EncodedTxSize{234, 125, 153})); + EXPECT_TRUE(validateEstimatedSize(signedTx, -1, 1)); + // https://mempool.space/tx/5df51e13bfeb79f386e1e17237f06d1b5c87c5bfcaa907c0c1cfe51cd7ca446d + EXPECT_EQ(hex(serialized), // printed using prettyPrintTransaction + "01000000" // version + "0001" // marker & flag + "01" // inputs + "fb1314d3f72bdf97daef8570bc97029890dba08094875cbd97a7ae3e2ed74bc2" "01000000" "00" "" "ffffffff" + "02" // outputs + "4c04000000000000" "22" "51205ee16f6144e2d46edea1427e1222ea879377e029b0c5d2e252517aee85948ec7" + "30bc000000000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + // witness + "02" + "47" "3044022021cea91157fdab33226e38ee7c1a686538fc323f5e28feb35775cf82ba8c62210220723743b150cea8ead877d8b8d059499779a5df69f9bdc755c9f968c56cfb528f01" + "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" + "00000000" // nLockTime + ); +} + +TEST(BitcoinSigning, Build_OpReturn_THORChainSwap_eb4c) { + auto coin = TWCoinTypeBitcoin; + auto ownAddress = "bc1q7s0a2l4aguksehx8hf93hs9yggl6njxds6m02g"; + auto toAddress = "bc1qxu5a8gtnjxw3xwdlmr2gl9d76h9fysu3zl656e"; + auto utxoAmount = 342101; + auto toAmount = 300000; + int fee = 36888; + + auto unsignedTx = Transaction(2, 0); + + auto hash0 = parse_hex("30b82960291a39de3664ec4c844a815e3e680e29b4d3a919e450f0c119cf4e35"); + std::reverse(hash0.begin(), hash0.end()); + auto outpoint0 = TW::Bitcoin::OutPoint(hash0, 1); + unsignedTx.inputs.emplace_back(outpoint0, Script(), UINT32_MAX); + + auto lockingScriptTo = Script::lockScriptForAddress(toAddress, coin); + unsignedTx.outputs.push_back(TransactionOutput(toAmount, lockingScriptTo)); + // change + auto lockingScriptChange = Script::lockScriptForAddress(ownAddress, coin); + unsignedTx.outputs.push_back(TransactionOutput(utxoAmount - toAmount - fee, lockingScriptChange)); + // memo OP_RETURN + Data memo = data("SWAP:THOR.RUNE:thor1tpercamkkxec0q0jk6ltdnlqvsw29guap8wmcl:"); + auto lockingScriptOpReturn = Script::buildOpReturnScript(memo); + EXPECT_EQ(hex(lockingScriptOpReturn.bytes), "6a3b535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a"); + unsignedTx.outputs.push_back(TransactionOutput(0, lockingScriptOpReturn)); + + Data unsignedData; + unsignedTx.encode(unsignedData, Transaction::SegwitFormatMode::Segwit); + EXPECT_EQ(unsignedData.size(), 186ul); + EXPECT_EQ(hex(unsignedData), // printed using prettyPrintTransaction + "02000000" // version + "0001" // marker & flag + "01" // inputs + "354ecf19c1f050e419a9d3b4290e683e5e814a844cec6436de391a296029b830" "01000000" "00" "" "ffffffff" + "03" // outputs + "e093040000000000" "16" "00143729d3a173919d1339bfd8d48f95bed5ca924391" + "5d14000000000000" "16" "0014f41fd57ebd472d0cdcc7ba4b1bc0a4423fa9c8cd" + "0000000000000000" "3d" "6a3b535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a" + // witness + "00" + "00000000" // nLockTime + ); + + // add signature + auto pubkey = parse_hex("0206121b83ebfddbb1997b50cb87b968190857269333e21e295142c8b88af9312a"); + auto sig = parse_hex("3045022100876eba8f9324d3fbb00b9dad9a34a8166dd75127d4facda63484c19703e9c178022052495a6229cc465d5f0fcf3cde3b22a0f861e762d0bb10acde26a57598bfe7e701"); + + // add witness stack + unsignedTx.inputs[0].scriptWitness.push_back(sig); + unsignedTx.inputs[0].scriptWitness.push_back(pubkey); + + unsignedData.clear(); + unsignedTx.encode(unsignedData, Transaction::SegwitFormatMode::Segwit); + EXPECT_EQ(unsignedData.size(), 293ul); + // https://blockchair.com/bitcoin/transaction/eb4c1b064bfaf593d7cc6a5c73b75f932ffefe12a0478acf5a7e3145476683fc + EXPECT_EQ(hex(unsignedData), + "02000000000101354ecf19c1f050e419a9d3b4290e683e5e814a844cec6436de391a296029b8300100000000ffffffff03e0930400000000001600143729d3a1" + "73919d1339bfd8d48f95bed5ca9243915d14000000000000160014f41fd57ebd472d0cdcc7ba4b1bc0a4423fa9c8cd00000000000000003d6a3b535741503a54" + "484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a02483045022100876eba8f" + "9324d3fbb00b9dad9a34a8166dd75127d4facda63484c19703e9c178022052495a6229cc465d5f0fcf3cde3b22a0f861e762d0bb10acde26a57598bfe7e70121" + "0206121b83ebfddbb1997b50cb87b968190857269333e21e295142c8b88af9312a00000000" + ); +} + +TEST(BitcoinSigning, Sign_OpReturn_THORChainSwap) { + PrivateKey privateKey = PrivateKey(parse_hex("6bd4096fa6f08bd3af2b437244ba0ca2d35045c5233b8d6796df37e61e974de5")); + PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto ownAddress = SegwitAddress(publicKey, "bc"); + auto ownAddressString = ownAddress.string(); + EXPECT_EQ(ownAddressString, "bc1q2gzg42w98ytatvmsgxfc8vrg6l24c25pydup9u"); + auto toAddress = "bc1qxu5a8gtnjxw3xwdlmr2gl9d76h9fysu3zl656e"; + auto utxoAmount = 342101; + auto toAmount = 300000; + int byteFee = 126; + Data memo = data("SWAP:THOR.RUNE:thor1tpercamkkxec0q0jk6ltdnlqvsw29guap8wmcl:"); + + SigningInput input; + input.coinType = TWCoinTypeBitcoin; + input.hashType = hashTypeForCoin(TWCoinTypeBitcoin); + input.amount = toAmount; + input.byteFee = byteFee; + input.toAddress = toAddress; + input.changeAddress = ownAddressString; + + input.privateKeys.push_back(privateKey); + input.outputOpReturn = memo; + + UTXO utxo; + auto utxoHash = parse_hex("30b82960291a39de3664ec4c844a815e3e680e29b4d3a919e450f0c119cf4e35"); + std::reverse(utxoHash.begin(), utxoHash.end()); + utxo.outPoint = OutPoint(utxoHash, 1, UINT32_MAX); + utxo.amount = utxoAmount; + + auto utxoPubkeyHash = Hash::ripemd(Hash::sha256(publicKey.bytes)); + EXPECT_EQ(hex(utxoPubkeyHash), "52048aa9c53917d5b370419383b068d7d55c2a81"); + auto utxoScript = Script::buildPayToWitnessPublicKeyHash(utxoPubkeyHash); + EXPECT_EQ(hex(utxoScript.bytes), "001452048aa9c53917d5b370419383b068d7d55c2a81"); + utxo.script = utxoScript; + input.utxos.push_back(utxo); + + { + // test plan (but do not reuse plan result) + auto plan = TransactionBuilder::plan(input); + EXPECT_TRUE(verifyPlan(plan, {342101}, 300000, 26586)); + EXPECT_EQ(plan.outputOpReturn.size(), 59ul); + } + + // Sign + auto result = TransactionSigner::sign(input); + + ASSERT_TRUE(result) << std::to_string(result.error()); + auto signedTx = result.payload(); + + Data serialized; + signedTx.encode(serialized); + EXPECT_EQ(getEncodedTxSize(signedTx), (EncodedTxSize{293, 183, 211})); + EXPECT_TRUE(validateEstimatedSize(signedTx, -1, 1)); + ASSERT_EQ(hex(serialized), // printed using prettyPrintTransaction + "01000000" // version + "0001" // marker & flag + "01" // inputs + "354ecf19c1f050e419a9d3b4290e683e5e814a844cec6436de391a296029b830" "01000000" "00" "" "ffffffff" + "03" // outputs + "e093040000000000" "16" "00143729d3a173919d1339bfd8d48f95bed5ca924391" + "9b3c000000000000" "16" "001452048aa9c53917d5b370419383b068d7d55c2a81" + "0000000000000000" "3d" "6a3b535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a" + // witness + "02" + "48" "3045022100ff6c0aaef512aa52f3036161bfbcef39046ac89eb9617fa461a0c9c43fe45eb3022055d208d3f81736e72e3ad8ef761dc79ac5dd3dc00721174bc69db416a74960e301" + "21" "02c2e5c8b4927812fb37444a7862466ad23978a4ac626f8eaf93e1d1a60d6abb80" + "00000000" // nLockTime + ); } +// clang-format on +} // namespace TW::Bitcoin diff --git a/tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp b/tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp new file mode 100644 index 00000000000..8df4be05c54 --- /dev/null +++ b/tests/chains/Bitcoin/TWBitcoinTransactionTests.cpp @@ -0,0 +1,39 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Bitcoin/Transaction.h" +#include "HexCoding.h" + +#include + +namespace TW::Bitcoin { + +TEST(BitcoinTransaction, Encode) { + auto transaction = Transaction(2, 0); + + auto po0 = OutPoint(parse_hex("5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f"), 0); + transaction.inputs.emplace_back(po0, Script(), 4294967295); + + auto po1 = OutPoint(parse_hex("bf829c6bcf84579331337659d31f89dfd138f7f7785802d5501c92333145ca7c"), 18); + transaction.inputs.emplace_back(po1, Script(), 4294967295); + + auto po2 = OutPoint(parse_hex("22a6f904655d53ae2ff70e701a0bbd90aa3975c0f40bfc6cc996a9049e31cdfc"), 1); + transaction.inputs.emplace_back(po2, Script(), 4294967295); + + auto oscript0 = Script(parse_hex("76a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac")); + transaction.outputs.emplace_back(18000000, oscript0); + + auto oscript1 = Script(parse_hex("0x76a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac")); + transaction.outputs.emplace_back(400000000, oscript1); + + Data unsignedData; + transaction.encode(unsignedData, Transaction::SegwitFormatMode::NonSegwit); + ASSERT_EQ(unsignedData.size(), 201ul); + ASSERT_EQ(hex(unsignedData), + "02000000035897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f0000000000ffffffffbf829c6bcf84579331337659d31f89dfd138f7f7785802d5501c92333145ca7c1200000000ffffffff22a6f904655d53ae2ff70e701a0bbd90aa3975c0f40bfc6cc996a9049e31cdfc0100000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d717000000001976a914f2d4db28cad6502226ee484ae24505c2885cb12d88ac00000000"); +} + +} // namespace TW::Bitcoin diff --git a/tests/chains/Bitcoin/TWCoinTypeTests.cpp b/tests/chains/Bitcoin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..3ce30dd594b --- /dev/null +++ b/tests/chains/Bitcoin/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWBitcoinCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBitcoin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0607f62530b68cfcc91c57a1702841dd399a899d0eecda8e31ecca3f52f01df2")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBitcoin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("17A16QmavnUfCW11DAApiJxp7ARnxN5pGX")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBitcoin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBitcoin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBitcoin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBitcoin), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeBitcoin)); + ASSERT_EQ(0x5, TWCoinTypeP2shPrefix(TWCoinTypeBitcoin)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBitcoin)); + assertStringsEqual(symbol, "BTC"); + assertStringsEqual(txUrl, "https://blockchair.com/bitcoin/transaction/0607f62530b68cfcc91c57a1702841dd399a899d0eecda8e31ecca3f52f01df2"); + assertStringsEqual(accUrl, "https://blockchair.com/bitcoin/address/17A16QmavnUfCW11DAApiJxp7ARnxN5pGX"); + assertStringsEqual(id, "bitcoin"); + assertStringsEqual(name, "Bitcoin"); +} diff --git a/tests/chains/Bitcoin/TWSegwitAddressTests.cpp b/tests/chains/Bitcoin/TWSegwitAddressTests.cpp new file mode 100644 index 00000000000..65c632ca6b9 --- /dev/null +++ b/tests/chains/Bitcoin/TWSegwitAddressTests.cpp @@ -0,0 +1,68 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include + +#include + +const char* address1 = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"; +const char* address2 = "bc1qr583w2swedy2acd7rung055k8t3n7udp7vyzyg"; +const char* address3Taproot = "bc1ptmsk7c2yut2xah4pgflpygh2s7fh0cpfkrza9cjj29awapv53mrslgd5cf"; + +TEST(TWSegwitAddress, PublicKeyToAddress) { + auto pkData = DATA("0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"); + auto publicKey = WRAP(TWPublicKey, TWPublicKeyCreateWithData(pkData.get(), TWPublicKeyTypeSECP256k1)); + + auto address = WRAP(TWSegwitAddress, TWSegwitAddressCreateWithPublicKey(TWHRPBitcoin, publicKey.get())); + auto string = WRAPS(TWSegwitAddressDescription(address.get())); + + ASSERT_STREQ(address1, TWStringUTF8Bytes(string.get())); +} + +TEST(TWSegwitAddress, InitWithAddress) { + auto string = STRING(address1); + auto address = WRAP(TWSegwitAddress, TWSegwitAddressCreateWithString(string.get())); + auto description = WRAPS(TWSegwitAddressDescription(address.get())); + + ASSERT_TRUE(address.get() != nullptr); + ASSERT_STREQ(address1, TWStringUTF8Bytes(description.get())); + + ASSERT_EQ(0, TWSegwitAddressWitnessVersion(address.get())); + + ASSERT_EQ(TWHRPBitcoin, TWSegwitAddressHRP(address.get())); +} + +TEST(TWSegwitAddress, TaprootString) { + const auto string = STRING(address3Taproot); + const auto address = WRAP(TWSegwitAddress, TWSegwitAddressCreateWithString(string.get())); + ASSERT_TRUE(address.get() != nullptr); + + const auto description = WRAPS(TWSegwitAddressDescription(address.get())); + ASSERT_STREQ(address3Taproot, TWStringUTF8Bytes(description.get())); + + ASSERT_EQ(1, TWSegwitAddressWitnessVersion(address.get())); // taproot has segwit version 1 + + ASSERT_EQ(TWHRPBitcoin, TWSegwitAddressHRP(address.get())); +} + +TEST(TWSegwitAddress, InvalidAddress) { + std::vector> strings = { + STRING("bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"), + STRING("bc1rw5uspcuh"), + STRING("bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"), + STRING("BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"), + STRING("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"), + STRING("bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"), + STRING("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"), + STRING("bc1gmk9yu"), + }; + for (auto& string : strings) { + ASSERT_TRUE(TWSegwitAddressCreateWithString(string.get()) == nullptr) << "Invalid address '" << TWStringUTF8Bytes(string.get()) << "' reported as valid."; + } +} diff --git a/tests/Bitcoin/TransactionPlanTests.cpp b/tests/chains/Bitcoin/TransactionPlanTests.cpp similarity index 88% rename from tests/Bitcoin/TransactionPlanTests.cpp rename to tests/chains/Bitcoin/TransactionPlanTests.cpp index c290bf76a58..d906c863056 100644 --- a/tests/Bitcoin/TransactionPlanTests.cpp +++ b/tests/chains/Bitcoin/TransactionPlanTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2021 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,14 +10,13 @@ #include "Bitcoin/TransactionPlan.h" #include "Bitcoin/TransactionBuilder.h" #include "Bitcoin/FeeCalculator.h" +#include "HexCoding.h" #include "proto/Bitcoin.pb.h" #include #include -using namespace TW; -using namespace TW::Bitcoin; - +namespace TW::Bitcoin { TEST(TransactionPlan, OneTypical) { auto utxos = buildTestUTXOs({100'000}); @@ -120,6 +119,19 @@ TEST(TransactionPlan, OneFitsExactlyHighFee) { EXPECT_EQ(feeCalculator.calculate(1, 2, byteFee), 1740); } +TEST(TransactionPlan, OneMissingPrivateKey) { + auto utxos = buildTestUTXOs({100'000}); + auto byteFee = 1; + auto sigingInput = buildSigningInput(50'000, byteFee, utxos, false, TWCoinTypeBitcoin, true); + + auto txPlan = TransactionBuilder::plan(sigingInput); + + EXPECT_TRUE(verifyPlan(txPlan, {100'000}, 50'000, 147)); + + auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); + EXPECT_EQ(feeCalculator.calculate(1, 2, byteFee), 174); +} + TEST(TransactionPlan, TwoFirstEnough) { auto utxos = buildTestUTXOs({20'000, 80'000}); auto sigingInput = buildSigningInput(15'000, 1, utxos); @@ -182,7 +194,7 @@ TEST(TransactionPlan, ThreeNoDust) { } TEST(TransactionPlan, TenThree) { - auto utxos = buildTestUTXOs({1'000, 2'000, 100'000, 3'000, 4'000, 5,000, 125'000, 6'000, 150'000, 7'000}); + auto utxos = buildTestUTXOs({1'000, 2'000, 100'000, 3'000, 4'000, 5, 000, 125'000, 6'000, 150'000, 7'000}); auto sigingInput = buildSigningInput(300'000, 1, utxos); auto txPlan = TransactionBuilder::plan(sigingInput); @@ -249,7 +261,7 @@ TEST(TransactionPlan, Inputs5_33Req19NoDustFee2) { // UTXOs smaller than singleInputFee are not included auto txPlan = TransactionBuilder::plan(sigingInput); - auto expectedFee = 283*byteFee; + auto expectedFee = 283 * byteFee; EXPECT_TRUE(verifyPlan(txPlan, {6'000, 8'000, 10'000}, 19'000, expectedFee)); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); @@ -264,7 +276,7 @@ TEST(TransactionPlan, Inputs5_33Req19Dust1Fee5) { // UTXOs smaller than singleInputFee are not included auto txPlan = TransactionBuilder::plan(sigingInput); - auto expectedFee = 283*byteFee; + auto expectedFee = 283 * byteFee; EXPECT_TRUE(verifyPlan(txPlan, {6'000, 8'000, 10'000}, 19'000, expectedFee)); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); @@ -279,7 +291,7 @@ TEST(TransactionPlan, Inputs5_33Req19Dust1Fee9) { // UTXOs smaller than singleInputFee are not included auto txPlan = TransactionBuilder::plan(sigingInput); - auto expectedFee = 283*byteFee; + auto expectedFee = 283 * byteFee; EXPECT_TRUE(verifyPlan(txPlan, {6'000, 8'000, 10'000}, 19'000, expectedFee)); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); @@ -305,7 +317,7 @@ TEST(TransactionPlan, Inputs5_33Req13Fee20) { // UTXOs smaller than singleInputFee are not included auto txPlan = TransactionBuilder::plan(sigingInput); - auto expectedFee = 283*byteFee; + auto expectedFee = 283 * byteFee; EXPECT_TRUE(verifyPlan(txPlan, {6'000, 8'000, 10'000}, 13'000, expectedFee)); auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); @@ -518,7 +530,7 @@ TEST(TransactionPlan, ManyUtxosNonmax_400) { valueSum += val; } const uint64_t requestedAmount = valueSum / 8; - EXPECT_EQ(requestedAmount, 1'002'500); + EXPECT_EQ(requestedAmount, 1'002'500ul); auto utxos = buildTestUTXOs(values); auto sigingInput = buildSigningInput(requestedAmount, byteFee, utxos, false, TWCoinTypeBitcoin); @@ -533,8 +545,8 @@ TEST(TransactionPlan, ManyUtxosNonmax_400) { subset.push_back(val); subsetSum += val; } - EXPECT_EQ(subset.size(), 27); - EXPECT_EQ(subsetSum, 1'044'900); + EXPECT_EQ(subset.size(), 27ul); + EXPECT_EQ(subsetSum, 1'044'900ul); EXPECT_TRUE(verifyPlan(txPlan, subset, requestedAmount, 19'150)); } @@ -549,7 +561,7 @@ TEST(TransactionPlan, ManyUtxosNonmax_5000_simple) { valueSum += val; } const uint64_t requestedAmount = valueSum / 20; - EXPECT_EQ(requestedAmount, 62'512'500); + EXPECT_EQ(requestedAmount, 62'512'500ul); // Use Ravencoin, because of faster non-segwit estimation, and one of the original issues was with this coin. auto utxos = buildTestUTXOs(values); @@ -565,8 +577,8 @@ TEST(TransactionPlan, ManyUtxosNonmax_5000_simple) { subset.push_back(val); subsetSum += val; } - EXPECT_EQ(subset.size(), 1220); - EXPECT_EQ(subsetSum, 76'189'000); + EXPECT_EQ(subset.size(), 1220ul); + EXPECT_EQ(subsetSum, 76'189'000ul); EXPECT_TRUE(verifyPlan(txPlan, subset, requestedAmount, 1'806'380)); } @@ -598,10 +610,10 @@ TEST(TransactionPlan, ManyUtxosMax_400) { filteredValueSum += val; } } - EXPECT_EQ(valueSum, 8'020'000); - EXPECT_EQ(dustLimit, 1480); - EXPECT_EQ(filteredValues.size(), 386); - EXPECT_EQ(filteredValueSum, 80'09'500); + EXPECT_EQ(valueSum, 8'020'000ul); + EXPECT_EQ(dustLimit, 1480ul); + EXPECT_EQ(filteredValues.size(), 386ul); + EXPECT_EQ(filteredValueSum, 80'09'500ul); EXPECT_TRUE(verifyPlan(txPlan, filteredValues, 7'437'780, 571'720)); } @@ -633,9 +645,46 @@ TEST(TransactionPlan, ManyUtxosMax_5000_simple) { filteredValueSum += val; } } - EXPECT_EQ(valueSum, 1'250'250'000); - EXPECT_EQ(dustLimit, 1500); - EXPECT_EQ(filteredValues.size(), 3000); - EXPECT_EQ(filteredValueSum, 454'350'000); + EXPECT_EQ(valueSum, 1'250'250'000ul); + EXPECT_EQ(dustLimit, 1500ul); + EXPECT_EQ(filteredValues.size(), 3000ul); + EXPECT_EQ(filteredValueSum, 454'350'000ul); EXPECT_TRUE(verifyPlan(txPlan, filteredValues, 449'909'560, 4'440'440)); } + +TEST(TransactionPlan, OpReturn) { + auto ownAddress = "bc1q7s0a2l4aguksehx8hf93hs9yggl6njxds6m02g"; + auto toAddress = "bc1qxu5a8gtnjxw3xwdlmr2gl9d76h9fysu3zl656e"; + auto utxoAmount = 342101; + auto toAmount = 300000; + int byteFee = 126; + Data memo = data("SWAP:THOR.RUNE:thor1tpercamkkxec0q0jk6ltdnlqvsw29guap8wmcl:"); + + auto signingInput = Proto::SigningInput(); + signingInput.set_hash_type(TWBitcoinSigHashTypeAll); + signingInput.set_amount(toAmount); + signingInput.set_byte_fee(byteFee); + signingInput.set_to_address(toAddress); + signingInput.set_change_address(ownAddress); + signingInput.set_output_op_return(memo.data(), memo.size()); + + auto& utxo = *signingInput.add_utxo(); + auto utxoHash = parse_hex("30b82960291a39de3664ec4c844a815e3e680e29b4d3a919e450f0c119cf4e35"); + std::reverse(utxoHash.begin(), utxoHash.end()); + utxo.mutable_out_point()->set_hash(utxoHash.data(), utxoHash.size()); + utxo.mutable_out_point()->set_index(1); + utxo.mutable_out_point()->set_sequence(UINT32_MAX); + utxo.set_amount(utxoAmount); + + auto txPlan = TransactionBuilder::plan(signingInput); + + EXPECT_TRUE(verifyPlan(txPlan, {342101}, 300000, 205 * byteFee)); + EXPECT_EQ(txPlan.outputOpReturn.size(), 59ul); + EXPECT_EQ(hex(txPlan.outputOpReturn), "535741503a54484f522e52554e453a74686f72317470657263616d6b6b7865633071306a6b366c74646e6c7176737732396775617038776d636c3a"); + + auto& feeCalculator = getFeeCalculator(TWCoinTypeBitcoin); + EXPECT_EQ(feeCalculator.calculate(1, 2, byteFee), 174 * byteFee); + EXPECT_EQ(feeCalculator.calculate(1, 3, byteFee), 205 * byteFee); +} + +} // namespace TW::Bitcoin diff --git a/tests/Bitcoin/TxComparisonHelper.cpp b/tests/chains/Bitcoin/TxComparisonHelper.cpp similarity index 84% rename from tests/Bitcoin/TxComparisonHelper.cpp rename to tests/chains/Bitcoin/TxComparisonHelper.cpp index bc2d55d4506..f99fae78c82 100644 --- a/tests/Bitcoin/TxComparisonHelper.cpp +++ b/tests/chains/Bitcoin/TxComparisonHelper.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -16,12 +16,10 @@ #include "HexCoding.h" #include "BinaryCoding.h" -#include #include #include -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { auto emptyTxOutPoint = OutPoint(parse_hex("1d0f172a0ecb48aee1be1f2687d2963ae33f71a1"), 0); @@ -30,30 +28,33 @@ UTXO buildTestUTXO(int64_t amount) { utxo.amount = amount; utxo.outPoint = emptyTxOutPoint; utxo.outPoint.sequence = UINT32_MAX; - utxo.script = Script(parse_hex("0014" "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); + utxo.script = Script(parse_hex("0014" + "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); return utxo; } UTXOs buildTestUTXOs(const std::vector& amounts) { UTXOs utxos; - for (auto it = amounts.begin(); it != amounts.end(); it++) { - utxos.push_back(buildTestUTXO(*it)); + for (long long amount : amounts) { + utxos.push_back(buildTestUTXO(amount)); } return utxos; } -SigningInput buildSigningInput(Amount amount, int byteFee, const UTXOs& utxos, bool useMaxAmount, enum TWCoinType coin) { +SigningInput buildSigningInput(Amount amount, int byteFee, const UTXOs& utxos, bool useMaxAmount, enum TWCoinType coin, bool omitPrivateKey) { SigningInput input; input.amount = amount; input.byteFee = byteFee; input.useMaxAmount = useMaxAmount; input.coinType = coin; - - auto utxoKey = PrivateKey(parse_hex("619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9")); - auto pubKey = utxoKey.getPublicKey(TWPublicKeyTypeSECP256k1); - auto utxoPubkeyHash = Hash::ripemd(Hash::sha256(pubKey.bytes)); - assert(hex(utxoPubkeyHash) == "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1"); - input.privateKeys.push_back(utxoKey); + + if (!omitPrivateKey) { + auto utxoKey = PrivateKey(parse_hex("619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9")); + auto pubKey = utxoKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto utxoPubkeyHash = Hash::ripemd(Hash::sha256(pubKey.bytes)); + assert(hex(utxoPubkeyHash) == "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1"); + input.privateKeys.push_back(utxoKey); + } input.utxos = utxos; input.toAddress = "1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx"; @@ -63,7 +64,7 @@ SigningInput buildSigningInput(Amount amount, int byteFee, const UTXOs& utxos, b int64_t sumUTXOs(const UTXOs& utxos) { int64_t s = 0u; - for (auto& utxo: utxos) { + for (auto& utxo : utxos) { s += utxo.amount; } return s; @@ -76,7 +77,7 @@ bool verifySelectedUTXOs(const UTXOs& selected, const std::vector& expe std::cerr << "Wrong number of selected UTXOs, " << selected.size() << " vs. " << expectedAmounts.size() << std::endl; } int errorCount = 0; - for (auto i = 0; i < selected.size() && i < expectedAmounts.size(); ++i) { + for (auto i = 0ul; i < selected.size() && i < expectedAmounts.size(); ++i) { if (expectedAmounts[i] != selected[i].amount) { ret = false; ++errorCount; @@ -102,8 +103,8 @@ bool verifyPlan(const TransactionPlan& plan, const std::vector& utxoAmo std::cerr << "Mismatch in fee, act " << plan.fee << ", exp " << fee << std::endl; } int64_t sumExpectedUTXOs = 0; - for (auto i = 0; i < utxoAmounts.size(); ++i) { - sumExpectedUTXOs += utxoAmounts[i]; + for (long long utxoAmount : utxoAmounts) { + sumExpectedUTXOs += utxoAmount; } if (plan.availableAmount != sumExpectedUTXOs) { ret = false; @@ -138,7 +139,7 @@ bool operator==(const EncodedTxSize& s1, const EncodedTxSize& s2) { } EncodedTxSize getEncodedTxSize(const Transaction& tx) { - EncodedTxSize size; + EncodedTxSize size{}; { // full segwit size Data data; tx.encode(data, Transaction::SegwitFormatMode::Segwit); @@ -154,13 +155,13 @@ EncodedTxSize getEncodedTxSize(const Transaction& tx) { Data data; tx.encodeWitness(data); witnessSize = data.size(); - assert(size.segwit - size.nonSegwit == 2 + witnessSize); + assert(size.segwit - size.nonSegwit == 2ul + witnessSize); } // compute virtual size: 3/4 of (smaller) non-segwit + 1/4 of segwit size - uint64_t sum = size.nonSegwit * 3 + size.segwit; + uint64_t sum = size.nonSegwit * 3 + size.segwit; size.virtualBytes = sum / 4 + (sum % 4 != 0); // alternative computation: (smaller) non-segwit + 1/4 of the diff (witness-only) - uint64_t vSize2 = size.nonSegwit + (witnessSize + 2)/ 4 + ((witnessSize + 2) % 4 != 0); + uint64_t vSize2 = size.nonSegwit + (witnessSize + 2) / 4 + ((witnessSize + 2) % 4 != 0); assert(size.virtualBytes == vSize2); return size; } @@ -194,7 +195,7 @@ void prettyPrintScript(const Script& script) { void prettyPrintTransaction(const Transaction& tx, bool useWitnessFormat) { Data data; - encode32LE(tx.version, data); + encode32LE(tx._version, data); std::cout << " \"" << hex(data) << "\" // version\n"; if (useWitnessFormat) { @@ -205,7 +206,7 @@ void prettyPrintTransaction(const Transaction& tx, bool useWitnessFormat) { data.clear(); encodeVarInt(tx.inputs.size(), data); std::cout << " \"" << hex(data) << "\" // inputs\n"; - for (auto& input: tx.inputs) { + for (auto& input : tx.inputs) { auto& outpoint = reinterpret_cast(input.previousOutput); std::cout << " \"" << hex(outpoint.hash) << "\""; data.clear(); @@ -221,7 +222,7 @@ void prettyPrintTransaction(const Transaction& tx, bool useWitnessFormat) { data.clear(); encodeVarInt(tx.outputs.size(), data); std::cout << " \"" << hex(data) << "\" // outputs\n"; - for (auto& output: tx.outputs) { + for (auto& output : tx.outputs) { data.clear(); encode64LE(output.value, data); std::cout << " \"" << hex(data) << "\""; @@ -231,11 +232,11 @@ void prettyPrintTransaction(const Transaction& tx, bool useWitnessFormat) { if (useWitnessFormat) { std::cout << " // witness\n"; - for (auto& input: tx.inputs) { + for (auto& input : tx.inputs) { data.clear(); encodeVarInt(input.scriptWitness.size(), data); std::cout << " \"" << hex(data) << "\"\n"; - for (auto& item: input.scriptWitness) { + for (auto& item : input.scriptWitness) { data.clear(); encodeVarInt(item.size(), data); std::cout << " \"" << hex(data) << "\""; @@ -249,3 +250,5 @@ void prettyPrintTransaction(const Transaction& tx, bool useWitnessFormat) { std::cout << " \"" << hex(data) << "\" // nLockTime\n"; std::cout << "\n"; } + +} // namespace TW::Bitcoin diff --git a/tests/Bitcoin/TxComparisonHelper.h b/tests/chains/Bitcoin/TxComparisonHelper.h similarity index 91% rename from tests/Bitcoin/TxComparisonHelper.h rename to tests/chains/Bitcoin/TxComparisonHelper.h index 984a818cc9b..4cb2f92a1d2 100644 --- a/tests/Bitcoin/TxComparisonHelper.h +++ b/tests/chains/Bitcoin/TxComparisonHelper.h @@ -16,9 +16,7 @@ #include #include -using namespace TW; -using namespace TW::Bitcoin; - +namespace TW::Bitcoin { /// Build a dummy UTXO with the given amount UTXO buildTestUTXO(int64_t amount); @@ -26,8 +24,8 @@ UTXO buildTestUTXO(int64_t amount); /// Build a set of dummy UTXO with the given amounts UTXOs buildTestUTXOs(const std::vector& amounts); -SigningInput buildSigningInput(Amount amount, int byteFee, const UTXOs& utxos, - bool useMaxAmount = false, enum TWCoinType coin = TWCoinTypeBitcoin); +SigningInput buildSigningInput(Amount amount, int byteFee, const UTXOs& utxos, + bool useMaxAmount = false, enum TWCoinType coin = TWCoinTypeBitcoin, bool omitPrivateKey = false); /// Compare a set of selected UTXOs to the expected set of amounts. /// Returns false on mismatch, and error is printed (stderr). @@ -56,3 +54,5 @@ bool validateEstimatedSize(const Transaction& tx, int smallerTolerance = -1, int /// Print out a transaction in a nice format, as structured hex strings. void prettyPrintTransaction(const Transaction& tx, bool useWitnessFormat = true); + +} // namespace TW::Bitcoin diff --git a/tests/BitcoinCash/TWBitcoinCashTests.cpp b/tests/chains/BitcoinCash/TWBitcoinCashTests.cpp similarity index 83% rename from tests/BitcoinCash/TWBitcoinCashTests.cpp rename to tests/chains/BitcoinCash/TWBitcoinCashTests.cpp index 30536e06cea..19f6fc564d4 100644 --- a/tests/BitcoinCash/TWBitcoinCashTests.cpp +++ b/tests/chains/BitcoinCash/TWBitcoinCashTests.cpp @@ -8,7 +8,7 @@ #include "Bitcoin/SigHashType.h" #include "HexCoding.h" #include "proto/Bitcoin.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include @@ -23,21 +23,43 @@ #include using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin::tests { + +// clang-format off TEST(BitcoinCash, Address) { EXPECT_TRUE(TWAnyAddressIsValid(STRING("pqx578nanz2h2estzmkr53zqdg6qt8xyqvwhn6qeyc").get(), TWCoinTypeBitcoinCash)); + EXPECT_TRUE(TWAnyAddressIsValid(STRING("bitcoincash:pqx578nanz2h2estzmkr53zqdg6qt8xyqvwhn6qeyc").get(), TWCoinTypeBitcoinCash)); } TEST(BitcoinCash, ValidAddress) { auto string = STRING("bitcoincash:qqa2qx0d8tegw32xk8u75ws055en4x3h2u0e6k46y4"); auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeBitcoinCash)); ASSERT_NE(address.get(), nullptr); - + auto script = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(string.get(), TWCoinTypeBitcoinCash)); ASSERT_FALSE(TWBitcoinScriptSize(script.get()) == 0); } +TEST(BitcoinCash, InvalidAddress) { + // Wrong checksum + EXPECT_FALSE(TWAnyAddressIsValid(STRING("pqx578nanz2h2estzmkr53zqdg6qt8xyqvffffffff").get(), TWCoinTypeBitcoinCash)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("bitcoincash:pqx578nanz2h2estzmkr53zqdg6qt8xyqvffffffff").get(), TWCoinTypeBitcoinCash)); + + // Valid eCash addresses are invalid for BCH + EXPECT_TRUE(TWAnyAddressIsValid(STRING("pqx578nanz2h2estzmkr53zqdg6qt8xyqvh683mrz0").get(), TWCoinTypeECash)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("pqx578nanz2h2estzmkr53zqdg6qt8xyqvh683mrz0").get(), TWCoinTypeBitcoinCash)); + + EXPECT_TRUE(TWAnyAddressIsValid(STRING("ecash:pqx578nanz2h2estzmkr53zqdg6qt8xyqvh683mrz0").get(), TWCoinTypeECash)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("ecash:pqx578nanz2h2estzmkr53zqdg6qt8xyqvh683mrz0").get(), TWCoinTypeBitcoinCash)); + + // Wrong prefix + EXPECT_FALSE(TWAnyAddressIsValid(STRING("bcash:pqx578nanz2h2estzmkr53zqdg6qt8xyqvwhn6qeyc").get(), TWCoinTypeBitcoinCash)); + + // Wrong base 32 (characters o, i) + EXPECT_FALSE(TWAnyAddressIsValid(STRING("poi578nanz2h2estzmkr53zqdg6qt8xyqvwhn6qeyc").get(), TWCoinTypeBitcoinCash)); +} + TEST(BitcoinCash, LegacyToCashAddr) { auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA("28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4").get())); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), true)); @@ -131,7 +153,7 @@ TEST(BitcoinCash, SignTransaction) { EXPECT_EQ(output.transaction().outputs_size(), 2); EXPECT_EQ(output.transaction().outputs(0).value(), amount); EXPECT_EQ(output.transaction().outputs(1).value(), 4325); - EXPECT_EQ(output.encoded().length(), 226); + EXPECT_EQ(output.encoded().length(), 226ul); ASSERT_EQ(hex(output.encoded()), "01000000" "01" @@ -141,3 +163,6 @@ TEST(BitcoinCash, SignTransaction) { "e510000000000000" "1976a9149e089b6889e032d46e3b915a3392edfd616fb1c488ac" "00000000"); } +// clang-format on + +} // namespace TW::Bitcoin::tests diff --git a/tests/chains/BitcoinCash/TWCoinTypeTests.cpp b/tests/chains/BitcoinCash/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..666aa70328c --- /dev/null +++ b/tests/chains/BitcoinCash/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWBitcoinCashCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBitcoinCash)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBitcoinCash, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBitcoinCash, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBitcoinCash)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBitcoinCash)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBitcoinCash), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeBitcoinCash)); + ASSERT_EQ(0x5, TWCoinTypeP2shPrefix(TWCoinTypeBitcoinCash)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBitcoinCash)); + assertStringsEqual(symbol, "BCH"); + assertStringsEqual(txUrl, "https://blockchair.com/bitcoin-cash/transaction/t123"); + assertStringsEqual(accUrl, "https://blockchair.com/bitcoin-cash/address/a12"); + assertStringsEqual(id, "bitcoincash"); + assertStringsEqual(name, "Bitcoin Cash"); +} diff --git a/tests/BitcoinGold/TWAddressTests.cpp b/tests/chains/BitcoinGold/TWAddressTests.cpp similarity index 96% rename from tests/BitcoinGold/TWAddressTests.cpp rename to tests/chains/BitcoinGold/TWAddressTests.cpp index 77c192461bb..977c77900e1 100644 --- a/tests/BitcoinGold/TWAddressTests.cpp +++ b/tests/chains/BitcoinGold/TWAddressTests.cpp @@ -5,7 +5,7 @@ // file LICENSE at the root of the source code distribution tree. // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/Address.h" #include "PrivateKey.h" #include "PublicKey.h" diff --git a/tests/BitcoinGold/TWBitcoinGoldTests.cpp b/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp similarity index 97% rename from tests/BitcoinGold/TWBitcoinGoldTests.cpp rename to tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp index 1621692b37b..760cd79b9a6 100644 --- a/tests/BitcoinGold/TWBitcoinGoldTests.cpp +++ b/tests/chains/BitcoinGold/TWBitcoinGoldTests.cpp @@ -6,25 +6,27 @@ #include #include -#include #include +#include #include #include -#include "Bitcoin/SegwitAddress.h" -#include "proto/Bitcoin.pb.h" #include "Bitcoin/OutPoint.h" #include "Bitcoin/Script.h" +#include "Bitcoin/SegwitAddress.h" +#include "Bitcoin/SigHashType.h" #include "Bitcoin/Transaction.h" #include "Bitcoin/TransactionBuilder.h" #include "Bitcoin/TransactionSigner.h" -#include "Bitcoin/SigHashType.h" #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "proto/Bitcoin.pb.h" +#include "TestUtilities.h" using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin::tests { + +// clang-format off TEST(TWBitcoinGoldScript, LockScriptTest) { auto script = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("btg1q6572ulr0kmywle8a30lvagm9xsg9k9n5cmzfdj").get(), TWCoinTypeBitcoinGold)); auto scriptData = WRAPD(TWBitcoinScriptData(script.get())); @@ -79,7 +81,7 @@ TEST(TWBitcoinGoldTxGeneration, TxGeneration) { auto utxoKey0 = parse_hex("cbe13a79b82ec7f8871b336a64fd8d531f598e7c9022e29c67e824cfd54af57f"); input.add_private_key(utxoKey0.data(), utxoKey0.size()); input.set_lock_time(0x00098971); - + auto scriptPub1 = Script(parse_hex("0014db746a75d9aae8995d135b1e19a04d7765242a8f")); auto scriptHash = std::vector(); @@ -94,7 +96,7 @@ TEST(TWBitcoinGoldTxGeneration, TxGeneration) { auto utxo0Script = parse_hex("0014d53cae7c6fb6c8efe4fd8bfecea36534105b1674"); utxo0->set_script(utxo0Script.data(), utxo0Script.size()); utxo0->set_amount(10000); - + auto hash0 = parse_hex("5727794fa2b94aa22a226e206130524201ede9b50e032526e713c848493a890f"); utxo0->mutable_out_point()->set_hash(hash0.data(), hash0.size()); utxo0->mutable_out_point()->set_index(0); @@ -122,4 +124,6 @@ TEST(TWBitcoinGoldTxGeneration, TxGeneration) { "71890900" // nLockTime ); } - +// clang-format on + +} // namespace TW::Bitcoin::tests diff --git a/tests/chains/BitcoinGold/TWCoinTypeTests.cpp b/tests/chains/BitcoinGold/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..5ab3db7a03b --- /dev/null +++ b/tests/chains/BitcoinGold/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWBitcoinGoldCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBitcoinGold)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("2f807d7734de35d2236a1b3d8704eb12954f5f82ea66987949b10e94d9999b23")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBitcoinGold, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("GJjz2Du9BoJQ3CPcoyVTHUJZSj62i1693U")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBitcoinGold, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBitcoinGold)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBitcoinGold)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBitcoinGold), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeBitcoinGold)); + ASSERT_EQ(23, TWCoinTypeP2shPrefix(TWCoinTypeBitcoinGold)); + ASSERT_EQ(0, TWCoinTypeStaticPrefix(TWCoinTypeBitcoinGold)); + assertStringsEqual(symbol, "BTG"); + assertStringsEqual(txUrl, "https://explorer.bitcoingold.org/insight/tx/2f807d7734de35d2236a1b3d8704eb12954f5f82ea66987949b10e94d9999b23"); + assertStringsEqual(accUrl, "https://explorer.bitcoingold.org/insight/address/GJjz2Du9BoJQ3CPcoyVTHUJZSj62i1693U"); + assertStringsEqual(id, "bitcoingold"); + assertStringsEqual(name, "Bitcoin Gold"); +} diff --git a/tests/chains/BitcoinGold/TWSegwitAddressTests.cpp b/tests/chains/BitcoinGold/TWSegwitAddressTests.cpp new file mode 100644 index 00000000000..0f8c1413fe1 --- /dev/null +++ b/tests/chains/BitcoinGold/TWSegwitAddressTests.cpp @@ -0,0 +1,57 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "TestUtilities.h" +#include "Bitcoin/SegwitAddress.h" +#include "Coin.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "HexCoding.h" +#include +#include + +using namespace TW; + +TEST(TWBitcoinGoldSegwitAddress, Valid) { + ASSERT_TRUE(Bitcoin::SegwitAddress::isValid("btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk")); + ASSERT_FALSE(Bitcoin::SegwitAddress::isValid("btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sl")); +} + +/// Initializes a Bech32 address with a human-readable part, a witness +/// version, and a witness program. +TEST(TWBitcoinGoldSegwitAddress, WitnessProgramToAddress) { + auto address = Bitcoin::SegwitAddress("btg", 0, parse_hex("5e6132a9ad21f7423081441ab4ae229501f6c8a8")); + + ASSERT_TRUE(Bitcoin::SegwitAddress::isValid(address.string())); + ASSERT_EQ(address.string(), "btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk"); +} + +/// Get address data from a Bech32 address +TEST(TWBitcoinGoldSegwitAddress, addressToData) { + auto data = TW::addressToData(TWCoinTypeBitcoinGold, "btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk"); + ASSERT_EQ(hex(data), "5e6132a9ad21f7423081441ab4ae229501f6c8a8"); +} + +/// Initializes a Bech32 address with a public key and a HRP prefix. +TEST(TWBitcoinGoldSegwitAddress, PubkeyToAddress) { + const auto publicKey = PublicKey(parse_hex("02f74712b5d765a73b52a14c1e113f2ef3f9502d09d5987ee40f53828cfe68b9a6"), TWPublicKeyTypeSECP256k1); + + /// construct with public key + auto address = Bitcoin::SegwitAddress(publicKey, "btg"); + + ASSERT_TRUE(Bitcoin::SegwitAddress::isValid(address.string())); + ASSERT_EQ(address.string(), "btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk"); +} + +/// Decodes a SegWit address. +TEST(TWBitcoinGoldSegwitAddress, Decode) { + auto result = Bitcoin::SegwitAddress::decode("btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk"); + + ASSERT_TRUE(std::get<2>(result)); + ASSERT_EQ(std::get<0>(result).string(), "btg1qtesn92ddy8m5yvypgsdtft3zj5qldj9g2u52sk"); + ASSERT_EQ(std::get<1>(result), "btg"); +} diff --git a/tests/BitcoinGold/TWSignerTests.cpp b/tests/chains/BitcoinGold/TWSignerTests.cpp similarity index 92% rename from tests/BitcoinGold/TWSignerTests.cpp rename to tests/chains/BitcoinGold/TWSignerTests.cpp index 9dc2d2b44d7..673e6437912 100644 --- a/tests/BitcoinGold/TWSignerTests.cpp +++ b/tests/chains/BitcoinGold/TWSignerTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,20 +9,18 @@ #include #include -#include "Bitcoin/SegwitAddress.h" -#include "proto/Bitcoin.pb.h" #include "Bitcoin/OutPoint.h" #include "Bitcoin/Script.h" +#include "Bitcoin/SigHashType.h" #include "Bitcoin/Transaction.h" #include "Bitcoin/TransactionBuilder.h" #include "Bitcoin/TransactionSigner.h" -#include "Bitcoin/SigHashType.h" #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "proto/Bitcoin.pb.h" #include "../Bitcoin/TxComparisonHelper.h" +#include "TestUtilities.h" -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { TEST(TWBitcoinGoldSigner, SignTransaction) { const int64_t amount = 10000; @@ -39,7 +37,6 @@ TEST(TWBitcoinGoldSigner, SignTransaction) { auto utxoKey0 = parse_hex("cbe13a79b82ec7f8871b336a64fd8d531f598e7c9022e29c67e824cfd54af57f"); input.add_private_key(utxoKey0.data(), utxoKey0.size()); - auto scriptPub1 = Script(parse_hex("0014db746a75d9aae8995d135b1e19a04d7765242a8f")); auto scriptHash = std::vector(); scriptPub1.matchPayToWitnessPublicKeyHash(scriptHash); @@ -53,18 +50,17 @@ TEST(TWBitcoinGoldSigner, SignTransaction) { auto utxo0Script = parse_hex("0014d53cae7c6fb6c8efe4fd8bfecea36534105b1674"); utxo0->set_script(utxo0Script.data(), utxo0Script.size()); utxo0->set_amount(99000); - + auto hash0 = parse_hex("1d4653041a1915b3a52d47aeaa119c8f79ed7634a93692a6e811173067464f03"); utxo0->mutable_out_point()->set_hash(hash0.data(), hash0.size()); utxo0->mutable_out_point()->set_index(1); utxo0->mutable_out_point()->set_sequence(0xfffffffd); input.set_lock_time(0x00098971); - Proto::TransactionPlan plan; { // try plan first - ANY_PLAN(input, plan, TWCoinTypeGroestlcoin); + ANY_PLAN(input, plan, TWCoinTypeBitcoinGold); EXPECT_TRUE(verifyPlan(plan, {99'000}, amount, 141)); } @@ -83,7 +79,8 @@ TEST(TWBitcoinGoldSigner, SignTransaction) { Data serialized; signedTx.encode(serialized); // BitcoinGold Mainnet: https://btg2.trezor.io/tx/db26faec66d070045df0da56140349beb5a12bd14bca12b162fded8f84d18afa - EXPECT_EQ(serialized.size(), 222); + EXPECT_EQ(serialized.size(), 222ul); + // clang-format off ASSERT_EQ(hex(serialized), "01000000" "0001" @@ -96,6 +93,8 @@ TEST(TWBitcoinGoldSigner, SignTransaction) { "4730440220325c56363b17e1b1329efeb400c0933a3d9adfb304f29889b3ef01084aef19e302202a69d9be9ef668b5a5517fbfa42e1fc26b3f8b582c721bd1eabd721322bc2b6c41" "2103e00b5dec8078d526fba090247bd92db6b67a4dd1953b788cea9b52de9471b8cf" "71890900" - ); + ); + // clang-format on } - + +} // namespace TW::Bitcoin diff --git a/tests/chains/Bluzelle/TWCoinTypeTests.cpp b/tests/chains/Bluzelle/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..09dcb1aed7b --- /dev/null +++ b/tests/chains/Bluzelle/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWCoinTypeBluzelle, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBluzelle)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("AC026E0EC6E33A77D5EA6B9CEF9810699BC2AD8C5582E007E7857457C6D3B819")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBluzelle, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("bluzelle1q9cryfal7u3jvnq6er5ufety20xtzw6ycx2te9")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBluzelle, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBluzelle)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBluzelle)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBluzelle), 6); + ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeBluzelle)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeBluzelle)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBluzelle)); + assertStringsEqual(symbol, "BLZ"); + assertStringsEqual(txUrl, "https://bigdipper.net.bluzelle.com/transactions/AC026E0EC6E33A77D5EA6B9CEF9810699BC2AD8C5582E007E7857457C6D3B819"); + assertStringsEqual(accUrl, "https://bigdipper.net.bluzelle.com/account/bluzelle1q9cryfal7u3jvnq6er5ufety20xtzw6ycx2te9"); + assertStringsEqual(id, "bluzelle"); + assertStringsEqual(name, "Bluzelle"); +} diff --git a/tests/chains/Boba/TWCoinTypeTests.cpp b/tests/chains/Boba/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..9d9d27f52cf --- /dev/null +++ b/tests/chains/Boba/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWBobaCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeBoba)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x31533707c3feb3b10f7deeea387ff8893f229253e65ca6b14d2400bf95b5d103")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeBoba, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x4F96F50eDB37a19216d87693E5dB241e31bD3735")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeBoba, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeBoba)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeBoba)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeBoba), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeBoba)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeBoba)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeBoba)); + assertStringsEqual(symbol, "BOBAETH"); + assertStringsEqual(txUrl, "https://blockexplorer.boba.network/tx/0x31533707c3feb3b10f7deeea387ff8893f229253e65ca6b14d2400bf95b5d103"); + assertStringsEqual(accUrl, "https://blockexplorer.boba.network/address/0x4F96F50eDB37a19216d87693E5dB241e31bD3735"); + assertStringsEqual(id, "boba"); + assertStringsEqual(name, "Boba"); +} diff --git a/tests/chains/Callisto/TWCoinTypeTests.cpp b/tests/chains/Callisto/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..e742718974f --- /dev/null +++ b/tests/chains/Callisto/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWCallistoCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCallisto)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCallisto, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCallisto, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCallisto)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCallisto)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCallisto), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeCallisto)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeCallisto)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeCallisto)); + assertStringsEqual(symbol, "CLO"); + assertStringsEqual(txUrl, "https://explorer.callisto.network/tx/t123"); + assertStringsEqual(accUrl, "https://explorer.callisto.network/addr/a12"); + assertStringsEqual(id, "callisto"); + assertStringsEqual(name, "Callisto"); +} diff --git a/tests/chains/Cardano/AddressTests.cpp b/tests/chains/Cardano/AddressTests.cpp new file mode 100644 index 00000000000..8a8918fe6c9 --- /dev/null +++ b/tests/chains/Cardano/AddressTests.cpp @@ -0,0 +1,510 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cardano/AddressV3.h" + +#include "Coin.h" +#include "HDWallet.h" +#include "HexCoding.h" +#include "PrivateKey.h" + +#include + +using namespace TW; +using namespace std; + +namespace TW::Cardano::tests { + +const auto dummyKey = parse_hex("1111111111111111111111111111111111111111111111111111111111111111"); + +TEST(CardanoAddress, V3NetworkIdKind) { + EXPECT_EQ(AddressV3::firstByte(AddressV3::Network_Test, AddressV3::Kind_Base), 0); + EXPECT_EQ(AddressV3::firstByte(AddressV3::Network_Production, AddressV3::Kind_Base), 1); + EXPECT_EQ(AddressV3::firstByte(AddressV3::NetworkId(2), AddressV3::Kind(3)), 50); + + EXPECT_EQ(AddressV3::networkIdFromFirstByte(0), AddressV3::Network_Test); + EXPECT_EQ(AddressV3::networkIdFromFirstByte(1), AddressV3::Network_Production); + EXPECT_EQ(AddressV3::networkIdFromFirstByte(50), AddressV3::NetworkId(2)); + + EXPECT_EQ(AddressV3::kindFromFirstByte(0), AddressV3::Kind_Base); + EXPECT_EQ(AddressV3::kindFromFirstByte(1), AddressV3::Kind_Base); + EXPECT_EQ(AddressV3::kindFromFirstByte(50), AddressV3::Kind(3)); +} + +TEST(CardanoAddress, Validation) { + // valid V3 address + ASSERT_TRUE(AddressV3::isValidLegacy("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23")); + + ASSERT_TRUE(AddressV3::isValid("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23")); + ASSERT_TRUE(AddressV3::isValid("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5")); + ASSERT_TRUE(AddressV3::isValid("addr1vyuca7esanpgs4ke0um3ft6f4yaeuz3ftpfqx9nxpct2uyqu7dvlp")); // enterprise + ASSERT_TRUE(AddressV3::isValid("stake1uy9ggsc9qls4pu9qvyyacwnmr9tt0gzcdt5s0zj4au8qkqc65geks")); // reward + ASSERT_TRUE(AddressV3::isValid("addr1sxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qmxapsy")); // kind 8 + + // valid V2 address + ASSERT_TRUE(AddressV3::isValidLegacy("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx")); + ASSERT_TRUE(AddressV3::isValidLegacy("Ae2tdPwUPEZ6RUCnjGHFqi59k5WZLiv3HoCCNGCW8SYc5H9srdTzn1bec4W")); + + ASSERT_FALSE(AddressV3::isValid("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx")); + + // valid V1 address + ASSERT_TRUE(AddressV3::isValidLegacy("DdzFFzCqrhssmYoG5Eca1bKZFdGS8d6iag1mU4wbLeYcSPVvBNF2wRG8yhjzQqErbg63N6KJA4DHqha113tjKDpGEwS5x1dT2KfLSbSJ")); + ASSERT_TRUE(AddressV3::isValidLegacy("DdzFFzCqrht7HGoJ87gznLktJGywK1LbAJT2sbd4txmgS7FcYLMQFhawb18ojS9Hx55mrbsHPr7PTraKh14TSQbGBPJHbDZ9QVh6Z6Di")); + + // invalid V3, invalid network + ASSERT_FALSE(AddressV3::isValidLegacy("addr1sna05l45z33zpkm8z44q8f0h57wxvm0c86e34wlmua7gtcrdgrdrzy8ny3walyfjanhe33nsyuh088qr5gepqaen6jsa9r94xvvd7fh6jc3e6x")); + // invalid V3, invalid prefix + ASSERT_FALSE(AddressV3::isValidLegacy("prefix1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35q3hm7lv")); + // invalid V3, length + ASSERT_FALSE(AddressV3::isValidLegacy("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32xsmpqws7")); + // invalid checksum V3 + ASSERT_FALSE(AddressV3::isValidLegacy("PREFIX1qvqsyqcyq5rqwzqfpg9scrgwpugpzysnzs23v9ccrydpk8qarc0jqxuzx4s")); + // invalid checksum V2 + ASSERT_FALSE(AddressV3::isValidLegacy("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvm")); + // random + ASSERT_FALSE(AddressV3::isValidLegacy("hasoiusaodiuhsaijnnsajnsaiussai")); + // empty + ASSERT_FALSE(AddressV3::isValidLegacy("")); + ASSERT_FALSE(AddressV3::isValidLegacy("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk2")); +} + +TEST(CardanoAddress, FromStringV2) { + { + auto address = AddressV3("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_EQ(address.kind, AddressV3::Kind_Base); + EXPECT_EQ(hex(address.data()), "01" "8d98bea0414243dc84070f96265577e7e6cf702d62e871016885034e" "cc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + } + { + auto address = AddressV3("addr1qxteqxsgxrs4he9d28lh70qu7qfz7saj6dmxwsqyle2yp3xvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35quehtx3"); + EXPECT_EQ(address.kind, AddressV3::Kind_Base); + EXPECT_EQ(hex(address.data()), "01" "97901a0830e15be4ad51ff7f3c1cf0122f43b2d376674004fe5440c4" "cc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + } + { + auto address = AddressV3("addr1q8sfzcwce0fqll3symd7f0amayxqq68nxt2u8pgen9y00tkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35q40ytea"); + EXPECT_EQ(address.kind, AddressV3::Kind_Base); + EXPECT_EQ(hex(address.data()), "01" "e09161d8cbd20ffe3026dbe4bfbbe90c0068f332d5c385199948f7ae" "cc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + } + { + auto address = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); + ASSERT_EQ(address.string(), "Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); + } + { + auto address = AddressV3("DdzFFzCqrhssmYoG5Eca1bKZFdGS8d6iag1mU4wbLeYcSPVvBNF2wRG8yhjzQqErbg63N6KJA4DHqha113tjKDpGEwS5x1dT2KfLSbSJ"); + ASSERT_EQ(address.string(), "DdzFFzCqrhssmYoG5Eca1bKZFdGS8d6iag1mU4wbLeYcSPVvBNF2wRG8yhjzQqErbg63N6KJA4DHqha113tjKDpGEwS5x1dT2KfLSbSJ"); + } +} + +TEST(CardanoAddress, FromStringV3_Base) { + { + auto address = AddressV3("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_EQ(address.string(), "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_EQ(address.string("addr"), "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_EQ(AddressV3::Network_Production, address.networkId); + EXPECT_EQ(AddressV3::Kind_Base, address.kind); + EXPECT_EQ("8d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468", hex(address.bytes)); + } +} + +TEST(CardanoAddress, FromStringV3_Enterprise) { + { + auto address = AddressV3("addr1vyuca7esanpgs4ke0um3ft6f4yaeuz3ftpfqx9nxpct2uyqu7dvlp"); + EXPECT_EQ(address.string(), "addr1vyuca7esanpgs4ke0um3ft6f4yaeuz3ftpfqx9nxpct2uyqu7dvlp"); + EXPECT_EQ(AddressV3::Network_Production, address.networkId); + EXPECT_EQ(AddressV3::Kind_Enterprise, address.kind); + EXPECT_EQ("398efb30ecc28856d97f3714af49a93b9e0a2958520316660e16ae10", hex(address.bytes)); + } +} + +TEST(CardanoAddress, FromStringV3_Reward) { + { + auto address = AddressV3("stake1uy9ggsc9qls4pu9qvyyacwnmr9tt0gzcdt5s0zj4au8qkqc65geks"); + EXPECT_EQ(address.string(), "stake1uy9ggsc9qls4pu9qvyyacwnmr9tt0gzcdt5s0zj4au8qkqc65geks"); + EXPECT_EQ(AddressV3::Network_Production, address.networkId); + EXPECT_EQ(AddressV3::Kind_Reward, address.kind); + EXPECT_EQ("0a84430507e150f0a06109dc3a7b1956b7a0586ae9078a55ef0e0b03", hex(address.bytes)); + } +} + +TEST(CardanoAddress, MnemonicToAddressV3) { + { + // Test from cardano-crypto.js; Test wallet + const auto mnemonic = "cost dash dress stove morning robust group affair stomach vacant route volume yellow salute laugh"; + const auto coin = TWCoinTypeCardano; + const auto derivPath = derivationPath(coin); + + const auto wallet = HDWallet(mnemonic, ""); + + // check entropy + EXPECT_EQ("30a6f50aeb58ff7699b822d63e0ef27aeff17d9f", hex(wallet.getEntropy())); + + { + PrivateKey masterPrivKey = wallet.getMasterKey(TWCurve::TWCurveED25519ExtendedCardano); + PrivateKey masterPrivKeyExt = wallet.getMasterKeyExtension(TWCurve::TWCurveED25519ExtendedCardano); + // the two together matches first half of keypair + ASSERT_EQ("a018cd746e128a0be0782b228c275473205445c33b9000a33dd5668b430b5744", hex(masterPrivKey.bytes)); + ASSERT_EQ("26877cfe435fddda02409b839b7386f3738f10a30b95a225f4b720ee71d2505b", hex(masterPrivKeyExt.bytes)); + + PublicKey masterPublicKey = masterPrivKey.getPublicKey(TWPublicKeyTypeED25519); + ASSERT_EQ("3aecb95953edd0b16db20366097ddedcb3512fe36193473c5fca2af774d44739", hex(masterPublicKey.bytes)); + } + { + string addr = wallet.deriveAddress(TWCoinType::TWCoinTypeCardano); + EXPECT_EQ("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq", addr); + } + { + const auto privateKey = wallet.getKey(coin, derivPath); + EXPECT_EQ(hex(privateKey.bytes), "e8c8c5b2df13f3abed4e6b1609c808e08ff959d7e6fc3d849e3f2880550b574437aa559095324d78459b9bb2da069da32337e1cc5da78f48e1bd084670107f3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fae0d152bb611cb9ff34e945e4ff627e6fba81da687a601a879759cd76530b5744424db69a75edd4780a5fbc05d1a3c84ac4166ff8e424808481dd8e77627ce5f5bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276"); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + + const auto address = AddressV3(publicKey); + EXPECT_EQ(address.string(), "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_EQ(address.networkId, AddressV3::Network_Production); + EXPECT_EQ(address.kind, AddressV3::Kind_Base); + EXPECT_EQ(hex(address.bytes), "8d98bea0414243dc84070f96265577e7e6cf702d62e871016885034e" "cc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + } + { + PrivateKey privateKey = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/1852'/1815'/0'/0/0")); + EXPECT_EQ("e8c8c5b2df13f3abed4e6b1609c808e08ff959d7e6fc3d849e3f2880550b5744", hex(privateKey.key())); + EXPECT_EQ("37aa559095324d78459b9bb2da069da32337e1cc5da78f48e1bd084670107f31", hex(privateKey.extension())); + EXPECT_EQ("10f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fa", hex(privateKey.chainCode())); + EXPECT_EQ("e0d152bb611cb9ff34e945e4ff627e6fba81da687a601a879759cd76530b5744", hex(privateKey.secondKey())); + EXPECT_EQ("424db69a75edd4780a5fbc05d1a3c84ac4166ff8e424808481dd8e77627ce5f5", hex(privateKey.secondExtension())); + EXPECT_EQ("bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276", hex(privateKey.secondChainCode())); + PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + EXPECT_EQ("fafa7eb4146220db67156a03a5f7a79c666df83eb31abbfbe77c85e06d40da3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26faf4b8d5201961e68f2e177ba594101f513ee70fe70a41324e8ea8eb787ffda6f4bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276", hex(publicKey.bytes)); + string addr = AddressV3(publicKey).string(); + EXPECT_EQ("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq", addr); + } + { + PrivateKey privKey0 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/44'/1815'/0'/0/0")); + PublicKey pubKey0 = privKey0.getPublicKey(TWPublicKeyTypeED25519Cardano); + auto addr0 = AddressV2(pubKey0); + EXPECT_EQ("Ae2tdPwUPEZ6RUCnjGHFqi59k5WZLiv3HoCCNGCW8SYc5H9srdTzn1bec4W", addr0.string()); + } + { + PrivateKey privKey1 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/44'/1815'/0'/0/1")); + PublicKey pubKey1 = privKey1.getPublicKey(TWPublicKeyTypeED25519Cardano); + auto addr1 = AddressV2(pubKey1); + EXPECT_EQ("Ae2tdPwUPEZ7dnds6ZyhQdmgkrDFFPSDh8jG9RAhswcXt1bRauNw5jczjpV", addr1.string()); + } + { + PrivateKey privKey1 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/44'/1815'/0'/0/2")); + PublicKey pubKey1 = privKey1.getPublicKey(TWPublicKeyTypeED25519Cardano); + auto addr1 = AddressV2(pubKey1); + EXPECT_EQ("Ae2tdPwUPEZ8LAVy21zj4BF97iWxKCmPv12W6a18zLX3V7rZDFFVgqUBkKw", addr1.string()); + } + { + PrivateKey privKey0 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/1852'/1815'/0'/0/0")); + PublicKey pubKey0 = privKey0.getPublicKey(TWPublicKeyTypeED25519Cardano); + auto addr0 = AddressV3(pubKey0); + EXPECT_EQ("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq", addr0.string()); + } + { + PrivateKey privKey1 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/1852'/1815'/0'/0/1")); + PublicKey pubKey1 = privKey1.getPublicKey(TWPublicKeyTypeED25519Cardano); + auto addr1 = AddressV3(pubKey1); + EXPECT_EQ("addr1q9068st87h22h3l6w6t5evnlm067rag94llqya2hkjrsd3wvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qpmxzjt", addr1.string()); + } + { + PrivateKey privKey1 = wallet.getKey(TWCoinTypeCardano, DerivationPath("m/1852'/1815'/0'/0/2")); + PublicKey pubKey1 = privKey1.getPublicKey(TWPublicKeyTypeED25519Cardano); + auto addr1 = AddressV3(pubKey1); + EXPECT_EQ("addr1qxteqxsgxrs4he9d28lh70qu7qfz7saj6dmxwsqyle2yp3xvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35quehtx3", addr1.string()); + } + } + { + auto mnemonicPlay1 = "youth away raise north opinion slice dash bus soldier dizzy bitter increase saddle live champion"; + auto wallet = HDWallet(mnemonicPlay1, ""); + string addr = wallet.deriveAddress(TWCoinType::TWCoinTypeCardano); + EXPECT_EQ("addr1q83nm9ntq3eaz8dya49txxtle6nn8geq4gmyylrzhzs7v0qjdwm6zuahwwds6c7mj8t6a09rup6m2cnh6zvzddnafp2slmcu95", addr); + } + { + auto mnemonicPlay2 = "return custom two home gain guilt kangaroo supply market current curtain tomorrow heavy blue robot"; + auto wallet = HDWallet(mnemonicPlay2, ""); + string addr = wallet.deriveAddress(TWCoinType::TWCoinTypeCardano); + EXPECT_EQ("addr1qywxuqm7dx0yvqnn2yllye9urz5f2e4fgwanluzh008r22e53hart525dxgjcl0xzm0kes4n5tan8f5pz7ej0tkzgyrqtfmlal", addr); + } + { + auto mnemonicALDemo = "civil void tool perfect avocado sweet immense fluid arrow aerobic boil flash"; + auto wallet = HDWallet(mnemonicALDemo, ""); + string addr = wallet.deriveAddress(TWCoinType::TWCoinTypeCardano); + EXPECT_EQ("addr1q94zzrtl32tjp8j96auatnhxd2y35fnk6wuxqvqm9364vp9spdkjdsmyfhvfagjzh4uzp9zs6p5djw89jac2g0ujs2eqsuy7pu", addr); + } + { + // V2 Tested against AdaLite + auto mnemonicPlay1 = "youth away raise north opinion slice dash bus soldier dizzy bitter increase saddle live champion"; + auto wallet = HDWallet(mnemonicPlay1, ""); + PrivateKey privateKey = wallet.getKey(TWCoinTypeCardano, DerivationPath(TWPurposeBIP44, TWCoinTypeCardano, DerivationPathIndex(0, true).derivationIndex(), 0, 0)); + PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + string addr = AddressV2(publicKey).string(); + EXPECT_EQ("Ae2tdPwUPEZJYT9g1JgQWtLveUHavyRxQGi6hVzoQjct7yyCLGgk3pCyx7h", addr); + } + { + // V2 Tested against AdaLite + auto mnemonicPlay2 = "return custom two home gain guilt kangaroo supply market current curtain tomorrow heavy blue robot"; + auto wallet = HDWallet(mnemonicPlay2, ""); + PrivateKey privateKey = wallet.getKey(TWCoinTypeCardano, DerivationPath(TWPurposeBIP44, TWCoinTypeCardano, DerivationPathIndex(0, true).derivationIndex(), 0, 0)); + PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + string addr = AddressV2(publicKey).string(); + EXPECT_EQ("Ae2tdPwUPEZLtJx7LA2XZ3zzwonH9x9ieX3dMzaTBD3TfXuKczjMSjTecr1", addr); + } + { + // V2 AdaLite Demo phrase, 12-word. AdaLite uses V1 for it, in V2 it produces different addresses. + // In AdaLite V1 addr0 is DdzFFzCqrht7HGoJ87gznLktJGywK1LbAJT2sbd4txmgS7FcYLMQFhawb18ojS9Hx55mrbsHPr7PTraKh14TSQbGBPJHbDZ9QVh6Z6Di + auto mnemonicALDemo = "civil void tool perfect avocado sweet immense fluid arrow aerobic boil flash"; + auto wallet = HDWallet(mnemonicALDemo, ""); + PrivateKey privateKey = wallet.getKey(TWCoinTypeCardano, DerivationPath(TWPurposeBIP44, TWCoinTypeCardano, DerivationPathIndex(0, true).derivationIndex(), 0, 0)); + PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + string addr = AddressV2(publicKey).string(); + EXPECT_EQ("Ae2tdPwUPEZJbLcD8iLgN7hVGvq66WdR4zocntRekSP97Ds3MvCfmEDjJYu", addr); + } +} + +TEST(CardanoAddress, KeyHashV2) { + auto xpub = parse_hex("e6f04522f875c1563682ca876ddb04c2e2e3ae718e3ff9f11c03dd9f9dccf69869272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000"); + auto hash = AddressV2::keyHash(xpub); + ASSERT_EQ("a1eda96a9952a56c983d9f49117f935af325e8a6c9d38496e945faa8", hex(hash)); +} + +TEST(CardanoAddress, FromDataV3_Base) { + auto address0 = AddressV3("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_EQ(address0.string(), "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_EQ(hex(address0.data()), "018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + { + auto address = AddressV3(parse_hex("018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468")); + EXPECT_EQ(address.string(), "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_EQ(address.kind, AddressV3::Kind_Base); + EXPECT_EQ(address.networkId, AddressV3::Network_Production); + EXPECT_EQ(hex(address.bytes), "8d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + } + { + auto address = AddressV3::createBase(AddressV3::Network_Production, + PublicKey(parse_hex("fafa7eb4146220db67156a03a5f7a79c666df83eb31abbfbe77c85e06d40da31"), TWPublicKeyTypeED25519), + PublicKey(parse_hex("f4b8d5201961e68f2e177ba594101f513ee70fe70a41324e8ea8eb787ffda6f4"), TWPublicKeyTypeED25519)); + EXPECT_EQ(address.string(), "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + } +} + +TEST(CardanoAddress, FromDataV3_Enterprise) { + auto address = AddressV3(parse_hex("61398efb30ecc28856d97f3714af49a93b9e0a2958520316660e16ae10")); + EXPECT_EQ(address.string(), "addr1vyuca7esanpgs4ke0um3ft6f4yaeuz3ftpfqx9nxpct2uyqu7dvlp"); + EXPECT_EQ(address.kind, AddressV3::Kind_Enterprise); + EXPECT_EQ(address.networkId, AddressV3::Network_Production); + EXPECT_EQ(hex(address.bytes), "398efb30ecc28856d97f3714af49a93b9e0a2958520316660e16ae10"); +} + +TEST(CardanoAddress, FromDataV3_Reward) { + auto address = AddressV3(parse_hex("e10a84430507e150f0a06109dc3a7b1956b7a0586ae9078a55ef0e0b03")); + EXPECT_EQ(address.string(), "stake1uy9ggsc9qls4pu9qvyyacwnmr9tt0gzcdt5s0zj4au8qkqc65geks"); + EXPECT_EQ(address.kind, AddressV3::Kind_Reward); + EXPECT_EQ(address.networkId, AddressV3::Network_Production); + EXPECT_EQ(hex(address.bytes), "0a84430507e150f0a06109dc3a7b1956b7a0586ae9078a55ef0e0b03"); +} + +TEST(CardanoAddress, FromDataV3_Invalid) { + { // base, invalid length + auto address = AddressV3(parse_hex("018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a34")); + EXPECT_EQ(address.string(), "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32xsmpqws7"); + EXPECT_EQ(address.kind, AddressV3::Kind_Base); + EXPECT_EQ(address.networkId, AddressV3::Network_Production); + EXPECT_EQ(hex(address.bytes), "8d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a34"); + } + { // kind = 8 + auto address = AddressV3(parse_hex("818d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468")); + EXPECT_EQ(address.string(), "addr1sxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qmxapsy"); + EXPECT_EQ(address.kind, static_cast(8)); + EXPECT_EQ(address.networkId, AddressV3::Network_Production); + EXPECT_EQ(hex(address.bytes), "8d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + } +} + +TEST(CardanoAddress, FromPublicKeyV2) { + { + // caradano-crypto.js test + auto publicKey = PublicKey(parse_hex( + "e6f04522f875c1563682ca876ddb04c2e2e3ae718e3ff9f11c03dd9f9dccf69869272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000" + "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" // dummy second + ), TWPublicKeyTypeED25519Cardano); + auto address = AddressV2(publicKey); + ASSERT_EQ(address.string(), "Ae2tdPwUPEZCxt4UV1Uj2AMMRvg5pYPypqZowVptz3GYpK4pkcvn3EjkuNH"); + } + { + // Adalite test account addr0 + auto publicKey = PublicKey(parse_hex( + "57fd54be7b38bb8952782c2f59aa276928a4dcbb66c8c62ce44f9d623ecd5a03bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4" + "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" // dummy second + ), TWPublicKeyTypeED25519Cardano); + auto address = AddressV2(publicKey); + ASSERT_EQ(address.string(), "Ae2tdPwUPEZ6RUCnjGHFqi59k5WZLiv3HoCCNGCW8SYc5H9srdTzn1bec4W"); + } + { + // Adalite test account addr1 + auto publicKey = PublicKey(parse_hex( + "25af99056d600f7956312406bdd1cd791975bb1ae91c9d034fc65f326195fcdb247ee97ec351c0820dd12de4ca500232f73a35fe6f86778745bcd57f34d1048d" + "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" // dummy second + ), TWPublicKeyTypeED25519Cardano); + auto address = AddressV2(publicKey); + ASSERT_EQ(address.string(), "Ae2tdPwUPEZ7dnds6ZyhQdmgkrDFFPSDh8jG9RAhswcXt1bRauNw5jczjpV"); + } + { + // Play1 addr0 + auto publicKey = PublicKey(parse_hex( + "7cee0f30b9d536a786547dd77b35679b6830e945ffde768eb4f2a061b9dba016e513fa1290da1d22e83a41f17eed72d4489483b561fff36b9555ffdb91c430e2" + "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" // dummy second + ), TWPublicKeyTypeED25519Cardano); + auto address = AddressV2(publicKey); + ASSERT_EQ(address.string(), "Ae2tdPwUPEZJYT9g1JgQWtLveUHavyRxQGi6hVzoQjct7yyCLGgk3pCyx7h"); + } +} + +TEST(CardanoAddress, FromPrivateKeyV2) { + { + // mnemonic Test, addr0 + auto privateKey = PrivateKey( + parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), + parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), + parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4"), + dummyKey, dummyKey, dummyKey + ); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + ASSERT_EQ(hex(publicKey.bytes), + "57fd54be7b38bb8952782c2f59aa276928a4dcbb66c8c62ce44f9d623ecd5a03" + "bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4" + "857eed804ff087b97f87848f6493e87257a8c5203cb9f422f6e7a7d8a4d299f3" + "1111111111111111111111111111111111111111111111111111111111111111"); + auto address = AddressV2(publicKey); + ASSERT_EQ(address.string(), "Ae2tdPwUPEZ6RUCnjGHFqi59k5WZLiv3HoCCNGCW8SYc5H9srdTzn1bec4W"); + } + { + // mnemonic Play1, addr0 + auto privateKey = PrivateKey( + parse_hex("a089c9423100960440ccd5b7adbd202d1ab1993a7bb30fc88b287d94016df247"), + parse_hex("da86a87f08fb15de1431a6c0ccd5ebf51c3bee81f7eaf714801bbbe4d903154a"), + parse_hex("e513fa1290da1d22e83a41f17eed72d4489483b561fff36b9555ffdb91c430e2"), + dummyKey, dummyKey, dummyKey + ); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + ASSERT_EQ(hex(publicKey.bytes), + "7cee0f30b9d536a786547dd77b35679b6830e945ffde768eb4f2a061b9dba016" + "e513fa1290da1d22e83a41f17eed72d4489483b561fff36b9555ffdb91c430e2" + "857eed804ff087b97f87848f6493e87257a8c5203cb9f422f6e7a7d8a4d299f3" + "1111111111111111111111111111111111111111111111111111111111111111"); + auto address = AddressV2(publicKey); + ASSERT_EQ(address.string(), "Ae2tdPwUPEZJYT9g1JgQWtLveUHavyRxQGi6hVzoQjct7yyCLGgk3pCyx7h"); + } + { + // from cardano-crypto.js test + auto privateKey = PrivateKey( + parse_hex("d809b1b4b4c74734037f76aace501730a3fe2fca30b5102df99ad3f7c0103e48"), + parse_hex("d54cde47e9041b31f3e6873d700d83f7a937bea746dadfa2c5b0a6a92502356c"), + parse_hex("69272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000"), + dummyKey, dummyKey, dummyKey + ); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + ASSERT_EQ(hex(publicKey.bytes), + "e6f04522f875c1563682ca876ddb04c2e2e3ae718e3ff9f11c03dd9f9dccf698" + "69272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000" + "857eed804ff087b97f87848f6493e87257a8c5203cb9f422f6e7a7d8a4d299f3" + "1111111111111111111111111111111111111111111111111111111111111111"); + auto address = AddressV2(publicKey); + ASSERT_EQ(address.string(), "Ae2tdPwUPEZCxt4UV1Uj2AMMRvg5pYPypqZowVptz3GYpK4pkcvn3EjkuNH"); + } +} + +TEST(CardanoAddress, PrivateKeyExtended) { + // check extended key lengths, private key 2x3x32 bytes, public key 2x64 bytes + auto privateKeyExt = PrivateKey( + parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), + parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), + parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4"), + dummyKey, dummyKey, dummyKey + ); + auto publicKeyExt = privateKeyExt.getPublicKey(TWPublicKeyTypeED25519Cardano); + ASSERT_EQ(128ul, publicKeyExt.bytes.size()); + + // Non-extended: both are 32 bytes. + auto privateKeyNonext = PrivateKey( + parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744") + ); + auto publicKeyNonext = privateKeyNonext.getPublicKey(TWPublicKeyTypeED25519); + ASSERT_EQ(32ul, publicKeyNonext.bytes.size()); +} + +TEST(CardanoAddress, FromStringNegativeInvalidString) { + try { + auto address = AddressV3("__INVALID_ADDRESS__"); + } catch (...) { + return; + } + FAIL() << "Expected exception!"; +} + +TEST(CardanoAddress, FromStringNegativeBadChecksumV2) { + try { + auto address = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvm"); + } catch (...) { + return; + } + FAIL() << "Expected exception!"; +} + +TEST(CardanoAddress, CopyConstructorLegacy) { + AddressV3 address1 = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); + EXPECT_TRUE(address1.legacyAddressV2.has_value()); + AddressV3 address2 = AddressV3(address1); + EXPECT_TRUE(address2.legacyAddressV2.has_value()); + EXPECT_TRUE(*(address2.legacyAddressV2) == *(address1.legacyAddressV2)); + // if it was not a deep copy, double freeing would occur +} + +TEST(CardanoAddress, AssignmentOperatorLegacy) { + AddressV3 addr1leg = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); + EXPECT_TRUE(addr1leg.legacyAddressV2.has_value()); + AddressV3 addr2nonleg = AddressV3("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_FALSE(addr2nonleg.legacyAddressV2.has_value()); + AddressV3 addr3leg = AddressV3("Ae2tdPwUPEZ18ZjTLnLVr9CEvUEUX4eW1LBHbxxxJgxdAYHrDeSCSbCxrvx"); + EXPECT_TRUE(addr3leg.legacyAddressV2.has_value()); + + AddressV3 address = addr1leg; + EXPECT_TRUE(address.legacyAddressV2.has_value()); + EXPECT_TRUE(*address.legacyAddressV2 == *addr1leg.legacyAddressV2); + address = addr2nonleg; + EXPECT_FALSE(address.legacyAddressV2.has_value()); + address = addr3leg; + EXPECT_TRUE(address.legacyAddressV2.has_value()); + EXPECT_TRUE(*address.legacyAddressV2 == *addr3leg.legacyAddressV2); +} + +TEST(CardanoAddress, StakingKey) { + { + auto address = AddressV3("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23"); + EXPECT_EQ(hex(address.data()), "01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b"); + EXPECT_EQ(address.getStakingAddress(), "stake1u80jysjtdzqt88jt4jx93h5lumfr67d273r4vwyasfa2pxcwxllmx"); + EXPECT_EQ(hex(AddressV3(address.getStakingAddress()).data()), "e1df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b"); + EXPECT_EQ(hex(AddressV3(address.getStakingAddress()).bytes), "df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b"); + } + { + auto address = AddressV3("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + EXPECT_EQ(hex(address.data()), "018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + EXPECT_EQ(address.getStakingAddress(), "stake1u8xxf0e93w8rxr8sehvlmvp7zz6wftqg7hdplhkxyg4rg6qwgxzhc"); + EXPECT_EQ(hex(AddressV3(address.getStakingAddress()).data()), "e1cc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + EXPECT_EQ(hex(AddressV3(address.getStakingAddress()).bytes), "cc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468"); + } + { + auto address = AddressV3("addr1q8lcljuzfg8yvpuv94x02sytmwd8jsalzf6u0j8muhq69wng9ejcvpyczmw0zx7wguq2dml4xdl2wj3k7uexsfnxep2q9ja352"); + EXPECT_EQ(hex(address.data()), "01ff8fcb824a0e46078c2d4cf5408bdb9a7943bf1275c7c8fbe5c1a2ba682e6586049816dcf11bce4700a6eff5337ea74a36f732682666c854"); + EXPECT_EQ(address.getStakingAddress(), "stake1u95zuevxqjvpdh83r08ywq9xal6nxl48fgm0wvngyenvs4qh0hqf9"); + EXPECT_EQ(hex(AddressV3(address.getStakingAddress()).data()), "e1682e6586049816dcf11bce4700a6eff5337ea74a36f732682666c854"); + EXPECT_EQ(hex(AddressV3(address.getStakingAddress()).bytes), "682e6586049816dcf11bce4700a6eff5337ea74a36f732682666c854"); + } + { // negative case: cannot get staking address from non-base address + auto address = AddressV3("stake1u95zuevxqjvpdh83r08ywq9xal6nxl48fgm0wvngyenvs4qh0hqf9"); + EXPECT_EQ(hex(address.data()), "e1682e6586049816dcf11bce4700a6eff5337ea74a36f732682666c854"); + EXPECT_EQ(address.getStakingAddress(), ""); + } +} + +} // namespace TW::Cardano::tests diff --git a/tests/chains/Cardano/SigningTests.cpp b/tests/chains/Cardano/SigningTests.cpp new file mode 100644 index 00000000000..c4d6dcd1c73 --- /dev/null +++ b/tests/chains/Cardano/SigningTests.cpp @@ -0,0 +1,817 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cardano/AddressV3.h" +#include "Cardano/Signer.h" +#include "proto/Cardano.pb.h" +#include + +#include "Cbor.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "uint256.h" +#include "TestUtilities.h" +#include + +#include +#include + +#ifdef _MSC_VER + typedef unsigned int uint; +#endif + +using namespace TW; +using namespace std; + +namespace TW::Cardano::SigningTests { + +const auto privateKeyTest1 = "089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e"; +const auto ownAddress1 = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23"; +const auto sundaeTokenPolicy = "9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77"; + +TEST(CardanoSigning, SelectInputs) { + const auto inputs = std::vector({ + TxInput{{parse_hex("0001"), 0}, "ad01", 700, {}}, + TxInput{{parse_hex("0002"), 1}, "ad02", 900, {}}, + TxInput{{parse_hex("0003"), 2}, "ad03", 300, {}}, + TxInput{{parse_hex("0004"), 3}, "ad04", 600, {}}, + }); + + { // 2 + const auto s1 = Signer::selectInputsWithTokens(inputs, 1500, {}); + ASSERT_EQ(s1.size(), 2ul); + EXPECT_EQ(s1[0].amount, 900ul); + EXPECT_EQ(s1[1].amount, 700ul); + } + { // all + const auto s1 = Signer::selectInputsWithTokens(inputs, 10000, {}); + ASSERT_EQ(s1.size(), 4ul); + EXPECT_EQ(s1[0].amount, 900ul); + EXPECT_EQ(s1[1].amount, 700ul); + EXPECT_EQ(s1[2].amount, 600ul); + EXPECT_EQ(s1[3].amount, 300ul); + } + { // 3 + const auto s1 = Signer::selectInputsWithTokens(inputs, 2000, {}); + ASSERT_EQ(s1.size(), 3ul); + } + { // 1 + const auto s1 = Signer::selectInputsWithTokens(inputs, 500, {}); + ASSERT_EQ(s1.size(), 1ul); + } + { // at least 0 is returned + const auto s1 = Signer::selectInputsWithTokens(inputs, 0, {}); + ASSERT_EQ(s1.size(), 1ul); + } +} + +Proto::SigningInput createSampleInput(uint64_t amount, int utxoCount = 10, + const std::string& alternateToAddress = "", bool omitPrivateKey = false) { + const std::string toAddress = (alternateToAddress.length() > 0) ? alternateToAddress : "addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5"; + + Proto::SigningInput input; + if (utxoCount >= 1) { + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(1); + utxo1->set_address(ownAddress1); + utxo1->set_amount(1500000); + } + if (utxoCount >= 2) { + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0"); + utxo2->mutable_out_point()->set_tx_hash(txHash2.data(), txHash2.size()); + utxo2->mutable_out_point()->set_output_index(0); + utxo2->set_address(ownAddress1); + utxo2->set_amount(6500000); + } + + if (!omitPrivateKey) { + const auto privateKeyData = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + } + input.mutable_transfer_message()->set_to_address(toAddress); + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(amount); + input.mutable_transfer_message()->set_use_max_amount(false); + input.set_ttl(53333333); + return input; +} + +TEST(CardanoSigning, Plan) { + auto input = createSampleInput(7000000); + + { + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, 7000000ul); + EXPECT_EQ(plan.fee, 170196ul); + EXPECT_EQ(plan.change, 829804ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + EXPECT_EQ(plan.error, Common::Proto::OK); + } + { // very small target amount + input.mutable_transfer_message()->set_amount(1); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.utxos.size(), 1ul); + EXPECT_EQ(plan.availableAmount, 6500000ul); + EXPECT_EQ(plan.amount, 1ul); + EXPECT_EQ(plan.fee, 168435ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // small target amount + input.mutable_transfer_message()->set_amount(2000000); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.utxos.size(), 1ul); + EXPECT_EQ(plan.availableAmount, 6500000ul); + EXPECT_EQ(plan.amount, 2000000ul); + EXPECT_EQ(plan.fee, 168611ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // small target amount requested, but max amount + input.mutable_transfer_message()->set_amount(2000000); + input.mutable_transfer_message()->set_use_max_amount(true); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, 7832667ul); + EXPECT_EQ(plan.fee, 167333ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } +} + +TEST(CardanoSigning, PlanForceFee) { + auto requestedAmount = 6500000ul; + auto availableAmount = 8000000ul; + auto input = createSampleInput(requestedAmount); + + { + auto fee = 170147ul; + input.mutable_transfer_message()->set_force_fee(fee); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, requestedAmount); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, availableAmount - requestedAmount - fee); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + EXPECT_EQ(plan.error, Common::Proto::OK); + } + { // tiny fee + auto fee = 100ul; + input.mutable_transfer_message()->set_force_fee(fee); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, requestedAmount); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, availableAmount - requestedAmount - fee); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // large fee + auto fee = 1200000ul; + input.mutable_transfer_message()->set_force_fee(fee); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, requestedAmount); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, availableAmount - requestedAmount - fee); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // very large fee, larger than possible, truncated + auto fee = 3000000ul; + input.mutable_transfer_message()->set_force_fee(fee); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, requestedAmount); + EXPECT_EQ(plan.fee, 1500000ul); + EXPECT_EQ(plan.change, 0ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // force fee and max amount: fee is used, amount is max, change 0 + auto fee = 160000ul; + input.mutable_transfer_message()->set_force_fee(fee); + input.mutable_transfer_message()->set_use_max_amount(true); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, 7840000ul); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, 0ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } +} + +TEST(CardanoSigning, PlanMissingPrivateKey) { + auto input = createSampleInput(7000000, 10, "", true); + + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, 7000000ul); + EXPECT_EQ(plan.fee, 170196ul); + EXPECT_EQ(plan.change, 829804ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + EXPECT_EQ(plan.error, Common::Proto::OK); +} + +TEST(CardanoSigning, SignTransfer1) { + const auto input = createSampleInput(7000000); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a006acfc082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a000ca96c021a000298d4031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058407cf591599852b5f5e007fdc241062405c47e519266c0d884b0767c1d4f5eacce00db035998e53ed10ca4ba5ce4aac8693798089717ce6cf4415f345cc764200ef6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "9b5b15e133cd73ccaa85307d2986aebc846505118a2eb4e6111e6b4b67d1f389"); + + { + const auto decode = Cbor::Decode(encoded); + ASSERT_TRUE(decode.isValid()); + EXPECT_EQ(decode.dumpToString(), "[{0: [[h\"554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0\", 0], [h\"f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767\", 1]], 1: [[h\"01558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd5\", 7000000], [h\"01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\", 829804]], 2: 170196, 3: 53333333}, {0: [[h\"6d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df290\", h\"7cf591599852b5f5e007fdc241062405c47e519266c0d884b0767c1d4f5eacce00db035998e53ed10ca4ba5ce4aac8693798089717ce6cf4415f345cc764200e\"]]}, null]"); + EXPECT_EQ(decode.getArrayElements().size(), 3ul); + } +} + +TEST(CardanoSigning, PlanAndSignTransfer1) { + uint amount = 6000000; + auto input = createSampleInput(amount); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, amount); + EXPECT_EQ(plan.fee, 170196ul); + EXPECT_EQ(plan.change, 8000000 - amount - 170196); + ASSERT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.utxos[0].amount, 6500000ul); + EXPECT_EQ(plan.utxos[1].amount, 1500000ul); + + // perform sign with default plan + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a005b8d8082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a001bebac021a000298d4031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058404abc749ffaffcf2f87970e4f1983c5e44b352ee1515b60017fc65e581d42b3a6ed146d5eb35d04a770460b0541a25afd5aedfd027fdaded82686f43454196a0cf6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "3852f809245d7000ad0c5ccb1357e5d333b0dd25158924581e4c7049ec68c564"); + } + + // set different plan, with one input only + input.mutable_plan()->set_amount(amount); + input.mutable_plan()->set_available_amount(6500000); + input.mutable_plan()->set_fee(165489); + input.mutable_plan()->set_change(17191988); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40081825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a005b8d8082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a01065434021a00028671031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058408311a058035d75545a47b844fea401aa9c23e99fe7bc8136b554396eef135d4cd93062c5df38e613185c21bb1c98b881d1e0fd1024d3539b163c8e14d1a6e40df6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "e319c0bfc99cdb79d64f00b7e8fb8bfbf29fa70554c84f101e92b7dfed172448"); +} + +TEST(CardanoSigning, PlanAndSignMaxAmount) { + auto input = createSampleInput(7000000); + input.mutable_transfer_message()->set_use_max_amount(true); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, 8000000 - 167333ul); + EXPECT_EQ(plan.fee, 167333ul); + EXPECT_EQ(plan.change, 0ul); + ASSERT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.utxos[0].amount, 1500000ul); + EXPECT_EQ(plan.utxos[1].amount, 6500000ul); + } + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000018182583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a0077845b021a00028da5031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058403e64473e08adc863953c0e9f820b658dda0b8a423d6172fdccff73fcd5559956c9df8ed93ff67405331d368a0c11fd18c69781046384946582e1555e9e8ec70bf6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "ca0f1e12f20c95011da7d686d206a1eb98df94accd74c4df4ef403c5ce836057"); +} + +TEST(CardanoSigning, SignNegative) { + { // plan with error + auto input = createSampleInput(7000000); + const auto error = Common::Proto::Error_invalid_memo; + input.mutable_plan()->set_error(error); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), error); + } + { // zero requested amount + auto input = createSampleInput(0); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), Common::Proto::Error_zero_amount_requested); + } + { // no utxo + auto input = createSampleInput(7000000, 0); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), Common::Proto::Error_missing_input_utxos); + } + { // low balance + auto input = createSampleInput(7000000000); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), Common::Proto::Error_low_balance); + } + { // missing private key + auto input = createSampleInput(7000000, 10, "", true); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), Common::Proto::Error_missing_private_key); + } +} + +TEST(CardanoSigning, SignTransfer_0db1ea) { + const auto amount = 1100000ul; + + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("81b935447bb994567f041d181b628a0afbcd747d0199c9ff4cd895686bbee8c6"); + utxo1->mutable_out_point()->set_tx_hash(std::string(txHash1.begin(), txHash1.end())); + utxo1->mutable_out_point()->set_output_index(0); + utxo1->set_address(ownAddress1); + utxo1->set_amount(1000000); + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("3a9068a273cc2af59b45593b78973841d972d01802abe992c55dbeecdffc561b"); + utxo2->mutable_out_point()->set_tx_hash(std::string(txHash2.begin(), txHash2.end())); + utxo2->mutable_out_point()->set_output_index(0); + utxo2->set_address(ownAddress1); + utxo2->set_amount(1800000); + + const auto privateKeyData1 = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData1.data(), privateKeyData1.size()); + input.mutable_transfer_message()->set_to_address("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(amount); + auto fee = 170147ul; + input.mutable_transfer_message()->set_use_max_amount(false); + input.mutable_transfer_message()->set_force_fee(fee); // use force fee feature here + input.set_ttl(54675589); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 2800000ul); + EXPECT_EQ(plan.amount, amount); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, 2800000ul - amount - fee); + EXPECT_EQ(plan.utxos.size(), 2ul); + } + + // set plan with specific fee, to match the real transaction + input.mutable_plan()->set_amount(amount); + input.mutable_plan()->set_available_amount(2800000); + input.mutable_plan()->set_fee(fee); + input.mutable_plan()->set_change(2800000 - amount - fee); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + *(input.mutable_plan()->add_utxos()) = input.utxos(1); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + // https://cardanoscan.io/transaction/0db1ea8c5c5828bbd027fcef3da02a63b86899db670ad7bb0630cefbe35944fa + // curl -d '{"txHash":"0db1ea..44fa","txBody":"83a400..06f6"}' -H "Content-Type: application/json" https:///api/txs/submit + EXPECT_EQ(output.error(), Common::Proto::OK); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a4008282582081b935447bb994567f041d181b628a0afbcd747d0199c9ff4cd895686bbee8c6008258203a9068a273cc2af59b45593b78973841d972d01802abe992c55dbeecdffc561b000182825839018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a34681a0010c8e082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a001757fd021a000298a3031a03424885a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058406300b52aaff1e26067a3e0a48ae26f4f068765f46f934fabeab872c1d25535fc94893ec72feacd787f0174fbabd8933727d9a2b319b406e7a855843b0c051806f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "0db1ea8c5c5828bbd027fcef3da02a63b86899db670ad7bb0630cefbe35944fa"); +} + +TEST(CardanoSigning, SignTransferFromLegacy) { + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(1); + utxo1->set_address("Ae2tdPwUPEZMRgecV9jV2e9RdbrmnWu7YgRie4de16xLdkWhy6q7ypmRhgn"); + utxo1->set_amount(1500000); + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0"); + utxo2->mutable_out_point()->set_tx_hash(txHash2.data(), txHash2.size()); + utxo2->mutable_out_point()->set_output_index(0); + utxo2->set_address("Ae2tdPwUPEZMRgecV9jV2e9RdbrmnWu7YgRie4de16xLdkWhy6q7ypmRhgn"); + utxo2->set_amount(6500000); + + const auto privateKeyData = parse_hex("c031e942f6bf2b2864700e7da20964ee6bb6d716345ce2e24d8c00e6500b574411111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); + { + const auto privKey = PrivateKey(privateKeyData); + const auto pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + const auto addr = AddressV2(pubKey); + EXPECT_EQ(addr.string(), "Ae2tdPwUPEZMRgecV9jV2e9RdbrmnWu7YgRie4de16xLdkWhy6q7ypmRhgn"); + } + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + input.mutable_transfer_message()->set_to_address("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5"); + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(7000000); + input.mutable_transfer_message()->set_use_max_amount(false); + input.set_ttl(53333333); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_address); + EXPECT_EQ(hex(output.encoded()), ""); +} + +TEST(CardanoSigning, SignTransferToLegacy) { + const auto toAddressLegacy = "DdzFFzCqrhssmYoG5Eca1bKZFdGS8d6iag1mU4wbLeYcSPVvBNF2wRG8yhjzQqErbg63N6KJA4DHqha113tjKDpGEwS5x1dT2KfLSbSJ"; + EXPECT_FALSE(AddressV3::isValid(toAddressLegacy)); // not V3 + EXPECT_TRUE(AddressV3::isValidLegacy(toAddressLegacy)); + + const auto input = createSampleInput(7000000, 10, toAddressLegacy); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + EXPECT_EQ(hex(output.encoded()), "83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282584c82d818584283581c6aebd89cf88271c3ee76339930d8956b03f018b2f4871522f88eb8f9a101581e581c692a37dae3bc63dfc3e1463f12011f26655ab1d1e0f4ed4b8fc63708001ad8a9555b1a006acfc082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a000ca627021a00029c19031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840db9becdc733f4c08c0e7abc29b5cc6469f9339d32f565df8bf77455439ae1f949facc9b831754e74d3fbb42e99647eedd6c28de1461d18c315485f5d24b5b90af6"); + EXPECT_EQ(hex(data(output.tx_id())), "f9b713e9987ec1377ac223f50d63c7a5e155915302de43f40d7b2627accabf69"); +} + +TEST(CardanoSigning, SignTransferToInvalid) { + const auto input = createSampleInput(7000000, 10, "__INVALID_ADDRESS__"); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_address); + EXPECT_EQ(hex(output.encoded()), ""); +} + +TEST(CardanoSigning, SignTransferToken) { + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(1); + utxo1->set_address(ownAddress1); + utxo1->set_amount(8051373); + // some token, to be preserved + auto* token3 = utxo1->add_token_amount(); + token3->set_policy_id(sundaeTokenPolicy); + token3->set_asset_name("CUBY"); + const auto tokenAmount3 = store(uint256_t(3000000)); + token3->set_amount(tokenAmount3.data(), tokenAmount3.size()); + + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"); + utxo2->mutable_out_point()->set_tx_hash(txHash2.data(), txHash2.size()); + utxo2->mutable_out_point()->set_output_index(2); + utxo2->set_address(ownAddress1); + utxo2->set_amount(2000000); + // some SUNDAE token, to be transferred + auto* token1 = utxo2->add_token_amount(); + token1->set_policy_id(sundaeTokenPolicy); + token1->set_asset_name("SUNDAE"); + const auto tokenAmount1 = store(uint256_t(80996569)); + token1->set_amount(tokenAmount1.data(), tokenAmount1.size()); + // some other token, to be preserved + auto* token2 = utxo2->add_token_amount(); + token2->set_policy_id(sundaeTokenPolicy); + token2->set_asset_name("CUBY"); + const auto tokenAmount2 = store(uint256_t(2000000)); + token2->set_amount(tokenAmount2.data(), tokenAmount2.size()); + + const auto privateKeyData = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + input.mutable_transfer_message()->set_to_address("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5"); + input.mutable_transfer_message()->set_change_address("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + input.mutable_transfer_message()->set_amount(1500000); + auto* toToken = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + toToken->set_policy_id(sundaeTokenPolicy); + toToken->set_asset_name("SUNDAE"); + const auto toTokenAmount = store(uint256_t(20000000)); + toToken->set_amount(toTokenAmount.data(), toTokenAmount.size()); + input.mutable_transfer_message()->set_use_max_amount(false); + input.set_ttl(53333333); + + { // check min ADA amount, set it + const auto bundleProtoData = data(input.transfer_message().token_amount().SerializeAsString()); + const auto minAdaAmount = TWCardanoMinAdaAmount(&bundleProtoData); + EXPECT_EQ(minAdaAmount, 1444443ul); + input.mutable_transfer_message()->set_amount(minAdaAmount); + } + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 10051373ul); + EXPECT_EQ(plan.amount, 1444443ul); + EXPECT_EQ(plan.fee, 174601ul); + EXPECT_EQ(plan.change, 8432329ul); + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableTokens.size(), 2ul); + EXPECT_EQ(plan.availableTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_CUBY"), 5000000); + EXPECT_EQ(plan.availableTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 80996569); + EXPECT_EQ(plan.outputTokens.size(), 1ul); + EXPECT_EQ(plan.outputTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_CUBY"), 0); + EXPECT_EQ(plan.outputTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 20000000); + EXPECT_EQ(plan.changeTokens.size(), 2ul); + EXPECT_EQ(plan.changeTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_CUBY"), 5000000); + EXPECT_EQ(plan.changeTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 60996569); + } + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76702018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd5821a00160a5ba1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a01312d00825839018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468821a0080aac9a1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a244435542591a004c4b404653554e4441451a03a2bbd9021a0002aa09031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840d90dcfbd190cbe59c42094e59eeb49b3de9d80a85b786cc311f932c5c9302d1c8c6c577b22aa70ff7955c139c700ea918f8cb425c3ba43a27980e1d238e4e908f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "201c537693b005b64a0f0528e366ec67a84be0119ed4363b547f141f2a7770c2"); + + { + // also test proto toProto / fromProto + const Proto::TransactionPlan planProto = Signer::plan(input); + const auto plan2 = TransactionPlan::fromProto(planProto); + EXPECT_EQ(plan2.amount, 1444443ul); + EXPECT_EQ(plan2.change, 8432329ul); + } +} + +TEST(CardanoSigning, SignTransferToken_1dd248) { + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("f2d2b11c8c07c5c646f5b5af20fddf2f0a174743c6a1b13cca27e28a6ca34710"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(0); + utxo1->set_address(ownAddress1); + utxo1->set_amount(1500000); + // some token + auto* token3 = utxo1->add_token_amount(); + token3->set_policy_id(sundaeTokenPolicy); + token3->set_asset_name("SUNDAE"); + const auto tokenAmount3 = store(uint256_t(20000000)); + token3->set_amount(tokenAmount3.data(), tokenAmount3.size()); + + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("6975fcf7bbca745c85f50777f956219868fd9cad14ba496fed1371252e8df60f"); + utxo2->mutable_out_point()->set_tx_hash(txHash2.data(), txHash2.size()); + utxo2->mutable_out_point()->set_output_index(0); + utxo2->set_address(ownAddress1); + utxo2->set_amount(10258890); + + const auto privateKeyData = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + input.mutable_transfer_message()->set_to_address("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); // Test + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(1600000); + auto* toToken = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + toToken->set_policy_id(sundaeTokenPolicy); + toToken->set_asset_name("SUNDAE"); + const auto toTokenAmount = store(uint256_t(11000000)); + toToken->set_amount(toTokenAmount.data(), toTokenAmount.size()); + input.mutable_transfer_message()->set_use_max_amount(false); + input.set_ttl(61232158); + + { // check min ADA amount + const auto bundleProtoData = data(input.transfer_message().token_amount().SerializeAsString()); + EXPECT_EQ(TWCardanoMinAdaAmount(&bundleProtoData), 1444443ul); + EXPECT_GT(input.transfer_message().amount(), TWCardanoMinAdaAmount(&bundleProtoData)); + } + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 11758890ul); + EXPECT_EQ(plan.amount, 11758890 - 9984729 - 174161ul); + EXPECT_EQ(plan.fee, 174161ul); + EXPECT_EQ(plan.change, 9984729ul); + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableTokens.size(), 1ul); + EXPECT_EQ(plan.availableTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 20000000); + EXPECT_EQ(plan.outputTokens.size(), 1ul); + EXPECT_EQ(plan.outputTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 11000000); + EXPECT_EQ(plan.changeTokens.size(), 1ul); + EXPECT_EQ(plan.changeTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 9000000); + } + + // set plan with specific fee, to match the real transaction + input.mutable_plan()->set_available_amount(11758890); + input.mutable_plan()->set_amount(1600000); + input.mutable_plan()->set_fee(174102); + input.mutable_plan()->set_change(9984788); + *(input.mutable_plan()->add_available_tokens()) = input.utxos(0).token_amount(0); + *(input.mutable_plan()->add_output_tokens()) = input.utxos(0).token_amount(0); + input.mutable_plan()->mutable_output_tokens(0)->set_amount(toTokenAmount.data(), toTokenAmount.size()); + *(input.mutable_plan()->add_change_tokens()) = input.utxos(0).token_amount(0); + const auto changeTokenAmount = store(uint256_t(9000000)); + input.mutable_plan()->mutable_change_tokens(0)->set_amount(changeTokenAmount.data(), changeTokenAmount.size()); + *(input.mutable_plan()->add_utxos()) = input.utxos(1); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + // https://cardanoscan.io/transaction/1dd24872d93d3b5091b98e19b9f920cd0c4369e4c5ca178e898152c52f00c162 + // curl -d '{"txHash":"1dd248..c162","txBody":"83a400..08f6"}' -H "Content-Type: application/json" https:///api/txs/submit + EXPECT_EQ(output.error(), Common::Proto::OK); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a400828258206975fcf7bbca745c85f50777f956219868fd9cad14ba496fed1371252e8df60f00825820f2d2b11c8c07c5c646f5b5af20fddf2f0a174743c6a1b13cca27e28a6ca34710000182825839018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468821a00186a00a1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a00a7d8c082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b821a00985b14a1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a00895440021a0002a816031a03a6541ea100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840c8cdee32bfd584f55cf334b4ec6f734635144736d48f882e647a7a6283f230bc5a67d4dd66a9e523e0c29c812ed1e3589febbcf96547a1fc6d061a7ccfb81308f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "1dd24872d93d3b5091b98e19b9f920cd0c4369e4c5ca178e898152c52f00c162"); +} + +TEST(CardanoSigning, SignTransferTokenMaxAmount_620b71) { + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("46964521ad00d9b3f3d41f77c07e1b3093848048dbdf2d95cf900e15cdac0d7f"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(0); + utxo1->set_address(ownAddress1); + utxo1->set_amount(2170871); + // some token + auto* token1 = utxo1->add_token_amount(); + token1->set_policy_id(sundaeTokenPolicy); + token1->set_asset_name("SUNDAE"); + const auto tokenAmount1 = store(uint256_t(20000000)); + token1->set_amount(tokenAmount1.data(), tokenAmount1.size()); + + const auto privateKeyData = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + input.mutable_transfer_message()->set_to_address("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5"); + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(666); // doesn't matter, max is used + auto* toToken = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + toToken->set_policy_id(sundaeTokenPolicy); + toToken->set_asset_name("SUNDAE"); + const auto toTokenAmount = store(uint256_t(666)); // doesn't matter, max is used + input.mutable_transfer_message()->set_use_max_amount(true); + input.set_ttl(61085916); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 2170871ul); + EXPECT_EQ(plan.amount, 2170871 - 167730ul); + EXPECT_EQ(plan.fee, 167730ul); + EXPECT_EQ(plan.change, 0ul); + EXPECT_EQ(plan.utxos.size(), 1ul); + EXPECT_EQ(plan.availableTokens.size(), 1ul); + EXPECT_EQ(plan.availableTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 20000000); + EXPECT_EQ(plan.outputTokens.size(), 1ul); + EXPECT_EQ(plan.outputTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 20000000); + EXPECT_EQ(plan.changeTokens.size(), 0ul); + } + + // set plan with specific fee, to match the real transaction + input.mutable_plan()->set_available_amount(2170871); + input.mutable_plan()->set_amount(1998526); + input.mutable_plan()->set_fee(172345); + input.mutable_plan()->set_change(0); + *(input.mutable_plan()->add_available_tokens()) = input.utxos(0).token_amount(0); + *(input.mutable_plan()->add_output_tokens()) = input.utxos(0).token_amount(0); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + // https://cardanoscan.io/transaction/620b719338efb419b0e1417bfbe01fc94a62d5669a4b8cbbf4e32ecc1ca3b872 + // curl -d '{"txHash":"620b71..b872","txBody":"83a400..08f6"}' -H "Content-Type: application/json" https:///api/txs/submit + EXPECT_EQ(output.error(), Common::Proto::OK); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a4008182582046964521ad00d9b3f3d41f77c07e1b3093848048dbdf2d95cf900e15cdac0d7f00018182583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd5821a001e7ebea1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a01312d00021a0002a139031a03a418dca100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840e1d1565cd747b20b0f10a92f068f3d5faebdee92b4b4a4b96ce14736d975e17d1446f7f51e64997a0bb38e0151dc738468161d574d6cfcd8040e4455ff46bc08f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "620b719338efb419b0e1417bfbe01fc94a62d5669a4b8cbbf4e32ecc1ca3b872"); +} + +TEST(CardanoSigning, SignTransferTwoTokens) { + auto input = createSampleInput(7000000); + input.mutable_transfer_message()->set_amount(1500000); + auto* token1 = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + token1->set_policy_id(sundaeTokenPolicy); + token1->set_asset_name("SUNDAE"); + const auto tokenAmount1 = store(uint256_t(40000000)); + token1->set_amount(tokenAmount1.data(), tokenAmount1.size()); + auto* token2 = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + token2->set_policy_id(sundaeTokenPolicy); + token2->set_asset_name("CUBY"); + const auto tokenAmount2 = store(uint256_t(2000000)); + token2->set_amount(tokenAmount2.data(), tokenAmount2.size()); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_requested_token_amount); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(output.encoded()), ""); +} + +TEST(CardanoSigning, SignMessageWithKey) { + // test case from cardano-crypto.js + + const auto privateKey = PrivateKey(parse_hex( + "d809b1b4b4c74734037f76aace501730a3fe2fca30b5102df99ad3f7c0103e48" + "d54cde47e9041b31f3e6873d700d83f7a937bea746dadfa2c5b0a6a92502356c" + "69272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000" + "1111111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111")); + + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + EXPECT_EQ(hex(publicKey.bytes), + "e6f04522f875c1563682ca876ddb04c2e2e3ae718e3ff9f11c03dd9f9dccf698" + "69272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000" + "857eed804ff087b97f87848f6493e87257a8c5203cb9f422f6e7a7d8a4d299f3" + "1111111111111111111111111111111111111111111111111111111111111111"); + + const auto sampleMessageStr = "Hello world"; + const auto sampleMessage = data(sampleMessageStr); + + const auto signature = privateKey.sign(sampleMessage, TWCurveED25519ExtendedCardano); + + const auto sampleRightSignature = "1096ddcfb2ad21a4c0d861ef3fabe18841e8de88105b0d8e36430d7992c588634ead4100c32b2800b31b65e014d54a8238bdda63118d829bf0bcf1b631e86f0e"; + EXPECT_EQ(hex(signature), sampleRightSignature); +} + +TEST(CardanoSigning, AnySignTransfer1) { + const auto input = createSampleInput(7000000); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeCardano); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a006acfc082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a000ca96c021a000298d4031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058407cf591599852b5f5e007fdc241062405c47e519266c0d884b0767c1d4f5eacce00db035998e53ed10ca4ba5ce4aac8693798089717ce6cf4415f345cc764200ef6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "9b5b15e133cd73ccaa85307d2986aebc846505118a2eb4e6111e6b4b67d1f389"); +} + +TEST(CardanoSigning, AnyPlan1) { + const auto input = createSampleInput(7000000); + + Proto::TransactionPlan plan; + ANY_PLAN(input, plan, TWCoinTypeCardano); + + EXPECT_EQ(plan.error(), Common::Proto::OK); + EXPECT_EQ(plan.amount(), 7000000ul); + EXPECT_EQ(plan.available_amount(), 8000000ul); + EXPECT_EQ(plan.fee(), 170196ul); + EXPECT_EQ(plan.change(), 829804ul); + ASSERT_EQ(plan.utxos_size(), 2); + EXPECT_EQ(plan.utxos(0).amount(), 6500000ul); + EXPECT_EQ(plan.utxos(1).amount(), 1500000ul); + + EXPECT_EQ(hex(plan.SerializeAsString()), "0880a4e80310c09fab0318d4b10a20ecd2324292010a220a20554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af01267616464723171383034336d356865656179646e76746d6d6b7975686536717635686176766873663064323671336a7967737370786c796670796b3679716b77307968747976747230666c656b6a3834753634617a38326375666d716e36357a6473796c7a6b323318a0dd8c034293010a240a20f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76710011267616464723171383034336d356865656179646e76746d6d6b7975686536717635686176766873663064323671336a7967737370786c796670796b3679716b77307968747976747230666c656b6a3834753634617a38326375666d716e36357a6473796c7a6b323318e0c65b"); + + { + // also test fromProto + const auto plan2 = TransactionPlan::fromProto(plan); + EXPECT_EQ(plan2.amount, plan.amount()); + EXPECT_EQ(plan2.change, plan.change()); + } +} + +} // namespace TW::Cardano::tests \ No newline at end of file diff --git a/tests/chains/Cardano/StakingTests.cpp b/tests/chains/Cardano/StakingTests.cpp new file mode 100644 index 00000000000..cf7636b23c5 --- /dev/null +++ b/tests/chains/Cardano/StakingTests.cpp @@ -0,0 +1,351 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cardano/AddressV3.h" +#include "Cardano/Signer.h" +#include "proto/Cardano.pb.h" +#include + +#include "Cbor.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "uint256.h" +#include "TestUtilities.h" +#include + +#include +#include + +using namespace TW; +using namespace std; + +namespace TW::Cardano::StakingTests { + +const auto privateKeyTest1 = "089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e"; +const auto ownAddress1 = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23"; +const auto stakingAddress1 = "stake1u80jysjtdzqt88jt4jx93h5lumfr67d273r4vwyasfa2pxcwxllmx"; +const auto poolIdNufi = "7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6"; + +TEST(CardanoStaking, RegisterStakingKey) { + const auto privateKeyData = parse_hex(privateKeyTest1); + const auto publicKey = PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519Cardano); + const auto ownAddress = AddressV3(publicKey).string(); + EXPECT_EQ(ownAddress, ownAddress1); + const auto stakingAddress = AddressV3(publicKey).getStakingAddress(); + EXPECT_EQ(stakingAddress, stakingAddress1); + const auto poolId = parse_hex(poolIdNufi); + + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("cba84549f07f2128410c0a22731f2c57f2a617746e8edc61b295cd8792638dca"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(1); + utxo1->set_address(ownAddress); + utxo1->set_amount(10000000ul); + + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + + input.mutable_transfer_message()->set_to_address(ownAddress); + input.mutable_transfer_message()->set_change_address(ownAddress); + input.mutable_transfer_message()->set_amount(5000000ul); // not relevant if we use MaxAmount + input.mutable_transfer_message()->set_use_max_amount(true); + input.set_ttl(69986091ul); + + // Register staking key, 2 ADA deposit + input.mutable_register_staking_key()->set_staking_address(stakingAddress); + input.mutable_register_staking_key()->set_deposit_amount(2000000ul); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a50081825820cba84549f07f2128410c0a22731f2c57f2a617746e8edc61b295cd8792638dca01018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a007772fa021a00029f06031a042be72b048182008200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09ba100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840d08ed71da87d0928090edd9e226496ab109f2eee7926ac2ce51e7abe89a4f513c4afe2b85b71595e862e7f6fc992d14d2416a6e53a1961da7d26d3cf3f823400825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e06932584079ed55400cebc70c56ca87ba09009dfc298c64768f90a9139bf2e7f134250927c614ee846253fac33e652f1b50373d349fdfe13c207968c2a10991824fe2a10ef6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "6a206fe4df76e12499b4fd9722f33429f4d93f8a996f9f523fa6c02a8301386b"); + + { + const auto decode = Cbor::Decode(encoded); + ASSERT_TRUE(decode.isValid()); + EXPECT_EQ(decode.dumpToString(), "[{0: [[h\"cba84549f07f2128410c0a22731f2c57f2a617746e8edc61b295cd8792638dca\", 1]], 1: [[h\"01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\", 7828218]], 2: 171782, 3: 69986091, 4: [[0, [0, h\"df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\"]]]}, {0: [[h\"6d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df290\", h\"d08ed71da87d0928090edd9e226496ab109f2eee7926ac2ce51e7abe89a4f513c4afe2b85b71595e862e7f6fc992d14d2416a6e53a1961da7d26d3cf3f823400\"], [h\"e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e06932\", h\"79ed55400cebc70c56ca87ba09009dfc298c64768f90a9139bf2e7f134250927c614ee846253fac33e652f1b50373d349fdfe13c207968c2a10991824fe2a10e\"]]}, null]"); + EXPECT_EQ(decode.getArrayElements().size(), 3ul); + } +} + +TEST(CardanoStaking, DeregisterStakingKey) { + const auto privateKeyData = parse_hex(privateKeyTest1); + const auto publicKey = PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519Cardano); + const auto ownAddress = AddressV3(publicKey).string(); + EXPECT_EQ(ownAddress, ownAddress1); + const auto stakingAddress = AddressV3(publicKey).getStakingAddress(); + EXPECT_EQ(stakingAddress, stakingAddress1); + const auto poolId = parse_hex(poolIdNufi); + + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("cba84549f07f2128410c0a22731f2c57f2a617746e8edc61b295cd8792638dca"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(1); + utxo1->set_address(ownAddress); + utxo1->set_amount(10000000ul); + + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + + input.mutable_transfer_message()->set_to_address(ownAddress); + input.mutable_transfer_message()->set_change_address(ownAddress); + input.mutable_transfer_message()->set_amount(5000000ul); // not relevant if we use MaxAmount + input.mutable_transfer_message()->set_use_max_amount(true); + input.set_ttl(69986091ul); + + // Deregister staking key, get back 2 ADA deposit + input.mutable_deregister_staking_key()->set_staking_address(stakingAddress); + input.mutable_deregister_staking_key()->set_undeposit_amount(2000000ul); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a50081825820cba84549f07f2128410c0a22731f2c57f2a617746e8edc61b295cd8792638dca01018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a00b47bfa021a00029f06031a042be72b048182018200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09ba100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df290584056619a7d6192b6f68c31a43e927c893161fd994d5c1bcc16f3710cf5e5e652e01f118d55f0110e9de34edc050d509748bea637db5c34f4fe342ae262ccb5520d825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e069325840d23680fdd8aa63e10efccc550eb726743b653008952f9d731d076d1df8106b0401823ebb195127b211389f1bc2c3f6ededbcec04bc8f0de93607a2409421e006f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "1caae2456e5471cc77e73410da475fb0a23874c18c1ea55f9267c59767caef0a"); + + { + const auto decode = Cbor::Decode(encoded); + ASSERT_TRUE(decode.isValid()); + EXPECT_EQ(decode.dumpToString(), "[{0: [[h\"cba84549f07f2128410c0a22731f2c57f2a617746e8edc61b295cd8792638dca\", 1]], 1: [[h\"01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\", 11828218]], 2: 171782, 3: 69986091, 4: [[1, [0, h\"df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\"]]]}, {0: [[h\"6d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df290\", h\"56619a7d6192b6f68c31a43e927c893161fd994d5c1bcc16f3710cf5e5e652e01f118d55f0110e9de34edc050d509748bea637db5c34f4fe342ae262ccb5520d\"], [h\"e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e06932\", h\"d23680fdd8aa63e10efccc550eb726743b653008952f9d731d076d1df8106b0401823ebb195127b211389f1bc2c3f6ededbcec04bc8f0de93607a2409421e006\"]]}, null]"); + EXPECT_EQ(decode.getArrayElements().size(), 3ul); + } +} + +TEST(CardanoStaking, Redelegate) { + const auto privateKeyData = parse_hex(privateKeyTest1); + const auto publicKey = PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519Cardano); + const auto ownAddress = AddressV3(publicKey).string(); + EXPECT_EQ(ownAddress, ownAddress1); + const auto stakingAddress = AddressV3(publicKey).getStakingAddress(); + EXPECT_EQ(stakingAddress, stakingAddress1); + const auto poolId = parse_hex(poolIdNufi); + + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("cba84549f07f2128410c0a22731f2c57f2a617746e8edc61b295cd8792638dca"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(1); + utxo1->set_address(ownAddress); + utxo1->set_amount(10000000ul); + + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + + input.mutable_transfer_message()->set_to_address(ownAddress); + input.mutable_transfer_message()->set_change_address(ownAddress); + input.mutable_transfer_message()->set_amount(5000000ul); // not relevant if we use MaxAmount + input.mutable_transfer_message()->set_use_max_amount(true); + input.set_ttl(69986091ul); + + // Delegate, no deposit + input.mutable_delegate()->set_staking_address(stakingAddress); + input.mutable_delegate()->set_pool_id(poolId.data(), poolId.size()); + input.mutable_delegate()->set_deposit_amount(0ul); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a50081825820cba84549f07f2128410c0a22731f2c57f2a617746e8edc61b295cd8792638dca01018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a0095f251021a0002a42f031a042be72b048183028200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b581c7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6a100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840fb48f3ddbfc2d4ca231a0581c5b456019aa4215ed5a2447ba89a4860569f9e7296afd3a0a81506882d8bda33683e623e6d8033786275369f7e247d866e017c06825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e069325840e26f696a6cd1c34101623568c9efe3796ff5855ada0e2e0cf557c7fc2148f6b2af176aff40a1f9c13fb29d9636c49f774d4a967c71f052f865cfaf0d02d5bb05f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "985f613fb8b86dad35f075599099776e50fc2a6aa74ee4b37c14fd9f2c0f0891"); + + { + const auto decode = Cbor::Decode(encoded); + ASSERT_TRUE(decode.isValid()); + EXPECT_EQ(decode.dumpToString(), "[{0: [[h\"cba84549f07f2128410c0a22731f2c57f2a617746e8edc61b295cd8792638dca\", 1]], 1: [[h\"01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\", 9826897]], 2: 173103, 3: 69986091, 4: [[2, [0, h\"df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\"], h\"7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6\"]]}, {0: [[h\"6d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df290\", h\"fb48f3ddbfc2d4ca231a0581c5b456019aa4215ed5a2447ba89a4860569f9e7296afd3a0a81506882d8bda33683e623e6d8033786275369f7e247d866e017c06\"], [h\"e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e06932\", h\"e26f696a6cd1c34101623568c9efe3796ff5855ada0e2e0cf557c7fc2148f6b2af176aff40a1f9c13fb29d9636c49f774d4a967c71f052f865cfaf0d02d5bb05\"]]}, null]"); + EXPECT_EQ(decode.getArrayElements().size(), 3ul); + } +} + +TEST(CardanoStaking, RegisterAndDelegate_similar53339b) { + const auto privateKeyData = parse_hex(privateKeyTest1); + const auto publicKey = PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519Cardano); + const auto ownAddress = AddressV3(publicKey).string(); + EXPECT_EQ(ownAddress, ownAddress1); + const auto stakingAddress = AddressV3(publicKey).getStakingAddress(); + EXPECT_EQ(stakingAddress, stakingAddress1); + const auto poolId = parse_hex(poolIdNufi); + + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("9b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(0); + utxo1->set_address(ownAddress); + utxo1->set_amount(4000000ul); + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("9b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e"); + utxo2->mutable_out_point()->set_tx_hash(txHash2.data(), txHash2.size()); + utxo2->mutable_out_point()->set_output_index(1); + utxo2->set_address(ownAddress); + utxo2->set_amount(26651312ul); + + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + + input.mutable_transfer_message()->set_to_address(ownAddress); + input.mutable_transfer_message()->set_change_address(ownAddress); + input.mutable_transfer_message()->set_amount(4000000ul); // not relevant if we use MaxAmount + input.mutable_transfer_message()->set_use_max_amount(true); + input.set_ttl(69885081ul); + + // Register staking key, 2 ADA desposit + input.mutable_register_staking_key()->set_staking_address(stakingAddress); + input.mutable_register_staking_key()->set_deposit_amount(2000000ul); + + // Delegate + input.mutable_delegate()->set_staking_address(stakingAddress); + input.mutable_delegate()->set_pool_id(poolId.data(), poolId.size()); + input.mutable_delegate()->set_deposit_amount(0ul); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + const auto amount = 28475125ul; + const auto availAmount = 30651312ul; + EXPECT_EQ(plan.availableAmount, availAmount); + EXPECT_EQ(plan.amount, amount); + const auto fee = 176187ul; + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, availAmount - 2000000ul - amount - fee); + EXPECT_EQ(plan.change, 0ul); + + // perform sign with default plan + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a500828258209b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e008258209b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e01018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a01b27ef5021a0002b03b031a042a5c99048282008200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b83028200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b581c7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6a100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840677c901704be027d9a1734e8aa06f0700009476fa252baaae0de280331746a320a61456d842d948ea5c0e204fc36f3bd04c88ca7ee3d657d5a38014243c37c07825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e0693258401fa21bdc62b85ca217bf08cbacdeba2fadaf33dc09ee3af9cc25b40f24822a1a42cfbc03585cc31a370ef75aaec4d25db6edcf329e40a4e725ec8718c94f220af6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "96a781fd6481b6a7fd3926da110265e8c44b53947b81daa84da5b148825d02aa"); + } + + // set different plan, with exact fee + const auto amount = 28467322ul; + input.mutable_plan()->set_amount(amount); + input.mutable_plan()->set_available_amount(28651312ul); + input.mutable_plan()->set_fee(183990); + input.mutable_plan()->set_change(0); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + *(input.mutable_plan()->add_utxos()) = input.utxos(1); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + // Similar to (but with different key): https://cardanoscan.io/transaction/53339b758009a0896a87e9569cadcdb5a095ffe0c100cc7483d72e817e81b60b + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a500828258209b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e008258209b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e01018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a01b2607a021a0002ceb6031a042a5c99048282008200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b83028200581cdf22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b581c7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6a100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058405d8b21c993aec7a7bdf0c832e5688920b64b665e1e36a2e6040d6dd8ad195e7774df3c1377047737d8b676fa4115e38fbf6ef854904db6d9c8ee3e26e8561408825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e06932584088a3f6387693f9077d11a6e245e024b791074bcaa26c034e687d67f3324b6f90a437d33d0343e11c7dee1a28270c223e02080e452fe97cdc93d26c720ab6b805f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "23e1d1bc27f6de57e323d232d44c909fb41ee2ebfff28b82ca8cae6947866ea7"); + + { + const auto decode = Cbor::Decode(encoded); + ASSERT_TRUE(decode.isValid()); + EXPECT_EQ(decode.dumpToString(), "[{0: [[h\"9b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e\", 0], [h\"9b06de86b253549b99f6a050b61217d8824085ca5ed4eb107a5e7cce4f93802e\", 1]], 1: [[h\"01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\", 28467322]], 2: 183990, 3: 69885081, 4: [[0, [0, h\"df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\"]], [2, [0, h\"df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\"], h\"7d7ac07a2f2a25b7a4db868a40720621c4939cf6aefbb9a11464f1a6\"]]}, {0: [[h\"6d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df290\", h\"5d8b21c993aec7a7bdf0c832e5688920b64b665e1e36a2e6040d6dd8ad195e7774df3c1377047737d8b676fa4115e38fbf6ef854904db6d9c8ee3e26e8561408\"], [h\"e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e06932\", h\"88a3f6387693f9077d11a6e245e024b791074bcaa26c034e687d67f3324b6f90a437d33d0343e11c7dee1a28270c223e02080e452fe97cdc93d26c720ab6b805\"]]}, null]"); + EXPECT_EQ(decode.getArrayElements().size(), 3ul); + } +} + +TEST(CardanoStaking, Withdraw_similarf48098) { + const auto privateKeyData = parse_hex(privateKeyTest1); + const auto publicKey = PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519Cardano); + const auto ownAddress = AddressV3(publicKey).string(); + EXPECT_EQ(ownAddress, ownAddress1); + const auto stakingAddress = AddressV3(publicKey).getStakingAddress(); + EXPECT_EQ(stakingAddress, stakingAddress1); + + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("7dfd2c579794314b1f84efc9db932a098e440ccefb874945591f1d4e85a9152a"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(0); + utxo1->set_address(ownAddress); + utxo1->set_amount(6305913ul); + + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + + input.mutable_transfer_message()->set_to_address(ownAddress); + input.mutable_transfer_message()->set_change_address(ownAddress); + input.mutable_transfer_message()->set_amount(6000000ul); // not relevant if we use MaxAmount + input.mutable_transfer_message()->set_use_max_amount(true); + input.set_ttl(71678326ul); + + // Withdraw available amount + const auto withdrawAmount = 3468ul; + input.mutable_withdraw()->set_staking_address(stakingAddress); + input.mutable_withdraw()->set_withdraw_amount(withdrawAmount); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + const auto amount = 6137599ul; + const auto availAmount = 6305913ul; + EXPECT_EQ(plan.availableAmount, availAmount); + EXPECT_EQ(plan.amount, amount); + const auto fee = 171782ul; + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, availAmount + withdrawAmount - amount - fee); + EXPECT_EQ(plan.change, 0ul); + + // perform sign with default plan + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a500818258207dfd2c579794314b1f84efc9db932a098e440ccefb874945591f1d4e85a9152a00018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a005da6ff021a00029f06031a0445b97605a1581de1df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b190d8ca100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058401ebaca2876fd17122404912a2558a98109cdf0f990a938d2917fa2c3b8c4e55e18a2cbabfa82fff03fa0d7ab8b88ca01ed18e42af3bfc4cda7f423a3aa30c00b825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e069325840777f04fa8f083fe562aecf78898aaaaac36e2cc6ca962f6ffb01e84a421cae1860496db79b2c5fb2879524c3d5121060b9ea1e693336230c6e5338e14c4c3303f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "6dcf3956232953fc25b8355fb1ded1e912b5802090fd21434d789087d6329683"); + } + + // set different plan, with exact fee + const auto amount = 6137599ul; + input.mutable_plan()->set_amount(amount); + input.mutable_plan()->set_available_amount(6305913ul); + input.mutable_plan()->set_fee(171782ul); + input.mutable_plan()->set_change(0); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + // Similar to (but with different key): https://cardanoscan.io/transaction/f480985662886e419a22673d31944455ab8891a80940bf392a37d9288ea9cf01?tab=withdrawals + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a500818258207dfd2c579794314b1f84efc9db932a098e440ccefb874945591f1d4e85a9152a00018182583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a005da6ff021a00029f06031a0445b97605a1581de1df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b190d8ca100828258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058401ebaca2876fd17122404912a2558a98109cdf0f990a938d2917fa2c3b8c4e55e18a2cbabfa82fff03fa0d7ab8b88ca01ed18e42af3bfc4cda7f423a3aa30c00b825820e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e069325840777f04fa8f083fe562aecf78898aaaaac36e2cc6ca962f6ffb01e84a421cae1860496db79b2c5fb2879524c3d5121060b9ea1e693336230c6e5338e14c4c3303f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "6dcf3956232953fc25b8355fb1ded1e912b5802090fd21434d789087d6329683"); + + { + const auto decode = Cbor::Decode(encoded); + ASSERT_TRUE(decode.isValid()); + EXPECT_EQ(decode.dumpToString(), "[{0: [[h\"7dfd2c579794314b1f84efc9db932a098e440ccefb874945591f1d4e85a9152a\", 0]], 1: [[h\"01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\", 6137599]], 2: 171782, 3: 71678326, 5: {h\"e1df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\": 3468}}, {0: [[h\"6d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df290\", h\"1ebaca2876fd17122404912a2558a98109cdf0f990a938d2917fa2c3b8c4e55e18a2cbabfa82fff03fa0d7ab8b88ca01ed18e42af3bfc4cda7f423a3aa30c00b\"], [h\"e554163344aafc2bbefe778a6953ddce0583c2f8e0a0686929c020ca33e06932\", h\"777f04fa8f083fe562aecf78898aaaaac36e2cc6ca962f6ffb01e84a421cae1860496db79b2c5fb2879524c3d5121060b9ea1e693336230c6e5338e14c4c3303\"]]}, null]"); + + EXPECT_EQ(decode.getArrayElements().size(), 3ul); + } +} + +} // namespace TW::Cardano::tests diff --git a/tests/chains/Cardano/TWCardanoAddressTests.cpp b/tests/chains/Cardano/TWCardanoAddressTests.cpp new file mode 100644 index 00000000000..f610ffd1bba --- /dev/null +++ b/tests/chains/Cardano/TWCardanoAddressTests.cpp @@ -0,0 +1,74 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include +#include +#include +#include +#include +#include +#include "TestUtilities.h" +#include "PrivateKey.h" + +#include + +TEST(TWCardano, AddressFromPublicKey) { + auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA( + "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4" + "639aadd8b6499ae39b78018b79255fbd8f585cbda9cbb9e907a72af86afb7a05d41a57c2dec9a6a19d6bf3b1fa784f334f3a0048d25ccb7b78a7b44066f9ba7bed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a" + ).get())); + ASSERT_NE(nullptr, privateKey.get()); + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519Cardano(privateKey.get())); + ASSERT_NE(nullptr, publicKey.get()); + ASSERT_EQ(128ul, publicKey.get()->impl.bytes.size()); + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeCardano)); + auto addressString = WRAPS(TWAnyAddressDescription(address.get())); + assertStringsEqual(addressString, "addr1qx4z6twzknkkux0hhp0kq6hvdfutczp56g56y5em8r8mgvxalp7nkkk25vuspleke2zltaetmlwrfxv7t049cq9jmwjswmfw6t"); + + auto address2 = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(STRING("addr1qx4z6twzknkkux0hhp0kq6hvdfutczp56g56y5em8r8mgvxalp7nkkk25vuspleke2zltaetmlwrfxv7t049cq9jmwjswmfw6t").get(), TWCoinTypeCardano)); + ASSERT_NE(nullptr, address2.get()); + auto address2String = WRAPS(TWAnyAddressDescription(address2.get())); + assertStringsEqual(address2String, "addr1qx4z6twzknkkux0hhp0kq6hvdfutczp56g56y5em8r8mgvxalp7nkkk25vuspleke2zltaetmlwrfxv7t049cq9jmwjswmfw6t"); + + ASSERT_TRUE(TWAnyAddressEqual(address.get(), address2.get())); +} + +TEST(TWCardano, AddressFromWallet) { + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic( + STRING("cost dash dress stove morning robust group affair stomach vacant route volume yellow salute laugh").get(), + STRING("").get() + )); + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeCardano)); + auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); + EXPECT_EQ(TWDataSize(privateKeyData.get()), 192ul); + + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519Cardano(privateKey.get())); + auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); + EXPECT_EQ(TWDataSize(publicKeyData.get()), 128ul); + assertHexEqual(publicKeyData, "fafa7eb4146220db67156a03a5f7a79c666df83eb31abbfbe77c85e06d40da3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26faf4b8d5201961e68f2e177ba594101f513ee70fe70a41324e8ea8eb787ffda6f4bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276"); + + auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeCardano, privateKey.get())); + assertStringsEqual(address, "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); +} + +/* +TEST(TWCardano, GetStakingKey) { + { + auto stakingAddress = WRAPS(TWCardanoGetStakingAddress(STRING("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23").get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(stakingAddress.get())), "stake1u80jysjtdzqt88jt4jx93h5lumfr67d273r4vwyasfa2pxcwxllmx"); + } + { // negative case: cannot get staking address from non-base address + auto stakingAddress = WRAPS(TWCardanoGetStakingAddress(STRING("stake1u95zuevxqjvpdh83r08ywq9xal6nxl48fgm0wvngyenvs4qh0hqf9").get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(stakingAddress.get())), ""); + } + { // negative case: cannot get staking address from invalid address, should not throw + auto stakingAddress = WRAPS(TWCardanoGetStakingAddress(STRING("__THIS_IS_NOT_A_VALID_CARDANO_ADDRESS__").get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(stakingAddress.get())), ""); + } +} +*/ diff --git a/tests/chains/Cardano/TWCoinTypeTests.cpp b/tests/chains/Cardano/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..33f8c9e197d --- /dev/null +++ b/tests/chains/Cardano/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWCardanoCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCardano)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("b7a6c5cadab0f64bdc89c77ee4a351463aba5c33f2cef6bbd6542a74a90a3af3")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCardano, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("DdzFFzCqrhstpwKc8WMvPwwBb5oabcTW9zc5ykA37wJR4tYQucvsR9dXb2kEGNXkFJz2PtrpzfRiZkx8R1iNo8NYqdsukVmv7EAybFwC")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCardano, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCardano)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCardano)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCardano), 6); + ASSERT_EQ(TWBlockchainCardano, TWCoinTypeBlockchain(TWCoinTypeCardano)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeCardano)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeCardano)); + assertStringsEqual(symbol, "ADA"); + assertStringsEqual(txUrl, "https://cardanoscan.io/transaction/b7a6c5cadab0f64bdc89c77ee4a351463aba5c33f2cef6bbd6542a74a90a3af3"); + assertStringsEqual(accUrl, "https://cardanoscan.io/address/DdzFFzCqrhstpwKc8WMvPwwBb5oabcTW9zc5ykA37wJR4tYQucvsR9dXb2kEGNXkFJz2PtrpzfRiZkx8R1iNo8NYqdsukVmv7EAybFwC"); + assertStringsEqual(id, "cardano"); + assertStringsEqual(name, "Cardano"); +} diff --git a/tests/chains/Cardano/TransactionTests.cpp b/tests/chains/Cardano/TransactionTests.cpp new file mode 100644 index 00000000000..702a32ce77a --- /dev/null +++ b/tests/chains/Cardano/TransactionTests.cpp @@ -0,0 +1,126 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cardano/Transaction.h" +#include "Cardano/AddressV3.h" +#include + +#include "HexCoding.h" +#include "Cbor.h" + +#include + + +namespace TW::Cardano { + +Transaction createTx() { + Transaction tx; + tx.inputs.emplace_back(parse_hex("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"), 1); + tx.inputs.emplace_back(parse_hex("554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0"), 0); + tx.outputs.emplace_back( + AddressV3("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23").data(), + 2000000); + tx.outputs.emplace_back( + AddressV3("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5").data(), + 16749189); + tx.fee = 165555; + tx.ttl = 53333345; + return tx; +} + +TEST(CardanoTransaction, Encode) { + const Transaction tx = createTx(); + + const auto encoded = tx.encode(); + EXPECT_EQ(hex(encoded), "a40082825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000018282583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a001e848082583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a00ff9285021a000286b3031a032dcd61"); + + { + const auto decode = Cbor::Decode(encoded); + ASSERT_TRUE(decode.isValid()); + EXPECT_EQ(decode.getMapElements().size(), 4ul); + EXPECT_EQ(decode.dumpToString(), "{0: [[h\"f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767\", 1], [h\"554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0\", 0]], 1: [[h\"01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\", 2000000], [h\"01558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd5\", 16749189]], 2: 165555, 3: 53333345}"); + } +} + +TEST(CardanoTransaction, GetId) { + const Transaction tx = createTx(); + + const auto txid = tx.getId(); + EXPECT_EQ(hex(txid), "cc262713a3e15a0fa245b062f33ffc6c2aa5a64c3ae7bfa793414069914e1bbf"); +} + +TEST(CardanoTransaction, minAdaAmount) { + const auto policyId = "9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77"; + + { // ADA only + const auto tb = TokenBundle(); + EXPECT_EQ(tb.minAdaAmount(), 1000000ul); + } + { // 1 policyId, 1 6-char asset name + const auto tb = TokenBundle({TokenAmount(policyId, "TOKEN1", 0)}); + EXPECT_EQ(tb.minAdaAmount(), 1444443ul); + } + { // 2 policyId, 2 4-char asset names + auto tb = TokenBundle(); + tb.add(TokenAmount("012345678901234567890POLICY1", "TOK1", 20)); + tb.add(TokenAmount("012345678901234567890POLICY2", "TOK2", 20)); + EXPECT_EQ(tb.minAdaAmount(), 1629628ul); + } + { // 10 policyId, 10 6-char asset names + auto tb = TokenBundle(); + for (auto i = 0; i < 10; ++i) { + std::string policyId1 = +"012345678901234567890123456" + std::to_string(i); + std::string name = "ASSET" + std::to_string(i); + tb.add(TokenAmount(policyId1, name, 0)); + } + EXPECT_EQ(tb.minAdaAmount(), 3370367ul); + } + + EXPECT_EQ(TokenBundle::minAdaAmountHelper(0, 0, 0), 1000000ul); // ADA only + EXPECT_EQ(TokenBundle::minAdaAmountHelper(1, 0, 0), 1370369ul); // 1 policyId, no asset name + EXPECT_EQ(TokenBundle::minAdaAmountHelper(1, 1, 1), 1444443ul); // 1 policyId, 1 1-char asset name + EXPECT_EQ(TokenBundle::minAdaAmountHelper(1, 1, 6), 1444443ul); // 1 policyId, 1 6-char asset name + EXPECT_EQ(TokenBundle::minAdaAmountHelper(1, 1, 32), 1555554ul); // 1 policyId, 1 32-char asset name + EXPECT_EQ(TokenBundle::minAdaAmountHelper(1, 110, 110 * 32), 23777754ul); // 1 policyId, 110 32-char asset name + EXPECT_EQ(TokenBundle::minAdaAmountHelper(2, 2, 8), 1629628ul); // 2 policyId, 2 4-char asset names + EXPECT_EQ(TokenBundle::minAdaAmountHelper(3, 5, 20), 1999998ul); // 3 policyId, 5 4-char asset names + EXPECT_EQ(TokenBundle::minAdaAmountHelper(10, 10, 10 * 6), 3370367ul); // 10 policyId, 10 6-char asset names + EXPECT_EQ(TokenBundle::minAdaAmountHelper(60, 60, 60 * 32), 21222201ul); // 60 policyId, 60 32-char asset names +} + +TEST(CardanoTransaction, getPolicyIDs) { + const auto policyId1 = "012345678901234567890POLICY1"; + const auto policyId2 = "012345678901234567890POLICY2"; + const auto tb = TokenBundle({ + TokenAmount(policyId1, "TOK1", 10), + TokenAmount(policyId2, "TOK2", 20), + TokenAmount(policyId2, "TOK3", 30), // duplicate policyId + }); + ASSERT_EQ(tb.getPolicyIds().size(), 2ul); + EXPECT_TRUE(tb.getPolicyIds().contains(policyId1)); + EXPECT_TRUE(tb.getPolicyIds().contains(policyId2)); + + EXPECT_EQ(tb.getByPolicyId(policyId1).size(), 1ul); + EXPECT_EQ(tb.getByPolicyId(policyId2).size(), 2ul); +} + +TEST(TWCardanoTransaction, minAdaAmount) { + { // ADA-only + const auto bundleProto = TokenBundle().toProto(); + const auto bundleProtoData = data(bundleProto.SerializeAsString()); + EXPECT_EQ(TWCardanoMinAdaAmount(&bundleProtoData), 1000000ul); + } + { // 2 policyId, 2 4-char asset names + auto bundle = TokenBundle(); + bundle.add(TokenAmount("012345678901234567890POLICY1", "TOK1", 20)); + bundle.add(TokenAmount("012345678901234567890POLICY2", "TOK2", 20)); + const auto bundleProto = bundle.toProto(); + const auto bundleProtoData = data(bundleProto.SerializeAsString()); + EXPECT_EQ(TWCardanoMinAdaAmount(&bundleProtoData), 1629628ul); + } +} + +} // namespace TW::Cardano diff --git a/tests/chains/Celo/TWCoinTypeTests.cpp b/tests/chains/Celo/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..35fd5a76d73 --- /dev/null +++ b/tests/chains/Celo/TWCoinTypeTests.cpp @@ -0,0 +1,30 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWCeloCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCelo)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xaf41ee58afe633dc7b179c15693cca40fe0372c1d7c70351620105d59d326693")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCelo, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xFBFf95e2Fa7e4Ff3aeA34eFB05fB60F9968a6aaD")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCelo, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCelo)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCelo)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCelo), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeCelo)); + + assertStringsEqual(symbol, "CELO"); + assertStringsEqual(txUrl, "https://explorer.celo.org/tx/0xaf41ee58afe633dc7b179c15693cca40fe0372c1d7c70351620105d59d326693"); + assertStringsEqual(accUrl, "https://explorer.celo.org/address/0xFBFf95e2Fa7e4Ff3aeA34eFB05fB60F9968a6aaD"); + assertStringsEqual(id, "celo"); + assertStringsEqual(name, "Celo"); +} diff --git a/tests/Cosmos/AddressTests.cpp b/tests/chains/Cosmos/AddressTests.cpp similarity index 100% rename from tests/Cosmos/AddressTests.cpp rename to tests/chains/Cosmos/AddressTests.cpp diff --git a/tests/chains/Cosmos/Protobuf/.gitignore b/tests/chains/Cosmos/Protobuf/.gitignore new file mode 100644 index 00000000000..c96d61208c0 --- /dev/null +++ b/tests/chains/Cosmos/Protobuf/.gitignore @@ -0,0 +1,3 @@ +*.cc +*.h + diff --git a/tests/chains/Cosmos/Protobuf/Article.proto b/tests/chains/Cosmos/Protobuf/Article.proto new file mode 100644 index 00000000000..d6f8ade819e --- /dev/null +++ b/tests/chains/Cosmos/Protobuf/Article.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package blog; + +enum Type { + TYPE_UNSPECIFIED = 0; + IMAGES = 1; + NEWS = 2; +}; + +enum Review { + REVIEW_UNSPECIFIED = 0; + ACCEPTED = 1; + REJECTED = 2; +}; + +message Article { + string title = 1; + string description = 2; + uint64 created = 3; + uint64 updated = 4; + bool public = 5; + bool promoted = 6; + Type type = 7; + Review review = 8; + repeated string comments = 9; + repeated string backlinks = 10; +}; diff --git a/tests/chains/Cosmos/ProtobufTests.cpp b/tests/chains/Cosmos/ProtobufTests.cpp new file mode 100644 index 00000000000..89b36346ac5 --- /dev/null +++ b/tests/chains/Cosmos/ProtobufTests.cpp @@ -0,0 +1,67 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base64.h" +#include "Cosmos/Address.h" +#include "Cosmos/Protobuf/authz_tx.pb.h" +#include "Cosmos/Protobuf/bank_tx.pb.h" +#include "Cosmos/Protobuf/tx.pb.h" +#include "Data.h" +#include "HexCoding.h" + +#include "Protobuf/Article.pb.h" +#include "TestUtilities.h" + +#include +#include + +#include + +namespace TW::Cosmos::tests { + +using json = nlohmann::json; + +TEST(CosmosProtobuf, SendMsg) { + auto msgSend = cosmos::bank::v1beta1::MsgSend(); + msgSend.set_from_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); + msgSend.set_to_address("cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"); + auto coin = msgSend.add_amount(); + coin->set_denom("muon"); + coin->set_amount("1"); + + auto txBody = cosmos::TxBody(); + txBody.add_messages()->PackFrom(msgSend); + txBody.set_memo(""); + txBody.set_timeout_height(0); + + const auto serialized = data(txBody.SerializeAsString()); + EXPECT_EQ(hex(serialized), "0a9c010a2f747970652e676f6f676c65617069732e636f6d2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412690a2d636f736d6f733168736b366a727979716a6668703564686335357463396a74636b796778306570683664643032122d636f736d6f73317a743530617a7570616e716c66616d356166687633686578777975746e756b656834633537331a090a046d756f6e120131"); + EXPECT_EQ(Base64::encode(serialized), "CpwBCi90eXBlLmdvb2dsZWFwaXMuY29tL2Nvc21vcy5iYW5rLnYxYmV0YTEuTXNnU2VuZBJpCi1jb3Ntb3MxaHNrNmpyeXlxamZocDVkaGM1NXRjOWp0Y2t5Z3gwZXBoNmRkMDISLWNvc21vczF6dDUwYXp1cGFucWxmYW01YWZodjNoZXh3eXV0bnVrZWg0YzU3MxoJCgRtdW9uEgEx"); + + std::string json; + google::protobuf::util::MessageToJsonString(txBody, &json); + assertJSONEqual(json, R"({"messages":[{"@type":"type.googleapis.com/cosmos.bank.v1beta1.MsgSend","amount":[{"amount":"1","denom":"muon"}],"fromAddress":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","toAddress":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}]})"); +} + +TEST(CosmosProtobuf, DeterministicSerialization_Article) { + // https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-027-deterministic-protobuf-serialization.md + auto article = blog::Article(); + article.set_title("The world needs change 🌳"); + article.set_description(""); + article.set_created(1596806111080); + article.set_updated(0); + article.set_public_(true); + article.set_promoted(false); + article.set_type(blog::NEWS); + article.set_review(blog::REVIEW_UNSPECIFIED); + *article.add_comments() = "Nice one"; + *article.add_comments() = "Thank you"; + + const auto serialized = data(article.SerializeAsString()); + EXPECT_EQ(hex(serialized), "0a1b54686520776f726c64206e65656473206368616e676520f09f8cb318e8bebec8bc2e280138024a084e696365206f6e654a095468616e6b20796f75"); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Cosmos/SignerTests.cpp b/tests/chains/Cosmos/SignerTests.cpp new file mode 100644 index 00000000000..d9202607cc0 --- /dev/null +++ b/tests/chains/Cosmos/SignerTests.cpp @@ -0,0 +1,325 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Coin.h" +#include "HexCoding.h" +#include "Base64.h" +#include "proto/Cosmos.pb.h" +#include "Cosmos/Address.h" +#include "Cosmos/Signer.h" +#include "TestUtilities.h" +#include "Cosmos/Protobuf/bank_tx.pb.h" + +#include +#include + +namespace TW::Cosmos::tests { + +TEST(CosmosSigner, SignTxProtobuf) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + input.set_memo(""); + input.set_sequence(8); + + auto fromAddress = Address("cosmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); + auto toAddress = Address("cosmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("muon"); + amountOfTx->set_amount("1"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount("200"); + + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + + EXPECT_EQ(R"({"signingMode":"Protobuf","accountNumber":"1037","chainId":"gaia-13003","fee":{"amounts":[{"denom":"muon","amount":"200"}],"gas":"200000"},"sequence":"8","messages":[{"sendCoinsMessage":{"fromAddress":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","toAddress":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573","amounts":[{"denom":"muon","amount":"1"}]}}]})", json); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + assertJSONEqual(output.serialized(), "{\"tx_bytes\": \"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATESZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAgSEQoLCgRtdW9uEgMyMDAQwJoMGkD54fQAFlekIAnE62hZYl0uQelh/HLv0oQpCciY5Dn8H1SZFuTsrGdu41PH1Uxa4woptCELi/8Ov9yzdeEFAC9H\", \"mode\": \"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "f9e1f4001657a42009c4eb6859625d2e41e961fc72efd2842909c898e439fc1f549916e4ecac676ee353c7d54c5ae30a29b4210b8bff0ebfdcb375e105002f47"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(CosmosSigner, SignProtobuf_ErrorMissingMessage) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + input.set_memo(""); + input.set_sequence(8); + + auto fromAddress = Address("cosmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); + auto toAddress = Address("cosmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount("200"); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + EXPECT_EQ(output.error(), "Error: No message found"); + EXPECT_EQ(output.serialized(), ""); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(hex(output.signature()), ""); +} + +TEST(CosmosSigner, SignTxJson) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::JSON); // obsolete + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + input.set_memo(""); + input.set_sequence(8); + + auto fromAddress = Address("cosmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); + auto toAddress = Address("cosmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("muon"); + amountOfTx->set_amount("1"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount("200"); + + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + + EXPECT_EQ(R"({"accountNumber":"1037","chainId":"gaia-13003","fee":{"amounts":[{"denom":"muon","amount":"200"}],"gas":"200000"},"sequence":"8","messages":[{"sendCoinsMessage":{"fromAddress":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","toAddress":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573","amounts":[{"denom":"muon","amount":"1"}]}}]})", json); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + // the sample tx on testnet + // https://hubble.figment.network/chains/gaia-13003/blocks/142933/transactions/3A9206598C3D2E75A5EC074FD33EA53EB18EC729357F0965971C1C51F812AEA3?format=json + EXPECT_EQ(R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}})", output.json()); + + EXPECT_EQ(hex(output.signature()), "fc3ef899d206c88077fec42f21ba0b4df4bd3fd115fdf606ae01d9136fef363f57e9e33a7b9ec6ddab658cd07e3c0067470de94e4e75b979a1085a29f0efd926"); + EXPECT_EQ(output.serialized(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(CosmosSigner, SignTxJson_WithMode) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::JSON); // obsolete + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + input.set_memo(""); + input.set_sequence(8); + input.set_mode(Proto::BroadcastMode::ASYNC); + + auto fromAddress = Address("cosmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); + auto toAddress = Address("cosmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("muon"); + amountOfTx->set_amount("1"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount("200"); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + { + auto output = Signer::sign(input, TWCoinTypeCosmos); + EXPECT_EQ(R"({"mode":"async","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}})", output.json()); + EXPECT_EQ(output.error(), ""); + } + input.set_mode(Proto::BroadcastMode::SYNC); + { + auto output = Signer::sign(input, TWCoinTypeCosmos); + EXPECT_EQ(R"({"mode":"sync","tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"},"memo":"","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"1","denom":"muon"}],"from_address":"cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02","to_address":"cosmos1zt50azupanqlfam5afhv3hexwyutnukeh4c573"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F"},"signature":"/D74mdIGyIB3/sQvIboLTfS9P9EV/fYGrgHZE2/vNj9X6eM6e57G3atljNB+PABnRw3pTk51uXmhCFop8O/ZJg=="}]}})", output.json()); + EXPECT_EQ(output.error(), ""); + } +} + +TEST(CosmosSigner, SignIbcTransferProtobuf_817101) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(546179); + input.set_chain_id("cosmoshub-4"); + input.set_sequence(2); + + Address fromAddress; + EXPECT_TRUE(Address::decode("cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx", fromAddress)); + Address toAddress; + EXPECT_TRUE(Address::decode("osmo18s0hdnsllgcclweu9aymw4ngktr2k0rkvn7jmn", toAddress)); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_transfer_tokens_message(); + message.set_source_port("transfer"); + message.set_source_channel("channel-141"); + message.set_sender(fromAddress.string()); + message.set_receiver(toAddress.string()); + message.mutable_token()->set_denom("uatom"); + message.mutable_token()->set_amount("100000"); // 0.1 ATOM + message.mutable_timeout_height()->set_revision_number(1); + message.mutable_timeout_height()->set_revision_height(8800000); + + auto& fee = *input.mutable_fee(); + fee.set_gas(500000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uatom"); + amountOfFee->set_amount("12500"); + + auto privateKey = parse_hex("8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af"); + EXPECT_EQ(Cosmos::Address(TWCoinTypeCosmos, PrivateKey(privateKey).getPublicKey(TWPublicKeyTypeSECP256k1)).string(), "cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + // real-world tx: https://www.mintscan.io/cosmos/txs/817101F3D96314AD028733248B28BAFAD535024D7D2C8875D3FE31DC159F096B + // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "Cr4BCr...1yKOU=", "mode": "BROADCAST_MODE_BLOCK"}' https://api.cosmos.network/cosmos/tx/v1beta1/txs + // also similar TX: BCDAC36B605576C8182C2829C808B30A69CAD4959D5ED1E6FF9984ABF280D603 + assertJSONEqual(output.serialized(), "{\"tx_bytes\": \"Cr4BCrsBCikvaWJjLmFwcGxpY2F0aW9ucy50cmFuc2Zlci52MS5Nc2dUcmFuc2ZlchKNAQoIdHJhbnNmZXISC2NoYW5uZWwtMTQxGg8KBXVhdG9tEgYxMDAwMDAiLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseCorb3NtbzE4czBoZG5zbGxnY2Nsd2V1OWF5bXc0bmdrdHIyazBya3ZuN2ptbjIHCAEQgI6ZBBJoClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEC7O9c5DejAsZ/lUaN5LMfNukR9GfX5qUrQcHhPh1WNkkSBAoCCAEYAhIUCg4KBXVhdG9tEgUxMjUwMBCgwh4aQK0HIWdFMk+C6Gi1KG/vELe1ffcc1aEWUIqz2t/ZhwqNNHxUUSp27wteiugHEMVTEIOBhs84t2gIcT/nD/1yKOU=\", \"mode\": \"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "ad07216745324f82e868b5286fef10b7b57df71cd5a116508ab3dadfd9870a8d347c54512a76ef0b5e8ae80710c55310838186cf38b76808713fe70ffd7228e5"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(CosmosSigner, SignDirect1) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_sign_direct_message(); + const auto bodyBytes = parse_hex("0a89010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412690a2d636f736d6f733168736b366a727979716a6668703564686335357463396a74636b796778306570683664643032122d636f736d6f73317a743530617a7570616e716c66616d356166687633686578777975746e756b656834633537331a090a046d756f6e120131"); + message.set_body_bytes(bodyBytes.data(), bodyBytes.size()); + const auto authInfoBytes = parse_hex("0a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a210257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc512040a020801180812110a0b0a046d756f6e120332303010c09a0c"); + message.set_auth_info_bytes(authInfoBytes.data(), authInfoBytes.size()); + + { + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + EXPECT_EQ(R"({"signingMode":"Protobuf","accountNumber":"1037","chainId":"gaia-13003","messages":[{"signDirectMessage":{"bodyBytes":"CokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATE=","authInfoBytes":"ClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYCBIRCgsKBG11b24SAzIwMBDAmgw="}}]})", json); + } + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + assertJSONEqual(output.serialized(), "{\"tx_bytes\": \"CowBCokBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmkKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhItY29zbW9zMXp0NTBhenVwYW5xbGZhbTVhZmh2M2hleHd5dXRudWtlaDRjNTczGgkKBG11b24SATESZQpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAgSEQoLCgRtdW9uEgMyMDAQwJoMGkD54fQAFlekIAnE62hZYl0uQelh/HLv0oQpCciY5Dn8H1SZFuTsrGdu41PH1Uxa4woptCELi/8Ov9yzdeEFAC9H\", \"mode\": \"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "f9e1f4001657a42009c4eb6859625d2e41e961fc72efd2842909c898e439fc1f549916e4ecac676ee353c7d54c5ae30a29b4210b8bff0ebfdcb375e105002f47"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(CosmosSigner, SignDirect_0a90010a) { + const auto bodyBytes = parse_hex("0a90010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412700a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637"); + { // verify contents of body + auto msgSend = cosmos::bank::v1beta1::MsgSend(); + msgSend.set_from_address("cosmos1pkptre7fdkl6gfrzlesjjvhxhlc3r4gmmk8rs6"); + msgSend.set_to_address("cosmos1qypqxpq9qcrsszg2pvxq6rs0zqg3yyc5lzv7xu"); + auto amount = msgSend.add_amount(); + amount->set_denom("ucosm"); + amount->set_amount("1234567"); + const auto msgSendSer = msgSend.SerializeAsString(); + const auto bodyBytes1 = data(msgSendSer); + ASSERT_EQ(hex(bodyBytes1), "0a2d636f736d6f7331706b707472653766646b6c366766727a6c65736a6a766878686c63337234676d6d6b38727336122d636f736d6f7331717970717870713971637273737a673270767871367273307a716733797963356c7a763778751a100a0575636f736d120731323334353637"); + const auto prefix = "/cosmos.bank.v1beta1.MsgSend"; + const auto bodyBytes2 = parse_hex("0a90010a1c" + hex(data(prefix)) + "1270" + hex(bodyBytes1)); + ASSERT_EQ(hex(bodyBytes2), hex(bodyBytes)); + } + + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1); + input.set_chain_id("cosmoshub-4"); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_sign_direct_message(); + message.set_body_bytes(bodyBytes.data(), bodyBytes.size()); + const auto authInfoBytes = parse_hex("0a0a0a0012040a020801180112130a0d0a0575636f736d12043230303010c09a0c"); + message.set_auth_info_bytes(authInfoBytes.data(), authInfoBytes.size()); + + { + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + EXPECT_EQ(R"({"signingMode":"Protobuf","accountNumber":"1","chainId":"cosmoshub-4","messages":[{"signDirectMessage":{"bodyBytes":"CpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKLWNvc21vczFwa3B0cmU3ZmRrbDZnZnJ6bGVzamp2aHhobGMzcjRnbW1rOHJzNhItY29zbW9zMXF5cHF4cHE5cWNyc3N6ZzJwdnhxNnJzMHpxZzN5eWM1bHp2N3h1GhAKBXVjb3NtEgcxMjM0NTY3","authInfoBytes":"CgoKABIECgIIARgBEhMKDQoFdWNvc20SBDIwMDAQwJoM"}}]})", json); + } + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + assertJSONEqual(output.serialized(), "{\"tx_bytes\": \"CpMBCpABChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnAKLWNvc21vczFwa3B0cmU3ZmRrbDZnZnJ6bGVzamp2aHhobGMzcjRnbW1rOHJzNhItY29zbW9zMXF5cHF4cHE5cWNyc3N6ZzJwdnhxNnJzMHpxZzN5eWM1bHp2N3h1GhAKBXVjb3NtEgcxMjM0NTY3EiEKCgoAEgQKAggBGAESEwoNCgV1Y29zbRIEMjAwMBDAmgwaQEgXmSAlm4M5bz+OX1GtvvZ3fBV2wrZrp4A/Imd55KM7ASivB/siYJegmYiOKzQ82uwoEmFalNnG2BrHHDwDR2Y=\", \"mode\": \"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "48179920259b83396f3f8e5f51adbef6777c1576c2b66ba7803f226779e4a33b0128af07fb226097a099888e2b343cdaec2812615a94d9c6d81ac71c3c034766"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(CosmosSigner, MsgVote) { + // Successfully broadcasted https://www.mintscan.io/cosmos/txs/2EFA054B842B1641B131137B13360F95164C6C1D51BB4A4AC6DE8F75F504AA4C + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1366160); + input.set_chain_id("cosmoshub-4"); + input.set_memo(""); + input.set_sequence(0); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_msg_vote(); + message.set_voter("cosmos1mry47pkga5tdswtluy0m8teslpalkdq07pswu4"); + message.set_proposal_id(77); + message.set_option(TW::Cosmos::Proto::Message_VoteOption_YES); + + auto& fee = *input.mutable_fee(); + fee.set_gas(97681); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uatom"); + amountOfFee->set_amount("2418"); + + auto privateKey = parse_hex("a498a9ee41af9bab5ef2a8be63d5c970135c3c109e70efc8c56c534e6636b433"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + auto expected = R"( + {"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"ClQKUgobL2Nvc21vcy5nb3YudjFiZXRhMS5Nc2dWb3RlEjMITRItY29zbW9zMW1yeTQ3cGtnYTV0ZHN3dGx1eTBtOHRlc2xwYWxrZHEwN3Bzd3U0GAESZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAsv9teRyiTMiKU5gzwiD1D30MeEInSnstEep5tVQRarlEgQKAggBEhMKDQoFdWF0b20SBDI0MTgQkfsFGkA+Nb3NULc38quGC1x+8ZXry4w9mMX3IA7wUjFboTv7kVOwPlleIc8UqIsjVvKTUFnUuW8dlGQzNR1KkvbvZ1NA"})"; + assertJSONEqual(output.serialized(), expected); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Cosmos/StakingTests.cpp b/tests/chains/Cosmos/StakingTests.cpp new file mode 100644 index 00000000000..0abee814e66 --- /dev/null +++ b/tests/chains/Cosmos/StakingTests.cpp @@ -0,0 +1,246 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base64.h" +#include "Coin.h" +#include "Cosmos/Address.h" +#include "Cosmos/Signer.h" +#include "HexCoding.h" +#include "proto/Cosmos.pb.h" +#include "TestUtilities.h" +#include + +namespace TW::Cosmos::tests { + +TEST(CosmosStaking, CompoundingAuthz) { + // Successfully broadcasted https://www.mintscan.io/cosmos/txs/C4629BC7C88690518D8F448E7A8D239C9D63975B11F8E1CE2F95CC2ADA3CCF67 + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1290826); + input.set_chain_id("cosmoshub-4"); + input.set_memo(""); + input.set_sequence(5); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_auth_grant(); + message.set_granter("cosmos13k0q0l7lg2kr32kvt7ly236ppldy8v9dzwh3gd"); + message.set_grantee("cosmos1fs7lu28hx5m9akm7rp0c2422cn8r2f7gurujhf"); + auto& grant_stake = *message.mutable_grant_stake(); + grant_stake.mutable_allow_list()->add_address("cosmosvaloper1gjtvly9lel6zskvwtvlg5vhwpu9c9waw7sxzwx"); + grant_stake.set_authorization_type(TW::Cosmos::Proto::Message_AuthorizationType_DELEGATE); + message.set_expiration(1692309600); + + auto& fee = *input.mutable_fee(); + fee.set_gas(96681); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uatom"); + amountOfFee->set_amount("2418"); + + auto privateKey = parse_hex("c7764249cdf77f8f1d840fa8af431579e5e41cf1af937e1e23afa22f3f4f0ccc"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + auto expected = R"( + { + "mode":"BROADCAST_MODE_BLOCK", + "tx_bytes":"CvgBCvUBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQS0gEKLWNvc21vczEzazBxMGw3bGcya3IzMmt2dDdseTIzNnBwbGR5OHY5ZHp3aDNnZBItY29zbW9zMWZzN2x1MjhoeDVtOWFrbTdycDBjMjQyMmNuOHIyZjdndXJ1amhmGnIKaAoqL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuU3Rha2VBdXRob3JpemF0aW9uEjoSNgo0Y29zbW9zdmFsb3BlcjFnanR2bHk5bGVsNnpza3Z3dHZsZzV2aHdwdTljOXdhdzdzeHp3eCABEgYI4LD6pgYSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAUSEwoNCgV1YXRvbRIEMjQxOBCp8wUaQIFyfuijGKf87Hz61ZqxasfLI1PZnNge4RDq/tRyB/tZI6p80iGRqHecoV6+84EQkc9GTlNRQOSlApRCsivT9XI=" + })"; + assertJSONEqual(output.serialized(), expected); +} + +TEST(CosmosStaking, RevokeCompoundingAuthz) { + // Successfully broadcasted: https://www.mintscan.io/cosmos/txs/E3218F634BB6A1BE256545EBE38275D5B02D41E88F504A43F97CD9CD2B624D44 + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1290826); + input.set_chain_id("cosmoshub-4"); + input.set_memo(""); + input.set_sequence(4); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_auth_revoke(); + message.set_grantee("cosmos1fs7lu28hx5m9akm7rp0c2422cn8r2f7gurujhf"); + message.set_granter("cosmos13k0q0l7lg2kr32kvt7ly236ppldy8v9dzwh3gd"); + message.set_msg_type_url("/cosmos.staking.v1beta1.MsgDelegate"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(87735); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uatom"); + amountOfFee->set_amount("2194"); + + auto privateKey = parse_hex("c7764249cdf77f8f1d840fa8af431579e5e41cf1af937e1e23afa22f3f4f0ccc"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + auto expected = R"( + { + "mode":"BROADCAST_MODE_BLOCK", + "tx_bytes":"CqoBCqcBCh8vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnUmV2b2tlEoMBCi1jb3Ntb3MxM2swcTBsN2xnMmtyMzJrdnQ3bHkyMzZwcGxkeTh2OWR6d2gzZ2QSLWNvc21vczFmczdsdTI4aHg1bTlha203cnAwYzI0MjJjbjhyMmY3Z3VydWpoZhojL2Nvc21vcy5zdGFraW5nLnYxYmV0YTEuTXNnRGVsZWdhdGUSZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA/fcQw1hCVUx904t+kCXTiiziaLIY8lyssu1ENfzaN1KEgQKAggBGAQSEwoNCgV1YXRvbRIEMjE5NBC3rQUaQI7K+W7MMBoD6FbFZxRBqs9VTjErztjWTy57+fvrLaTCIZ+eBs7CuaKqfUZdSN8otjubSHVTQID3k9DpPAX0yDo=" + })"; + assertJSONEqual(output.serialized(), expected); +} + +TEST(CosmosStaking, Staking) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + input.set_memo(""); + input.set_sequence(7); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_stake_message(); + message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); + message.set_validator_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"); + auto& amountOfTx = *message.mutable_amount(); + amountOfTx.set_denom("muon"); + amountOfTx.set_amount("10"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(101721); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount("1018"); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"CpsBCpgBCiMvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dEZWxlZ2F0ZRJxCi1jb3Ntb3MxaHNrNmpyeXlxamZocDVkaGM1NXRjOWp0Y2t5Z3gwZXBoNmRkMDISNGNvc21vc3ZhbG9wZXIxemt1cHI4M2hyemtuM3VwNWVsa3R6Y3EzdHVmdDhueHNtd2RxZ3AaCgoEbXVvbhICMTASZgpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3FEgQKAggBGAcSEgoMCgRtdW9uEgQxMDE4ENmaBhpA8O9Jm/kL6Za2I3poDs5vpMowYJgNvYCJBRU/vxAjs0lNZYsq40qpTbwOTbORjJA5UjQ6auc40v6uCFT4q4z+uA==\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "f0ef499bf90be996b6237a680ece6fa4ca3060980dbd808905153fbf1023b3494d658b2ae34aa94dbc0e4db3918c903952343a6ae738d2feae0854f8ab8cfeb8"); + EXPECT_EQ(output.error(), ""); + + { // Json-serialization, for coverage (to be removed later) + input.set_signing_mode(Proto::JSON); + auto signingOutput = Signer::sign(input, TWCoinTypeCosmos); + ASSERT_EQ(hex(signingOutput.signature()), "c08bdf6c2b0b4428f37975e85d329f1cb19745b000994a743b5df81d57d573aa5f755349befcc848c1d1507818723b1288594bc91df685e89aff22e0303b4861"); + EXPECT_EQ(signingOutput.error(), ""); + EXPECT_EQ(hex(signingOutput.serialized()), ""); + } +} + +TEST(CosmosStaking, Unstaking) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + input.set_memo(""); + input.set_sequence(7); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_unstake_message(); + message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); + message.set_validator_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"); + auto& amountOfTx = *message.mutable_amount(); + amountOfTx.set_denom("muon"); + amountOfTx.set_amount("10"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(101721); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount("1018"); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"Cp0BCpoBCiUvY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dVbmRlbGVnYXRlEnEKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhI0Y29zbW9zdmFsb3BlcjF6a3VwcjgzaHJ6a24zdXA1ZWxrdHpjcTN0dWZ0OG54c213ZHFncBoKCgRtdW9uEgIxMBJmClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYBxISCgwKBG11b24SBDEwMTgQ2ZoGGkBhlxHFnjBERxLtjLbMCKXcrDctaSZ9djtWCa3ely1bpV6m+6aAFjpr8aEZH+q2AtjJSEdgpQRJxP+9/gQsRTnZ\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "619711c59e30444712ed8cb6cc08a5dcac372d69267d763b5609adde972d5ba55ea6fba680163a6bf1a1191feab602d8c9484760a50449c4ffbdfe042c4539d9"); + EXPECT_EQ(output.error(), ""); + + { // Json-serialization, for coverage (to be removed later) + input.set_signing_mode(Proto::JSON); + auto signingOutput = Signer::sign(input, TWCoinTypeCosmos); + ASSERT_EQ(hex(signingOutput.signature()), "8f85a9515a211881daebfb346c2beeca3ab5c2d406a9b3ad402cfddaa3d08e2b13378e13cfef8ecf1d6500fe85d0ce3e793034dd77aba90f216427807cbff79f"); + EXPECT_EQ(signingOutput.error(), ""); + EXPECT_EQ(hex(signingOutput.serialized()), ""); + } +} + +TEST(CosmosStaking, Restaking) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + input.set_memo(""); + input.set_sequence(7); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_restake_message(); + message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); + message.set_validator_dst_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); + message.set_validator_src_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"); + + auto& amountOfTx = *message.mutable_amount(); + amountOfTx.set_denom("muon"); + amountOfTx.set_amount("10"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(101721); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount("1018"); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"CtIBCs8BCiovY29zbW9zLnN0YWtpbmcudjFiZXRhMS5Nc2dCZWdpblJlZGVsZWdhdGUSoAEKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhI0Y29zbW9zdmFsb3BlcjF6a3VwcjgzaHJ6a24zdXA1ZWxrdHpjcTN0dWZ0OG54c213ZHFncBotY29zbW9zMWhzazZqcnl5cWpmaHA1ZGhjNTV0YzlqdGNreWd4MGVwaDZkZDAyIgoKBG11b24SAjEwEmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQJXKG7D830zVXu7qgALJ3RKyQI6qZZ8rnWhgdH/kfqdxRIECgIIARgHEhIKDAoEbXVvbhIEMTAxOBDZmgYaQJ52qO5xdtBkNUeFeWrnqUXkngyHFKCXnOPPClyVI0HrULdp5jbwGra2RujEOn4BrbFCb3JFnpc2o1iuLXbKQxg=\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "9e76a8ee7176d064354785796ae7a945e49e0c8714a0979ce3cf0a5c952341eb50b769e636f01ab6b646e8c43a7e01adb1426f72459e9736a358ae2d76ca4318"); + EXPECT_EQ(output.error(), ""); + + { // Json-serialization, for coverage (to be removed later) + input.set_signing_mode(Proto::JSON); + auto signingOutput = Signer::sign(input, TWCoinTypeCosmos); + ASSERT_EQ(hex(signingOutput.signature()), "e64d3761bd25a28befcda80c0a0e208d024fdb0a2b89955170e65a5c5d454aba2ce81d57e01f0c126de5a59c2b58124c109560c9803d65a17a14b548dd6c50db"); + EXPECT_EQ(signingOutput.error(), ""); + EXPECT_EQ(hex(signingOutput.serialized()), ""); + } +} + +TEST(CosmosStaking, Withdraw) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1037); + input.set_chain_id("gaia-13003"); + input.set_memo(""); + input.set_sequence(7); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_withdraw_stake_reward_message(); + message.set_delegator_address("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); + message.set_validator_address("cosmosvaloper1zkupr83hrzkn3up5elktzcq3tuft8nxsmwdqgp"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(101721); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount("1018"); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeCosmos); + + assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"CqMBCqABCjcvY29zbW9zLmRpc3RyaWJ1dGlvbi52MWJldGExLk1zZ1dpdGhkcmF3RGVsZWdhdG9yUmV3YXJkEmUKLWNvc21vczFoc2s2anJ5eXFqZmhwNWRoYzU1dGM5anRja3lneDBlcGg2ZGQwMhI0Y29zbW9zdmFsb3BlcjF6a3VwcjgzaHJ6a24zdXA1ZWxrdHpjcTN0dWZ0OG54c213ZHFncBJmClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYBxISCgwKBG11b24SBDEwMTgQ2ZoGGkBW1Cd+0pNfMPEVXQtqG1VIijDjZP2UOiDlvUF478axnxlF8PaOAsY0S5OdUE3Wz7+nu8YVmrLZQS/8mlqLaK05\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "56d4277ed2935f30f1155d0b6a1b55488a30e364fd943a20e5bd4178efc6b19f1945f0f68e02c6344b939d504dd6cfbfa7bbc6159ab2d9412ffc9a5a8b68ad39"); + EXPECT_EQ(output.error(), ""); + + { // Json-serialization, for coverage (to be removed later) + input.set_signing_mode(Proto::JSON); + auto signingOutput = Signer::sign(input, TWCoinTypeCosmos); + ASSERT_EQ(hex(signingOutput.signature()), "546f0d67356f6af94cfb5ab22b974e499c33123f2c2c292f4f0e64878e0e728f4643105fd771550beb3f2371f08880aaa38fa8f2334c103a779f1d82d2db98d6"); + EXPECT_EQ(signingOutput.error(), ""); + EXPECT_EQ(hex(signingOutput.serialized()), ""); + } +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Cosmos/TWAnyAddressTests.cpp b/tests/chains/Cosmos/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..27559eff580 --- /dev/null +++ b/tests/chains/Cosmos/TWAnyAddressTests.cpp @@ -0,0 +1,21 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include + +#include + +TEST(CosmosAnyAddress, Cosmos) { + auto string = STRING("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeCosmos)); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "bc2da90c84049370d1b7c528bc164bc588833f21"); +} diff --git a/tests/chains/Cosmos/TWAnySignerTests.cpp b/tests/chains/Cosmos/TWAnySignerTests.cpp new file mode 100644 index 00000000000..014bd26d4f4 --- /dev/null +++ b/tests/chains/Cosmos/TWAnySignerTests.cpp @@ -0,0 +1,65 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cosmos/Address.h" +#include "HexCoding.h" +#include "proto/Cosmos.pb.h" +#include "TestUtilities.h" +#include +#include + +namespace TW::Cosmos::tests { + +TEST(TWAnySignerCosmos, SignTx) { + auto privateKey = parse_hex("8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af"); + Proto::SigningInput input; + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(546179); + input.set_chain_id("cosmoshub-4"); + input.set_memo(""); + input.set_sequence(0); + input.set_private_key(privateKey.data(), privateKey.size()); + + Address fromAddress; + EXPECT_TRUE(Address::decode("cosmos1mky69cn8ektwy0845vec9upsdphktxt03gkwlx", fromAddress)); + Address toAddress; + EXPECT_TRUE(Address::decode("cosmos18s0hdnsllgcclweu9aymw4ngktr2k0rkygdzdp", toAddress)); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("uatom"); + amountOfTx->set_amount("400000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uatom"); + amountOfFee->set_amount("1000"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeCosmos); + + // https://www.mintscan.io/cosmos/txs/85392373F54577562067030BF0D61596C91188AA5E6CA8FFE731BD0349296411 + // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "CpIBC...JXoCX", "mode": "BROADCAST_MODE_BLOCK"}' https://api.cosmos.network/cosmos/tx/v1beta1/txs + assertJSONEqual(output.serialized(), "{\"tx_bytes\": \"CpIBCo8BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm8KLWNvc21vczFta3k2OWNuOGVrdHd5MDg0NXZlYzl1cHNkcGhrdHh0MDNna3dseBItY29zbW9zMThzMGhkbnNsbGdjY2x3ZXU5YXltdzRuZ2t0cjJrMHJreWdkemRwGg8KBXVhdG9tEgY0MDAwMDASZQpOCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAuzvXOQ3owLGf5VGjeSzHzbpEfRn1+alK0HB4T4dVjZJEgQKAggBEhMKDQoFdWF0b20SBDEwMDAQwJoMGkCvvVE6d29P30cO9/lnXyGunWMPxNY12NuqDcCnFkNM0H4CUQdl1Gc9+ogIJbro5nyzZzlv9rl2/GsZox/JXoCX\", \"mode\": \"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "afbd513a776f4fdf470ef7f9675f21ae9d630fc4d635d8dbaa0dc0a716434cd07e02510765d4673dfa880825bae8e67cb367396ff6b976fc6b19a31fc95e8097"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(TWAnySignerCosmos, SignJSON) { + auto json = STRING(R"({"accountNumber":"8733","chainId":"cosmoshub-2","fee":{"amounts":[{"denom":"uatom","amount":"5000"}],"gas":"200000"}, "memo":"Testing", "messages":[{"sendCoinsMessage":{"fromAddress":"cosmos1ufwv9ymhqaal6xz47n0jhzm2wf4empfqvjy575","toAddress":"cosmos135qla4294zxarqhhgxsx0sw56yssa3z0f78pm0","amounts":[{"denom":"uatom","amount":"995000"}]}}]})"); + auto key = DATA("c9b0a273831931aa4a5f8d1a570d5021dda91d3319bd3819becdaabfb7b44e3b"); + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeCosmos)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeCosmos)); + assertStringsEqual(result, R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"5000","denom":"uatom"}],"gas":"200000"},"memo":"Testing","msg":[{"type":"cosmos-sdk/MsgSend","value":{"amount":[{"amount":"995000","denom":"uatom"}],"from_address":"cosmos1ufwv9ymhqaal6xz47n0jhzm2wf4empfqvjy575","to_address":"cosmos135qla4294zxarqhhgxsx0sw56yssa3z0f78pm0"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A6EsukEXB53GhohQVeDpxtkeH8KQIayd/Co/ApYRYkTm"},"signature":"ULEpUqNzoAnYEx2x22F3ANAiPXquAU9+mqLWoAA/ZOUGTMsdb6vryzsW6AKX2Kqj1pGNdrTcQ58Z09JPyjpgEA=="}]}})"); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Cosmos/TWCoinTypeTests.cpp b/tests/chains/Cosmos/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..228564d05d3 --- /dev/null +++ b/tests/chains/Cosmos/TWCoinTypeTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWCosmosCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCosmos)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("541FA06FB37AC1BF61922143783DD76FECA361830F9876D0342536EE8A87A790")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCosmos, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("cosmos1gu6y2a0ffteesyeyeesk23082c6998xyzmt9mz")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCosmos, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCosmos)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCosmos)); + const auto chainId = WRAPS(TWCoinTypeChainId(TWCoinTypeCosmos)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCosmos), 6); + ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeCosmos)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeCosmos)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeCosmos)); + assertStringsEqual(chainId, "cosmoshub-4"); + assertStringsEqual(symbol, "ATOM"); + assertStringsEqual(txUrl, "https://mintscan.io/cosmos/txs/541FA06FB37AC1BF61922143783DD76FECA361830F9876D0342536EE8A87A790"); + assertStringsEqual(accUrl, "https://mintscan.io/cosmos/account/cosmos1gu6y2a0ffteesyeyeesk23082c6998xyzmt9mz"); + assertStringsEqual(id, "cosmos"); + assertStringsEqual(name, "Cosmos Hub"); +} diff --git a/tests/chains/Cronos/TWAnyAddressTests.cpp b/tests/chains/Cronos/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..115c061283f --- /dev/null +++ b/tests/chains/Cronos/TWAnyAddressTests.cpp @@ -0,0 +1,21 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include + +#include + +TEST(CronosAnyAddress, Validate) { + auto string = STRING("0xEC49280228b0D05Aa8e8b756503254e1eE7835ab"); + + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeCronosChain)); + + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "ec49280228b0d05aa8e8b756503254e1ee7835ab"); +} diff --git a/tests/chains/Cronos/TWCoinTypeTests.cpp b/tests/chains/Cronos/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..b3f60a93b58 --- /dev/null +++ b/tests/chains/Cronos/TWCoinTypeTests.cpp @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include + +TEST(TWCronosCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCronosChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x850131282053328ad569fa91200aa970cbed7d97b52951fe8b449cca3708789e")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCronosChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x44eed2bb80b688a8778173c19fe11cd6876af15a")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCronosChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCronosChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCronosChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCronosChain), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeCronosChain)); + + assertStringsEqual(symbol, "CRO"); + assertStringsEqual(txUrl, "https://cronoscan.com/tx/0x850131282053328ad569fa91200aa970cbed7d97b52951fe8b449cca3708789e"); + assertStringsEqual(accUrl, "https://cronoscan.com/address/0x44eed2bb80b688a8778173c19fe11cd6876af15a"); + assertStringsEqual(id, "cronos"); + assertStringsEqual(name, "Cronos Chain"); +} diff --git a/tests/chains/CryptoOrg/AddressTests.cpp b/tests/chains/CryptoOrg/AddressTests.cpp new file mode 100644 index 00000000000..8bd3bfdd62f --- /dev/null +++ b/tests/chains/CryptoOrg/AddressTests.cpp @@ -0,0 +1,53 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Cosmos/Address.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include + +#include +#include + +namespace TW::Cosmos::tests { + +TEST(CryptoorgAddress, Valid) { + ASSERT_TRUE(Address::isValid(TWCoinTypeCryptoOrg, "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0")); + ASSERT_TRUE(Address::isValid(TWCoinTypeCryptoOrg, "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus")); + ASSERT_TRUE(Address::isValid(TWCoinTypeCryptoOrg, "cro1z53wwe7md6cewz9sqwqzn0aavpaun0gw39h3rd")); + ASSERT_TRUE(Address::isValid(TWCoinTypeCryptoOrg, "cro1y8ua5laceufhqtwzyhahq0qk7rm87hhugtsfey")); +} + +TEST(CryptoorgAddress, Invalid) { + EXPECT_FALSE(Address::isValid(TWCoinTypeCryptoOrg, "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf1")); + EXPECT_FALSE(Address::isValid(TWCoinTypeCryptoOrg, "cro1z53wwe7md6cewz9sqwqzn0aavpaun0gw39h3re")); + EXPECT_FALSE(Address::isValid(TWCoinTypeCryptoOrg, "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")); // valid cosmos +} + +TEST(CryptoorgAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e")); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto address = Address(TWCoinTypeCryptoOrg, publicKey); + EXPECT_EQ(address.string(), "cro1z53wwe7md6cewz9sqwqzn0aavpaun0gw39h3rd"); + EXPECT_EQ(hex(address.getKeyHash()), "1522e767db6eb19708b0038029bfbd607bc9bd0e"); +} + +TEST(CryptoorgAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("03ed997e396cf4292f5fce5a42bba41599ccd5d96e313154a7c9ea7049de317c77"), TWPublicKeyTypeSECP256k1); + auto address = Address(TWCoinTypeCryptoOrg, publicKey); + EXPECT_EQ(address.string(), "cro1z53wwe7md6cewz9sqwqzn0aavpaun0gw39h3rd"); + EXPECT_EQ(hex(address.getKeyHash()), "1522e767db6eb19708b0038029bfbd607bc9bd0e"); +} + +TEST(CryptoorgAddress, FromString) { + Address address; + EXPECT_TRUE(Address::decode("cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", address)); + EXPECT_EQ(address.string(), "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0"); + EXPECT_EQ(hex(address.getKeyHash()), "c2dcbc3828b42c429cedbaefa943e66679b471ed"); +} + +} // namespace TW::Cosmos::tests \ No newline at end of file diff --git a/tests/chains/CryptoOrg/SignerTests.cpp b/tests/chains/CryptoOrg/SignerTests.cpp new file mode 100644 index 00000000000..bfc9dc634b5 --- /dev/null +++ b/tests/chains/CryptoOrg/SignerTests.cpp @@ -0,0 +1,173 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "proto/Cosmos.pb.h" +#include "Cosmos/Signer.h" +#include "Cosmos/Address.h" +#include "HexCoding.h" +#include "PublicKey.h" +#include "TestUtilities.h" + +#include +#include + +namespace TW::Cosmos::tests { + +TEST(CryptoorgSigner, SignTx_DDCCE4) { + auto input = Cosmos::Proto::SigningInput(); + input.set_account_number(125798); + input.set_sequence(0); + input.set_chain_id("crypto-org-chain-mainnet-1"); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address("cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0"); + message.set_to_address("cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus"); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("basecro"); + amountOfTx->set_amount("100000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("basecro"); + amountOfFee->set_amount("5000"); + + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + + assertJSONEqual(json, R"( + { + "accountNumber": "125798", + "chainId": "crypto-org-chain-mainnet-1", + "fee": { + "amounts": [ + { + "denom": "basecro", + "amount": "5000" + } + ], + "gas": "200000" + }, + "messages": [ + { + "sendCoinsMessage": { + "fromAddress": "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", + "toAddress": "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus", + "amounts": [ + { + "denom": "basecro", + "amount": "100000000" + } + ] + } + } + ] + } + )"); + + auto privateKey = parse_hex("200e439e39cf1aad465ee3de6166247f914cbc0f823fc2dd48bf16dcd556f39d"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Cosmos::Signer::sign(input, TWCoinTypeCryptoOrg); + + assertJSONEqual(output.json(), R"( + { + "mode": "block", + "tx": { + "fee": { + "amount": [ + { + "amount": "5000", + "denom": "basecro" + } + ], + "gas": "200000" + }, + "memo": "", + "msg": [ + { + "type": "cosmos-sdk/MsgSend", + "value": { + "amount": [ + { + "amount": "100000000", + "denom": "basecro" + } + ], + "from_address": "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", + "to_address": "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus" + } + } + ], + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A4gxsGFiPn6L5Z2IjHEISkXI0IkwfL9exV3GLB171Wvj" + }, + "signature": "5+5rSFFg0FE9cTklQWQHNktBDJsz7UCnMSgF0t0+gYcrIhEWUyTtibXaHZQbKAAaciJ1BkHXYREjU55VswByVg==" + } + ] + } + } + )"); + EXPECT_EQ(hex(output.signature()), "e7ee6b485160d0513d713925416407364b410c9b33ed40a7312805d2dd3e81872b2211165324ed89b5da1d941b28001a7222750641d7611123539e55b3007256"); + + /// https://crypto.org/explorer/tx/DDCCE4052040B05914CADEFE78C0A75BE363AE39504E7EF6B2EDB8A9072AD44B + /// curl -H 'Content-Type: application/json' --data-binary '{"mode":"block","tx":{"fee": ... }}' https://mainnet.crypto.org:1317/txs +} + +TEST(CryptoorgSigner, SignJson) { + auto inputJson = R"({"accountNumber":"125798","chainId":"crypto-org-chain-mainnet-1","fee":{"amounts":[{"denom":"basecro","amount":"5000"}],"gas":"200000"},"messages":[{"sendCoinsMessage":{"fromAddress":"cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0","toAddress":"cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus","amounts":[{"denom":"basecro","amount":"100000000"}]}}]})"; + auto privateKey = parse_hex("200e439e39cf1aad465ee3de6166247f914cbc0f823fc2dd48bf16dcd556f39d"); + + auto outputJson = Cosmos::Signer::signJSON(inputJson, privateKey, TWCoinTypeCryptoOrg); + + assertJSONEqual(outputJson, R"( + { + "mode": "block", + "tx": { + "fee": { + "amount": [ + { + "amount": "5000", + "denom": "basecro" + } + ], + "gas": "200000" + }, + "memo": "", + "msg": [ + { + "type": "cosmos-sdk/MsgSend", + "value": { + "amount": [ + { + "amount": "100000000", + "denom": "basecro" + } + ], + "from_address": "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", + "to_address": "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus" + } + } + ], + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A4gxsGFiPn6L5Z2IjHEISkXI0IkwfL9exV3GLB171Wvj" + }, + "signature": "5+5rSFFg0FE9cTklQWQHNktBDJsz7UCnMSgF0t0+gYcrIhEWUyTtibXaHZQbKAAaciJ1BkHXYREjU55VswByVg==" + } + ] + } + } + )"); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/CryptoOrg/TWAnyAddressTests.cpp b/tests/chains/CryptoOrg/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..4af5812a464 --- /dev/null +++ b/tests/chains/CryptoOrg/TWAnyAddressTests.cpp @@ -0,0 +1,28 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(CryptoorgAnyAddress, IsValid) { + EXPECT_TRUE(TWAnyAddressIsValid(STRING("cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0").get(), TWCoinTypeCryptoOrg)); + EXPECT_TRUE(TWAnyAddressIsValid(STRING("cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus").get(), TWCoinTypeCryptoOrg)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02").get(), TWCoinTypeCryptoOrg)); +} + +TEST(CryptoorgAnyAddress, Create) { + auto string = STRING("cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeCryptoOrg)); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "c2dcbc3828b42c429cedbaefa943e66679b471ed"); +} diff --git a/tests/chains/CryptoOrg/TWAnySignerTests.cpp b/tests/chains/CryptoOrg/TWAnySignerTests.cpp new file mode 100644 index 00000000000..62519f35671 --- /dev/null +++ b/tests/chains/CryptoOrg/TWAnySignerTests.cpp @@ -0,0 +1,132 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "proto/Cosmos.pb.h" +#include "HexCoding.h" +#include "Base64.h" +#include "Data.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + + +const auto Address1 = "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0"; +const auto Address2 = "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus"; +const auto PrivateKey1 = "200e439e39cf1aad465ee3de6166247f914cbc0f823fc2dd48bf16dcd556f39d"; + +TEST(TWAnySignerCryptoorg, SignTx_Proto_BCB213) { + auto input = Cosmos::Proto::SigningInput(); + input.set_signing_mode(Cosmos::Proto::Protobuf); + input.set_account_number(125798); + input.set_sequence(2); + input.set_chain_id("crypto-org-chain-mainnet-1"); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(Address1); + message.set_to_address(Address2); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("basecro"); + amountOfTx->set_amount("50000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("basecro"); + amountOfFee->set_amount("5000"); + + auto privateKey = parse_hex(PrivateKey1); + input.set_private_key(privateKey.data(), privateKey.size()); + + Cosmos::Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeCryptoOrg); + + // https://crypto.org/explorer/tx/BCB213B0A121F0CF11BECCF52475F1C8328D6070F3CFDA9E14C42E6DB30E847E + // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "CpABC...F0SI=", "mode": "BROADCAST_MODE_BLOCK"}' https://mainnet.crypto.org:1317/cosmos/tx/v1beta1/txs + assertJSONEqual(output.serialized(), "{\"tx_bytes\": \"CpABCo0BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm0KKmNybzFjdHd0Y3dwZ2tza3k5ODhkaHRoNmpzbHh2ZXVtZ3UwZDQ1emdmMBIqY3JvMXhwYWh5NmM3d2xkeGFjdjZsZDk5aDQzNW1odmZuc3VwMjR2Y3VzGhMKB2Jhc2Vjcm8SCDUwMDAwMDAwEmkKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQOIMbBhYj5+i+WdiIxxCEpFyNCJMHy/XsVdxiwde9Vr4xIECgIIARgCEhUKDwoHYmFzZWNybxIENTAwMBDAmgwaQAcxK9xk6r69gmz+1UWaCnYxNuXPXZdp59YcqKPJE5d6fp+IICTBOwd2rs8MiApcf8kNSrbZ6oECxcGQAdxF0SI=\", \"mode\": \"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "07312bdc64eabebd826cfed5459a0a763136e5cf5d9769e7d61ca8a3c913977a7e9f882024c13b0776aecf0c880a5c7fc90d4ab6d9ea8102c5c19001dc45d122"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(TWAnySignerCryptoorg, SignTx_Json_DDCCE4) { + auto input = Cosmos::Proto::SigningInput(); + input.set_signing_mode(Cosmos::Proto::JSON); // obsolete + input.set_account_number(125798); + input.set_sequence(0); + input.set_chain_id("crypto-org-chain-mainnet-1"); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(Address1); + message.set_to_address(Address2); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("basecro"); + amountOfTx->set_amount("100000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("basecro"); + amountOfFee->set_amount("5000"); + + auto privateKey = parse_hex(PrivateKey1); + input.set_private_key(privateKey.data(), privateKey.size()); + + Cosmos::Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeCryptoOrg); + + assertJSONEqual(output.json(), R"( + { + "mode": "block", + "tx": { + "fee": { + "amount": [ + { + "amount": "5000", + "denom": "basecro" + } + ], + "gas": "200000" + }, + "memo": "", + "msg": [ + { + "type": "cosmos-sdk/MsgSend", + "value": { + "amount": [ + { + "amount": "100000000", + "denom": "basecro" + } + ], + "from_address": "cro1ctwtcwpgksky988dhth6jslxveumgu0d45zgf0", + "to_address": "cro1xpahy6c7wldxacv6ld99h435mhvfnsup24vcus" + } + } + ], + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A4gxsGFiPn6L5Z2IjHEISkXI0IkwfL9exV3GLB171Wvj" + }, + "signature": "5+5rSFFg0FE9cTklQWQHNktBDJsz7UCnMSgF0t0+gYcrIhEWUyTtibXaHZQbKAAaciJ1BkHXYREjU55VswByVg==" + } + ] + } + } + )"); + EXPECT_EQ(hex(output.signature()), "e7ee6b485160d0513d713925416407364b410c9b33ed40a7312805d2dd3e81872b2211165324ed89b5da1d941b28001a7222750641d7611123539e55b3007256"); + EXPECT_EQ(output.serialized(), ""); + EXPECT_EQ(output.error(), ""); + + /// https://crypto.org/explorer/tx/DDCCE4052040B05914CADEFE78C0A75BE363AE39504E7EF6B2EDB8A9072AD44B + /// curl -H 'Content-Type: application/json' --data-binary '{"mode":"block","tx":{"fee": ... }}' https://mainnet.crypto.org:1317/txs +} diff --git a/tests/chains/CryptoOrg/TWCoinTypeTests.cpp b/tests/chains/CryptoOrg/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..0e19d390f0b --- /dev/null +++ b/tests/chains/CryptoOrg/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWCryptoorgCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeCryptoOrg)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("D87D2EB46B21466886EE149C1DEA3AE6C2E019C7D8C24FA1533A95439DDCE1E2")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeCryptoOrg, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("cro10wrflcdc4pys9vvgqm98yg7yv5ltj7d3xehent")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeCryptoOrg, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeCryptoOrg)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeCryptoOrg)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeCryptoOrg), 8); + ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeCryptoOrg)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeCryptoOrg)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeCryptoOrg)); + assertStringsEqual(symbol, "CRO"); + assertStringsEqual(txUrl, "https://crypto.org/explorer/tx/D87D2EB46B21466886EE149C1DEA3AE6C2E019C7D8C24FA1533A95439DDCE1E2"); + assertStringsEqual(accUrl, "https://crypto.org/explorer/account/cro10wrflcdc4pys9vvgqm98yg7yv5ltj7d3xehent"); + assertStringsEqual(id, "cryptoorg"); + assertStringsEqual(name, "Crypto.org"); +} diff --git a/tests/chains/Dash/TWCoinTypeTests.cpp b/tests/chains/Dash/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..9995965fda6 --- /dev/null +++ b/tests/chains/Dash/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWDashCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeDash)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeDash, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeDash, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeDash)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeDash)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeDash), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeDash)); + ASSERT_EQ(0x10, TWCoinTypeP2shPrefix(TWCoinTypeDash)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeDash)); + assertStringsEqual(symbol, "DASH"); + assertStringsEqual(txUrl, "https://blockchair.com/dash/transaction/t123"); + assertStringsEqual(accUrl, "https://blockchair.com/dash/address/a12"); + assertStringsEqual(id, "dash"); + assertStringsEqual(name, "Dash"); +} diff --git a/tests/Dash/TWDashTests.cpp b/tests/chains/Dash/TWDashTests.cpp similarity index 95% rename from tests/Dash/TWDashTests.cpp rename to tests/chains/Dash/TWDashTests.cpp index c1ad9c58117..a0d86239464 100644 --- a/tests/Dash/TWDashTests.cpp +++ b/tests/chains/Dash/TWDashTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/chains/Decred/AddressTests.cpp b/tests/chains/Decred/AddressTests.cpp new file mode 100644 index 00000000000..9629a821d21 --- /dev/null +++ b/tests/chains/Decred/AddressTests.cpp @@ -0,0 +1,48 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Decred/Address.h" + +#include "Coin.h" +#include "HDWallet.h" +#include "HexCoding.h" + +#include + +namespace TW::Decred::tests { + +TEST(DecredAddress, FromPublicKey) { + const auto publicKey = PublicKey(parse_hex("0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"), TWPublicKeyTypeSECP256k1); + const auto address = Address(publicKey); + ASSERT_EQ(address.string(), "DsmcYVbP1Nmag2H4AS17UTvmWXmGeA7nLDx"); +} + +TEST(DecredAddress, Valid) { + ASSERT_TRUE(Address::isValid("DsmcYVbP1Nmag2H4AS17UTvmWXmGeA7nLDx")); + ASSERT_TRUE(Address::isValid("Dcur2mcGjmENx4DhNqDctW5wJCVyT3Qeqkx")); +} + +TEST(DecredAddress, Invalid) { + ASSERT_FALSE(Address::isValid("rnBFvgZphmN39GWzUJeUitaP22Fr9be75H")); + ASSERT_FALSE(Address::isValid("t3gQDEavk5VzAAHK8TrQu2BWDLxEiF1unBm")); +} + +TEST(DecredAddress, FromString) { + const auto string = "DsmcYVbP1Nmag2H4AS17UTvmWXmGeA7nLDx"; + const auto address = Address(string); + + ASSERT_EQ(address.string(), string); +} + +TEST(DecredAddress, Derive) { + const auto mnemonic = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; + const auto wallet = HDWallet(mnemonic, ""); + const auto path = TW::derivationPath(TWCoinTypeDecred); + const auto address = TW::deriveAddress(TWCoinTypeDecred, wallet.getKey(TWCoinTypeDecred, path)); + ASSERT_EQ(address, "DsVMHD5D86dpRnt2GPZvv4bYUJZg6B9Pzqa"); +} + +} // namespace TW::Decred::tests diff --git a/tests/chains/Decred/SignerTests.cpp b/tests/chains/Decred/SignerTests.cpp new file mode 100644 index 00000000000..85cf02ea128 --- /dev/null +++ b/tests/chains/Decred/SignerTests.cpp @@ -0,0 +1,434 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Decred/Address.h" +#include "Decred/Signer.h" +#include "proto/Decred.pb.h" + +#include "Hash.h" +#include "HexCoding.h" +#include "PrivateKey.h" + +#include +#include + +using namespace TW; + +namespace TW::Decred::tests { + +// clang-format off +TEST(DecredSigner, SignP2PKH) { + const auto privateKey = PrivateKey(parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + const auto keyhash = Hash::ripemd(Hash::blake256(publicKey.bytes)); + + const auto address = Address(publicKey); + ASSERT_EQ(address.string(), "DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + + + // For this example, create a fake transaction that represents what would + // ordinarily be the real transaction that is being spent. It contains a + // single output that pays to address in the amount of 1 DCR. + auto originTx = Transaction(); + + auto txInOrigin = TransactionInput(); + txInOrigin.previousOutput = OutPoint(std::array{}, UINT32_MAX, 0); + txInOrigin.valueIn = 100'000'000; + txInOrigin.script = Bitcoin::Script(Data{OP_0, OP_0}); + originTx.inputs.push_back(txInOrigin); + + auto txOutOrigin = TransactionOutput(); + txOutOrigin.value = 100'000'000; + txOutOrigin.script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); + originTx.outputs.push_back(txOutOrigin); + + ASSERT_EQ(hex(originTx.hash()), "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a"); + + + // Setup input + Bitcoin::Proto::SigningInput input; + input.set_hash_type(TWBitcoinSigHashTypeAll); + input.set_amount(100'000'000); + input.set_byte_fee(1); + input.set_to_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + input.set_change_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + + auto utxoKey0 = parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c"); + input.add_private_key(utxoKey0.data(), utxoKey0.size()); + + auto utxo0 = input.add_utxo(); + auto utxo0Script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); + utxo0->set_script(utxo0Script.bytes.data(), utxo0Script.bytes.size()); + utxo0->set_amount(100'000'000); + utxo0->mutable_out_point()->set_hash(originTx.hash().data(), originTx.hash().size()); + utxo0->mutable_out_point()->set_index(0); + + + // Create the transaction to redeem the fake transaction. + auto redeemTx = Transaction(); + + auto txIn = TransactionInput(); + txIn.previousOutput = OutPoint(originTx.hash(), 0, 0); + txIn.valueIn = 100'000'000; + redeemTx.inputs.push_back(txIn); + + auto txOut = TransactionOutput(); + redeemTx.outputs.push_back(txOut); + + auto plan = input.mutable_plan(); + plan->set_amount(100'000'000); + plan->set_available_amount(100'000'000); + plan->set_fee(0); + plan->set_change(0); + auto utxop0 = plan->add_utxos(); + *utxop0 = *utxo0; + + // Sign + auto signer = Signer(std::move(input)); + signer._transaction = redeemTx; + signer.txPlan.amount = 100'000'000; + const auto result = signer.sign(); + + ASSERT_TRUE(result); + + const auto expectedSignature = "47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f0121" + "02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5"; + EXPECT_EQ(hex(result.payload().inputs[0].script.bytes), expectedSignature); + + const auto expectedEncoded = + "0100" // Serialize type + "0000" // Version + "01" // Inputs + "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a" // Hash + "00000000" // Index + "00" // Tree + "ffffffff" // Sequence + "01" // Outputs + "0000000000000000" // Value + "0000" // Version + "00" // Script + "00000000" // Lock time + "00000000" // Expiry + "01" + "00e1f50500000000" // Value + "00000000" // Block height + "ffffffff" // Block index + "6a47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f012102a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5"; + auto encoded = Data(); + result.payload().encode(encoded); + EXPECT_EQ(hex(encoded), expectedEncoded); +} + +TEST(DecredSigner, SignP2SH) { + const auto privateKey = PrivateKey(parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + const auto keyhash = Hash::ripemd(Hash::blake256(publicKey.bytes)); + + const auto address = Address(publicKey); + ASSERT_EQ(address.string(), "DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + + + // For this example, create a fake transaction that represents what would + // ordinarily be the real transaction that is being spent. It contains a + // single output that pays to address in the amount of 1 DCR. + auto originTx = Transaction(); + + auto txInOrigin = TransactionInput(); + txInOrigin.previousOutput = OutPoint(std::array{}, UINT32_MAX, 0); + txInOrigin.valueIn = 100'000'000; + txInOrigin.script = Bitcoin::Script(Data{OP_0, OP_0}); + originTx.inputs.push_back(txInOrigin); + + auto txOutOrigin = TransactionOutput(); + txOutOrigin.value = 100'000'000; + txOutOrigin.script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); + originTx.outputs.push_back(txOutOrigin); + + ASSERT_EQ(hex(originTx.hash()), "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a"); + + + // Setup input + Bitcoin::Proto::SigningInput input; + input.set_hash_type(TWBitcoinSigHashTypeAll); + input.set_amount(100'000'000); + input.set_byte_fee(1); + input.set_to_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + input.set_change_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + + auto utxoKey0 = parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c"); + input.add_private_key(utxoKey0.data(), utxoKey0.size()); + + auto redeemScript = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); + auto scriptHash = Hash::ripemd(Hash::sha256(redeemScript.bytes)); + auto scriptString = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); + (*input.mutable_scripts())[hex(scriptHash.begin(), scriptHash.end())] = scriptString; + + auto utxo0 = input.add_utxo(); + auto utxo0Script = Bitcoin::Script::buildPayToScriptHash(scriptHash); + utxo0->set_script(utxo0Script.bytes.data(), utxo0Script.bytes.size()); + utxo0->set_amount(100'000'000); + utxo0->mutable_out_point()->set_hash(originTx.hash().data(), originTx.hash().size()); + utxo0->mutable_out_point()->set_index(0); + + auto plan = input.mutable_plan(); + plan->set_amount(100'000'000); + plan->set_available_amount(100'000'000); + plan->set_fee(0); + plan->set_change(0); + auto utxop0 = plan->add_utxos(); + *utxop0 = *utxo0; + + // Create the transaction to redeem the fake transaction. + auto redeemTx = Transaction(); + + auto txIn = TransactionInput(); + txIn.previousOutput = OutPoint(originTx.hash(), 0, 0); + txIn.valueIn = 100'000'000; + redeemTx.inputs.push_back(txIn); + + auto txOut = TransactionOutput(); + redeemTx.outputs.push_back(txOut); + + + // Sign + auto signer = Signer(std::move(input)); + signer._transaction = redeemTx; + signer.txPlan.amount = 100'000'000; + const auto result = signer.sign(); + + ASSERT_TRUE(result); + + const auto expectedSignature = "47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f012102a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf51976a914f5eba6730a4052ddeef0a93d93d24004f49db51e88ac1976a914f5eba6730a4052ddeef0a93d93d24004f49db51e88ac"; + EXPECT_EQ(hex(result.payload().inputs[0].script.bytes), expectedSignature); + + const auto expectedEncoded = + "0100" // Serialize type + "0000" // Version + "01" // Inputs + "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a" // Hash + "00000000" // Index + "00" // Tree + "ffffffff" // Sequence + "01" // Outputs + "0000000000000000" // Value + "0000" // Version + "00" // Script + "00000000" // Lock time + "00000000" // Expiry + "01" + "00e1f50500000000" // Value + "00000000" // Block height + "ffffffff" // Block index + "9e47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f012102a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf51976a914f5eba6730a4052ddeef0a93d93d24004f49db51e88ac1976a914f5eba6730a4052ddeef0a93d93d24004f49db51e88ac"; + auto encoded = Data(); + result.payload().encode(encoded); + EXPECT_EQ(hex(encoded), expectedEncoded); +} + +TEST(DecredSigner, SignNegativeNoUtxo) { + const auto privateKey = PrivateKey(parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + const auto keyhash = Hash::ripemd(Hash::blake256(publicKey.bytes)); + + const auto address = Address(publicKey); + ASSERT_EQ(address.string(), "DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + + auto originTx = Transaction(); + + auto txInOrigin = TransactionInput(); + txInOrigin.previousOutput = OutPoint(std::array{}, UINT32_MAX, 0); + txInOrigin.valueIn = 100'000'000; + txInOrigin.script = Bitcoin::Script(Data{OP_0, OP_0}); + originTx.inputs.push_back(txInOrigin); + + auto txOutOrigin = TransactionOutput(); + txOutOrigin.value = 100'000'000; + txOutOrigin.script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); + originTx.outputs.push_back(txOutOrigin); + + ASSERT_EQ(hex(originTx.hash()), "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a"); + + // Setup input + Bitcoin::Proto::SigningInput input; + input.set_hash_type(TWBitcoinSigHashTypeAll); + input.set_amount(100'000'000); + input.set_byte_fee(1); + input.set_to_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + input.set_change_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + + auto utxoKey0 = parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c"); + input.add_private_key(utxoKey0.data(), utxoKey0.size()); + + // Create the transaction to redeem the fake transaction. + auto redeemTx = Transaction(); + + auto txIn = TransactionInput(); + txIn.previousOutput = OutPoint(originTx.hash(), 0, 0); + txIn.valueIn = 100'000'000; + redeemTx.inputs.push_back(txIn); + + auto txOut = TransactionOutput(); + redeemTx.outputs.push_back(txOut); + + // Sign + auto signer = Signer(std::move(input)); + signer._transaction = redeemTx; + signer.txPlan.amount = 100'000'000; + const auto result = signer.sign(); + + // Fails as there are 0 utxos + ASSERT_FALSE(result); +} + +TEST(DecredSigner, SignP2PKH_NoPlan) { + const auto privateKey = PrivateKey(parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + const auto keyhash = Hash::ripemd(Hash::blake256(publicKey.bytes)); + + const auto address = Address(publicKey); + ASSERT_EQ(address.string(), "DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + + + // For this example, create a fake transaction that represents what would + // ordinarily be the real transaction that is being spent. It contains a + // single output that pays to address in the amount of 1 DCR. + auto originTx = Transaction(); + + auto txInOrigin = TransactionInput(); + txInOrigin.previousOutput = OutPoint(std::array{}, UINT32_MAX, 0); + txInOrigin.valueIn = 150'000'000; + txInOrigin.script = Bitcoin::Script(Data{OP_0, OP_0}); + originTx.inputs.push_back(txInOrigin); + + auto txOutOrigin = TransactionOutput(); + txOutOrigin.value = 100'000'000; + txOutOrigin.script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); + originTx.outputs.push_back(txOutOrigin); + + ASSERT_EQ(hex(originTx.hash()), "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a"); + + + // Setup input + Bitcoin::Proto::SigningInput input; + input.set_hash_type(TWBitcoinSigHashTypeAll); + input.set_amount(100'000'000); + input.set_byte_fee(1); + input.set_to_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + input.set_change_address("DsoPDLh462ULTy1QMSvBGLqGKQENerrdZDH"); + + auto utxoKey0 = parse_hex("22a47fa09a223f2aa079edf85a7c2d4f8720ee63e502ee2869afab7de234b80c"); + input.add_private_key(utxoKey0.data(), utxoKey0.size()); + + auto utxo0 = input.add_utxo(); + auto utxo0Script = Bitcoin::Script::buildPayToPublicKeyHash(keyhash); + utxo0->set_script(utxo0Script.bytes.data(), utxo0Script.bytes.size()); + utxo0->set_amount(150'000'000); + utxo0->mutable_out_point()->set_hash(originTx.hash().data(), originTx.hash().size()); + utxo0->mutable_out_point()->set_index(0); + + + // Create the transaction to redeem the fake transaction. + auto redeemTx = Transaction(); + + auto txIn = TransactionInput(); + txIn.previousOutput = OutPoint(originTx.hash(), 0, 0); + txIn.valueIn = 100'000'000; + redeemTx.inputs.push_back(txIn); + + auto txOut = TransactionOutput(); + redeemTx.outputs.push_back(txOut); + + + // Sign + auto signer = Signer(std::move(input)); + signer._transaction = redeemTx; + //signer.txPlan.utxos.push_back(*utxo0); + //signer.txPlan.amount = 100'000'000; + const auto result = signer.sign(); + + ASSERT_TRUE(result); + ASSERT_TRUE(result.payload().inputs.size() >= 1); + + const auto expectedSignature = "47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f0121" + "02a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5"; + EXPECT_EQ(hex(result.payload().inputs[0].script.bytes), expectedSignature); + + const auto expectedEncoded = + "0100" // Serialize type + "0000" // Version + "01" // Inputs + "0ff6ff7c6774a56ccc51598b11724c9c441cadc52978ddb5f08f3511a0cc777a" // Hash + "00000000" // Index + "00" // Tree + "ffffffff" // Sequence + "01" // Outputs + "0000000000000000" // Value + "0000" // Version + "00" // Script + "00000000" // Lock time + "00000000" // Expiry + "01" + "00e1f50500000000" // Value + "00000000" // Block height + "ffffffff" // Block index + "6a47304402201ac7bdf56a9d12f3bc09cf7b47cdfafc1348628f659e37b455d497cb6e7a748802202b3630eedee1bbc9248424e4a1b8671e14631a069f36ac8860dee0bb9ea1541f012102a673638cb9587cb68ea08dbef685c6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5"; + auto encoded = Data(); + result.payload().encode(encoded); + EXPECT_EQ(hex(encoded), expectedEncoded); +} + +TEST(DecredSigning, SignP2WPKH_NegativeAddressWrongType) { + auto hash0 = parse_hex("fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f"); + auto hash1 = parse_hex("ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a"); + + // Setup input + Bitcoin::Proto::SigningInput input; + input.set_hash_type(TWBitcoinSigHashTypeAll); + input.set_amount(335'790'000); + input.set_byte_fee(1); + input.set_to_address("1Bp9U1ogV3A14FMvKbRJms7ctyso4Z4Tcx"); + input.set_change_address("1FQc5LdgGHMHEN9nwkjmz6tWkxhPpxBvBU"); + + auto utxoKey0 = parse_hex("bbc27228ddcb9209d7fd6f36b02f7dfa6252af40bb2f1cbc7a557da8027ff866"); + input.add_private_key(utxoKey0.data(), utxoKey0.size()); + + auto utxoKey1 = parse_hex("619c335025c7f4012e556c2a58b2506e30b8511b53ade95ea316fd8c3286feb9"); + input.add_private_key(utxoKey1.data(), utxoKey1.size()); + + auto scriptPub1 = Bitcoin::Script(parse_hex("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1")); + auto scriptHash = std::vector(); + scriptPub1.matchPayToWitnessPublicKeyHash(scriptHash); + auto scriptHashHex = hex(scriptHash.begin(), scriptHash.end()); + ASSERT_EQ(scriptHashHex, "1d0f172a0ecb48aee1be1f2687d2963ae33f71a1"); + + auto redeemScript = Bitcoin::Script::buildPayToPublicKeyHash(scriptHash); + auto scriptString = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); + (*input.mutable_scripts())[scriptHashHex] = scriptString; + + auto utxo0 = input.add_utxo(); + auto utxo0Script = parse_hex("2103c9f4836b9a4f77fc0d81f7bcb01b7f1b35916864b9476c241ce9fc198bd25432ac"); + utxo0->set_script(utxo0Script.data(), utxo0Script.size()); + utxo0->set_amount(625'000'000); + utxo0->mutable_out_point()->set_hash(hash0.data(), hash0.size()); + utxo0->mutable_out_point()->set_index(0); + utxo0->mutable_out_point()->set_sequence(UINT32_MAX); + + auto utxo1 = input.add_utxo(); + auto utxo1Script = parse_hex("00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1"); + utxo1->set_script(utxo1Script.data(), utxo1Script.size()); + utxo1->set_amount(600'000'000); + utxo1->mutable_out_point()->set_hash(hash1.data(), hash1.size()); + utxo1->mutable_out_point()->set_index(1); + utxo1->mutable_out_point()->set_sequence(UINT32_MAX); + + // Sign + auto result = Signer(std::move(input)).sign(); + + ASSERT_FALSE(result) << std::to_string(result.error()); +} +// clang-format on + +} // namespace TW::Decred::tests diff --git a/tests/chains/Decred/TWAnySignerTests.cpp b/tests/chains/Decred/TWAnySignerTests.cpp new file mode 100644 index 00000000000..2960a5fcb1c --- /dev/null +++ b/tests/chains/Decred/TWAnySignerTests.cpp @@ -0,0 +1,114 @@ + +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include "HexCoding.h" +#include "proto/Bitcoin.pb.h" +#include "proto/Decred.pb.h" +#include +#include +#include +#include + + +namespace TW::Decred { + +Bitcoin::Proto::SigningInput createInput() { + const int64_t utxoValue = 39900000; + const int64_t amount = 10000000; + + auto input = Bitcoin::Proto::SigningInput(); + input.set_hash_type(TWBitcoinSigHashTypeAll); + input.set_amount(amount); + input.set_byte_fee(1); + input.set_to_address("Dsesp1V6DZDEtcq2behmBVKdYqKMdkh96hL"); + input.set_change_address("DsUoWCAxprdGNtKQqambFbTcSBgH1SHn9Gp"); + input.set_coin_type(TWCoinTypeDecred); + + auto& utxo = *input.add_utxo(); + + auto hash = parse_hex("fdbfe9dd703f306794a467f175be5bd9748a7925033ea1cf9889d7cf4dd11550"); + auto script = parse_hex("76a914b75fdec70b2e731795dd123ab40f918bf099fee088ac"); + auto utxoKey = parse_hex("ba005cd605d8a02e3d5dfd04234cef3a3ee4f76bfbad2722d1fb5af8e12e6764"); + + utxo.set_amount(utxoValue); + utxo.set_script(script.data(), script.size()); + + auto& outpoint = *utxo.mutable_out_point(); + outpoint.set_hash(hash.data(), hash.size()); + outpoint.set_index(0); + + input.add_private_key(utxoKey.data(), utxoKey.size()); + return input; +} + +TEST(TWAnySignerDecred, Signing) { + auto input = createInput(); + + const int64_t utxoValue = 39900000; + const int64_t amount = 10000000; + const int64_t fee = 100000; + + auto& plan = *input.mutable_plan(); + plan.set_amount(amount); + plan.set_available_amount(utxoValue); + plan.set_fee(fee); + plan.set_change(utxoValue - amount - fee); + auto& planUtxo = *plan.add_utxos(); + planUtxo = input.utxo(0); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeDecred); + + ASSERT_EQ(output.error(), Common::Proto::OK); + ASSERT_EQ(hex(output.encoded()), "0100000001fdbfe9dd703f306794a467f175be5bd9748a7925033ea1cf9889d7cf4dd1155000000000000000000002809698000000000000001976a914989b1aecabf1c24e213cc0f2d8a22ffee25dd4e188ac40b6c6010000000000001976a9142a194fc92e27fef9cc2b057bc9060c580cbb484888ac000000000000000001000000000000000000000000ffffffff6a47304402206ee887c9239e5fff0048674bdfff2a8cfbeec6cd4a3ccebcc12fac44b24cc5ac0220718f7c760818fde18bc5ba8457d43d5a145cc4cf13d2a5557cba9107e9f4558d0121026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6"); +} + +TEST(TWAnySignerDecred, Plan) { + auto input = createInput(); + + Bitcoin::Proto::TransactionPlan plan; + ANY_PLAN(input, plan, TWCoinTypeDecred); + + EXPECT_EQ(plan.amount(), 10000000); + EXPECT_EQ(plan.available_amount(), 39900000); + EXPECT_EQ(plan.fee(), 254); + EXPECT_EQ(plan.change(), 29899746); + EXPECT_EQ(plan.utxos_size(), 1); + EXPECT_EQ(plan.branch_id(), ""); +} + +TEST(TWAnySignerDecred, PlanAndSign) { + auto input = createInput(); + + Bitcoin::Proto::TransactionPlan plan; + ANY_PLAN(input, plan, TWCoinTypeDecred); + + EXPECT_EQ(plan.amount(), 10000000); + EXPECT_EQ(plan.available_amount(), 39900000); + EXPECT_EQ(plan.fee(), 254); + EXPECT_EQ(plan.change(), 29899746); + EXPECT_EQ(plan.utxos_size(), 1); + EXPECT_EQ(plan.branch_id(), ""); + + // copy over plan fields + *input.mutable_plan() = plan; + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeDecred); + + ASSERT_EQ(output.error(), Common::Proto::OK); + ASSERT_EQ(output.encoded().size(), 251ul); + ASSERT_EQ(hex(output.encoded()), "0100000001fdbfe9dd703f306794a467f175be5bd9748a7925033ea1cf9889d7cf4dd1155000000000000000000002809698000000000000001976a914989b1aecabf1c24e213cc0f2d8a22ffee25dd4e188ace23bc8010000000000001976a9142a194fc92e27fef9cc2b057bc9060c580cbb484888ac000000000000000001000000000000000000000000ffffffff6a47304402203e6ee9e16d6bc36bb4242f7a4cac333a1c2a150ea16143412b88b721f6ae16bf02201019affdf815a5c22e4b0fb7e4685c4707094922d6a41354f9055d3bb0f26e630121026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6"); +} + +TEST(TWAnySignerDecred, SupportsJSON) { + ASSERT_FALSE(TWAnySignerSupportsJSON(TWCoinTypeDecred)); +} + +} // namespace diff --git a/tests/chains/Decred/TWCoinTypeTests.cpp b/tests/chains/Decred/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..f355997d41b --- /dev/null +++ b/tests/chains/Decred/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWDecredCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeDecred)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeDecred, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeDecred, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeDecred)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeDecred)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeDecred), 8); + ASSERT_EQ(TWBlockchainDecred, TWCoinTypeBlockchain(TWCoinTypeDecred)); + ASSERT_EQ(0x1a, TWCoinTypeP2shPrefix(TWCoinTypeDecred)); + ASSERT_EQ(0x7, TWCoinTypeStaticPrefix(TWCoinTypeDecred)); + assertStringsEqual(symbol, "DCR"); + assertStringsEqual(txUrl, "https://dcrdata.decred.org/tx/t123"); + assertStringsEqual(accUrl, "https://dcrdata.decred.org/address/a12"); + assertStringsEqual(id, "decred"); + assertStringsEqual(name, "Decred"); +} diff --git a/tests/Decred/TWDecredTests.cpp b/tests/chains/Decred/TWDecredTests.cpp similarity index 98% rename from tests/Decred/TWDecredTests.cpp rename to tests/chains/Decred/TWDecredTests.cpp index 1b05944929b..661420c887c 100644 --- a/tests/Decred/TWDecredTests.cpp +++ b/tests/chains/Decred/TWDecredTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/DigiByte/TWCoinTypeTests.cpp b/tests/chains/DigiByte/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..46d2adfa5de --- /dev/null +++ b/tests/chains/DigiByte/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWDigiByteCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeDigiByte)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeDigiByte, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeDigiByte, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeDigiByte)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeDigiByte)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeDigiByte), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeDigiByte)); + ASSERT_EQ(0x3f, TWCoinTypeP2shPrefix(TWCoinTypeDigiByte)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeDigiByte)); + assertStringsEqual(symbol, "DGB"); + assertStringsEqual(txUrl, "https://digiexplorer.info/tx/t123"); + assertStringsEqual(accUrl, "https://digiexplorer.info/address/a12"); + assertStringsEqual(id, "digibyte"); + assertStringsEqual(name, "DigiByte"); +} diff --git a/tests/DigiByte/TWDigiByteTests.cpp b/tests/chains/DigiByte/TWDigiByteTests.cpp similarity index 89% rename from tests/DigiByte/TWDigiByteTests.cpp rename to tests/chains/DigiByte/TWDigiByteTests.cpp index c135a7d662a..c824018607c 100644 --- a/tests/DigiByte/TWDigiByteTests.cpp +++ b/tests/chains/DigiByte/TWDigiByteTests.cpp @@ -1,10 +1,10 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/OutPoint.h" #include "Bitcoin/TransactionBuilder.h" @@ -16,8 +16,7 @@ #include -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { TEST(DigiByteTransaction, SignTransaction) { /* @@ -71,18 +70,19 @@ TEST(DigiByteTransaction, SignTransaction) { hex(serialized), "01000000" "01" - "ea63bdc39035ebe02df7ad999581156f996303a70f9a3358811454a7ca806b96" - "00000000" - "6a" - "473044022003e9756b12ecbe5788fdb6eb4b6d7b58f9f9410df32f3047edb0dd0ebffb0d630220499d00d17e50c48b4bac6c0ce148f13bb3109a8845fa3400a2d6a57dabf2c4010121024e525e582452cece7b869532d9e354cfec58b71cbed76f7238c91274a64b2116" - "ffffffff" - "02" - "4023050600000000""19" - "76a9142d5b215a11029ee51a1dd9404d271c7e4a74f5f288ac" - "18053d0000000000""19" - "76a91447825943ca6a936b177fdc7c9dc05251640169c288ac" + "ea63bdc39035ebe02df7ad999581156f996303a70f9a3358811454a7ca806b96" "00000000" - ); + "6a" + "473044022003e9756b12ecbe5788fdb6eb4b6d7b58f9f9410df32f3047edb0dd0ebffb0d630220499d00d17e50c48b4bac6c0ce148f13bb3109a8845fa3400a2d6a57dabf2c4010121024e525e582452cece7b869532d9e354cfec58b71cbed76f7238c91274a64b2116" + "ffffffff" + "02" + "4023050600000000" + "19" + "76a9142d5b215a11029ee51a1dd9404d271c7e4a74f5f288ac" + "18053d0000000000" + "19" + "76a91447825943ca6a936b177fdc7c9dc05251640169c288ac" + "00000000"); } TEST(DigiByteTransaction, SignP2WPKH) { @@ -127,7 +127,7 @@ TEST(DigiByteTransaction, SignP2WPKH) { TEST(DigiByteTransaction, LockScripts) { // https://dgb2.trezor.io/tx/966b80caa754148158339a0fa70363996f15819599adf72de0eb3590c3bd63ea - + auto script = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("DBfCffUdSbhqKZhjuvrJ6AgvJofT4E2kp4").get(), TWCoinTypeDigiByte)); auto scriptData = WRAPD(TWBitcoinScriptData(script.get())); assertHexEqual(scriptData, "76a91447825943ca6a936b177fdc7c9dc05251640169c288ac"); @@ -137,8 +137,10 @@ TEST(DigiByteTransaction, LockScripts) { assertHexEqual(scriptData2, "0014885534ab5dc680b68d95c0af49ec2acc2e9915c4"); // https://dgb2.trezor.io/tx/965eb4afcd0aa6e3f4f8fc3513ca042f09e6e2235367fa006cbd1f546c293a2a - + auto script3 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("SUngTA1vaC2E62mbnc81Mdos3TcvZHwsVo").get(), TWCoinTypeDigiByte)); auto scriptData3 = WRAPD(TWBitcoinScriptData(script3.get())); assertHexEqual(scriptData3, "a91452356ed3d2d31eb8b263ace5d164e3cf3b37096687"); } + +} // namespace TW::Bitcoin diff --git a/tests/chains/Dogecoin/TWCoinTypeTests.cpp b/tests/chains/Dogecoin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..3196965f758 --- /dev/null +++ b/tests/chains/Dogecoin/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWDogecoinCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeDogecoin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeDogecoin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeDogecoin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeDogecoin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeDogecoin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeDogecoin), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeDogecoin)); + ASSERT_EQ(0x16, TWCoinTypeP2shPrefix(TWCoinTypeDogecoin)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeDogecoin)); + assertStringsEqual(symbol, "DOGE"); + assertStringsEqual(txUrl, "https://blockchair.com/dogecoin/transaction/t123"); + assertStringsEqual(accUrl, "https://blockchair.com/dogecoin/address/a12"); + assertStringsEqual(id, "doge"); + assertStringsEqual(name, "Dogecoin"); +} diff --git a/tests/Dogecoin/TWDogeTests.cpp b/tests/chains/Dogecoin/TWDogeTests.cpp similarity index 95% rename from tests/Dogecoin/TWDogeTests.cpp rename to tests/chains/Dogecoin/TWDogeTests.cpp index 76f83bdb96a..627fc78a72e 100644 --- a/tests/Dogecoin/TWDogeTests.cpp +++ b/tests/chains/Dogecoin/TWDogeTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/chains/ECO/TWCoinTypeTests.cpp b/tests/chains/ECO/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..a72cb7b7877 --- /dev/null +++ b/tests/chains/ECO/TWCoinTypeTests.cpp @@ -0,0 +1,30 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWHECOCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeECOChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x2e62832615f5b68b3bbcd72046a24260ce47052841c1679841b9c574d3959f13")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeECOChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xc5a5b3e49e5d06afe163553c942dc59b4e358cf1")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeECOChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeECOChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeECOChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeECOChain), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeECOChain)); + + assertStringsEqual(symbol, "HT"); + assertStringsEqual(txUrl, "https://hecoinfo.com/tx/0x2e62832615f5b68b3bbcd72046a24260ce47052841c1679841b9c574d3959f13"); + assertStringsEqual(accUrl, "https://hecoinfo.com/address/0xc5a5b3e49e5d06afe163553c942dc59b4e358cf1"); + assertStringsEqual(id, "heco"); + assertStringsEqual(name, "Huobi ECO Chain"); +} diff --git a/tests/chains/ECash/TWCoinTypeTests.cpp b/tests/chains/ECash/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..d8f9305bc12 --- /dev/null +++ b/tests/chains/ECash/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWECashCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeECash)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("6bc767e69cfacffd954c9e5acd178d3140bf00d094b92c6f6052b517500c553b")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeECash, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("ecash:pqnqv9lt7e5vjyp0w88zf2af0l92l8rxdg2jj94l5j")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeECash, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeECash)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeECash)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeECash), 2); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeECash)); + ASSERT_EQ(0x5, TWCoinTypeP2shPrefix(TWCoinTypeECash)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeECash)); + assertStringsEqual(symbol, "XEC"); + assertStringsEqual(txUrl, "https://explorer.bitcoinabc.org/tx/6bc767e69cfacffd954c9e5acd178d3140bf00d094b92c6f6052b517500c553b"); + assertStringsEqual(accUrl, "https://explorer.bitcoinabc.org/address/ecash:pqnqv9lt7e5vjyp0w88zf2af0l92l8rxdg2jj94l5j"); + assertStringsEqual(id, "ecash"); + assertStringsEqual(name, "eCash"); +} diff --git a/tests/chains/ECash/TWECashTests.cpp b/tests/chains/ECash/TWECashTests.cpp new file mode 100644 index 00000000000..f258e4a4816 --- /dev/null +++ b/tests/chains/ECash/TWECashTests.cpp @@ -0,0 +1,168 @@ +// Copyright © 2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Bitcoin/Address.h" +#include "Bitcoin/SigHashType.h" +#include "HexCoding.h" +#include "proto/Bitcoin.pb.h" +#include "TestUtilities.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace TW::Bitcoin { + +TEST(ECash, Address) { + EXPECT_TRUE(TWAnyAddressIsValid(STRING("pqx578nanz2h2estzmkr53zqdg6qt8xyqvh683mrz0").get(), TWCoinTypeECash)); +} + +TEST(ECash, ValidAddress) { + auto string = STRING("ecash:qqra3amvnyyhrltyn5h97klwe68cuw3sfcgry9hl9k"); + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeECash)); + ASSERT_NE(address.get(), nullptr); + + auto script = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(string.get(), TWCoinTypeECash)); + ASSERT_FALSE(TWBitcoinScriptSize(script.get()) == 0); +} + +TEST(ECash, InvalidAddress) { + // Wrong checksum + EXPECT_FALSE(TWAnyAddressIsValid(STRING("pqx578nanz2h2estzmkr53zqdg6qt8xyqvffffffff").get(), TWCoinTypeECash)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("ecash:pqx578nanz2h2estzmkr53zqdg6qt8xyqvffffffff").get(), TWCoinTypeECash)); + + // Valid BCH addresses are invalid for eCash + EXPECT_TRUE(TWAnyAddressIsValid(STRING("pqx578nanz2h2estzmkr53zqdg6qt8xyqvwhn6qeyc").get(), TWCoinTypeBitcoinCash)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("pqx578nanz2h2estzmkr53zqdg6qt8xyqvwhn6qeyc").get(), TWCoinTypeECash)); + + EXPECT_TRUE(TWAnyAddressIsValid(STRING("bitcoincash:pqx578nanz2h2estzmkr53zqdg6qt8xyqvwhn6qeyc").get(), TWCoinTypeBitcoinCash)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("bitcoincash:pqx578nanz2h2estzmkr53zqdg6qt8xyqvwhn6qeyc").get(), TWCoinTypeECash)); + + // Wrong prefix + EXPECT_FALSE(TWAnyAddressIsValid(STRING("fcash:pqx578nanz2h2estzmkr53zqdg6qt8xyqvh683mrz0").get(), TWCoinTypeECash)); + + // Wrong base 32 (characters o, i) + EXPECT_FALSE(TWAnyAddressIsValid(STRING("poi578nanz2h2estzmkr53zqdg6qt8xyqvwhn6qeyc").get(), TWCoinTypeECash)); +} + +TEST(ECash, LegacyToECashAddr) { + auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA("28071bf4e2b0340db41b807ed8a5514139e5d6427ff9d58dbd22b7ed187103a4").get())); + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), true)); + auto address = WRAP(TWBitcoinAddress, TWBitcoinAddressCreateWithPublicKey(publicKey.get(), 0)); + auto addressString = WRAPS(TWBitcoinAddressDescription(address.get())); + assertStringsEqual(addressString, "1PeUvjuxyf31aJKX6kCXuaqxhmG78ZUdL1"); + + auto ecashAddress = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeECash)); + auto ecashAddressString = WRAPS(TWAnyAddressDescription(ecashAddress.get())); + assertStringsEqual(ecashAddressString, "ecash:qruxj7zq6yzpdx8dld0e9hfvt7u47zrw9gswqul42q"); +} + +TEST(ECash, LockScript) { + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(STRING("ecash:qpk05r5kcd8uuzwqunn8rlx5xvuvzjqju564r6szft").get(), TWCoinTypeECash)); + auto data = WRAPD(TWAnyAddressData(address.get())); + auto rawData = WRAPD(TWDataCreateWithSize(0)); + TWDataAppendByte(rawData.get(), 0x00); + TWDataAppendData(rawData.get(), data.get()); + + auto legacyAddress = WRAP(TWBitcoinAddress, TWBitcoinAddressCreateWithData(rawData.get())); + auto legacyString = WRAPS(TWBitcoinAddressDescription(legacyAddress.get())); + assertStringsEqual(legacyString, "1AwDXywmyhASpCCFWkqhySgZf8KiswFoGh"); + + auto keyHash = WRAPD(TWDataCreateWithBytes(legacyAddress.get()->impl.bytes.data() + 1, 20)); + auto script = WRAP(TWBitcoinScript, TWBitcoinScriptBuildPayToPublicKeyHash(keyHash.get())); + auto scriptData = WRAPD(TWBitcoinScriptData(script.get())); + assertHexEqual(scriptData, "76a9146cfa0e96c34fce09c0e4e671fcd43338c14812e588ac"); + + auto script2 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("pzukqjmcyzrkh3gsqzdcy3e3d39cqxhl3gkypy0vjg").get(), TWCoinTypeECash)); + auto scriptData2 = WRAPD(TWBitcoinScriptData(script2.get())); + assertHexEqual(scriptData2, "a914b9604b7820876bc510009b8247316c4b801aff8a87"); +} + +TEST(ECash, ExtendedKeys) { + // Same test as BCH, but with the 899 derivation path + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic( + STRING("ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal").get(), + STRING("TREZOR").get())); + + auto xprv = WRAPS(TWHDWalletGetExtendedPrivateKey(wallet.get(), TWPurposeBIP44, TWCoinTypeECash, TWHDVersionXPRV)); + auto xpub = WRAPS(TWHDWalletGetExtendedPublicKey(wallet.get(), TWPurposeBIP44, TWCoinTypeECash, TWHDVersionXPUB)); + + assertStringsEqual(xprv, "xprv9xjBcTizebJaV61xMkuMJ89vis7saMmwFgTYeF83KwinEksJ4frk7wB4mDiKiwXDCbJmgmh6Bp1FkF8SopNZhbF3B5wyX32cuDVFZtuUDvB"); + assertStringsEqual(xpub, "xpub6BiY1yFtUxrsha6RTnSMfG6fGtxMypVncuP9SdXetHFm7ZCScDAzfjVYcW32bkNCGJ5DTqawAHSTbJdTBL8wVxqUDGpxnRtukrhhBoS7Wy7"); +} + +TEST(ECash, DeriveFromXPub) { + auto xpub = STRING("xpub6BiY1yFtUxrsha6RTnSMfG6fGtxMypVncuP9SdXetHFm7ZCScDAzfjVYcW32bkNCGJ5DTqawAHSTbJdTBL8wVxqUDGpxnRtukrhhBoS7Wy7"); + auto pubKey2 = WRAP(TWPublicKey, TWHDWalletGetPublicKeyFromExtended(xpub.get(), TWCoinTypeECash, STRING("m/44'/899'/0'/0/2").get())); + auto pubKey9 = WRAP(TWPublicKey, TWHDWalletGetPublicKeyFromExtended(xpub.get(), TWCoinTypeECash, STRING("m/44'/899'/0'/0/9").get())); + + auto address2 = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(pubKey2.get(), TWCoinTypeECash)); + auto address2String = WRAPS(TWAnyAddressDescription(address2.get())); + + auto address9 = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(pubKey9.get(), TWCoinTypeECash)); + auto address9String = WRAPS(TWAnyAddressDescription(address9.get())); + + assertStringsEqual(address2String, "ecash:qpttymfhuq3v8tasfv7drlglhq6ne6zxquqltu3dcj"); + assertStringsEqual(address9String, "ecash:qqjraw2s5pwqwzql4znjpvp4vtvy3c9gmugq62r2j7"); +} + +TEST(ECash, SignTransaction) { + const int64_t amount = 600; + + // Transaction on eCash Mainnet + // https://blockchair.com/ecash/transaction/96ee20002b34e468f9d3c5ee54f6a8ddaa61c118889c4f35395c2cd93ba5bbb4 + + auto input = Proto::SigningInput(); + input.set_hash_type(hashTypeForCoin(TWCoinTypeECash)); + input.set_amount(amount); + input.set_byte_fee(1); + input.set_to_address("ecash:qpmfhhledgp0jy66r5vmwjwmdfu0up7ujqpvm4v8rm"); + input.set_change_address("ecash:qz0q3xmg38sr94rw8wg45vujah7kzma3cs0tssg5fd"); + + auto hash0 = DATA("e28c2b955293159898e34c6840d99bf4d390e2ee1c6f606939f18ee1e2000d05"); + auto utxo0 = input.add_utxo(); + utxo0->mutable_out_point()->set_hash(TWDataBytes(hash0.get()), TWDataSize(hash0.get())); + utxo0->mutable_out_point()->set_index(2); + utxo0->mutable_out_point()->set_sequence(UINT32_MAX); + utxo0->set_amount(5151); + auto script0 = parse_hex("76a914aff1e0789e5fe316b729577665aa0a04d5b0f8c788ac"); + utxo0->set_script(script0.data(), script0.size()); + + auto utxoKey0 = DATA("7fdafb9db5bc501f2096e7d13d331dc7a75d9594af3d251313ba8b6200f4e384"); + input.add_private_key(TWDataBytes(utxoKey0.get()), TWDataSize(utxoKey0.get())); + + // Sign + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeECash); + + EXPECT_EQ(output.transaction().outputs_size(), 2); + EXPECT_EQ(output.transaction().outputs(0).value(), amount); + EXPECT_EQ(output.transaction().outputs(1).value(), 4325); + EXPECT_EQ(output.encoded().length(), 226ul); + ASSERT_EQ(hex(output.encoded()), + "01000000" + "01" + "e28c2b955293159898e34c6840d99bf4d390e2ee1c6f606939f18ee1e2000d05" + "02000000" + "6b483045022100b70d158b43cbcded60e6977e93f9a84966bc0cec6f2dfd1463d1223a90563f0d02207548d081069de570a494d0967ba388ff02641d91cadb060587ead95a98d4e3534121038eab72ec78e639d02758e7860cdec018b49498c307791f785aa3019622f4ea5b" + "ffffffff" + "02" + "5802000000000000" + "1976a914769bdff96a02f9135a1d19b749db6a78fe07dc9088ac" + "e510000000000000" + "1976a9149e089b6889e032d46e3b915a3392edfd616fb1c488ac" + "00000000"); +} + +} // namespace TW::Bitcoin diff --git a/tests/chains/EOS/AddressTests.cpp b/tests/chains/EOS/AddressTests.cpp new file mode 100644 index 00000000000..e11efc7feeb --- /dev/null +++ b/tests/chains/EOS/AddressTests.cpp @@ -0,0 +1,76 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "EOS/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include + +#include + +using namespace TW; + +namespace TW::EOS::tests { + +TEST(EOSAddress, Invalid) { + ASSERT_FALSE(Address::isValid("abc")); + ASSERT_FALSE(Address::isValid("65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF")); + ASSERT_FALSE(Address::isValid("EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjT")); + ASSERT_FALSE(Address::isValid("PUB_5hieQEFWh68h6bjaYAY25Ptd2bmqLCaFsunaneh9gZsmSgUBUe")); + ASSERT_FALSE(Address::isValid("PUB_K1_5hieQEFWh68h6bjaYAY25Ptd2bmqLCaFsunaneh9gZsmSgUBUe")); + + ASSERT_THROW(Address("PUB_K1_65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF"), std::invalid_argument); + ASSERT_THROW(EOS::Address(Data(0)), std::invalid_argument); +} + +TEST(EOSAddress, Base58) { + ASSERT_EQ( + Address("EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF").string(), + "EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF"); + ASSERT_EQ( + Address("EOS55hdeEZHoArE8LLTv6drj2yR1K1AH8wAPT4kjTVSnkmQc3nzwQ").string(), + "EOS55hdeEZHoArE8LLTv6drj2yR1K1AH8wAPT4kjTVSnkmQc3nzwQ"); + ASSERT_EQ( + Address("PUB_R1_5hieQEFWh68h6bjaYAY25Ptd2bmqLCaFsunaneh9gZsmSgUBUe").string(), + "PUB_R1_5hieQEFWh68h6bjaYAY25Ptd2bmqLCaFsunaneh9gZsmSgUBUe"); + ASSERT_EQ( + Address("PUB_R1_7M9ckjr6p5CmS3N3yLPg9vcTB5NHmLcMHwZ3iGccEVfbjJRHv3").string(), + "PUB_R1_7M9ckjr6p5CmS3N3yLPg9vcTB5NHmLcMHwZ3iGccEVfbjJRHv3"); +} + +TEST(EOSAddress, FromPrivateKey) { + std::string privArray[]{"8e14ef506fee5e0aaa32f03a45242d32d0eb993ffe25ce77542ef07219db667c", + "e2bfd815c5923f404388a3257aa5527f0f52e92ce364e1e26a04d270c901edda", + "e6b783120a21cb234d8e15077ce186c47261d1043781ab8b16b84f2acd377668", + "bb96c0a4a6ec9c93ccc0b2cbad6b0e8110b9ca4731aef9c6937b99552a319b03"}; + + Type privTypes[]{Type::Legacy, Type::Legacy, Type::ModernR1, Type::ModernR1}; + + std::string pubArray[]{"EOS6TFKUKVvtvjRq9T4fV9pdxNUuJke92nyb4rzSFtZfdR5ssmVuY", + "EOS5YtaCcbPJ3BknNBTDezE9eJoGNnAVuUwT8bnxhSRS5dqRvyfxr", + "PUB_R1_67itCyDj42CRgtpyP4fLbAccBYnVHGeZQujQAeK3fyNbvfvZM6", + "PUB_R1_5DpVkbrMBDnY4JRhiEdHLmdLDKGQLNfL7X7it2pqT7Uk83ccDL"}; + + for (int i = 0; i < 4; i++) { + const auto privateKey = PrivateKey(parse_hex(privArray[i])); + const auto publicKey = PublicKey(privateKey.getPublicKey(privTypes[i] == Type::Legacy ? TWPublicKeyTypeSECP256k1 : TWPublicKeyTypeNIST256p1)); + const auto address = Address(publicKey, privTypes[i]); + + ASSERT_EQ(address.string(), pubArray[i]); + } +} + +TEST(EOSAddress, IsValid) { + ASSERT_TRUE(Address::isValid("EOS6Vm7RWMS1KKAM9kDXgggpu4sJkFMEpARhmsWA84tk4P22m29AV")); + ASSERT_TRUE(Address::isValid("PUB_R1_6pQRUVU5vdneRnmjSiZPsvu3zBqcptvg6iK2Vz4vKo4ugnzow3")); + ASSERT_TRUE(Address::isValid("EOS5mGcPvsqFDe8YRrA3yMMjQgjrCa6yiCho79KViDhvxh4ajQjgS")); + ASSERT_TRUE(Address::isValid("PUB_R1_82dMu3zSSfyHYc4cvWJ6SPsHZWB5mBNAyhL53xiM5xpqmfqetN")); + + ASSERT_NO_THROW(Address(parse_hex("039d91164ea04f4e751762643ef4ae520690af361b8e677cf341fd213419956b356cb721b7"), Type::ModernR1)); + ASSERT_NO_THROW(Address(parse_hex("02d3c8e736a9a50889766caf3c37bd16e2fecc7340b3130e25d4c01b153f996a10a78afc0e"), Type::Legacy)); +} + +} // namespace TW::EOS::tests diff --git a/tests/EOS/AssetTests.cpp b/tests/chains/EOS/AssetTests.cpp similarity index 90% rename from tests/EOS/AssetTests.cpp rename to tests/chains/EOS/AssetTests.cpp index 979d54e2550..b697bea654b 100644 --- a/tests/EOS/AssetTests.cpp +++ b/tests/chains/EOS/AssetTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,8 +9,7 @@ #include -using namespace TW; -using namespace TW::EOS; +namespace TW::EOS::tests { TEST(EOSAsset, Serialization) { Data buf; @@ -30,3 +29,5 @@ TEST(EOSAsset, Serialization) { // add tests for negative amounts, fractional amounts } + +} // namespace TW::EOS diff --git a/tests/EOS/NameTests.cpp b/tests/chains/EOS/NameTests.cpp similarity index 76% rename from tests/EOS/NameTests.cpp rename to tests/chains/EOS/NameTests.cpp index 76cd5cb5e52..e9b64201926 100644 --- a/tests/EOS/NameTests.cpp +++ b/tests/chains/EOS/NameTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,18 +6,16 @@ #include "EOS/Name.h" #include "HexCoding.h" -#include "PrivateKey.h" #include -using namespace TW; -using namespace TW::EOS; +namespace TW::EOS::tests { TEST(EOSName, Invalid) { ASSERT_THROW(Name(std::string(14, 'a')), std::invalid_argument); - std::string invalidNames[] = {"Alice", "alice16", "12345satoshis"}; - for(auto name: invalidNames) { + std::string invalidNames[] = {"Alice", "alice16", "12345satoshis"}; + for (auto name : invalidNames) { ASSERT_FALSE(Name(name).string() == name); } } @@ -30,4 +28,6 @@ TEST(EOSName, Valid) { Data buf; Name(validName).serialize(buf); ASSERT_EQ(hex(buf), "458608d8354cb3c1"); -} \ No newline at end of file +} + +} // namespace TW::EOS::tests \ No newline at end of file diff --git a/tests/chains/EOS/SignatureTests.cpp b/tests/chains/EOS/SignatureTests.cpp new file mode 100644 index 00000000000..72f3fb85a2c --- /dev/null +++ b/tests/chains/EOS/SignatureTests.cpp @@ -0,0 +1,45 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "EOS/Transaction.h" +#include "HexCoding.h" + +#include + +namespace TW::EOS::tests { + +TEST(EOSSignature, Serialization) { + Data buf; + Signature* sig = new Signature(parse_hex("1f14262320d5b145220fb94d8fe204117edd25e52bbe9557b6e0909dd00307af266f5be1deef001446979523ac9de32c7eae5e5be4180b5a60c0e6bf14b2dd3e05"), Type::ModernK1); + sig->serialize(buf); + + ASSERT_EQ( + hex(buf), + "001f14262320d5b145220fb94d8fe204117edd25e52bbe9557b6e0909dd00307af266f5be1deef001446979523ac9de32c7eae5e5be4180b5a60c0e6bf14b2dd3e05"); + + ASSERT_EQ( + sig->string(), + "SIG_K1_JwtfgsdSx5RuF5aejedQ7FJTexaKMrQyYosPUWUrU1mzdLx6JUgLTZJd7zWA8q8VdnXht3YmVt7jafmD2eEK7hTRpT9rY5"); + + delete sig; + sig = new Signature(parse_hex("1f5c419d16f573ddbf07d2eb959621f690f9cb856ea2d113e3af02b3b40005488410e82ffa37a079e119844d213f4eb066a640507db68851752bea6e61eb864d84"), Type::ModernR1); + buf.clear(); + sig->serialize(buf); + + ASSERT_EQ( + hex(buf), + "011f5c419d16f573ddbf07d2eb959621f690f9cb856ea2d113e3af02b3b40005488410e82ffa37a079e119844d213f4eb066a640507db68851752bea6e61eb864d84"); + + ASSERT_EQ( + sig->string(), + "SIG_R1_K7KpdLYqa6ebCP22TuiYAY9YoJh1dTWTZEVkdPzdoadFL6f8PkMYk5N8wtsF11cneEJ91XnEZP6wDJHhRyqr1fr68ouYcz"); + + delete sig; + ASSERT_THROW(sig = new Signature(parse_hex("1f5c419d16f573ddbf07d2eb959621f690f9cb856ea2d113e3af02b3b40005488410e82ffa37a079e119844d213f4eb066a640507db68851752bea6e61eb864d84"), Type::Legacy), std::invalid_argument); + ASSERT_THROW(sig = new Signature(parse_hex("011f5c419d16f573ddbf07d2eb959621f690f9cb856ea2d113e3af02b3b40005488410e82ffa37a079e119844d213f4eb066a640507db68851752bea6e61eb864d84"), Type::ModernR1), std::invalid_argument); +} + +} // namespace TW::EOS::tests \ No newline at end of file diff --git a/tests/chains/EOS/TWAnySignerTests.cpp b/tests/chains/EOS/TWAnySignerTests.cpp new file mode 100644 index 00000000000..e075c7ad8d8 --- /dev/null +++ b/tests/chains/EOS/TWAnySignerTests.cpp @@ -0,0 +1,54 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include "HexCoding.h" +#include "proto/EOS.pb.h" + +#include + +namespace TW::EOS::tests { + +TEST(TWAnySignerEOS, Sign) { + Proto::SigningInput input; + auto chainId = parse_hex("cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f"); + auto refBlock = parse_hex("000067d6f6a7e7799a1f3d487439a679f8cf95f1c986f35c0d2fa320f51a7144"); + auto key = parse_hex("559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd"); + + auto& asset = *input.mutable_asset(); + asset.set_amount(300000); + asset.set_decimals(4); + asset.set_symbol("TKN"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_reference_block_id(refBlock.data(), refBlock.size()); + input.set_reference_block_time(1554209118); + input.set_currency("token"); + input.set_sender("token"); + input.set_recipient("eosio"); + input.set_memo("my second transfer"); + input.set_private_key(key.data(), key.size()); + input.set_private_key_type(Proto::KeyType::MODERNK1); + + { + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEOS); + + EXPECT_EQ(output.error(), Common::Proto::OK); + EXPECT_EQ(output.json_encoded(), R"({"compression":"none","packed_context_free_data":"","packed_trx":"7c59a35cd6679a1f3d4800000000010000000080a920cd000000572d3ccdcd010000000080a920cd00000000a8ed3232330000000080a920cd0000000000ea3055e09304000000000004544b4e00000000126d79207365636f6e64207472616e7366657200","signatures":["SIG_K1_KfCdjsrTnx5cBpbA5cUdHZAsRYsnC9uKzuS1shFeqfMCfdZwX4PBm9pfHwGRT6ffz3eavhtkyNci5GoFozQAx8P8PBnDmj"]})"); + } + + input.set_private_key_type(Proto::KeyType::LEGACY); + { + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEOS); + EXPECT_EQ(output.error(), Common::Proto::Error_internal); + EXPECT_TRUE(output.json_encoded().empty()); + } +} + +} // namespace TW::EOS::tests diff --git a/tests/chains/EOS/TWCoinTypeTests.cpp b/tests/chains/EOS/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..18edd68aa46 --- /dev/null +++ b/tests/chains/EOS/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWEOSCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeEOS)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeEOS, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeEOS, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeEOS)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeEOS)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeEOS), 4); + ASSERT_EQ(TWBlockchainEOS, TWCoinTypeBlockchain(TWCoinTypeEOS)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeEOS)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeEOS)); + assertStringsEqual(symbol, "EOS"); + assertStringsEqual(txUrl, "https://bloks.io/transaction/t123"); + assertStringsEqual(accUrl, "https://bloks.io/account/a12"); + assertStringsEqual(id, "eos"); + assertStringsEqual(name, "EOS"); +} diff --git a/tests/chains/EOS/TransactionTests.cpp b/tests/chains/EOS/TransactionTests.cpp new file mode 100644 index 00000000000..65bfa0fb392 --- /dev/null +++ b/tests/chains/EOS/TransactionTests.cpp @@ -0,0 +1,116 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "EOS/Transaction.h" +#include "EOS/Signer.h" +#include "EOS/Action.h" +#include "EOS/Address.h" +#include "PrivateKey.h" +#include "HexCoding.h" +#include "Hash.h" + +#include + +using namespace TW; +namespace TW::EOS::tests { + +static std::string k1Sigs[5] { + "SIG_K1_KfCdjsrTnx5cBpbA5cUdHZAsRYsnC9uKzuS1shFeqfMCfdZwX4PBm9pfHwGRT6ffz3eavhtkyNci5GoFozQAx8P8PBnDmj", + "SIG_K1_K6wW678ngyWT7fgR4nNqm5XoKZBp9NDN4tKsctyzzADjXn15iAH9tcnQ393t6uvsqYxHKjdnxxduT1nyKMLiZbRqL7dHYr", + "SIG_K1_K6cUbZX6xfWcV5iotVprnf12Lc5AmV8SKmN5hVdv39gcM8wfEcwcNScvTuGLWpWzDT463dyhNmUfMB4nqt7tJVFnzx8mSi", + "SIG_K1_Khj7xhMd8HxrT6dUzuwiFM1MfMHtog5jCygJj7ADvdmUGkzZkmjymZXucEAud3whJ2qsMcxHcKtLWs8Ndm6be14qjTAH2a", + "SIG_K1_K93MjjE39CSH7kwJBgoRsSF2VaH6a8psQKU29nSg4xxxrVhz2iQuubyyB5t2ACZFFYSkNHSdYia5efhnW6Z9SPtbQTquMY" +}; + +static std::string r1Sigs[5] { + "SIG_R1_Kbywy4Mjun4Ymrh23Xk5yRtKJxcDWaDjQjLKERAny6Vs6oT1DYoEdoAj9AJK9iukHdEd9HBYnf3GmLtA55JFY5VaNszJMa", + "SIG_R1_KAhJJg4QGYBWY7hG6BKGAbW57fg6g8xTh3LG3Sss3bGv4BwiwHmRV1jsgh6hrnVRUoCaKMbJQzzWy9HXy6PnDmfJ6fbZMJ", + "SIG_R1_KxAwVKfpLr2MeK4aSAp5LSi2Vohsp94Uhk5UvZZDUJqd7ccBkhc2kYY1L6z5rjRNNo7BeP1Qr6H2xPFqo54YQ6DjczAqLW", + "SIG_R1_K1isJT8pJhkrHi3mcvrfY12nY6jirMCWaAHWuBXvu2ondcm3QHkgdaTwERskftZ9cqB5k2r8ajoYS4VWsiivjbd56D6pxX", + "SIG_R1_KWtgvnj2LaaYdtBTjM7bTR23LPBytDHFE7gPEfGTZ7PWc4yc6piPuPUHsVJVkvKmpW2gEUhq3toCfjkt34itSxMgekovdG" +}; + +TEST(EOSTransaction, Serialization) { + ASSERT_THROW(TransferAction("token", "eosio", "token", Asset::fromString("-20.1234 TKN"), "my first transfer"), std::invalid_argument); + + Data referenceBlockId = parse_hex("000046dc08ad384ca452d92c59348aba888fcbb6ef1ebffc3181617706664d4c"); + int32_t referenceBlockTime = 1554121728; + auto chainId = parse_hex("cf057bbfb72640471fd910bcb67639c22df9f92470936cddc1ade0e2f2e7dc4f"); + + Transaction tx {referenceBlockId, referenceBlockTime}; + tx.actions.push_back(TransferAction("token", "eosio", "token", Asset::fromString("20.1234 TKN"), "my first transfer")); + ASSERT_EQ(tx.actions.back().serialize().dump(), "{\"account\":\"token\",\"authorizations\":[{\"actor\":\"eosio\",\"permission\":\"active\"}],\"data\":\"0000000000ea30550000000080a920cd121203000000000004544b4e00000000116d79206669727374207472616e73666572\",\"name\":\"transfer\"}"); + + Data buf; + tx.serialize(buf); + + Signer signer {chainId}; + + ASSERT_EQ( + hex(buf), + "1e04a25cdc46a452d92c00000000010000000080a920cd000000572d3ccdcd010000000000ea305500000000a8ed3232320000000000ea30550000000080a920cd121203000000000004544b4e00000000116d79206669727374207472616e7366657200" + ); + + ASSERT_EQ( + hex(signer.hash(tx)), + "5de974bb90b940b462688609735a1dd522fa853aba765c30d14bedd27d719dd1" + ); + + // make transaction invalid and see if signing succeeds + tx.maxNetUsageWords = UINT32_MAX; + ASSERT_THROW(signer.sign(PrivateKey(Hash::sha256(std::string("A"))), Type::ModernK1, tx), std::invalid_argument); + + referenceBlockId = parse_hex("000067d6f6a7e7799a1f3d487439a679f8cf95f1c986f35c0d2fa320f51a7144"); + referenceBlockTime = 1554209118; + + Transaction tx2 {referenceBlockId, referenceBlockTime}; + tx2.actions.push_back(TransferAction("token", "token", "eosio", Asset::fromString("30.0000 TKN"), "my second transfer")); + + buf.clear(); + tx2.serialize(buf); + ASSERT_EQ( + hex(buf), + "7c59a35cd6679a1f3d4800000000010000000080a920cd000000572d3ccdcd010000000080a920cd00000000a8ed3232330000000080a920cd0000000000ea3055e09304000000000004544b4e00000000126d79207365636f6e64207472616e7366657200" + ); + + ASSERT_EQ( + hex(signer.hash(tx2)), + "4dac38a8ad7f095a09ec0eb0cbd060c9d8ea0a842535d369c9ce526cdf1b5d85" + ); + + ASSERT_NO_THROW(tx2.serialize()); + + // verify k1 sigs + for (int i = 0; i < 5; i++) { + PrivateKey pk(Hash::sha256(std::string(i + 1, 'A'))); + ASSERT_NO_THROW(signer.sign(pk, Type::ModernK1, tx2)); + + ASSERT_EQ( + tx2.signatures.back().string(), + k1Sigs[i] + ); + } + + // verify r1 sigs + for (int i = 0; i < 5; i++) { + PrivateKey pk(Hash::sha256(std::string(i + 1, 'A'))); + ASSERT_NO_THROW(signer.sign(pk, Type::ModernR1, tx2)); + + ASSERT_EQ( + tx2.signatures.back().string(), + r1Sigs[i] + ); + } +} + +TEST(EOSTransaction, formatDate) { + EXPECT_EQ(Transaction::formatDate(1554209148), "2019-04-02T12:45:48"); + EXPECT_EQ(Transaction::formatDate(1654160000), "2022-06-02T08:53:20"); + EXPECT_EQ(Transaction::formatDate(0), "1970-01-01T00:00:00"); + EXPECT_EQ(Transaction::formatDate(std::numeric_limits::max()), "2038-01-19T03:14:07"); +} + +} // namespace TW::EOS::tests diff --git a/tests/chains/Elrond/AddressTests.cpp b/tests/chains/Elrond/AddressTests.cpp new file mode 100644 index 00000000000..a96a0f53c1a --- /dev/null +++ b/tests/chains/Elrond/AddressTests.cpp @@ -0,0 +1,69 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include + +#include "Elrond/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "TestAccounts.h" + +using namespace TW; + +namespace TW::Elrond::tests { + +TEST(ElrondAddress, Valid) { + ASSERT_TRUE(Address::isValid(ALICE_BECH32)); + ASSERT_TRUE(Address::isValid(BOB_BECH32)); +} + +TEST(ElrondAddress, Invalid) { + ASSERT_FALSE(Address::isValid("")); + ASSERT_FALSE(Address::isValid("foo")); + ASSERT_FALSE(Address::isValid("10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); + ASSERT_FALSE(Address::isValid("xerd10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); + ASSERT_FALSE(Address::isValid("foo10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); + ASSERT_FALSE(Address::isValid(ALICE_PUBKEY_HEX)); +} + +TEST(ElrondAddress, FromString) { + Address alice, bob, carol; + ASSERT_TRUE(Address::decode(ALICE_BECH32, alice)); + ASSERT_TRUE(Address::decode(BOB_BECH32, bob)); + + ASSERT_EQ(ALICE_PUBKEY_HEX, hex(alice.getKeyHash())); + ASSERT_EQ(BOB_PUBKEY_HEX, hex(bob.getKeyHash())); +} + +TEST(ElrondAddress, FromData) { + const auto alice = Address(parse_hex(ALICE_PUBKEY_HEX)); + const auto bob = Address(parse_hex(BOB_PUBKEY_HEX)); + + ASSERT_EQ(ALICE_BECH32, alice.string()); + ASSERT_EQ(BOB_BECH32, bob.string()); +} + +TEST(ElrondAddress, FromPrivateKey) { + auto aliceKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + auto alice = Address(aliceKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(ALICE_BECH32, alice.string()); + + auto bobKey = PrivateKey(parse_hex(BOB_SEED_HEX)); + auto bob = Address(bobKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(BOB_BECH32, bob.string()); +} + +TEST(ElrondAddress, FromPublicKey) { + auto alice = PublicKey(parse_hex(ALICE_PUBKEY_HEX), TWPublicKeyTypeED25519); + ASSERT_EQ(ALICE_BECH32, Address(alice).string()); + + auto bob = PublicKey(parse_hex(BOB_PUBKEY_HEX), TWPublicKeyTypeED25519); + ASSERT_EQ(BOB_BECH32, Address(bob).string()); +} + +} // namespace TW::Elrond::tests diff --git a/tests/chains/Elrond/SerializationTests.cpp b/tests/chains/Elrond/SerializationTests.cpp new file mode 100644 index 00000000000..c1a730de8c7 --- /dev/null +++ b/tests/chains/Elrond/SerializationTests.cpp @@ -0,0 +1,63 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "boost/format.hpp" +#include +#include + +#include "Elrond/Serialization.h" +#include "HexCoding.h" +#include "TestAccounts.h" + +using namespace TW; + +namespace TW::Elrond::tests { + +TEST(ElrondSerialization, SignableString) { + Transaction transaction; + transaction.nonce = 42; + transaction.value = "43"; + transaction.sender = "alice"; + transaction.receiver = "bob"; + transaction.data = "foo"; + transaction.chainID = "1"; + transaction.version = 1; + + string jsonString = serializeTransaction(transaction); + ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"bob","sender":"alice","gasPrice":0,"gasLimit":0,"data":"Zm9v","chainID":"1","version":1})", jsonString); +} + +TEST(ElrondSerialization, SignableStringWithRealData) { + Transaction transaction; + transaction.nonce = 15; + transaction.value = "100"; + transaction.sender = ALICE_BECH32; + transaction.receiver = BOB_BECH32; + transaction.gasPrice = 1000000000; + transaction.gasLimit = 50000; + transaction.data = "foo"; + transaction.chainID = "1"; + transaction.version = 1; + + string expected = (boost::format(R"({"nonce":15,"value":"100","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1})") % BOB_BECH32 % ALICE_BECH32).str(); + string actual = serializeTransaction(transaction); + ASSERT_EQ(expected, actual); +} + +TEST(ElrondSerialization, SignableStringWithoutData) { + Transaction transaction; + transaction.nonce = 42; + transaction.value = "43"; + transaction.sender = "feed"; + transaction.receiver = "abba"; + transaction.chainID = "1"; + transaction.version = 1; + + string jsonString = serializeTransaction(transaction); + ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"abba","sender":"feed","gasPrice":0,"gasLimit":0,"chainID":"1","version":1})", jsonString); +} + +} // namespace TW::Elrond::tests diff --git a/tests/chains/Elrond/SignerTests.cpp b/tests/chains/Elrond/SignerTests.cpp new file mode 100644 index 00000000000..aeaa8455a69 --- /dev/null +++ b/tests/chains/Elrond/SignerTests.cpp @@ -0,0 +1,234 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "boost/format.hpp" +#include + +#include "Elrond/Address.h" +#include "Elrond/Signer.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "TestAccounts.h" + +using namespace TW; + +namespace TW::Elrond::tests { + +TEST(ElrondSigner, SignGenericAction) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(7); + input.mutable_generic_action()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_generic_action()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_generic_action()->set_value("0"); + input.mutable_generic_action()->set_data("foo"); + input.mutable_generic_action()->set_version(1); + input.set_gas_price(1000000000); + input.set_gas_limit(50000); + input.set_chain_id("1"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "e8647dae8b16e034d518a1a860c6a6c38d16192d0f1362833e62424f424e5da660770dff45f4b951d9cc58bfb9d14559c977d443449bfc4b8783ff9c84065700"; + auto expectedEncoded = (boost::format(R"({"nonce":7,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedEncoded, encoded); + ASSERT_EQ(expectedSignature, signature); +} + +TEST(ElrondSigner, SignGenericActionJSON) { + // Shuffle some fields, assume arbitrary order in the input + auto input = (boost::format(R"({"genericAction" : {"accounts": {"senderNonce": 7, "receiver": "%1%", "sender": "%2%"}, "data": "foo", "value": "0", "version": 1}, "gasPrice": 1000000000, "gasLimit": 50000, "chainId": "1"})") % BOB_BECH32 % ALICE_BECH32).str(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + + auto encoded = Signer::signJSON(input, privateKey.bytes); + auto expectedSignature = "e8647dae8b16e034d518a1a860c6a6c38d16192d0f1362833e62424f424e5da660770dff45f4b951d9cc58bfb9d14559c977d443449bfc4b8783ff9c84065700"; + auto expectedEncoded = (boost::format(R"({"nonce":7,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(ElrondSigner, SignWithoutData) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(0); + input.mutable_generic_action()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_generic_action()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_generic_action()->set_value("0"); + input.mutable_generic_action()->set_data(""); + input.mutable_generic_action()->set_version(1); + input.set_gas_price(1000000000); + input.set_gas_limit(50000); + input.set_chain_id("1"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "c7253b821c68011584ebd3a5bb050ade19235c2d10260e411e523105826c40a79849b3eeb96fcc2a7a6b1fa140b6756f50b249e005be056ce0cf53125e0b1b00"; + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(ElrondSigner, SignJSONWithoutData) { + // Shuffle some fields, assume arbitrary order in the input + auto input = (boost::format(R"({"genericAction" : {"accounts": {"senderNonce": 0, "receiver": "%1%", "sender": "%2%"}, "value": "0", "version": 1}, "gasPrice": 1000000000, "gasLimit": 50000, "chainId": "1"})") % BOB_BECH32 % ALICE_BECH32).str(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + + auto encoded = Signer::signJSON(input, privateKey.bytes); + auto expectedSignature = "c7253b821c68011584ebd3a5bb050ade19235c2d10260e411e523105826c40a79849b3eeb96fcc2a7a6b1fa140b6756f50b249e005be056ce0cf53125e0b1b00"; + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(ElrondSigner, SignWithUsernames) { + // https://github.com/ElrondNetwork/elrond-go/blob/master/examples/construction_test.go, scenario "TestConstructTransaction_Usernames". + + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(89); + input.mutable_generic_action()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_generic_action()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_generic_action()->mutable_accounts()->set_sender_username("alice"); + input.mutable_generic_action()->mutable_accounts()->set_receiver_username("bob"); + input.mutable_generic_action()->set_value("0"); + input.mutable_generic_action()->set_data(""); + input.mutable_generic_action()->set_version(1); + input.set_gas_price(1000000000); + input.set_gas_limit(50000); + input.set_chain_id("local-testnet"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "1bed82c3f91c9d24f3a163e7b93a47453d70e8743201fe7d3656c0214569566a76503ef0968279ac942ca43b9c930bd26638dfb075a220ce80b058ab7bca140a"; + auto expectedEncoded = + (boost::format(R"({"nonce":89,"value":"0","receiver":"%1%","sender":"%2%","senderUsername":"%3%","receiverUsername":"%4%","gasPrice":1000000000,"gasLimit":50000,"chainID":"local-testnet","version":1,"signature":"%5%"})") % BOB_BECH32 % ALICE_BECH32 + // "alice" + % "YWxpY2U=" + // "bob" + % "Ym9i" % expectedSignature) + .str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(ElrondSigner, SignWithOptions) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(89); + input.mutable_generic_action()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_generic_action()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_generic_action()->set_value("0"); + input.mutable_generic_action()->set_data(""); + input.mutable_generic_action()->set_version(1); + // We'll set a dummy value on the "options" field (merely an example). + // Currently, the "options" field should be ignored (not set) by applications using TW Core. + // In the future, TW Core will handle specific transaction options + // (such as the "SignedWithHash" flag, as seen in https://github.com/ElrondNetwork/elrond-go-core/blob/main/core/versioning/txVersionChecker.go) + // when building and signing transactions. + input.mutable_generic_action()->set_options(42); + input.set_gas_price(1000000000); + input.set_gas_limit(50000); + input.set_chain_id("local-testnet"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "d9a624f13960ae1cc471de48bdb43b101b9d469bb8b159f68bb629bb32d0109e1acfebb62d6d2fc5786c0b85f9e7ce2caff74988864a8285f34797c5a5fa5801"; + auto expectedEncoded = + (boost::format(R"({"nonce":89,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"chainID":"local-testnet","version":1,"options":42,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedEncoded, encoded); + ASSERT_EQ(expectedSignature, signature); +} + +TEST(ElrondSigner, SignEGLDTransfer) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_egld_transfer()->mutable_accounts()->set_sender_nonce(7); + input.mutable_egld_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_egld_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_egld_transfer()->set_amount("1000000000000000000"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "7e1c4c63b88ea72dcf7855a54463b1a424eb357ac3feb4345221e512ce07c7a50afb6d7aec6f480b554e32cf2037082f3bc17263d1394af1f3ef240be53c930b"; + auto expectedEncoded = + (boost::format(R"({"nonce":7,"value":"1000000000000000000","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(ElrondSigner, SignESDTTransfer) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_esdt_transfer()->mutable_accounts()->set_sender_nonce(7); + input.mutable_esdt_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_esdt_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_esdt_transfer()->set_token_identifier("MYTOKEN-1234"); + input.mutable_esdt_transfer()->set_amount("10000000000000"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "9add6d9ac3f1a1fddb07b934e8a73cad3b8c232bdf29d723c1b38ad619905f03e864299d06eb3fe3bbb48a9f1d9b7f14e21dc5eaffe0c87f5718ad0c4198bb0c"; + auto expectedEncoded = + (boost::format(R"({"nonce":7,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":425000,"data":"%3%","chainID":"1","version":1,"signature":"%4%"})") % BOB_BECH32 % ALICE_BECH32 + // "ESDTTransfer@4d59544f4b454e2d31323334@09184e72a000" + % "RVNEVFRyYW5zZmVyQDRkNTk1NDRmNGI0NTRlMmQzMTMyMzMzNEAwOTE4NGU3MmEwMDA=" % expectedSignature) + .str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(ElrondSigner, SignESDTNFTTransfer) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_esdtnft_transfer()->mutable_accounts()->set_sender_nonce(7); + input.mutable_esdtnft_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_esdtnft_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_esdtnft_transfer()->set_token_collection("LKMEX-aab910"); + input.mutable_esdtnft_transfer()->set_token_nonce(4); + input.mutable_esdtnft_transfer()->set_amount("184300000000000000"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "cc935685d5b31525e059a16a832cba98dee751983a5a93de4198f6553a2c55f5f1e0b4300fe9077376fa754546da0b0f6697e66462101a209aafd0fc775ab60a"; + auto expectedEncoded = + (boost::format(R"({"nonce":7,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":937500,"data":"%3%","chainID":"1","version":1,"signature":"%4%"})") % ALICE_BECH32 % ALICE_BECH32 + // "ESDTNFTTransfer@4c4b4d45582d616162393130@04@028ec3dfa01ac000@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8" + % "RVNEVE5GVFRyYW5zZmVyQDRjNGI0ZDQ1NTgyZDYxNjE2MjM5MzEzMEAwNEAwMjhlYzNkZmEwMWFjMDAwQDgwNDlkNjM5ZTVhNjk4MGQxY2QyMzkyYWJjY2U0MTAyOWNkYTc0YTE1NjM1MjNhMjAyZjA5NjQxY2MyNjE4Zjg=" % expectedSignature) + .str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedEncoded, encoded); +} + +} // namespace TW::Elrond::tests diff --git a/tests/chains/Elrond/TWAnySignerTests.cpp b/tests/chains/Elrond/TWAnySignerTests.cpp new file mode 100644 index 00000000000..09785661191 --- /dev/null +++ b/tests/chains/Elrond/TWAnySignerTests.cpp @@ -0,0 +1,59 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "boost/format.hpp" +#include + +#include "Elrond/Signer.h" +#include "HexCoding.h" +#include "TestAccounts.h" +#include "TestUtilities.h" +#include + +using namespace TW; + +namespace TW::Elrond::tests { + +TEST(TWAnySignerElrond, Sign) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_generic_action()->mutable_accounts()->set_sender_nonce(7); + input.mutable_generic_action()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_generic_action()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_generic_action()->set_value("0"); + input.mutable_generic_action()->set_data("foo"); + input.mutable_generic_action()->set_version(1); + input.set_gas_price(1000000000); + input.set_gas_limit(50000); + input.set_chain_id("1"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeElrond); + + auto signature = output.signature(); + auto encoded = output.encoded(); + auto expectedSignature = "e8647dae8b16e034d518a1a860c6a6c38d16192d0f1362833e62424f424e5da660770dff45f4b951d9cc58bfb9d14559c977d443449bfc4b8783ff9c84065700"; + auto expectedEncoded = (boost::format(R"({"nonce":7,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedEncoded, encoded); +} + +TEST(TWAnySignerElrond, SignJSON) { + // Shuffle some fields, assume arbitrary order in the input + auto input = STRING((boost::format(R"({"genericAction" : {"accounts": {"senderNonce": 7, "receiver": "%1%", "sender": "%2%"}, "data": "foo", "value": "0", "version": 1}, "gasPrice": 1000000000, "gasLimit": 50000, "chainId": "1"})") % BOB_BECH32 % ALICE_BECH32).str().c_str()); + auto privateKey = DATA(ALICE_SEED_HEX); + auto encoded = WRAPS(TWAnySignerSignJSON(input.get(), privateKey.get(), TWCoinTypeElrond)); + auto expectedSignature = "e8647dae8b16e034d518a1a860c6a6c38d16192d0f1362833e62424f424e5da660770dff45f4b951d9cc58bfb9d14559c977d443449bfc4b8783ff9c84065700"; + auto expectedEncoded = (boost::format(R"({"nonce":7,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":1000000000,"gasLimit":50000,"data":"Zm9v","chainID":"1","version":1,"signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeElrond)); + assertStringsEqual(encoded, expectedEncoded.c_str()); +} + +} // namespace TW::Elrond::tests diff --git a/tests/chains/Elrond/TWCoinTypeTests.cpp b/tests/chains/Elrond/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..1f7a977bdc0 --- /dev/null +++ b/tests/chains/Elrond/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWElrondCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeElrond)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeElrond, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeElrond, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeElrond)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeElrond)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeElrond), 18); + ASSERT_EQ(TWBlockchainElrondNetwork, TWCoinTypeBlockchain(TWCoinTypeElrond)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeElrond)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeElrond)); + assertStringsEqual(symbol, "eGLD"); + assertStringsEqual(txUrl, "https://explorer.elrond.com/transactions/1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8"); + assertStringsEqual(accUrl, "https://explorer.elrond.com/address/erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj"); + assertStringsEqual(id, "elrond"); + assertStringsEqual(name, "Elrond"); +} diff --git a/tests/chains/Elrond/TestAccounts.h b/tests/chains/Elrond/TestAccounts.h new file mode 100644 index 00000000000..80e45e638c3 --- /dev/null +++ b/tests/chains/Elrond/TestAccounts.h @@ -0,0 +1,16 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +// Well-known accounts on Testnet & Devnet, +// https://github.com/ElrondNetwork/elrond-sdk-erdpy/tree/main/erdpy/testnet/wallets/users: +const auto ALICE_BECH32 = "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th"; +const auto ALICE_PUBKEY_HEX = "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1"; +const auto ALICE_SEED_HEX = "413f42575f7f26fad3317a778771212fdb80245850981e48b58a4f25e344e8f9"; +const auto BOB_BECH32 = "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx"; +const auto BOB_PUBKEY_HEX = "8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8"; +const auto BOB_SEED_HEX = "b8ca6f8203fb4b545a8e83c5384da033c415db155b53fb5b8eba7ff5a039d639"; diff --git a/tests/chains/Elrond/TransactionFactoryTests.cpp b/tests/chains/Elrond/TransactionFactoryTests.cpp new file mode 100644 index 00000000000..8837d2cab6b --- /dev/null +++ b/tests/chains/Elrond/TransactionFactoryTests.cpp @@ -0,0 +1,192 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include + +#include "Elrond/TransactionFactory.h" +#include "TestAccounts.h" + +namespace TW::Elrond::tests { + +TEST(ElrondTransactionFactory, fromEGLDTransfer) { + auto input = Proto::SigningInput(); + input.mutable_egld_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_egld_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_egld_transfer()->set_amount("1000000000000000000"); + + TransactionFactory factory; + Transaction transaction = factory.fromEGLDTransfer(input); + + ASSERT_EQ(ALICE_BECH32, transaction.sender); + ASSERT_EQ(BOB_BECH32, transaction.receiver); + ASSERT_EQ("", transaction.data); + ASSERT_EQ("1000000000000000000", transaction.value); + ASSERT_EQ(50000ul, transaction.gasLimit); + ASSERT_EQ(1000000000ul, transaction.gasPrice); + ASSERT_EQ("1", transaction.chainID); + ASSERT_EQ(1ul, transaction.version); +} + +TEST(ElrondTransactionFactory, fromESDTTransfer) { + auto input = Proto::SigningInput(); + input.mutable_esdt_transfer()->mutable_accounts()->set_sender_nonce(7); + input.mutable_esdt_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_esdt_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_esdt_transfer()->set_token_identifier("MYTOKEN-1234"); + input.mutable_esdt_transfer()->set_amount("10000000000000"); + + TransactionFactory factory; + Transaction transaction = factory.fromESDTTransfer(input); + + ASSERT_EQ(ALICE_BECH32, transaction.sender); + ASSERT_EQ(BOB_BECH32, transaction.receiver); + ASSERT_EQ("ESDTTransfer@4d59544f4b454e2d31323334@09184e72a000", transaction.data); + ASSERT_EQ("0", transaction.value); + ASSERT_EQ(425000ul, transaction.gasLimit); + ASSERT_EQ(1000000000ul, transaction.gasPrice); + ASSERT_EQ("1", transaction.chainID); + ASSERT_EQ(1ul, transaction.version); +} + +TEST(ElrondTransactionFactory, fromESDTNFTTransfer) { + auto input = Proto::SigningInput(); + input.mutable_esdtnft_transfer()->mutable_accounts()->set_sender_nonce(7); + input.mutable_esdtnft_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + input.mutable_esdtnft_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + input.mutable_esdtnft_transfer()->set_token_collection("LKMEX-aab910"); + input.mutable_esdtnft_transfer()->set_token_nonce(4); + input.mutable_esdtnft_transfer()->set_amount("184300000000000000"); + + TransactionFactory factory; + Transaction transaction = factory.fromESDTNFTTransfer(input); + + ASSERT_EQ(ALICE_BECH32, transaction.sender); + ASSERT_EQ(ALICE_BECH32, transaction.receiver); + ASSERT_EQ("ESDTNFTTransfer@4c4b4d45582d616162393130@04@028ec3dfa01ac000@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8", transaction.data); + ASSERT_EQ("0", transaction.value); + ASSERT_EQ(937500ul, transaction.gasLimit); + ASSERT_EQ(1000000000ul, transaction.gasPrice); + ASSERT_EQ("1", transaction.chainID); + ASSERT_EQ(1ul, transaction.version); +} + +TEST(ElrondTransactionFactory, createTransfersWithProvidedNetworkConfig) { + NetworkConfig networkConfig; + + // Set dummy values: + networkConfig.setChainId("T"); + networkConfig.setMinGasPrice(1500000000); + networkConfig.setMinGasLimit(60000); + networkConfig.setGasPerDataByte(2000); + networkConfig.setGasCostESDTTransfer(300000); + networkConfig.setGasCostESDTNFTTransfer(300000); + + Proto::SigningInput signingInputWithEGLDTransfer; + signingInputWithEGLDTransfer.mutable_egld_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + signingInputWithEGLDTransfer.mutable_egld_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + signingInputWithEGLDTransfer.mutable_egld_transfer()->set_amount("0"); + + Proto::SigningInput signingInputWithESDTTransfer; + signingInputWithESDTTransfer.mutable_esdt_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + signingInputWithESDTTransfer.mutable_esdt_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + signingInputWithESDTTransfer.mutable_esdt_transfer()->set_token_identifier("MYTOKEN-1234"); + signingInputWithESDTTransfer.mutable_esdt_transfer()->set_amount("10000000000000"); + + Proto::SigningInput signingInputWithESDTNFTTransfer; + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->set_token_collection("LKMEX-aab910"); + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->set_token_nonce(4); + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->set_amount("184300000000000000"); + + TransactionFactory factory(networkConfig); + Transaction tx1 = factory.fromEGLDTransfer(signingInputWithEGLDTransfer); + Transaction tx2 = factory.fromESDTTransfer(signingInputWithESDTTransfer); + Transaction tx3 = factory.fromESDTNFTTransfer(signingInputWithESDTNFTTransfer); + + ASSERT_EQ(60000ul, tx1.gasLimit); + ASSERT_EQ(1500000000ul, tx1.gasPrice); + ASSERT_EQ("T", tx1.chainID); + + ASSERT_EQ(560000ul, tx2.gasLimit); + ASSERT_EQ(1500000000ul, tx2.gasPrice); + ASSERT_EQ("T", tx2.chainID); + + ASSERT_EQ(1110000ul, tx3.gasLimit); + ASSERT_EQ(1500000000ul, tx3.gasPrice); + ASSERT_EQ("T", tx3.chainID); +} + +TEST(ElrondTransactionFactory, createTransfersWithOverriddenNetworkParameters) { + Proto::SigningInput signingInputWithEGLDTransfer; + signingInputWithEGLDTransfer.set_gas_limit(50500); + signingInputWithEGLDTransfer.set_gas_price(1000000001); + signingInputWithEGLDTransfer.set_chain_id("A"); + + Proto::SigningInput signingInputWithESDTTransfer; + signingInputWithESDTTransfer.set_gas_limit(5000000); + signingInputWithESDTTransfer.set_gas_price(1000000002); + signingInputWithESDTTransfer.set_chain_id("B"); + + Proto::SigningInput signingInputWithESDTNFTTransfer; + signingInputWithESDTNFTTransfer.set_gas_limit(10000000); + signingInputWithESDTNFTTransfer.set_gas_price(1000000003); + signingInputWithESDTNFTTransfer.set_chain_id("C"); + + TransactionFactory factory; + Transaction tx1 = factory.fromEGLDTransfer(signingInputWithEGLDTransfer); + Transaction tx2 = factory.fromESDTTransfer(signingInputWithESDTTransfer); + Transaction tx3 = factory.fromESDTNFTTransfer(signingInputWithESDTNFTTransfer); + + ASSERT_EQ(50500ul, tx1.gasLimit); + ASSERT_EQ(1000000001ul, tx1.gasPrice); + ASSERT_EQ("A", tx1.chainID); + + ASSERT_EQ(5000000ul, tx2.gasLimit); + ASSERT_EQ(1000000002ul, tx2.gasPrice); + ASSERT_EQ("B", tx2.chainID); + + ASSERT_EQ(10000000ul, tx3.gasLimit); + ASSERT_EQ(1000000003ul, tx3.gasPrice); + ASSERT_EQ("C", tx3.chainID); +} + +TEST(ElrondTransactionFactory, create) { + Proto::SigningInput signingInputWithGenericAction; + signingInputWithGenericAction.mutable_generic_action()->set_data("hello"); + + Proto::SigningInput signingInputWithEGLDTransfer; + signingInputWithEGLDTransfer.mutable_egld_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + signingInputWithEGLDTransfer.mutable_egld_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + signingInputWithEGLDTransfer.mutable_egld_transfer()->set_amount("1"); + + Proto::SigningInput signingInputWithESDTTransfer; + signingInputWithESDTTransfer.mutable_esdt_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + signingInputWithESDTTransfer.mutable_esdt_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + signingInputWithESDTTransfer.mutable_esdt_transfer()->set_token_identifier("MYTOKEN-1234"); + signingInputWithESDTTransfer.mutable_esdt_transfer()->set_amount("10000000000000"); + + Proto::SigningInput signingInputWithESDTNFTTransfer; + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->mutable_accounts()->set_sender(ALICE_BECH32); + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->mutable_accounts()->set_receiver(BOB_BECH32); + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->set_token_collection("LKMEX-aab910"); + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->set_token_nonce(4); + signingInputWithESDTNFTTransfer.mutable_esdtnft_transfer()->set_amount("184300000000000000"); + + TransactionFactory factory; + Transaction tx1 = factory.create(signingInputWithGenericAction); + Transaction tx2 = factory.create(signingInputWithEGLDTransfer); + Transaction tx3 = factory.create(signingInputWithESDTTransfer); + Transaction tx4 = factory.create(signingInputWithESDTNFTTransfer); + + ASSERT_EQ("hello", tx1.data); + ASSERT_EQ("1", tx2.value); + ASSERT_EQ("ESDTTransfer@4d59544f4b454e2d31323334@09184e72a000", tx3.data); + ASSERT_EQ("ESDTNFTTransfer@4c4b4d45582d616162393130@04@028ec3dfa01ac000@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8", tx4.data); +} + +} // namespace TW::Elrond::tests diff --git a/tests/Ethereum/AbiStructTests.cpp b/tests/chains/Ethereum/AbiStructTests.cpp similarity index 87% rename from tests/Ethereum/AbiStructTests.cpp rename to tests/chains/Ethereum/AbiStructTests.cpp index c636f554e2c..259fb4ef3f6 100644 --- a/tests/Ethereum/AbiStructTests.cpp +++ b/tests/chains/Ethereum/AbiStructTests.cpp @@ -7,19 +7,21 @@ #include "Ethereum/ABI.h" #include "Ethereum/Address.h" #include "Ethereum/Signer.h" +#include "TestUtilities.h" #include #include -#include "../interface/TWTestUtilities.h" #include #include -using namespace TW::Ethereum::ABI; -using namespace TW::Ethereum; using namespace TW; extern std::string TESTS_ROOT; +namespace TW::Ethereum::tests { + +using namespace ABI; + std::string load_file(const std::string path) { std::ifstream stream(path); std::string content((std::istreambuf_iterator(stream)), (std::istreambuf_iterator())); @@ -27,7 +29,7 @@ std::string load_file(const std::string path) { } // https://github.com/MetaMask/eth-sig-util/blob/main/test/index.ts - +// clang-format off ParamStruct msgPersonCow2("Person", std::vector>{ std::make_shared("name", std::make_shared("Cow")), std::make_shared("wallets", std::make_shared(std::vector>{ @@ -65,7 +67,7 @@ ParamStruct msgMailCow2Bob3("Mail", std::vector>{ std::make_shared("to", std::make_shared(std::make_shared(msgPersonBob3))), std::make_shared("contents", std::make_shared("Hello, Bob!")) }); -ParamStruct msgEIP712Domain("EIP712Domain", std::vector>{ +ParamStruct gMsgEIP712Domain("EIP712Domain", std::vector>{ std::make_shared("name", std::make_shared("Ether Mail")), std::make_shared("version", std::make_shared("1")), std::make_shared("chainId", std::make_shared(1)), @@ -86,7 +88,7 @@ TEST(EthereumAbiStruct, encodeTypes) { EXPECT_EQ(hex(msgMailCow1Bob1.hashStruct()), "c52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e"); - EXPECT_EQ(hex(msgEIP712Domain.hashStruct()), "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); + EXPECT_EQ(hex(gMsgEIP712Domain.hashStruct()), "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); Address address = Address(privateKeyCow.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); EXPECT_EQ(address.string(), "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"); @@ -150,7 +152,7 @@ TEST(EthereumAbiStruct, encodeTypes_v3) { EXPECT_EQ(hex(msgMailCow1Bob1.hashStruct()), "c52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e"); - EXPECT_EQ(hex(msgEIP712Domain.hashStruct()), "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); + EXPECT_EQ(hex(gMsgEIP712Domain.hashStruct()), "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); Address address = Address(privateKeyCow.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); EXPECT_EQ(address.string(), "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"); @@ -212,17 +214,17 @@ TEST(EthereumAbiStruct, encodeTypes_v4) { EXPECT_EQ(hex(msgPersonCow2.hashType()), "fabfe1ed996349fc6027709802be19d047da1aa5d6894ff5f6486d92db2e6860"); - EXPECT_EQ(hex(msgPersonCow2.encodeHashes()), - "fabfe1ed996349fc6027709802be19d047da1aa5d6894ff5f6486d92db2e6860" - "8c1d2bd5348394761719da11ec67eedae9502d137e8940fee8ecd6f641ee1648" - "8a8bfe642b9fc19c25ada5dadfd37487461dc81dd4b0778f262c163ed81b5e2a"); + EXPECT_EQ(hex(msgPersonCow2.encodeHashes()), + "fabfe1ed996349fc6027709802be19d047da1aa5d6894ff5f6486d92db2e6860" + "8c1d2bd5348394761719da11ec67eedae9502d137e8940fee8ecd6f641ee1648" + "8a8bfe642b9fc19c25ada5dadfd37487461dc81dd4b0778f262c163ed81b5e2a"); EXPECT_EQ(hex(msgPersonCow2.hashStruct()), "9b4846dd48b866f0ac54d61b9b21a9e746f921cefa4ee94c4c0a1c49c774f67f"); - EXPECT_EQ(hex(msgPersonBob3.encodeHashes()), - "fabfe1ed996349fc6027709802be19d047da1aa5d6894ff5f6486d92db2e6860" - "28cac318a86c8a0a6a9156c2dba2c8c2363677ba0514ef616592d81557e679b6" - "d2734f4c86cc3bd9cabf04c3097589d3165d95e4648fc72d943ed161f651ec6d"); + EXPECT_EQ(hex(msgPersonBob3.encodeHashes()), + "fabfe1ed996349fc6027709802be19d047da1aa5d6894ff5f6486d92db2e6860" + "28cac318a86c8a0a6a9156c2dba2c8c2363677ba0514ef616592d81557e679b6" + "d2734f4c86cc3bd9cabf04c3097589d3165d95e4648fc72d943ed161f651ec6d"); EXPECT_EQ(hex(msgPersonBob3.hashStruct()), "efa62530c7ae3a290f8a13a5fc20450bdb3a6af19d9d9d2542b5a94e631a9168"); @@ -230,15 +232,15 @@ TEST(EthereumAbiStruct, encodeTypes_v4) { EXPECT_EQ(hex(msgMailCow2Bob3.hashType()), "4bd8a9a2b93427bb184aca81e24beb30ffa3c747e2a33d4225ec08bf12e2e753"); - EXPECT_EQ(hex(msgMailCow2Bob3.encodeHashes()), - "4bd8a9a2b93427bb184aca81e24beb30ffa3c747e2a33d4225ec08bf12e2e753" - "9b4846dd48b866f0ac54d61b9b21a9e746f921cefa4ee94c4c0a1c49c774f67f" - "ca322beec85be24e374d18d582a6f2997f75c54e7993ab5bc07404ce176ca7cd" - "b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8"); + EXPECT_EQ(hex(msgMailCow2Bob3.encodeHashes()), + "4bd8a9a2b93427bb184aca81e24beb30ffa3c747e2a33d4225ec08bf12e2e753" + "9b4846dd48b866f0ac54d61b9b21a9e746f921cefa4ee94c4c0a1c49c774f67f" + "ca322beec85be24e374d18d582a6f2997f75c54e7993ab5bc07404ce176ca7cd" + "b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8"); EXPECT_EQ(hex(msgMailCow2Bob3.hashStruct()), "eb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8"); - EXPECT_EQ(hex(msgEIP712Domain.hashStruct()), "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); + EXPECT_EQ(hex(gMsgEIP712Domain.hashStruct()), "f2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f"); Address address = Address(privateKeyCow.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); EXPECT_EQ(address.string(), "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"); @@ -331,27 +333,27 @@ TEST(EthereumAbiStruct, encodeTypes_v4Rec) { EXPECT_EQ(hex(msgPersonRecursive.hashType()), "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116"); - EXPECT_EQ(hex(msgPersonRecursiveMother.encodeHashes()), - "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116" - "afe4142a2b3e7b0503b44951e6030e0e2c5000ef83c61857e2e6003e7aef8570" - "0000000000000000000000000000000000000000000000000000000000000000" - "88f14be0dd46a8ec608ccbff6d3923a8b4e95cdfc9648f0db6d92a99a264cb36"); + EXPECT_EQ(hex(msgPersonRecursiveMother.encodeHashes()), + "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116" + "afe4142a2b3e7b0503b44951e6030e0e2c5000ef83c61857e2e6003e7aef8570" + "0000000000000000000000000000000000000000000000000000000000000000" + "88f14be0dd46a8ec608ccbff6d3923a8b4e95cdfc9648f0db6d92a99a264cb36"); EXPECT_EQ(hex(msgPersonRecursiveMother.hashStruct()), "9ebcfbf94f349de50bcb1e3aa4f1eb38824457c99914fefda27dcf9f99f6178b"); - EXPECT_EQ(hex(msgPersonRecursiveFather.encodeHashes()), - "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116" - "b2a7c7faba769181e578a391a6a6811a3e84080c6a3770a0bf8a856dfa79d333" - "0000000000000000000000000000000000000000000000000000000000000000" - "02cc7460f2c9ff107904cff671ec6fee57ba3dd7decf999fe9fe056f3fd4d56e"); + EXPECT_EQ(hex(msgPersonRecursiveFather.encodeHashes()), + "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116" + "b2a7c7faba769181e578a391a6a6811a3e84080c6a3770a0bf8a856dfa79d333" + "0000000000000000000000000000000000000000000000000000000000000000" + "02cc7460f2c9ff107904cff671ec6fee57ba3dd7decf999fe9fe056f3fd4d56e"); EXPECT_EQ(hex(msgPersonRecursiveFather.hashStruct()), "b852e5abfeff916a30cb940c4e24c43cfb5aeb0fa8318bdb10dd2ed15c8c70d8"); - EXPECT_EQ(hex(msgPersonRecursive.encodeHashes()), - "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116" - "e8d55aa98b6b411f04dbcf9b23f29247bb0e335a6bc5368220032fdcb9e5927f" - "9ebcfbf94f349de50bcb1e3aa4f1eb38824457c99914fefda27dcf9f99f6178b" - "b852e5abfeff916a30cb940c4e24c43cfb5aeb0fa8318bdb10dd2ed15c8c70d8"); + EXPECT_EQ(hex(msgPersonRecursive.encodeHashes()), + "7c5c8e90cb92c8da53b893b24962513be98afcf1b57b00327ae4cc14e3a64116" + "e8d55aa98b6b411f04dbcf9b23f29247bb0e335a6bc5368220032fdcb9e5927f" + "9ebcfbf94f349de50bcb1e3aa4f1eb38824457c99914fefda27dcf9f99f6178b" + "b852e5abfeff916a30cb940c4e24c43cfb5aeb0fa8318bdb10dd2ed15c8c70d8"); EXPECT_EQ(hex(msgPersonRecursive.hashStruct()), "fdc7b6d35bbd81f7fa78708604f57569a10edff2ca329c8011373f0667821a45"); @@ -462,7 +464,7 @@ TEST(EthereumAbiStruct, hashStructJson) { })"); ASSERT_EQ(hex(hash), "0b4bb85394b9ebb1c2425e283c9e734a9a7a832622e97c998f77e1c7a3f01a20"); } - { // edge cases + { // edge cases EXPECT_EXCEPTION(ParamStruct::hashStructJson("NOT_A_JSON"), "Could not parse Json"); EXPECT_EXCEPTION(ParamStruct::hashStructJson("+/{\\"), "Could not parse Json"); EXPECT_EXCEPTION(ParamStruct::hashStructJson(""), "Could not parse Json"); @@ -484,9 +486,35 @@ TEST(EthereumAbiStruct, hashStructJson) { } } +TEST(EthereumAbiStruct, hashStruct_emptyString) { + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_emptyString.json"; + auto typeData = load_file(path); + auto hash = ParamStruct::hashStructJson(typeData); + EXPECT_EQ(hex(hash), "bc9d33285c5e42b00571f5deaf9636d2e498a6fa50e0d1be81095bded070117a"); + + // sign the hash + const auto rsv = Signer::sign(privateKeyOilTimes12, hash, true, 0); + EXPECT_EQ(hex(store(rsv.r)), "5df6cb46d874bc0acc519695f393008a837ca9d2e316836b669b8f0de7673638"); + EXPECT_EQ(hex(store(rsv.s)), "54cc0bcc0ad657f9222f7e7be3fbe0ec4a8edb9385c39d578dfac8d38727af12"); + EXPECT_EQ(hex(store(rsv.v)), "1c"); +} + +TEST(EthereumAbiStruct, hashStruct_emptyArray) { + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_emptyArray.json"; + auto typeData = load_file(path); + auto hash = ParamStruct::hashStructJson(typeData); + EXPECT_EQ(hex(hash), "9f1a1bc718e966d683c544aef6fd0b73c85a1d6244af9b64bb8f4a6fa6716086"); + + // sign the hash + const auto rsv = Signer::sign(privateKeyOilTimes12, hash, true, 0); + EXPECT_EQ(hex(store(rsv.r)), "de47efd592493f7189d44f071424ecb24b50d80750d3bd2bb6fc80451c13a52f"); + EXPECT_EQ(hex(store(rsv.s)), "202b8a2be1ef3c466853e8cd5275a6af15b11e7e1cc0ae4a7e249bc9bad591eb"); + EXPECT_EQ(hex(store(rsv.v)), "1c"); +} + TEST(EthereumAbiStruct, hashStruct_walletConnect) { // https://github.com/WalletConnect/walletconnect-example-dapp/blob/master/src/helpers/eip712.ts - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_walletconnect.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_walletconnect.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "abc79f527273b9e7bca1b3f1ac6ad1a8431fa6dc34ece900deabcd6969856b5e"); @@ -499,7 +527,7 @@ TEST(EthereumAbiStruct, hashStruct_walletConnect) { } TEST(EthereumAbiStruct, hashStruct_cryptofights) { - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_cryptofights.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_cryptofights.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "db12328a6d193965801548e1174936c3aa7adbe1b54b3535a3c905bd4966467c"); @@ -512,7 +540,7 @@ TEST(EthereumAbiStruct, hashStruct_cryptofights) { } TEST(EthereumAbiStruct, hashStruct_rarible) { - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_rarible.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_rarible.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "df0200de55c05eb55af2597012767ea3af653d68000be49580f8e05acd91d366"); @@ -525,7 +553,7 @@ TEST(EthereumAbiStruct, hashStruct_rarible) { } TEST(EthereumAbiStruct, hashStruct_snapshot) { - auto path = TESTS_ROOT + "/Ethereum/Data/eip712_snapshot_v4.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/eip712_snapshot_v4.json"; auto typeData = load_file(path); auto hash = ParamStruct::hashStructJson(typeData); EXPECT_EQ(hex(hash), "f558d08ad4a7651dbc9ec028cfcb4a8e6878a249073ef4fa694f85ee95f61c0f"); @@ -555,7 +583,7 @@ TEST(EthereumAbiStruct, ParamStructMakeStruct) { })"); ASSERT_NE(s.get(), nullptr); EXPECT_EQ(s->getType(), "Person"); - ASSERT_EQ(s->getCount(), 2); + ASSERT_EQ(s->getCount(), 2ul); EXPECT_EQ(s->encodeType(), "Person(string name,address wallet)"); EXPECT_EQ(hex(s->hashStruct()), "fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8"); } @@ -569,7 +597,7 @@ TEST(EthereumAbiStruct, ParamStructMakeStruct) { })"); ASSERT_NE(s.get(), nullptr); EXPECT_EQ(s->getType(), "Person"); - ASSERT_EQ(s->getCount(), 2); + ASSERT_EQ(s->getCount(), 2ul); EXPECT_EQ(s->encodeType(), "Person(string name,address[] wallets)"); EXPECT_EQ(hex(s->hashStruct()), "9b4846dd48b866f0ac54d61b9b21a9e746f921cefa4ee94c4c0a1c49c774f67f"); } @@ -579,12 +607,12 @@ TEST(EthereumAbiStruct, ParamStructMakeStruct) { R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}],"Mail": [{"name": "from", "type": "Person"},{"name": "to", "type": "Person"},{"name": "contents", "type": "string"}]})"); ASSERT_NE(s.get(), nullptr); EXPECT_EQ(s->getType(), "Mail"); - ASSERT_EQ(s->getCount(), 3); + ASSERT_EQ(s->getCount(), 3ul); EXPECT_EQ(s->encodeType(), "Mail(Person from,Person to,string contents)Person(string name,address wallet)"); EXPECT_EQ(hex(s->hashStruct()), "c52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e"); } - { // extra param + { // extra param std::shared_ptr s = ParamStruct::makeStruct("Person", R"({"extra_param": "extra_value", "name": "Cow", "wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"})", R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})"); @@ -592,14 +620,14 @@ TEST(EthereumAbiStruct, ParamStructMakeStruct) { EXPECT_EQ(s->encodeType(), "Person(string name,address wallet)"); EXPECT_EQ(hex(s->hashStruct()), "fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8"); } - { // empty array + { // empty array std::shared_ptr s = ParamStruct::makeStruct("Person", R"({"extra_param": "extra_value", "name": "Cow", "wallets": []})", R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallets", "type": "address[]"}]})"); ASSERT_NE(s.get(), nullptr); EXPECT_EQ(s->encodeType(), "Person(string name,address[] wallets)"); } - { // missing param + { // missing param std::shared_ptr s = ParamStruct::makeStruct("Person", R"({"wallet": "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"})", R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})"); @@ -649,7 +677,7 @@ TEST(EthereumAbiStruct, ParamStructMakeStruct) { TEST(EthereumAbiStruct, ParamFactoryMakeTypes) { { std::vector> tt = ParamStruct::makeTypes(R"({"Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})"); - ASSERT_EQ(tt.size(), 1); + ASSERT_EQ(tt.size(), 1ul); EXPECT_EQ(tt[0]->encodeType(), "Person(string name,address wallet)"); } { @@ -658,22 +686,22 @@ TEST(EthereumAbiStruct, ParamFactoryMakeTypes) { "Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}], "Mail": [{"name": "from", "type": "Person"}, {"name": "to", "type": "Person"}, {"name": "contents", "type": "string"}] })"); - ASSERT_EQ(tt.size(), 2); + ASSERT_EQ(tt.size(), 2ul); EXPECT_EQ(tt[0]->encodeType(), "Mail(Person from,Person to,string contents)Person(string name,address wallet)"); EXPECT_EQ(tt[1]->encodeType(), "Person(string name,address wallet)"); } - { // edge cases + { // edge cases EXPECT_EXCEPTION(ParamStruct::makeTypes("NOT_A_JSON"), "Could not parse types Json"); EXPECT_EXCEPTION(ParamStruct::makeTypes("+/{\\"), "Could not parse types Json"); EXPECT_EXCEPTION(ParamStruct::makeTypes(""), "Could not parse types Json"); EXPECT_EXCEPTION(ParamStruct::makeTypes("0"), "Expecting object"); EXPECT_EXCEPTION(ParamStruct::makeTypes("[]"), "Expecting object"); EXPECT_EXCEPTION(ParamStruct::makeTypes("[{}]"), "Expecting object"); - EXPECT_EQ(ParamStruct::makeTypes("{}").size(), 0); + EXPECT_EQ(ParamStruct::makeTypes("{}").size(), 0ul); EXPECT_EXCEPTION(ParamStruct::makeTypes("{\"a\": 0}"), "Expecting array"); EXPECT_EXCEPTION(ParamStruct::makeTypes(R"({"name": 0})"), "Expecting array"); // order does not matter - EXPECT_EQ(ParamStruct::makeTypes(R"({"Mail": [{"name": "from", "type": "Person"}, {"name": "to", "type": "Person"}, {"name": "contents", "type": "string"}], "Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})").size(), 2); + EXPECT_EQ(ParamStruct::makeTypes(R"({"Mail": [{"name": "from", "type": "Person"}, {"name": "to", "type": "Person"}, {"name": "contents", "type": "string"}], "Person": [{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}]})").size(), 2ul); } } @@ -682,10 +710,10 @@ TEST(EthereumAbiStruct, ParamFactoryMakeType) { std::shared_ptr t = ParamStruct::makeType("Person", R"([{"name": "name", "type": "string"}, {"name": "wallet", "type": "address"}])"); EXPECT_NE(t.get(), nullptr); EXPECT_EQ(t->getType(), "Person"); - ASSERT_EQ(t->getCount(), 2); + ASSERT_EQ(t->getCount(), 2ul); ASSERT_EQ(t->encodeType(), "Person(string name,address wallet)"); } - { // edge cases + { // edge cases EXPECT_EXCEPTION(ParamStruct::makeType("", ""), "Missing type name"); EXPECT_EXCEPTION(ParamStruct::makeType("Person", "NOT_A_JSON"), "Could not parse type Json"); EXPECT_EXCEPTION(ParamStruct::makeType("Person", "+/{\\"), "Could not parse type Json"); @@ -714,7 +742,7 @@ TEST(EthereumAbiStruct, ParamNamedMethods) { EXPECT_EQ(hex(encoded), "000000000000000000000000000000000000000000000000000000000000000548656c6c6f000000000000000000000000000000000000000000000000000000"); size_t offset = 0; EXPECT_EQ(pn->decode(encoded, offset), true); - EXPECT_EQ(offset, 64); + EXPECT_EQ(offset, 64ul); pn->setValueJson("World"); EXPECT_EQ(ps->getVal(), "World"); } @@ -723,7 +751,7 @@ TEST(EthereumAbiStruct, ParamSetNamed) { const auto pn1 = std::make_shared("param1", std::make_shared("Hello")); const auto pn2 = std::make_shared("param2", std::make_shared("World")); auto ps = std::make_shared(std::vector>{pn1, pn2}); - EXPECT_EQ(ps->getCount(), 2); + EXPECT_EQ(ps->getCount(), 2ul); EXPECT_EQ(ps->addParam(std::shared_ptr(nullptr)), -1); EXPECT_EQ(ps->findParamByName("NO_SUCH_PARAM"), nullptr); auto pf1 = ps->findParamByName("param2"); @@ -736,14 +764,14 @@ TEST(EthereumAbiStruct, ParamStructMethods) { const auto pn2 = std::make_shared("param2", std::make_shared("World")); auto ps = std::make_shared("struct", std::vector>{pn1, pn2}); - EXPECT_EQ(ps->getSize(), 2); + EXPECT_EQ(ps->getSize(), 2ul); EXPECT_EQ(ps->isDynamic(), true); Data encoded; ps->encode(encoded); EXPECT_EQ(hex(encoded), ""); size_t offset = 0; EXPECT_EQ(ps->decode(encoded, offset), true); - EXPECT_EQ(offset, 0); + EXPECT_EQ(offset, 0ul); EXPECT_FALSE(ps->setValueJson("dummy")); EXPECT_EQ(ps->findParamByName("param2")->getName(), "param2"); } @@ -864,6 +892,24 @@ TEST(EthereumAbiStruct, ParamHashStruct) { EXPECT_TRUE(p->setValueJson("0x0000000000000000000000000000000123456789")); EXPECT_EQ(hex(p->hashStruct()), "0000000000000000000000000000000000000000000000000000000123456789"); } + { + using collection = std::vector>; + auto p = std::make_shared(collection{std::make_shared(), std::make_shared(), std::make_shared()}); + EXPECT_TRUE(p->setValueJson("[1,0,1]")); + EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"); + } + { + using collection = std::vector>; + auto p = std::make_shared(collection{std::make_shared(), std::make_shared(), std::make_shared()}); + EXPECT_TRUE(p->setValueJson("[13,14,15]")); + EXPECT_EQ(hex(p->hashStruct()), "000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f"); + + // Coverage + EXPECT_FALSE(p->setValueJson("NotValidJson")); + EXPECT_FALSE(p->setValueJson("{}")); + EXPECT_FALSE(p->setValueJson("[1,2,3,4]")); + EXPECT_FALSE(p->setValueJson("[1,2]")); + } { auto p = std::make_shared(std::make_shared()); EXPECT_TRUE(p->setValueJson("[13,14,15]")); @@ -880,3 +926,5 @@ TEST(EthereumAbiStruct, ParamHashStruct) { EXPECT_EQ(hex(p->hashStruct()), "5c6090c0461491a2941743bda5c3658bf1ea53bbd3edcde54e16205e18b45792"); } } +// clang-format on +} // namespace TW::Ethereum::tests diff --git a/tests/Ethereum/AbiTests.cpp b/tests/chains/Ethereum/AbiTests.cpp similarity index 84% rename from tests/Ethereum/AbiTests.cpp rename to tests/chains/Ethereum/AbiTests.cpp index 6aa15115163..3575dcb0f95 100644 --- a/tests/Ethereum/AbiTests.cpp +++ b/tests/chains/Ethereum/AbiTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,13 +6,11 @@ #include "Ethereum/ABI.h" #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include -using namespace TW; -using namespace TW::Ethereum::ABI; - +namespace TW::Ethereum::ABI::tests { ///// Parameter types @@ -34,11 +32,23 @@ TEST(EthereumAbi, ParamTypeNames) { EXPECT_EQ("address", ParamAddress().getType()); EXPECT_EQ("bytes", ParamByteArray().getType()); EXPECT_EQ("bytes168", ParamByteArrayFix(168).getType()); - // ParamArray: needs a child parameter to obtain type - auto paramArray = ParamArray(); - EXPECT_EQ("empty[]", paramArray.getType()); - paramArray.addParam(std::make_shared()); - EXPECT_EQ("bool[]", paramArray.getType()); + { + // ParamArray, non-empty + auto paramArray = ParamArray(); + paramArray.addParam(std::make_shared()); + EXPECT_EQ("bool[]", paramArray.getType()); + } + { + // ParamArray, empty with prototype + auto paramArray = ParamArray(); + paramArray.setProto(std::make_shared()); + EXPECT_EQ("bool[]", paramArray.getType()); + } + { + // ParamArray, empty, no prototype + auto paramArray = ParamArray(); + EXPECT_EQ("__empty__[]", paramArray.getType()); + } EXPECT_EQ("()", ParamTuple().getType()); } @@ -51,7 +61,7 @@ TEST(EthereumAbi, ParamBool) { EXPECT_EQ("bool", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); } { auto param = ParamBool(false); @@ -82,7 +92,7 @@ TEST(EthereumAbi, ParamUInt8) { EXPECT_EQ("uint8", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); } { auto param = ParamUInt8(101); @@ -113,7 +123,7 @@ TEST(EthereumAbi, ParamUInt16) { EXPECT_EQ("uint16", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); } { auto param = ParamUInt16(101); @@ -138,13 +148,13 @@ TEST(EthereumAbi, ParamUInt16) { TEST(EthereumAbi, ParamUInt32) { { auto param = ParamUInt32(101); - EXPECT_EQ(101, param.getVal()); + EXPECT_EQ(101ul, param.getVal()); param.setVal(1234); - EXPECT_EQ(1234, param.getVal()); + EXPECT_EQ(1234ul, param.getVal()); EXPECT_EQ("uint32", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); } { auto param = ParamUInt32(101); @@ -153,7 +163,7 @@ TEST(EthereumAbi, ParamUInt32) { EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000065", hex(encoded)); size_t offset = 0; EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(101, param.getVal()); + EXPECT_EQ(101ul, param.getVal()); } { auto param = ParamUInt32(1234); @@ -162,20 +172,20 @@ TEST(EthereumAbi, ParamUInt32) { EXPECT_EQ("00000000000000000000000000000000000000000000000000000000000004d2", hex(encoded)); size_t offset = 0; EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(1234, param.getVal()); + EXPECT_EQ(1234ul, param.getVal()); } } TEST(EthereumAbi, ParamUInt64) { { auto param = ParamUInt64(101); - EXPECT_EQ(101, param.getVal()); + EXPECT_EQ(101ul, param.getVal()); param.setVal(1234); - EXPECT_EQ(1234, param.getVal()); + EXPECT_EQ(1234ul, param.getVal()); EXPECT_EQ("uint64", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); } { auto param = ParamUInt64(101); @@ -184,7 +194,7 @@ TEST(EthereumAbi, ParamUInt64) { EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000065", hex(encoded)); size_t offset = 0; EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(101, param.getVal()); + EXPECT_EQ(101ul, param.getVal()); } { auto param = ParamUInt64(1234); @@ -193,7 +203,7 @@ TEST(EthereumAbi, ParamUInt64) { EXPECT_EQ("00000000000000000000000000000000000000000000000000000000000004d2", hex(encoded)); size_t offset = 0; EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(1234, param.getVal()); + EXPECT_EQ(1234ul, param.getVal()); } } @@ -207,7 +217,7 @@ TEST(EthereumAbi, ParamUInt256) { EXPECT_EQ("uint256", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); } { auto param = ParamUInt256(uint256_t(101)); @@ -256,7 +266,7 @@ TEST(EthereumAbi, ParamUInt80) { EXPECT_EQ("uint80", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); // above number of bits, masked param.setVal(load(Data(parse_hex("1010101010101010101010101010101010101010101010101010101010101010")))); @@ -294,7 +304,7 @@ TEST(EthereumAbi, ParamInt80) { EXPECT_EQ("int80", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); param.setVal(int256_t(-101)); EXPECT_EQ(int256_t(-101), param.getVal()); @@ -350,7 +360,7 @@ TEST(EthereumAbi, ParamString) { EXPECT_EQ("string", param.getType()); EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ(3 * 32, param.getSize()); + EXPECT_EQ(3 * 32ul, param.getSize()); } { auto param = ParamString(helloStr); @@ -361,8 +371,8 @@ TEST(EthereumAbi, ParamString) { "48656c6c6f20576f726c64212020202048656c6c6f20576f726c642120202020" "48656c6c6f20576f726c64210000000000000000000000000000000000000000", hex(encoded)); - EXPECT_EQ(3 * 32, encoded.size()); - EXPECT_EQ(3 * 32, param.getSize()); + EXPECT_EQ(3 * 32ul, encoded.size()); + EXPECT_EQ(3 * 32ul, param.getSize()); size_t offset = 0; EXPECT_TRUE(param.decode(encoded, offset)); EXPECT_EQ(helloStr, param.getVal()); @@ -378,7 +388,7 @@ TEST(EthereumAbi, ParamAddress) { EXPECT_EQ("address", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); } { auto param = ParamAddress(val1); @@ -413,20 +423,20 @@ TEST(EthereumAbi, ParamByteArray) { EXPECT_EQ("bytes", param.getType()); EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ(2 * 32, param.getSize()); + EXPECT_EQ(2 * 32ul, param.getSize()); } { auto param = ParamByteArray(data10); Data encoded; param.encode(encoded); - EXPECT_EQ(2 * 32, encoded.size()); - EXPECT_EQ(2 * 32, param.getSize()); + EXPECT_EQ(2 * 32ul, encoded.size()); + EXPECT_EQ(2 * 32ul, param.getSize()); EXPECT_EQ( "000000000000000000000000000000000000000000000000000000000000000a" - "3132333435363738393000000000000000000000000000000000000000000000", + "3132333435363738393000000000000000000000000000000000000000000000", hex(encoded)); - EXPECT_EQ(2 * 32, encoded.size()); - EXPECT_EQ(2 * 32, param.getSize()); + EXPECT_EQ(2 * 32ul, encoded.size()); + EXPECT_EQ(2 * 32ul, param.getSize()); size_t offset = 0; EXPECT_TRUE(param.decode(encoded, offset)); EXPECT_EQ(data10, param.getVal()); @@ -441,16 +451,16 @@ TEST(EthereumAbi, ParamByteArrayFix) { EXPECT_EQ("bytes10", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, param.getSize()); } { auto param = ParamByteArrayFix(10, data10); Data encoded; param.encode(encoded); - EXPECT_EQ(32, encoded.size()); - EXPECT_EQ(32, param.getSize()); + EXPECT_EQ(32ul, encoded.size()); + EXPECT_EQ(32ul, param.getSize()); EXPECT_EQ( - "3132333435363738393000000000000000000000000000000000000000000000", + "3132333435363738393000000000000000000000000000000000000000000000", hex(encoded)); size_t offset = 0; EXPECT_TRUE(param.decode(encoded, offset)); @@ -464,14 +474,14 @@ TEST(EthereumAbi, ParamArrayByte) { param.addParam(std::make_shared(49)); param.addParam(std::make_shared(50)); param.addParam(std::make_shared(51)); - EXPECT_EQ(3, param.getVal().size()); + EXPECT_EQ(3ul, param.getVal().size()); EXPECT_EQ(49, (std::dynamic_pointer_cast(param.getVal()[0]))->getVal()); EXPECT_EQ(51, (std::dynamic_pointer_cast(param.getVal()[2]))->getVal()); EXPECT_EQ("uint8[]", param.getType()); EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ((3 + 1) * 32, param.getSize()); - EXPECT_EQ(3, param.getCount()); + EXPECT_EQ((3 + 1) * 32ul, param.getSize()); + EXPECT_EQ(3ul, param.getCount()); } { auto param = ParamArray(); @@ -480,35 +490,86 @@ TEST(EthereumAbi, ParamArrayByte) { param.addParam(std::make_shared(51)); Data encoded; param.encode(encoded); - EXPECT_EQ(4 * 32, encoded.size()); - EXPECT_EQ(4 * 32, param.getSize()); + EXPECT_EQ(4 * 32ul, encoded.size()); + EXPECT_EQ(4 * 32ul, param.getSize()); EXPECT_EQ( "0000000000000000000000000000000000000000000000000000000000000003" "0000000000000000000000000000000000000000000000000000000000000031" "0000000000000000000000000000000000000000000000000000000000000032" "0000000000000000000000000000000000000000000000000000000000000033", hex(encoded)); - EXPECT_EQ(4 * 32, encoded.size()); - EXPECT_EQ(4 * 32, param.getSize()); + EXPECT_EQ(4 * 32ul, encoded.size()); + EXPECT_EQ(4 * 32ul, param.getSize()); size_t offset = 0; EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(3, param.getVal().size()); + EXPECT_EQ(3ul, param.getVal().size()); EXPECT_EQ(49, (std::dynamic_pointer_cast(param.getVal()[0]))->getVal()); EXPECT_EQ(51, (std::dynamic_pointer_cast(param.getVal()[2]))->getVal()); } } +TEST(EthereumAbi, ParamArrayEmpty) { + auto param = ParamArray(); + param.setProto(std::make_shared(0)); + + EXPECT_EQ(0ul, param.getCount()); + Data encoded; + param.encode(encoded); + EXPECT_EQ(32ul, encoded.size()); + EXPECT_EQ(32ul, param.getSize()); + EXPECT_EQ( + "0000000000000000000000000000000000000000000000000000000000000000", + hex(encoded)); + EXPECT_EQ("uint8[]", param.getType()); + EXPECT_EQ("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", hex(param.hashStruct())); +} + +TEST(EthereumAbi, ParamFixedArrayAddress) { + { + auto param = ParamArrayFix({std::make_shared(Data(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077")))}); + EXPECT_EQ(param.getType(), "address[1]"); + EXPECT_EQ(param.getCount(), 1ul); + EXPECT_EQ(param.getSize(), 32ul); + EXPECT_FALSE(param.isDynamic()); + Data encoded; + param.encode(encoded); + EXPECT_EQ(hex(encoded), "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"); + std::size_t offset{0}; + EXPECT_TRUE(param.decode(encoded, offset)); + } + { + auto param = ParamArrayFix({std::make_shared(Data(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077"))), + std::make_shared(Data(parse_hex("2e00cd222cb42b616d86d037cc494e8ab7f5c9a3")))}); + EXPECT_EQ(param.getType(), "address[2]"); + EXPECT_EQ(param.getCount(), 2ul); + Data encoded; + param.encode(encoded); + EXPECT_EQ(hex(encoded), "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc0770000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3"); + std::size_t offset{0}; + EXPECT_TRUE(param.decode(encoded, offset)); + } + { + // nullptr + EXPECT_THROW(ParamArrayFix({nullptr}), std::runtime_error); + // Should be the same type + EXPECT_THROW( + ParamArrayFix({std::make_shared(Data(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077"))), + std::make_shared("Foo")}), + std::runtime_error); + } +} + TEST(EthereumAbi, ParamArrayAddress) { { auto param = ParamArray(); param.addParam(std::make_shared(Data(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077")))); param.addParam(std::make_shared(Data(parse_hex("2e00cd222cb42b616d86d037cc494e8ab7f5c9a3")))); - EXPECT_EQ(2, param.getVal().size()); + EXPECT_EQ(2ul, param.getVal().size()); EXPECT_EQ("address[]", param.getType()); EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ((2 + 1) * 32, param.getSize()); - EXPECT_EQ(2, param.getCount()); + EXPECT_EQ((2 + 1) * 32ul, param.getSize()); + EXPECT_EQ(2ul, param.getCount()); } { auto param = ParamArray(); @@ -516,21 +577,21 @@ TEST(EthereumAbi, ParamArrayAddress) { param.addParam(std::make_shared(Data(parse_hex("2e00cd222cb42b616d86d037cc494e8ab7f5c9a3")))); Data encoded; param.encode(encoded); - EXPECT_EQ(3 * 32, encoded.size()); - EXPECT_EQ(3 * 32, param.getSize()); + EXPECT_EQ(3 * 32ul, encoded.size()); + EXPECT_EQ(3 * 32ul, param.getSize()); EXPECT_EQ( "0000000000000000000000000000000000000000000000000000000000000002" "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077" "0000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3", hex(encoded)); - EXPECT_EQ(3 * 32, encoded.size()); - EXPECT_EQ(3 * 32, param.getSize()); + EXPECT_EQ(3 * 32ul, encoded.size()); + EXPECT_EQ(3 * 32ul, param.getSize()); size_t offset = 0; EXPECT_TRUE(param.decode(encoded, offset)); - EXPECT_EQ(2, param.getCount()); - EXPECT_EQ(2, param.getVal().size()); + EXPECT_EQ(2ul, param.getCount()); + EXPECT_EQ(2ul, param.getVal().size()); EXPECT_EQ( - "2e00cd222cb42b616d86d037cc494e8ab7f5c9a3", + "2e00cd222cb42b616d86d037cc494e8ab7f5c9a3", hex((std::dynamic_pointer_cast(param.getVal()[1]))->getData())); } } @@ -540,12 +601,12 @@ TEST(EthereumAbi, ParamArrayOfByteArray) { param.addParam(std::make_shared(parse_hex("1011"))); param.addParam(std::make_shared(parse_hex("102222"))); param.addParam(std::make_shared(parse_hex("10333333"))); - EXPECT_EQ(3, param.getVal().size()); + EXPECT_EQ(3ul, param.getVal().size()); EXPECT_EQ("bytes[]", param.getType()); EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ((1 + 3 + 3 * 2) * 32, param.getSize()); - EXPECT_EQ(3, param.getCount()); + EXPECT_EQ((1 + 3 + 3 * 2) * 32ul, param.getSize()); + EXPECT_EQ(3ul, param.getCount()); } TEST(EthereumAbi, ParamArrayBytesContract) { @@ -554,17 +615,17 @@ TEST(EthereumAbi, ParamArrayBytesContract) { param.addParam(std::make_shared(parse_hex("0x304e6adee71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000"))); param.addParam(std::make_shared(parse_hex("0x8b95dd71e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f000000000000000000000000000000000000000000000000000000000000003c0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000001447331175b23c2f067204b506ca1501c26731c990000000000000000000000000"))); param.addParam(std::make_shared(parse_hex("0x8b95dd71e71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c6f00000000000000000000000000000000000000000000000000000000000002ca00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000014d30f834b53d8f7e851e87b90ffa65757a35b8505000000000000000000000000"))); - EXPECT_EQ(4, param.getCount()); - EXPECT_EQ(4, param.getVal().size()); + EXPECT_EQ(4ul, param.getCount()); + EXPECT_EQ(4ul, param.getVal().size()); - EXPECT_EQ("bytes[]", param.getType()); + EXPECT_EQ("bytes[]", param.getType()); EXPECT_TRUE(param.isDynamic()); Data encoded; param.encode(encoded); - EXPECT_EQ(896, encoded.size()); + EXPECT_EQ(896ul, encoded.size()); - EXPECT_EQ(896, param.getSize()); + EXPECT_EQ(896ul, param.getSize()); } TEST(EthereumAbi, ParamTupleStatic) { @@ -574,18 +635,18 @@ TEST(EthereumAbi, ParamTupleStatic) { param.addParam(std::make_shared(123)); EXPECT_EQ("(bool,uint64)", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(2, param.getCount()); - EXPECT_EQ(64, param.getSize()); + EXPECT_EQ(2ul, param.getCount()); + EXPECT_EQ(64ul, param.getSize()); Data encoded; param.encode(encoded); EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000007b", hex(encoded)); - { // decode + { // decode size_t offset = 0; auto param2 = ParamTuple(); param2.addParam(std::make_shared()); param2.addParam(std::make_shared()); EXPECT_TRUE(param2.decode(encoded, offset)); - EXPECT_EQ(2, param2.getCount()); + EXPECT_EQ(2ul, param2.getCount()); Data encoded2; param2.encode(encoded2); EXPECT_EQ("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000007b", hex(encoded)); @@ -597,8 +658,8 @@ TEST(EthereumAbi, ParamTupleStatic) { param.addParam(std::make_shared(parse_hex("000102030405060708090a0b0c0d0e0f10111213"))); EXPECT_EQ("(uint64,address)", param.getType()); EXPECT_FALSE(param.isDynamic()); - EXPECT_EQ(2, param.getCount()); - EXPECT_EQ(64, param.getSize()); + EXPECT_EQ(2ul, param.getCount()); + EXPECT_EQ(64ul, param.getSize()); Data encoded; param.encode(encoded); EXPECT_EQ("00000000000000000000000000000000000000000000000000000000000001c8000000000000000000000000000102030405060708090a0b0c0d0e0f10111213", hex(encoded)); @@ -613,8 +674,8 @@ TEST(EthereumAbi, ParamTupleDynamic) { param.addParam(std::make_shared(parse_hex("00010203040506070809"))); EXPECT_EQ("(string,uint64,bytes)", param.getType()); EXPECT_TRUE(param.isDynamic()); - EXPECT_EQ(3, param.getCount()); - EXPECT_EQ(7 * 32, param.getSize()); + EXPECT_EQ(3ul, param.getCount()); + EXPECT_EQ(7 * 32ul, param.getSize()); Data encoded; param.encode(encoded); EXPECT_EQ( @@ -624,7 +685,8 @@ TEST(EthereumAbi, ParamTupleDynamic) { "0000000000000000000000000000000000000000000000000000000000000014" // len "446f6e27742074727573742c2076657269667921000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000a" // len - "0001020304050607080900000000000000000000000000000000000000000000", hex(encoded)); + "0001020304050607080900000000000000000000000000000000000000000000", + hex(encoded)); } } @@ -645,14 +707,14 @@ TEST(EthereumAbi, EncodeVectorByte) { p.encode(encoded); EXPECT_EQ( "000000000000000000000000000000000000000000000000000000000000000a" - "3132333435363738393000000000000000000000000000000000000000000000", hex(encoded)); + "3132333435363738393000000000000000000000000000000000000000000000", + hex(encoded)); } TEST(EthereumAbi, EncodeArrayByte) { auto p = ParamArray(std::vector>{ std::make_shared(parse_hex("1011")), - std::make_shared(parse_hex("102222")) - }); + std::make_shared(parse_hex("102222"))}); EXPECT_EQ("bytes[]", p.getType()); Data encoded; p.encode(encoded); @@ -664,10 +726,9 @@ TEST(EthereumAbi, EncodeArrayByte) { "1011000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000003" "1022220000000000000000000000000000000000000000000000000000000000", - hex(encoded) - ); - EXPECT_EQ((1 + 2 + 2 * 2) * 32, encoded.size()); - EXPECT_EQ((1 + 2 + 2 * 2) * 32, p.getSize()); + hex(encoded)); + EXPECT_EQ((1 + 2 + 2 * 2) * 32ul, encoded.size()); + EXPECT_EQ((1 + 2 + 2 * 2) * 32ul, p.getSize()); } TEST(EthereumAbi, DecodeUInt) { @@ -677,7 +738,7 @@ TEST(EthereumAbi, DecodeUInt) { bool res = ParamNumberType::decodeNumber(encoded, decoded, offset); EXPECT_TRUE(res); EXPECT_EQ(uint256_t(42), decoded); - EXPECT_EQ(32, offset); + EXPECT_EQ(32ul, offset); } TEST(EthereumAbi, DecodeUInt8) { @@ -687,7 +748,7 @@ TEST(EthereumAbi, DecodeUInt8) { bool res = ParamNumberType::decodeNumber(encoded, decoded, offset); EXPECT_TRUE(res); EXPECT_EQ(24, decoded); - EXPECT_EQ(32, offset); + EXPECT_EQ(32ul, offset); } TEST(EthereumAbi, DecodeUInt8WithOffset) { @@ -697,7 +758,7 @@ TEST(EthereumAbi, DecodeUInt8WithOffset) { bool res = ParamNumberType::decodeNumber(encoded, decoded, offset); EXPECT_TRUE(res); EXPECT_EQ(24, decoded); - EXPECT_EQ(3 + 32, offset); + EXPECT_EQ(3 + 32ul, offset); } TEST(EthereumAbi, DecodeUIntWithOffset) { @@ -707,7 +768,7 @@ TEST(EthereumAbi, DecodeUIntWithOffset) { bool res = decode(encoded, decoded, offset); EXPECT_TRUE(res); EXPECT_EQ(uint256_t(42), decoded); - EXPECT_EQ(3 + 32, offset); + EXPECT_EQ(3 + 32ul, offset); } TEST(EthereumAbi, DecodeUIntErrorTooShort) { @@ -717,7 +778,7 @@ TEST(EthereumAbi, DecodeUIntErrorTooShort) { bool res = decode(encoded, decoded, offset); EXPECT_FALSE(res); EXPECT_EQ(uint256_t(0), decoded); - EXPECT_EQ(0, offset); + EXPECT_EQ(0ul, offset); } TEST(EthereumAbi, DecodeArrayUint) { @@ -728,12 +789,12 @@ TEST(EthereumAbi, DecodeArrayUint) { std::vector decoded; bool res = ParamByteArray::decodeBytes(encoded, decoded, offset); EXPECT_TRUE(res); - EXPECT_EQ(10, decoded.size()); + EXPECT_EQ(10ul, decoded.size()); if (decoded.size() >= 2) { EXPECT_EQ(49u, decoded[0]); EXPECT_EQ(50u, decoded[1]); } - EXPECT_EQ(32 + 32, offset); + EXPECT_EQ(32 + 32ul, offset); } TEST(EthereumAbi, DecodeArrayTooShort) { @@ -763,7 +824,7 @@ TEST(EthereumAbi, DecodeByteArray) { bool res = ParamByteArray::decodeBytes(encoded, decoded, offset); EXPECT_TRUE(res); EXPECT_EQ("31323334353637383930", hex(decoded)); - EXPECT_EQ(32 + 32, offset); + EXPECT_EQ(32 + 32ul, offset); } TEST(EthereumAbi, DecodeByteArray10) { @@ -772,10 +833,10 @@ TEST(EthereumAbi, DecodeByteArray10) { Data decoded; bool res = ParamByteArrayFix::decodeBytesFix(encoded, 10, decoded, offset); EXPECT_TRUE(res); - EXPECT_EQ(10, decoded.size()); + EXPECT_EQ(10ul, decoded.size()); EXPECT_EQ(49u, decoded[0]); EXPECT_EQ(50u, decoded[1]); - EXPECT_EQ(32, offset); + EXPECT_EQ(32ul, offset); } TEST(EthereumAbi, DecodeArrayOfByteArray) { @@ -786,8 +847,7 @@ TEST(EthereumAbi, DecodeArrayOfByteArray) { "0000000000000000000000000000000000000000000000000000000000000002" "1011000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000003" - "1022200000000000000000000000000000000000000000000000000000000000" - ); + "1022200000000000000000000000000000000000000000000000000000000000"); size_t offset = 0; Data decoded; auto param = ParamArray(); @@ -795,24 +855,24 @@ TEST(EthereumAbi, DecodeArrayOfByteArray) { param.addParam(std::make_shared(Data())); bool res = param.decode(encoded, offset); EXPECT_TRUE(res); - EXPECT_EQ(2, param.getCount()); - EXPECT_EQ(7 * 32, offset); - EXPECT_EQ(2, param.getVal().size()); + EXPECT_EQ(2ul, param.getCount()); + EXPECT_EQ(7 * 32ul, offset); + EXPECT_EQ(2ul, param.getVal().size()); } ///// Parameters encode & decode TEST(EthereumAbi, EncodeParamsSimple) { - auto p = Parameters(std::vector>{ + auto p = Parameters(std::vector>{ std::make_shared(16u), std::make_shared(17u), - std::make_shared(true) }); + std::make_shared(true)}); EXPECT_EQ("(uint256,uint256,bool)", p.getType()); Data encoded; p.encode(encoded); - EXPECT_EQ(3 * 32, encoded.size()); - EXPECT_EQ(3 * 32, p.getSize()); + EXPECT_EQ(3 * 32ul, encoded.size()); + EXPECT_EQ(3 * 32ul, p.getSize()); EXPECT_EQ( "0000000000000000000000000000000000000000000000000000000000000010" "0000000000000000000000000000000000000000000000000000000000000011" @@ -822,22 +882,20 @@ TEST(EthereumAbi, EncodeParamsSimple) { TEST(EthereumAbi, EncodeParamsMixed) { auto p = Parameters(std::vector>{ - std::make_shared(69u), + std::make_shared(69u), std::make_shared(std::vector>{ std::make_shared(1), std::make_shared(2), - std::make_shared(3) - }), + std::make_shared(3)}), std::make_shared(true), std::make_shared("Hello"), - std::make_shared(Data{0x64, 0x61, 0x76, 0x65}) - }); + std::make_shared(Data{0x64, 0x61, 0x76, 0x65})}); EXPECT_EQ("(uint256,uint256[],bool,string,bytes)", p.getType()); Data encoded; p.encode(encoded); - EXPECT_EQ(13 * 32, encoded.size()); - EXPECT_EQ(13 * 32, p.getSize()); + EXPECT_EQ(13 * 32ul, encoded.size()); + EXPECT_EQ(13 * 32ul, p.getSize()); EXPECT_EQ( "0000000000000000000000000000000000000000000000000000000000000045" "00000000000000000000000000000000000000000000000000000000000000a0" @@ -879,8 +937,7 @@ TEST(EthereumAbi, DecodeParamsSimple) { auto p = Parameters(std::vector>{ std::make_shared(0), std::make_shared(0), - std::make_shared(false) - }); + std::make_shared(false)}); EXPECT_EQ("(uint256,uint256,bool)", p.getType()); size_t offset = 0; bool res = p.decode(encoded, offset); @@ -888,7 +945,7 @@ TEST(EthereumAbi, DecodeParamsSimple) { EXPECT_EQ(uint256_t(16u), (std::dynamic_pointer_cast(p.getParam(0)))->getVal()); EXPECT_EQ(uint256_t(17u), (std::dynamic_pointer_cast(p.getParam(1)))->getVal()); EXPECT_EQ(true, (std::dynamic_pointer_cast(p.getParam(2)))->getVal()); - EXPECT_EQ(3 * 32, offset); + EXPECT_EQ(3 * 32ul, offset); } TEST(EthereumAbi, DecodeParamsMixed) { @@ -906,8 +963,9 @@ TEST(EthereumAbi, DecodeParamsMixed) { append(encoded, parse_hex("48656c6c6f000000000000000000000000000000000000000000000000000000")); append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000004")); append(encoded, parse_hex("6461766500000000000000000000000000000000000000000000000000000000")); + // clang-format off auto p = Parameters(std::vector>{ - std::make_shared(), + std::make_shared(), std::make_shared(std::vector>{ std::make_shared(), std::make_shared(), @@ -917,37 +975,41 @@ TEST(EthereumAbi, DecodeParamsMixed) { std::make_shared(), std::make_shared() }); + // clang-format on EXPECT_EQ("(uint256,uint256[],bool,string,bytes)", p.getType()); size_t offset = 0; bool res = p.decode(encoded, offset); EXPECT_TRUE(res); EXPECT_EQ(uint256_t(69u), (std::dynamic_pointer_cast(p.getParam(0)))->getVal()); - EXPECT_EQ(3, (std::dynamic_pointer_cast(p.getParam(1)))->getCount()); + EXPECT_EQ(3ul, (std::dynamic_pointer_cast(p.getParam(1)))->getCount()); EXPECT_EQ(1, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(p.getParam(1)))->getParam(0)))->getVal()); EXPECT_EQ(3, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(p.getParam(1)))->getParam(2)))->getVal()); EXPECT_EQ(true, (std::dynamic_pointer_cast(p.getParam(2)))->getVal()); EXPECT_EQ("Hello", (std::dynamic_pointer_cast(p.getParam(3)))->getVal()); - EXPECT_EQ(13 * 32, offset); + EXPECT_EQ(13 * 32ul, offset); } ///// Function encode & decode TEST(EthereumAbi, EncodeSignature) { + // clang-format off auto func = Function("baz", std::vector>{ std::make_shared(69u), std::make_shared(true) }); + // clang-format on EXPECT_EQ("baz(uint256,bool)", func.getType()); Data encoded; func.encode(encoded); - EXPECT_EQ(encoded.size(), 32 * 2 + 4); + EXPECT_EQ(encoded.size(), 32 * 2 + 4ul); EXPECT_EQ(hex(encoded.begin(), encoded.begin() + 4), "72ed38b6"); - EXPECT_EQ(hex(encoded.begin() + 4, encoded.begin() + 36 ), "0000000000000000000000000000000000000000000000000000000000000045"); - EXPECT_EQ(hex(encoded.begin() + 36, encoded.begin() + 68 ), "0000000000000000000000000000000000000000000000000000000000000001"); + EXPECT_EQ(hex(encoded.begin() + 4, encoded.begin() + 36), "0000000000000000000000000000000000000000000000000000000000000045"); + EXPECT_EQ(hex(encoded.begin() + 36, encoded.begin() + 68), "0000000000000000000000000000000000000000000000000000000000000001"); } TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase1) { + // clang-format off auto func = Function("sam", std::vector>{ std::make_shared(Data{0x64, 0x61, 0x76, 0x65}), std::make_shared(true), @@ -957,15 +1019,16 @@ TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase1) { std::make_shared(3) }) }); + // clang-format on EXPECT_EQ("sam(bytes,bool,uint256[])", func.getType()); Data encoded; func.encode(encoded); - EXPECT_EQ(encoded.size(), 32 * 9 + 4); - EXPECT_EQ(hex(encoded.begin() + 0, encoded.begin() + 4 ), "a5643bf2"); - EXPECT_EQ(hex(encoded.begin() + 4, encoded.begin() + 36 ), "0000000000000000000000000000000000000000000000000000000000000060"); - EXPECT_EQ(hex(encoded.begin() + 36, encoded.begin() + 68 ), "0000000000000000000000000000000000000000000000000000000000000001"); - EXPECT_EQ(hex(encoded.begin() + 68, encoded.begin() + 100), "00000000000000000000000000000000000000000000000000000000000000a0"); + EXPECT_EQ(encoded.size(), 32 * 9 + 4ul); + EXPECT_EQ(hex(encoded.begin() + 0, encoded.begin() + 4), "a5643bf2"); + EXPECT_EQ(hex(encoded.begin() + 4, encoded.begin() + 36), "0000000000000000000000000000000000000000000000000000000000000060"); + EXPECT_EQ(hex(encoded.begin() + 36, encoded.begin() + 68), "0000000000000000000000000000000000000000000000000000000000000001"); + EXPECT_EQ(hex(encoded.begin() + 68, encoded.begin() + 100), "00000000000000000000000000000000000000000000000000000000000000a0"); EXPECT_EQ(hex(encoded.begin() + 100, encoded.begin() + 132), "0000000000000000000000000000000000000000000000000000000000000004"); EXPECT_EQ(hex(encoded.begin() + 132, encoded.begin() + 164), "6461766500000000000000000000000000000000000000000000000000000000"); EXPECT_EQ(hex(encoded.begin() + 164, encoded.begin() + 196), "0000000000000000000000000000000000000000000000000000000000000003"); @@ -975,24 +1038,25 @@ TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase1) { } TEST(EthereumAbi, EncodeFunctionWithDynamicArgumentsCase2) { + // clang-format off auto func = Function("f", std::vector>{ std::make_shared(0x123), std::make_shared(std::vector>{ std::make_shared(0x456), - std::make_shared(0x789) - }), + std::make_shared(0x789)}), std::make_shared(10, std::vector{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30}), std::make_shared("Hello, world!") }); + // clamp-format on EXPECT_EQ("f(uint256,uint32[],bytes10,string)", func.getType()); Data encoded; func.encode(encoded); - EXPECT_EQ(encoded.size(), 32 * 9 + 4); - EXPECT_EQ(hex(encoded.begin() + 0, encoded.begin() + 4 ), "47b941bf"); - EXPECT_EQ(hex(encoded.begin() + 4, encoded.begin() + 36 ), "0000000000000000000000000000000000000000000000000000000000000123"); - EXPECT_EQ(hex(encoded.begin() + 36, encoded.begin() + 68 ), "0000000000000000000000000000000000000000000000000000000000000080"); - EXPECT_EQ(hex(encoded.begin() + 68, encoded.begin() + 100), "3132333435363738393000000000000000000000000000000000000000000000"); + EXPECT_EQ(encoded.size(), 32 * 9 + 4ul); + EXPECT_EQ(hex(encoded.begin() + 0, encoded.begin() + 4), "47b941bf"); + EXPECT_EQ(hex(encoded.begin() + 4, encoded.begin() + 36), "0000000000000000000000000000000000000000000000000000000000000123"); + EXPECT_EQ(hex(encoded.begin() + 36, encoded.begin() + 68), "0000000000000000000000000000000000000000000000000000000000000080"); + EXPECT_EQ(hex(encoded.begin() + 68, encoded.begin() + 100), "3132333435363738393000000000000000000000000000000000000000000000"); EXPECT_EQ(hex(encoded.begin() + 100, encoded.begin() + 132), "00000000000000000000000000000000000000000000000000000000000000e0"); EXPECT_EQ(hex(encoded.begin() + 132, encoded.begin() + 164), "0000000000000000000000000000000000000000000000000000000000000002"); EXPECT_EQ(hex(encoded.begin() + 164, encoded.begin() + 196), "0000000000000000000000000000000000000000000000000000000000000456"); @@ -1005,54 +1069,56 @@ TEST(EthereumAbi, DecodeFunctionOutputCase1) { Data encoded; append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000045")); + // clang-format off auto func = Function("readout", std::vector>{ std::make_shared(parse_hex("f784682c82526e245f50975190ef0fff4e4fc077")), std::make_shared(1000) }); + // clang-format on func.addOutParam(std::make_shared()); EXPECT_EQ("readout(address,uint64)", func.getType()); // original output value std::shared_ptr param; EXPECT_TRUE(func.getOutParam(0, param)); - EXPECT_EQ(0, (std::dynamic_pointer_cast(param))->getVal()); + EXPECT_EQ(0ul, (std::dynamic_pointer_cast(param))->getVal()); size_t offset = 0; bool res = func.decodeOutput(encoded, offset); EXPECT_TRUE(res); - EXPECT_EQ(32, offset); + EXPECT_EQ(32ul, offset); // new output value - EXPECT_EQ(0x45, (std::dynamic_pointer_cast(param))->getVal()); + EXPECT_EQ(0x45ul, (std::dynamic_pointer_cast(param))->getVal()); } TEST(EthereumAbi, DecodeFunctionOutputCase2) { + // clang-format off auto func = Function("getAmountsOut", std::vector>{ std::make_shared(100), std::make_shared(std::make_shared(parse_hex("000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"))) }); + // clang-format on func.addOutParam(std::make_shared(std::vector>{ std::make_shared(66), - std::make_shared(67) - })); + std::make_shared(67)})); EXPECT_EQ("getAmountsOut(uint256,address[])", func.getType()); Data encoded; append(encoded, parse_hex( - "0000000000000000000000000000000000000000000000000000000000000020" - "0000000000000000000000000000000000000000000000000000000000000002" - "0000000000000000000000000000000000000000000000000000000000000004" - "0000000000000000000000000000000000000000000000000000000000000005" - )); + "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000002" + "0000000000000000000000000000000000000000000000000000000000000004" + "0000000000000000000000000000000000000000000000000000000000000005")); size_t offset = 0; bool res = func.decodeOutput(encoded, offset); EXPECT_TRUE(res); - EXPECT_EQ(128, offset); + EXPECT_EQ(128ul, offset); // new output values std::shared_ptr param; EXPECT_TRUE(func.getOutParam(0, param)); - EXPECT_EQ(2, (std::dynamic_pointer_cast(param))->getCount()); + EXPECT_EQ(2ul, (std::dynamic_pointer_cast(param))->getCount()); EXPECT_EQ(4, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getParam(0)))->getVal()); EXPECT_EQ(5, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getParam(1)))->getVal()); } @@ -1062,9 +1128,11 @@ TEST(EthereumAbi, DecodeInputSignature) { append(encoded, parse_hex("72ed38b6")); append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000045")); append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000001")); + // clang-format off auto func = Function("baz", std::vector>{ std::make_shared(), std::make_shared() }); + // clang-format on EXPECT_EQ("baz(uint256,bool)", func.getType()); size_t offset = 0; bool res = func.decodeInput(encoded, offset); @@ -1074,7 +1142,7 @@ TEST(EthereumAbi, DecodeInputSignature) { EXPECT_EQ(69u, (std::dynamic_pointer_cast(param))->getVal()); EXPECT_TRUE(func.getInParam(1, param)); EXPECT_EQ(true, (std::dynamic_pointer_cast(param))->getVal()); - EXPECT_EQ(4 + 2 * 32, offset); + EXPECT_EQ(4 + 2 * 32ul, offset); } TEST(EthereumAbi, DecodeFunctionInputWithDynamicArgumentsCase1) { @@ -1090,6 +1158,7 @@ TEST(EthereumAbi, DecodeFunctionInputWithDynamicArgumentsCase1) { append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000002")); append(encoded, parse_hex("0000000000000000000000000000000000000000000000000000000000000003")); + // clang-format off auto func = Function("sam", std::vector>{ std::make_shared(Data{0x64, 0x61, 0x76, 0x65}), std::make_shared(true), @@ -1099,6 +1168,7 @@ TEST(EthereumAbi, DecodeFunctionInputWithDynamicArgumentsCase1) { std::make_shared(3) }) }); + // clang-format on EXPECT_EQ("sam(bytes,bool,uint256[])", func.getType()); size_t offset = 0; @@ -1106,16 +1176,16 @@ TEST(EthereumAbi, DecodeFunctionInputWithDynamicArgumentsCase1) { EXPECT_TRUE(res); std::shared_ptr param; EXPECT_TRUE(func.getInParam(0, param)); - EXPECT_EQ(4, (std::dynamic_pointer_cast(param))->getCount()); + EXPECT_EQ(4ul, (std::dynamic_pointer_cast(param))->getCount()); EXPECT_EQ(0x64, (std::dynamic_pointer_cast(param))->getVal()[0]); EXPECT_EQ(0x65, (std::dynamic_pointer_cast(param))->getVal()[3]); EXPECT_TRUE(func.getInParam(1, param)); EXPECT_EQ(true, (std::dynamic_pointer_cast(param))->getVal()); EXPECT_TRUE(func.getInParam(2, param)); - EXPECT_EQ(3, (std::dynamic_pointer_cast(param))->getCount()); + EXPECT_EQ(3ul, (std::dynamic_pointer_cast(param))->getCount()); EXPECT_EQ(uint256_t(1), (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[0]))->getVal()); EXPECT_EQ(uint256_t(3), (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[2]))->getVal()); - EXPECT_EQ(4 + 9 * 32, offset); + EXPECT_EQ(4 + 9 * 32ul, offset); } TEST(EthereumAbi, DecodeFunctionInputWithDynamicArgumentsCase2) { @@ -1131,15 +1201,16 @@ TEST(EthereumAbi, DecodeFunctionInputWithDynamicArgumentsCase2) { append(encoded, parse_hex("000000000000000000000000000000000000000000000000000000000000000d")); append(encoded, parse_hex("48656c6c6f2c20776f726c642100000000000000000000000000000000000000")); + // clang-format off auto func = Function("f", std::vector>{ std::make_shared(0x123), std::make_shared(std::vector>{ std::make_shared(0x456), - std::make_shared(0x789) - }), + std::make_shared(0x789)}), std::make_shared(10, std::vector{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30}), std::make_shared("Hello, world!") }); + // clang-format on EXPECT_EQ("f(uint256,uint32[],bytes10,string)", func.getType()); size_t offset = 0; @@ -1149,15 +1220,15 @@ TEST(EthereumAbi, DecodeFunctionInputWithDynamicArgumentsCase2) { EXPECT_TRUE(func.getInParam(0, param)); EXPECT_EQ(uint256_t(0x123), (std::dynamic_pointer_cast(param))->getVal()); EXPECT_TRUE(func.getInParam(1, param)); - EXPECT_EQ(2, (std::dynamic_pointer_cast(param))->getCount()); - EXPECT_EQ(0x456, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[0]))->getVal()); - EXPECT_EQ(0x789, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[1]))->getVal()); + EXPECT_EQ(2ul, (std::dynamic_pointer_cast(param))->getCount()); + EXPECT_EQ(0x456ul, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[0]))->getVal()); + EXPECT_EQ(0x789ul, (std::dynamic_pointer_cast((std::dynamic_pointer_cast(param))->getVal()[1]))->getVal()); EXPECT_TRUE(func.getInParam(2, param)); - EXPECT_EQ(10, (std::dynamic_pointer_cast(param))->getCount()); + EXPECT_EQ(10ul, (std::dynamic_pointer_cast(param))->getCount()); EXPECT_EQ("31323334353637383930", hex((std::dynamic_pointer_cast(param))->getVal())); EXPECT_TRUE(func.getInParam(3, param)); EXPECT_EQ(std::string("Hello, world!"), (std::dynamic_pointer_cast(param))->getVal()); - EXPECT_EQ(4 + 9 * 32, offset); + EXPECT_EQ(4 + 9 * 32ul, offset); } TEST(EthereumAbi, DecodeFunctionContractMulticall) { @@ -1183,8 +1254,9 @@ TEST(EthereumAbi, DecodeFunctionContractMulticall) { "000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000" "000000000000000000000000000000000014d30f834b53d8f7e851e87b90ffa65757a35b850500000000000000" "000000000000000000000000000000000000000000000000000000000000000000"); - ASSERT_EQ(4 + 928, encoded.size()); + ASSERT_EQ(4 + 928ul, encoded.size()); + // clang-format off auto func = Function("multicall", std::vector>{ std::make_shared(std::vector>{ std::make_shared(Data()), @@ -1193,12 +1265,13 @@ TEST(EthereumAbi, DecodeFunctionContractMulticall) { std::make_shared(Data()) }), }); + // clang-format on EXPECT_EQ("multicall(bytes[])", func.getType()); size_t offset = 0; bool res = func.decodeInput(encoded, offset); EXPECT_TRUE(res); - EXPECT_EQ(4 + 29 * 32, offset); + EXPECT_EQ(4 + 29 * 32ul, offset); } TEST(EthereumAbi, ParamFactoryMake) { @@ -1266,18 +1339,18 @@ TEST(EthereumAbi, ParamFactoryGetArrayValue) { { auto pArray = std::make_shared(std::make_shared()); const auto vals = ParamFactory::getArrayValue(pArray, pArray->getType()); - ASSERT_EQ(vals.size(), 1); + ASSERT_EQ(vals.size(), 1ul); EXPECT_EQ(vals[0], "0"); } - { // wrong type, not array + { // wrong type, not array auto pArray = std::make_shared(std::make_shared()); const auto vals = ParamFactory::getArrayValue(pArray, "bool"); - EXPECT_EQ(vals.size(), 0); + EXPECT_EQ(vals.size(), 0ul); } - { // wrong param, not array + { // wrong param, not array auto pArray = std::make_shared(); const auto vals = ParamFactory::getArrayValue(pArray, "uint8[]"); - EXPECT_EQ(vals.size(), 0); + EXPECT_EQ(vals.size(), 0ul); } } @@ -1429,7 +1502,7 @@ TEST(EthereumAbi, ParamFactorySetGetValue) { EXPECT_EQ("", ParamFactory::getValue(p, p->getType())); EXPECT_TRUE(p->setValueJson("ABCdefGHI")); EXPECT_EQ("ABCdefGHI", ParamFactory::getValue(p, p->getType())); - EXPECT_EQ(9, p->getCount()); + EXPECT_EQ(9ul, p->getCount()); } { auto p = std::make_shared(); @@ -1478,12 +1551,31 @@ TEST(EthereumAbi, ParamFactorySetGetValue) { TEST(EthereumAbi, ParamFactoryGetValue) { const std::vector types = { - "uint8", "uint16", "uint32", "uint64", "uint128", "uint168", "uint256", - "int8", "int16", "int32", "int64", "int128", "int168", "int256", - "bool", "string", "bytes", "bytes168", "address", - "uint8[]", "address[]", "bool[]", "bytes[]", + "uint8", + "uint16", + "uint32", + "uint64", + "uint128", + "uint168", + "uint256", + "int8", + "int16", + "int32", + "int64", + "int128", + "int168", + "int256", + "bool", + "string", + "bytes", + "bytes168", + "address", + "uint8[]", + "address[]", + "bool[]", + "bytes[]", }; - for (auto t: types) { + for (auto t : types) { std::shared_ptr p = ParamFactory::make(t); EXPECT_EQ(t, p->getType()); @@ -1521,8 +1613,8 @@ TEST(EthereumAbi, ParamSetMethods) { { auto p = ParamSet(std::vector>{ std::make_shared(16u), - std::make_shared(true) }); - EXPECT_EQ(p.getCount(), 2); + std::make_shared(true)}); + EXPECT_EQ(p.getCount(), 2ul); EXPECT_EQ(p.addParam(std::shared_ptr(nullptr)), -1); std::shared_ptr getparam; @@ -1544,9 +1636,11 @@ TEST(EthereumAbi, ParamSetMethods) { TEST(EthereumAbi, ParametersMethods) { auto p = Parameters(std::vector>{ std::make_shared(16u), - std::make_shared(true) }); + std::make_shared(true)}); EXPECT_TRUE(p.isDynamic()); - EXPECT_EQ(p.getCount(), 2); + EXPECT_EQ(p.getCount(), 2ul); EXPECT_FALSE(p.setValueJson("value")); EXPECT_EQ(hex(p.hashStruct()), "755311b9e2cee471a91b161ccc5deed933d844b5af2b885543cc3c04eb640983"); } + +} // namespace TW::Ethereum::ABI::tests diff --git a/tests/chains/Ethereum/AddressTests.cpp b/tests/chains/Ethereum/AddressTests.cpp new file mode 100644 index 00000000000..2c4ca5745e7 --- /dev/null +++ b/tests/chains/Ethereum/AddressTests.cpp @@ -0,0 +1,59 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Ethereum/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" + +#include + +using namespace TW; + +namespace TW::Ethereum::tests { + +TEST(EthereumAddress, Invalid) { + ASSERT_FALSE(Address::isValid("abc")); + ASSERT_FALSE(Address::isValid("aaeb60f3e94c9b9a09f33669435e7ef1beaed")); + ASSERT_FALSE(Address::isValid("fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")); +} + +TEST(EthereumAddress, EIP55) { + ASSERT_EQ( + Address(parse_hex("5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")).string(), + "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); + ASSERT_EQ( + Address(parse_hex("0x5AAEB6053F3E94C9b9A09f33669435E7Ef1BEAED")).string(), + "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); + ASSERT_EQ( + Address(parse_hex("0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359")).string(), + "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"); + ASSERT_EQ( + Address(parse_hex("0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB")).string(), + "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB"); + ASSERT_EQ( + Address(parse_hex("0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb")).string(), + "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb"); +} + +TEST(EthereumAddress, String) { + const auto address = Address("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); + ASSERT_EQ(address.string(), "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"); +} + +TEST(EthereumAddress, FromPrivateKey) { + const auto privateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); + const auto address = Address(publicKey); + + ASSERT_EQ(address.string(), "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309"); +} + +TEST(EthereumAddress, IsValid) { + ASSERT_FALSE(Address::isValid("abc")); + ASSERT_TRUE(Address::isValid("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")); +} + +} // namespace TW::Ethereum::tests diff --git a/tests/Ethereum/ContractCallTests.cpp b/tests/chains/Ethereum/ContractCallTests.cpp similarity index 94% rename from tests/Ethereum/ContractCallTests.cpp rename to tests/chains/Ethereum/ContractCallTests.cpp index f7e27e1e634..bf21bcd21cb 100644 --- a/tests/Ethereum/ContractCallTests.cpp +++ b/tests/chains/Ethereum/ContractCallTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,11 +10,10 @@ #include #include -using namespace TW; -using namespace TW::Ethereum::ABI; - extern std::string TESTS_ROOT; +namespace TW::Ethereum::ABI::tests { + static nlohmann::json load_json(std::string path) { std::ifstream stream(path); nlohmann::json json; @@ -23,7 +22,7 @@ static nlohmann::json load_json(std::string path) { } TEST(ContractCall, Approval) { - auto path = TESTS_ROOT + "/Ethereum/Data/erc20.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/erc20.json"; auto abi = load_json(path); auto call = parse_hex("095ea7b30000000000000000000000005aaeb6053f3e94c9b9a09f33669435e7ef1beaed" "0000000000000000000000000000000000000000000000000000000000000001"); @@ -36,7 +35,7 @@ TEST(ContractCall, Approval) { } TEST(ContractCall, UniswapSwapTokens) { - auto path = TESTS_ROOT + "/Ethereum/Data/uniswap_router_v2.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/uniswap_router_v2.json"; auto abi = load_json(path); // https://etherscan.io/tx/0x57a2414f3cd9ca373b7e663ae67ecf933e40cb77a6e4ed28e4e28b5aa0d8ec63 auto call = parse_hex( @@ -56,7 +55,7 @@ TEST(ContractCall, UniswapSwapTokens) { } TEST(ContractCall, KyberTrade) { - auto path = TESTS_ROOT + "/Ethereum/Data/kyber_proxy.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/kyber_proxy.json"; auto abi = load_json(path); // https://etherscan.io/tx/0x51ffab782b9a27d754389505d5a50db525c04c68142ce20512d579f10f9e13e4 @@ -78,7 +77,7 @@ TEST(ContractCall, KyberTrade) { } TEST(ContractCall, ApprovalForAll) { - auto path = TESTS_ROOT + "/Ethereum/Data/erc721.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/erc721.json"; auto abi = load_json(path); // https://etherscan.io/tx/0xc2744000a107aee4761cf8a638657f91c3003a54e2f1818c37d781be7e48187a @@ -93,7 +92,7 @@ TEST(ContractCall, ApprovalForAll) { } TEST(ContractCall, CustomCall) { - auto path = TESTS_ROOT + "/Ethereum/Data/custom.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/custom.json"; auto abi = load_json(path); auto call = parse_hex("ec37a4a000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000000000000067472757374790000000000000000000000000000000000000000000000000000"); @@ -107,7 +106,7 @@ TEST(ContractCall, CustomCall) { TEST(ContractCall, SetResolver) { auto call = parse_hex("0x1896f70ae71cd96d4ba1c4b512b0c5bee30d2b6becf61e574c32a17a67156fa9ed3c4c" "6f0000000000000000000000004976fb03c32e5b8cfe2b6ccb31c09ba78ebaba41"); - auto path = TESTS_ROOT + "/Ethereum/Data/ens.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/ens.json"; auto abi = load_json(path); auto decoded = decodeCall(call, abi); auto expected = @@ -121,7 +120,7 @@ TEST(ContractCall, RenewENS) { "0xacf1a84100000000000000000000000000000000000000000000000000000000000000400000000000000000" "000000000000000000000000000000000000000001e18558000000000000000000000000000000000000000000" "000000000000000000000a68657769676f76656e7300000000000000000000000000000000000000000000"); - auto path = TESTS_ROOT + "/Ethereum/Data/ens.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/ens.json"; auto abi = load_json(path); auto decoded = decodeCall(call, abi); auto expected = @@ -153,8 +152,8 @@ TEST(ContractCall, Multicall) { "000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000" "000000000000000000000000000000000014d30f834b53d8f7e851e87b90ffa65757a35b850500000000000000" "000000000000000000000000000000000000000000000000000000000000000000"); - ASSERT_EQ(4 + 928, call.size()); - auto path = TESTS_ROOT + "/Ethereum/Data/ens.json"; + ASSERT_EQ(4 + 928ul, call.size()); + auto path = TESTS_ROOT + "/chains/Ethereum/Data/ens.json"; auto abi = load_json(path); auto decoded = decodeCall(call, abi); auto expected = @@ -174,9 +173,8 @@ TEST(ContractCall, GetAmountsOut) { "0000000000000000000000000000000000000000000000000000000000000064" "0000000000000000000000000000000000000000000000000000000000000040" "0000000000000000000000000000000000000000000000000000000000000001" - "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077" - ); - auto path = TESTS_ROOT + "/Ethereum/Data/getAmountsOut.json"; + "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"); + auto path = TESTS_ROOT + "/chains/Ethereum/Data/getAmountsOut.json"; auto abi = load_json(path); auto decoded = decodeCall(call, abi); @@ -187,13 +185,12 @@ TEST(ContractCall, GetAmountsOut) { } TEST(ContractCall, 1inch) { - auto path = TESTS_ROOT + "/Ethereum/Data/1inch.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/1inch.json"; auto abi = load_json(path); // https://etherscan.io/tx/0xc2d113151124579c21332d4cc6ab2b7f61e81d62392ed8596174513cb47e35ba auto call = parse_hex( - "7c02520000000000000000000000000027239549dd40e1d60f5b80b0c4196923745b1fd2000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001800000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000027239549dd40e1d60f5b80b0c4196923745b1fd20000000000000000000000001611c227725c5e420ef058275ae772b41775e261000000000000000000000000000000000000000000000000000005d0fadb1c0000000000000000000000000000000000000000000000000000000005c31df1da000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002080000000000000000000000069d91b94f0aaf8e8a2586909fa77a5c2c89818d50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000104128acb080000000000000000000000001611c227725c5e420ef058275ae772b41775e2610000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000005d0fadb1c0000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000400000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000" - ); + "7c02520000000000000000000000000027239549dd40e1d60f5b80b0c4196923745b1fd2000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001800000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000027239549dd40e1d60f5b80b0c4196923745b1fd20000000000000000000000001611c227725c5e420ef058275ae772b41775e261000000000000000000000000000000000000000000000000000005d0fadb1c0000000000000000000000000000000000000000000000000000000005c31df1da000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002080000000000000000000000069d91b94f0aaf8e8a2586909fa77a5c2c89818d50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000104128acb080000000000000000000000001611c227725c5e420ef058275ae772b41775e2610000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000005d0fadb1c0000000000000000000000000000000000000000000000000000000001000276a400000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000400000000000000000000000002b591e99afe9f32eaa6214f7b7629768c40eeb39000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000"); auto decoded = decodeCall(call, abi); ASSERT_TRUE(decoded.has_value()); auto expected = @@ -202,7 +199,7 @@ TEST(ContractCall, 1inch) { } TEST(ContractCall, TupleNested) { - auto path = TESTS_ROOT + "/Ethereum/Data/tuple_nested.json"; + auto path = TESTS_ROOT + "/chains/Ethereum/Data/tuple_nested.json"; auto abi = load_json(path); auto call = parse_hex( @@ -212,11 +209,12 @@ TEST(ContractCall, TupleNested) { "0000000000000000000000000000000000000000000000000000000000000003" "0000000000000000000000000000000000000000000000000000000000000004" "0000000000000000000000000000000000000000000000000000000000000005" - "0000000000000000000000000000000000000000000000000000000000000001" - ); + "0000000000000000000000000000000000000000000000000000000000000001"); auto decoded = decodeCall(call, abi); ASSERT_TRUE(decoded.has_value()); auto expected = R"|({"function":"nested_tuple(uint16,(uint16,(uint16,uint64),uint32),bool)","inputs":[{"name":"param1","type":"uint16","value":"1"},{"components":[{"name":"param21","type":"uint16","value":"2"},{"components":[{"name":"param221","type":"uint16","value":"3"},{"name":"param222","type":"uint64","value":"4"}],"name":"param22","type":"tuple"},{"name":"param23","type":"uint32","value":"5"}],"name":"param2","type":"tuple"},{"name":"param3","type":"bool","value":true}]})|"; EXPECT_EQ(decoded.value(), expected); } + +} // namespace TW::Ethereum::ABI::tests diff --git a/tests/Ethereum/Data/1inch.json b/tests/chains/Ethereum/Data/1inch.json similarity index 100% rename from tests/Ethereum/Data/1inch.json rename to tests/chains/Ethereum/Data/1inch.json diff --git a/tests/Ethereum/Data/custom.json b/tests/chains/Ethereum/Data/custom.json similarity index 100% rename from tests/Ethereum/Data/custom.json rename to tests/chains/Ethereum/Data/custom.json diff --git a/tests/Ethereum/Data/eip712_cryptofights.json b/tests/chains/Ethereum/Data/eip712_cryptofights.json similarity index 100% rename from tests/Ethereum/Data/eip712_cryptofights.json rename to tests/chains/Ethereum/Data/eip712_cryptofights.json diff --git a/tests/chains/Ethereum/Data/eip712_emptyArray.json b/tests/chains/Ethereum/Data/eip712_emptyArray.json new file mode 100644 index 00000000000..676a776d76c --- /dev/null +++ b/tests/chains/Ethereum/Data/eip712_emptyArray.json @@ -0,0 +1,108 @@ +{ + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "version", + "type": "string" + } + ], + "Action": [ + { + "name": "action", + "type": "string" + }, + { + "name": "params", + "type": "string" + } + ], + "Cell": [ + { + "name": "capacity", + "type": "string" + }, + { + "name": "lock", + "type": "string" + }, + { + "name": "type", + "type": "string" + }, + { + "name": "data", + "type": "string" + }, + { + "name": "extraData", + "type": "string" + } + ], + "Transaction": [ + { + "name": "DAS_MESSAGE", + "type": "string" + }, + { + "name": "inputsCapacity", + "type": "string" + }, + { + "name": "outputsCapacity", + "type": "string" + }, + { + "name": "fee", + "type": "string" + }, + { + "name": "action", + "type": "Action" + }, + { + "name": "inputs", + "type": "Cell[]" + }, + { + "name": "outputs", + "type": "Cell[]" + }, + { + "name": "digest", + "type": "bytes32" + } + ] + }, + "primaryType": "Transaction", + "domain": { + "chainId": 1, + "name": "da.systems", + "verifyingContract": "0x0000000000000000000000000000000020210722", + "version": "1" + }, + "message": { + "DAS_MESSAGE": "TRANSFER FROM 0x54366bcd1e73baf55449377bd23123274803236e(906.74221046 CKB) TO ckt1qyqvsej8jggu4hmr45g4h8d9pfkpd0fayfksz44t9q(764.13228446 CKB), 0x54366bcd1e73baf55449377bd23123274803236e(142.609826 CKB)", + "inputsCapacity": "906.74221046 CKB", + "outputsCapacity": "906.74211046 CKB", + "fee": "0.0001 CKB", + "digest": "0x29cd28dbeb470adb17548563ceb4988953fec7b499e716c16381e5ae4b04021f", + "action": { + "action": "transfer", + "params": "0x00" + }, + "inputs": [], + "outputs": [] + } +} \ No newline at end of file diff --git a/tests/chains/Ethereum/Data/eip712_emptyString.json b/tests/chains/Ethereum/Data/eip712_emptyString.json new file mode 100644 index 00000000000..0f507959149 --- /dev/null +++ b/tests/chains/Ethereum/Data/eip712_emptyString.json @@ -0,0 +1,132 @@ + +{ + "types": { + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "address" + }, + { + "name": "version", + "type": "string" + } + ], + "Action": [ + { + "name": "action", + "type": "string" + }, + { + "name": "params", + "type": "string" + } + ], + "Cell": [ + { + "name": "capacity", + "type": "string" + }, + { + "name": "lock", + "type": "string" + }, + { + "name": "type", + "type": "string" + }, + { + "name": "data", + "type": "string" + }, + { + "name": "extraData", + "type": "string" + } + ], + "Transaction": [ + { + "name": "DAS_MESSAGE", + "type": "string" + }, + { + "name": "inputsCapacity", + "type": "string" + }, + { + "name": "outputsCapacity", + "type": "string" + }, + { + "name": "fee", + "type": "string" + }, + { + "name": "action", + "type": "Action" + }, + { + "name": "inputs", + "type": "Cell[]" + }, + { + "name": "outputs", + "type": "Cell[]" + }, + { + "name": "digest", + "type": "bytes32" + } + ] + }, + "primaryType": "Transaction", + "domain": { + "chainId": "1", + "name": "did.id", + "verifyingContract": "0x0000000000000000000000000000000020210722", + "version": "1" + }, + "message": { + "DAS_MESSAGE": "SELL specer.bit FOR 100000 CKB", + "inputsCapacity": "1216.9999 CKB", + "outputsCapacity": "1216.9998 CKB", + "fee": "0.0001 CKB", + "digest": "0x53a6c0f19ec281604607f5d6817e442082ad1882bef0df64d84d3810dae561eb", + "action": { + "action": "start_account_sale", + "params": "0x00" + }, + "inputs": [ + { + "capacity": "218 CKB", + "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", + "type": "account-cell-type,0x01,0x", + "data": "{ account: specer.bit, expired_at: 1670913958 }", + "extraData": "{ status: 0, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" + } + ], + "outputs": [ + { + "capacity": "218 CKB", + "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", + "type": "account-cell-type,0x01,0x", + "data": "{ account: specer.bit, expired_at: 1670913958 }", + "extraData": "{ status: 1, records_hash: 0x55478d76900611eb079b22088081124ed6c8bae21a05dd1a0d197efcc7c114ce }" + }, + { + "capacity": "201 CKB", + "lock": "das-lock,0x01,0x051c152f77f8efa9c7c6d181cc97ee67c165c506...", + "type": "account-sale-cell-type,0x01,0x", + "data": "0x1209460ef3cb5f1c68ed2c43a3e020eec2d9de6e...", + "extraData": "" + } + ] + } +} \ No newline at end of file diff --git a/tests/Ethereum/Data/eip712_rarible.json b/tests/chains/Ethereum/Data/eip712_rarible.json similarity index 100% rename from tests/Ethereum/Data/eip712_rarible.json rename to tests/chains/Ethereum/Data/eip712_rarible.json diff --git a/tests/Ethereum/Data/eip712_snapshot_v4.json b/tests/chains/Ethereum/Data/eip712_snapshot_v4.json similarity index 100% rename from tests/Ethereum/Data/eip712_snapshot_v4.json rename to tests/chains/Ethereum/Data/eip712_snapshot_v4.json diff --git a/tests/Ethereum/Data/eip712_walletconnect.json b/tests/chains/Ethereum/Data/eip712_walletconnect.json similarity index 100% rename from tests/Ethereum/Data/eip712_walletconnect.json rename to tests/chains/Ethereum/Data/eip712_walletconnect.json diff --git a/tests/Ethereum/Data/ens.json b/tests/chains/Ethereum/Data/ens.json similarity index 100% rename from tests/Ethereum/Data/ens.json rename to tests/chains/Ethereum/Data/ens.json diff --git a/tests/Ethereum/Data/erc20.json b/tests/chains/Ethereum/Data/erc20.json similarity index 100% rename from tests/Ethereum/Data/erc20.json rename to tests/chains/Ethereum/Data/erc20.json diff --git a/tests/Ethereum/Data/erc721.json b/tests/chains/Ethereum/Data/erc721.json similarity index 100% rename from tests/Ethereum/Data/erc721.json rename to tests/chains/Ethereum/Data/erc721.json diff --git a/tests/Ethereum/Data/eth_feeHistory.json b/tests/chains/Ethereum/Data/eth_feeHistory.json similarity index 100% rename from tests/Ethereum/Data/eth_feeHistory.json rename to tests/chains/Ethereum/Data/eth_feeHistory.json diff --git a/tests/Ethereum/Data/eth_feeHistory2.json b/tests/chains/Ethereum/Data/eth_feeHistory2.json similarity index 100% rename from tests/Ethereum/Data/eth_feeHistory2.json rename to tests/chains/Ethereum/Data/eth_feeHistory2.json diff --git a/tests/Ethereum/Data/eth_feeHistory3.json b/tests/chains/Ethereum/Data/eth_feeHistory3.json similarity index 100% rename from tests/Ethereum/Data/eth_feeHistory3.json rename to tests/chains/Ethereum/Data/eth_feeHistory3.json diff --git a/tests/Ethereum/Data/eth_feeHistory4.json b/tests/chains/Ethereum/Data/eth_feeHistory4.json similarity index 100% rename from tests/Ethereum/Data/eth_feeHistory4.json rename to tests/chains/Ethereum/Data/eth_feeHistory4.json diff --git a/tests/Ethereum/Data/getAmountsOut.json b/tests/chains/Ethereum/Data/getAmountsOut.json similarity index 100% rename from tests/Ethereum/Data/getAmountsOut.json rename to tests/chains/Ethereum/Data/getAmountsOut.json diff --git a/tests/Ethereum/Data/kyber_proxy.json b/tests/chains/Ethereum/Data/kyber_proxy.json similarity index 100% rename from tests/Ethereum/Data/kyber_proxy.json rename to tests/chains/Ethereum/Data/kyber_proxy.json diff --git a/tests/chains/Ethereum/Data/seaport_712.json b/tests/chains/Ethereum/Data/seaport_712.json new file mode 100644 index 00000000000..3a427db3584 --- /dev/null +++ b/tests/chains/Ethereum/Data/seaport_712.json @@ -0,0 +1,84 @@ +{ + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "OrderComponents": [ + { "name": "offerer", "type": "address" }, + { "name": "zone", "type": "address" }, + { "name": "offer", "type": "OfferItem[]" }, + { "name": "consideration", "type": "ConsiderationItem[]" }, + { "name": "orderType", "type": "uint8" }, + { "name": "startTime", "type": "uint256" }, + { "name": "endTime", "type": "uint256" }, + { "name": "zoneHash", "type": "bytes32" }, + { "name": "salt", "type": "uint256" }, + { "name": "conduitKey", "type": "bytes32" }, + { "name": "counter", "type": "uint256" } + ], + "OfferItem": [ + { "name": "itemType", "type": "uint8" }, + { "name": "token", "type": "address" }, + { "name": "identifierOrCriteria", "type": "uint256" }, + { "name": "startAmount", "type": "uint256" }, + { "name": "endAmount", "type": "uint256" } + ], + "ConsiderationItem": [ + { "name": "itemType", "type": "uint8" }, + { "name": "token", "type": "address" }, + { "name": "identifierOrCriteria", "type": "uint256" }, + { "name": "startAmount", "type": "uint256" }, + { "name": "endAmount", "type": "uint256" }, + { "name": "recipient", "type": "address" } + ] + }, + "primaryType": "OrderComponents", + "domain": { + "name": "Seaport", + "version": "1.1", + "chainId": "1", + "verifyingContract": "0x00000000006c3852cbEf3e08E8dF289169EdE581" + }, + "message": { + "offerer": "0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1", + "offer": [ + { + "itemType": "2", + "token": "0x3F53082981815Ed8142384EDB1311025cA750Ef1", + "identifierOrCriteria": "134", + "startAmount": "1", + "endAmount": "1" + } + ], + "orderType": "2", + "consideration": [ + { + "itemType": "0", + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "975000000000000000", + "endAmount": "975000000000000000", + "recipient": "0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1" + }, + { + "itemType": "0", + "token": "0x0000000000000000000000000000000000000000", + "identifierOrCriteria": "0", + "startAmount": "25000000000000000", + "endAmount": "25000000000000000", + "recipient": "0x8De9C5A032463C561423387a9648c5C7BCC5BC90" + } + ], + "startTime": "1655450129", + "endTime": "1658042129", + "zone": "0x004C00500000aD104D7DBd00e3ae0A5C00560C00", + "zoneHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "salt": "795459960395409", + "conduitKey": "0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000", + "totalOriginalConsiderationItems": "2", + "counter": "0" + } +} diff --git a/tests/Ethereum/Data/tuple_nested.json b/tests/chains/Ethereum/Data/tuple_nested.json similarity index 100% rename from tests/Ethereum/Data/tuple_nested.json rename to tests/chains/Ethereum/Data/tuple_nested.json diff --git a/tests/Ethereum/Data/uniswap_router_v2.json b/tests/chains/Ethereum/Data/uniswap_router_v2.json similarity index 100% rename from tests/Ethereum/Data/uniswap_router_v2.json rename to tests/chains/Ethereum/Data/uniswap_router_v2.json diff --git a/tests/Ethereum/Data/zilliqa_data_tx.json b/tests/chains/Ethereum/Data/zilliqa_data_tx.json similarity index 100% rename from tests/Ethereum/Data/zilliqa_data_tx.json rename to tests/chains/Ethereum/Data/zilliqa_data_tx.json diff --git a/tests/chains/Ethereum/RLPTests.cpp b/tests/chains/Ethereum/RLPTests.cpp new file mode 100644 index 00000000000..52b01b75de4 --- /dev/null +++ b/tests/chains/Ethereum/RLPTests.cpp @@ -0,0 +1,306 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Ethereum/RLP.h" +#include "HexCoding.h" + +#include + +namespace TW::Ethereum::tests { + +using boost::multiprecision::uint256_t; + +std::string stringifyItems(const RLP::DecodedItem& di); + +std::string stringifyData(const Data& data) { + if (data.size() == 0) + return "0"; + // try if only letters + bool isLettersOnly = true; + for (auto i : data) { + if (!((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || i == ' ' || i == ',')) { + isLettersOnly = false; + break; + } + } + if (isLettersOnly) + return std::string("'") + std::string(data.begin(), data.end()) + "'"; + // try if it can be parsed (recursive) + if (data.size() >= 2) { + try { + const auto di = RLP::decode(data); + if (di.decoded.size() > 0 && di.remainder.size() == 0) { + return stringifyItems(di); + } + } catch (...) { + } + } + // any other: as hex string + return hex(data); +} + +std::string stringifyItems(const RLP::DecodedItem& di) { + const auto n = di.decoded.size(); + if (n == 0) { + return "-"; + } + if (n == 1) { + return stringifyData(di.decoded[0]); + } + std::string res = "(" + std::to_string(n) + ": "; + int count = 0; + for (auto i : di.decoded) { + if (count++) + res += " "; + res += stringifyData(i); + } + res += ")"; + return res; +} + +std::string decodeHelper(const std::string& hexData) { + const auto data = parse_hex(hexData); + const auto di = RLP::decode(data); + return stringifyItems(di); +} + +TEST(RLP, EncodeString) { + EXPECT_EQ(hex(RLP::encode("")), "80"); + EXPECT_EQ(hex(RLP::encode("d")), "64"); + EXPECT_EQ(hex(RLP::encode("dog")), "83646f67"); +} + +TEST(RLP, EncodeInteger) { + EXPECT_EQ(hex(RLP::encode(0)), "80"); + EXPECT_EQ(hex(RLP::encode(127)), "7f"); + EXPECT_EQ(hex(RLP::encode(128)), "8180"); + EXPECT_EQ(hex(RLP::encode(255)), "81ff"); + EXPECT_EQ(hex(RLP::encode(256)), "820100"); + EXPECT_EQ(hex(RLP::encode(1024)), "820400"); + EXPECT_EQ(hex(RLP::encode(0xffff)), "82ffff"); + EXPECT_EQ(hex(RLP::encode(0x010000)), "83010000"); + EXPECT_EQ(hex(RLP::encode(0xffffff)), "83ffffff"); + EXPECT_EQ(hex(RLP::encode(static_cast(0xffffffffULL))), "84ffffffff"); + EXPECT_EQ(hex(RLP::encode(static_cast(0xffffffffffffffULL))), "87ffffffffffffff"); +} + +TEST(RLP, EncodeUInt256) { + EXPECT_EQ(hex(RLP::encode(uint256_t(0))), "80"); + EXPECT_EQ(hex(RLP::encode(uint256_t(1))), "01"); + EXPECT_EQ(hex(RLP::encode(uint256_t(127))), "7f"); + EXPECT_EQ(hex(RLP::encode(uint256_t(128))), "8180"); + EXPECT_EQ(hex(RLP::encode(uint256_t(256))), "820100"); + EXPECT_EQ(hex(RLP::encode(uint256_t(1024))), "820400"); + EXPECT_EQ(hex(RLP::encode(uint256_t(0xffffff))), "83ffffff"); + EXPECT_EQ(hex(RLP::encode(uint256_t(0xffffffffULL))), "84ffffffff"); + EXPECT_EQ(hex(RLP::encode(uint256_t(0xffffffffffffffULL))), "87ffffffffffffff"); + EXPECT_EQ( + hex(RLP::encode(uint256_t("0x102030405060708090a0b0c0d0e0f2"))), + "8f102030405060708090a0b0c0d0e0f2"); + EXPECT_EQ( + hex(RLP::encode(uint256_t("0x0100020003000400050006000700080009000a000b000c000d000e01"))), + "9c0100020003000400050006000700080009000a000b000c000d000e01"); + EXPECT_EQ( + hex(RLP::encode(uint256_t("0x0100000000000000000000000000000000000000000000000000000000000000"))), + "a00100000000000000000000000000000000000000000000000000000000000000"); +} + +TEST(RLP, EncodeList) { + EXPECT_EQ(hex(RLP::encodeList(std::vector())), "c0"); + EXPECT_EQ(hex(RLP::encodeList(std::vector{1, 2, 3})), "c3010203"); + EXPECT_EQ(hex(RLP::encodeList(std::vector{"a", "b"})), "c26162"); + EXPECT_EQ(hex(RLP::encodeList(std::vector{"cat", "dog"})), "c88363617483646f67"); + { + const auto encoded = RLP::encodeList(std::vector(1024)); + EXPECT_EQ(hex(subData(encoded, 0, 20)), "f904008080808080808080808080808080808080"); + } +} + +TEST(RLP, EncodeListNested) { + const auto l11 = RLP::encodeList(std::vector{1, 2, 3}); + const auto l12 = RLP::encodeList(std::vector{"apple", "banana", "cherry"}); + const auto l21 = RLP::encodeList(std::vector{parse_hex("abcdef"), parse_hex("00010203040506070809")}); + const auto l22 = RLP::encodeList(std::vector{"bitcoin", "beeenbee", "eth"}); + const auto l1 = RLP::encodeList(std::vector{l11, l12}); + const auto l2 = RLP::encodeList(std::vector{l21, l22}); + const auto encoded = RLP::encodeList(std::vector{l1, l2}); + EXPECT_EQ(hex(encoded), "f8479cdb84c301020395d4856170706c658662616e616e6186636865727279a9e890cf83abcdef8a0001020304050607080996d587626974636f696e88626565656e62656583657468"); +} + +TEST(RLP, EncodeInvalid) { + ASSERT_TRUE(RLP::encode(-1).empty()); + ASSERT_TRUE(RLP::encodeList(std::vector{0, -1}).empty()); +} + +TEST(RLP, DecodeInteger) { + EXPECT_EQ(decodeHelper("00"), "00"); // not the primary encoding for 0 + EXPECT_EQ(decodeHelper("01"), "01"); + EXPECT_EQ(decodeHelper("09"), "09"); + EXPECT_EQ(decodeHelper("7f"), "7f"); + EXPECT_EQ(decodeHelper("80"), "0"); + EXPECT_EQ(decodeHelper("8180"), "80"); + EXPECT_EQ(decodeHelper("81ff"), "ff"); + EXPECT_EQ(decodeHelper("820100"), "0100"); + EXPECT_EQ(decodeHelper("820400"), "0400"); + EXPECT_EQ(decodeHelper("82ffff"), "ffff"); + EXPECT_EQ(decodeHelper("83010000"), "010000"); + EXPECT_EQ(decodeHelper("83ffffff"), "ffffff"); + EXPECT_EQ(decodeHelper("84ffffffff"), "ffffffff"); + EXPECT_EQ(decodeHelper("87ffffffffffffff"), "ffffffffffffff"); +} + +TEST(RLP, DecodeString) { + EXPECT_EQ(decodeHelper("80"), "0"); + EXPECT_EQ(decodeHelper("64"), "'d'"); + EXPECT_EQ(decodeHelper("83646f67"), "'dog'"); + EXPECT_EQ(decodeHelper("83636174"), "'cat'"); + EXPECT_EQ(decodeHelper("8f102030405060708090a0b0c0d0e0f2"), "102030405060708090a0b0c0d0e0f2"); + EXPECT_EQ(decodeHelper("9c0100020003000400050006000700080009000a000b000c000d000e01"), "0100020003000400050006000700080009000a000b000c000d000e01"); + EXPECT_EQ(decodeHelper("a00100000000000000000000000000000000000000000000000000000000000000"), "0100000000000000000000000000000000000000000000000000000000000000"); + // long string + EXPECT_EQ(decodeHelper("b87674686973206973206120612076657279206c6f6e6720737472696e672c2074686973206973206120612076657279206c6f6e6720737472696e672c2074686973206973206120612076657279206c6f6e6720737472696e672c2074686973206973206120612076657279206c6f6e6720737472696e67"), + "'this is a a very long string, this is a a very long string, this is a a very long string, this is a a very long string'"); +} + +TEST(RLP, DecodeList) { + // empty list + EXPECT_EQ(decodeHelper("c0"), "-"); + // short list + EXPECT_EQ(decodeHelper("c3010203"), "(3: 01 02 03)"); + EXPECT_EQ(decodeHelper("c26162"), "(2: 'a' 'b')"); + EXPECT_EQ(decodeHelper("c88363617483646f67"), "(2: 'cat' 'dog')"); + + // long list, raw ether transfer tx + EXPECT_EQ(decodeHelper("f86b81a985051f4d5ce982520894515778891c99e3d2e7ae489980cb7c77b37b5e76861b48eb57e0008025a0ad01c32a7c974df9d0bd48c8d7e0ecab62e90811917aa7dc0c966751a0c3f475a00dc77d9ec68484481bdf87faac14378f4f18d477f84c0810d29480372c1bbc65"), + "(9: " + "a9 " // nonce + "051f4d5ce9 " // gas price + "5208 " // gas limit + "515778891c99e3d2e7ae489980cb7c77b37b5e76 " // to + "1b48eb57e000 " // amount + "0 " // data + "25 " // v + "ad01c32a7c974df9d0bd48c8d7e0ecab62e90811917aa7dc0c966751a0c3f475 " // r + "0dc77d9ec68484481bdf87faac14378f4f18d477f84c0810d29480372c1bbc65" // s + ")"); + + // long list, raw token transfer tx + EXPECT_EQ(decodeHelper("f8aa81d485077359400082db9194dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000c6b6b55c8c4971145a842cc4e5db92d879d0b3e00000000000000000000000000000000000000000000000000000000002faf0801ca02843d8ed66b9623392dc336dd36d5dd5a630b2019962869b6e50fdb4ecb5b6aca05d9ea377bc65e2921f7fc257de8135530cc74e3188b6ba57a4b9cb284393050a"), + "(9: " + "d4 " + "0773594000 " + "db91 " + "dac17f958d2ee523a2206206994597c13d831ec7 " + "0 " + "a9059cbb000000000000000000000000c6b6b55c8c4971145a842cc4e5db92d879d0b3e00000000000000000000000000000000000000000000000000000000002faf080 " + "1c " + "2843d8ed66b9623392dc336dd36d5dd5a630b2019962869b6e50fdb4ecb5b6ac " + "5d9ea377bc65e2921f7fc257de8135530cc74e3188b6ba57a4b9cb284393050a" + ")"); + + { + // long list, with 2-byte size + const std::string elem = "0123"; + const std::size_t n = 500; + std::vector longarr; + for (auto i = 0ul; i < n; ++i) + longarr.push_back(elem); + + const Data encoded = RLP::encodeList(longarr); + ASSERT_EQ(hex(subData(encoded, 0, 20)), "f909c48430313233843031323384303132338430"); + + auto decoded = RLP::decode(encoded); + ASSERT_EQ(decoded.decoded.size(), n); + for (int i = 0; i < 20; i++) { + EXPECT_EQ(hex(decoded.decoded[i]), "30313233"); + } + } + { + // long list, with 3-byte size + const std::string elem = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; + const std::size_t n = 650; + std::vector longarr; + for (auto i = 0ul; i < n; ++i) + longarr.push_back(elem); + + const Data encoded = RLP::encodeList(longarr); + ASSERT_EQ(encoded.size(), 66304ul); + ASSERT_EQ(hex(subData(encoded, 0, 30)), "fa0102fcb864303132333435363738393031323334353637383930313233"); + + auto decoded = RLP::decode(encoded); + ASSERT_EQ(decoded.decoded.size(), n); + } + + // nested list + EXPECT_EQ(decodeHelper("f8479cdb84c301020395d4856170706c658662616e616e6186636865727279a9e890cf83abcdef8a0001020304050607080996d587626974636f696e88626565656e62656583657468"), + "(2: (2: (3: 01 02 03) (3: 'apple' 'banana' 'cherry')) (2: (2: abcdef 00010203040506070809) (3: 'bitcoin' 'beeenbee' 'eth')))"); +} + +TEST(RLP, DecodeInvalid) { + // decode empty data + EXPECT_THROW(RLP::decode(Data()), std::invalid_argument); + + // incorrect length + EXPECT_THROW(RLP::decode(parse_hex("0x81636174")), std::invalid_argument); + EXPECT_THROW(RLP::decode(parse_hex("0xb9ffff")), std::invalid_argument); + EXPECT_THROW(RLP::decode(parse_hex("0xc883636174")), std::invalid_argument); + + // some tests are from https://github.com/ethereum/tests/blob/develop/RLPTests/invalidRLPTest.json + // int32 overflow + EXPECT_THROW(RLP::decode(parse_hex("0xbf0f000000000000021111")), std::invalid_argument); + + // wrong size list + EXPECT_THROW(RLP::decode(parse_hex("0xf80180")), std::invalid_argument); + + // bytes should be single byte + EXPECT_THROW(RLP::decode(parse_hex("0x8100")), std::invalid_argument); + + // leading zeros in long length list + EXPECT_THROW(RLP::decode(parse_hex("fb00000040000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f")), std::invalid_argument); + EXPECT_THROW(RLP::decode(parse_hex("f800")), std::invalid_argument); +} + +TEST(RLP, putVarInt) { + EXPECT_EQ(hex(RLP::putVarInt(0)), "00"); + EXPECT_EQ(hex(RLP::putVarInt(1)), "01"); + EXPECT_EQ(hex(RLP::putVarInt(0x21)), "21"); + EXPECT_EQ(hex(RLP::putVarInt(0xff)), "ff"); + EXPECT_EQ(hex(RLP::putVarInt(0x100)), "0100"); + EXPECT_EQ(hex(RLP::putVarInt(0x4321)), "4321"); + EXPECT_EQ(hex(RLP::putVarInt(0x654321)), "654321"); + EXPECT_EQ(hex(RLP::putVarInt(0x87654321)), "87654321"); + EXPECT_EQ(hex(RLP::putVarInt(0xa987654321)), "a987654321"); + EXPECT_EQ(hex(RLP::putVarInt(0xcba987654321)), "cba987654321"); + EXPECT_EQ(hex(RLP::putVarInt(0xedcba987654321)), "edcba987654321"); + EXPECT_EQ(hex(RLP::putVarInt(0x21edcba987654321)), "21edcba987654321"); + EXPECT_EQ(hex(RLP::putVarInt(0xffffffffffffffff)), "ffffffffffffffff"); +} + +TEST(RLP, parseVarInt) { + EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("00"), 0))), "00"); + EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("01"), 0))), "01"); + EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("fc"), 0))), "fc"); + EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("ff"), 0))), "ff"); + EXPECT_EQ(hex(store(RLP::parseVarInt(1, parse_hex("abcd"), 1))), "cd"); + EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("0102"), 0))), "0102"); + EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("0100"), 0))), "0100"); + EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("fedc"), 0))), "fedc"); + EXPECT_EQ(hex(store(RLP::parseVarInt(2, parse_hex("ffff"), 0))), "ffff"); + EXPECT_EQ(hex(store(RLP::parseVarInt(3, parse_hex("010203"), 0))), "010203"); + EXPECT_EQ(hex(store(RLP::parseVarInt(4, parse_hex("01020304"), 0))), "01020304"); + EXPECT_EQ(hex(store(RLP::parseVarInt(5, parse_hex("0102030405"), 0))), "0102030405"); + EXPECT_EQ(hex(store(RLP::parseVarInt(6, parse_hex("010203040506"), 0))), "010203040506"); + EXPECT_EQ(hex(store(RLP::parseVarInt(7, parse_hex("01020304050607"), 0))), "01020304050607"); + EXPECT_EQ(hex(store(RLP::parseVarInt(8, parse_hex("0102030405060708"), 0))), "0102030405060708"); + EXPECT_EQ(hex(store(RLP::parseVarInt(8, parse_hex("abcd0102030405060708"), 2))), "0102030405060708"); + EXPECT_THROW(RLP::parseVarInt(0, parse_hex("01"), 0), std::invalid_argument); // wrong size + EXPECT_THROW(RLP::parseVarInt(9, parse_hex("010203040506070809"), 0), std::invalid_argument); // wrong size + EXPECT_THROW(RLP::parseVarInt(4, parse_hex("0102"), 0), std::invalid_argument); // too short + EXPECT_THROW(RLP::parseVarInt(4, parse_hex("01020304"), 2), std::invalid_argument); // too short + EXPECT_THROW(RLP::parseVarInt(2, parse_hex("0002"), 0), std::invalid_argument); // starts with 0 +} + +} // namespace TW::Ethereum::tests diff --git a/tests/Ethereum/SignerTests.cpp b/tests/chains/Ethereum/SignerTests.cpp similarity index 100% rename from tests/Ethereum/SignerTests.cpp rename to tests/chains/Ethereum/SignerTests.cpp diff --git a/tests/chains/Ethereum/TWAnySignerTests.cpp b/tests/chains/Ethereum/TWAnySignerTests.cpp new file mode 100644 index 00000000000..c50cef17583 --- /dev/null +++ b/tests/chains/Ethereum/TWAnySignerTests.cpp @@ -0,0 +1,488 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include "HexCoding.h" +#include "uint256.h" +#include "proto/Ethereum.pb.h" +#include "Ethereum/ABI/Function.h" +#include "Ethereum/ABI/ParamBase.h" +#include "Ethereum/ABI/ParamAddress.h" + +#include + +namespace TW::Ethereum { + +TEST(TWEthereumSigner, EmptyValue) { + auto str = std::string(""); + uint256_t zero = load(str); + + ASSERT_EQ(zero, uint256_t(0)); +} + +TEST(TWEthereumSigner, BigInt) { + // Check uint256_t loading + Data expectedData = {0x52, 0x08}; + auto value = uint256_t(21000); + auto loaded = load(expectedData); + ASSERT_EQ(loaded, value); + + // Check proto storing + Proto::SigningInput input; + auto storedData = store(value); + input.set_gas_limit(storedData.data(), storedData.size()); + ASSERT_EQ(hex(input.gas_limit()), hex(expectedData)); + + // Check proto loading + auto protoLoaded = load(input.gas_limit()); + ASSERT_EQ(protoLoaded, value); +} + +TEST(TWAnySignerEthereum, Sign) { + // from http://thetokenfactory.com/#/factory + // https://etherscan.io/tx/0x63879f20909a315bcffe629bc03b20e5bc65ba2a377bd7152e3b69c4bd4cd6cc + Proto::SigningInput input; + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(11)); + auto gasPrice = store(uint256_t(20000000000)); + auto gasLimit = store(uint256_t(1000000)); + auto data = parse_hex("0x60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161094b38038061094b83398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b5050505050506106da806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f357806318160ddd1461016857806323b872dd14610171578063313ce5671461025c57806354fd4d501461026857806370a08231146102c657806395d89b41146102f4578063a9059cbb14610352578063cae9ca51146103f7578063dd62ed3e146105be575b6105f2610002565b6040805160038054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6102e260025481565b610662600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101c4575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101d05750600082115b156106bf57600160a060020a0383811660008181526020818152604080832080548801905588851680845281842080548990039055600183528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016106c3565b61067660045460ff1681565b6040805160068054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b6105f46005805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03166000908152602081905260408120548290108015906103845750600082115b156106ca5733600160a060020a0390811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610162565b604080516020604435600481810135601f810184900484028501840190955284845261066294813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a03908116600081815260016020908152604080832094881680845294825280832087905580518781529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a383600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105965780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f19250505015156106d257610002565b6102e2600435602435600160a060020a03828116600090815260016020908152604080832093851683529290522054610162565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080519115158252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b505050505081565b5060005b9392505050565b506000610162565b5060016106c35600000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000754204275636b73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003544f540000000000000000000000000000000000000000000000000000000000"); + auto key = parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + // tx_mode not set, Legacy is the default + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_private_key(key.data(), key.size()); + auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); + transfer.set_data(data.data(), data.size()); + + std::string expected = "f90a9e0b8504a817c800830f42408080b90a4b60a060405260046060527f48302e31000000000000000000000000000000000000000000000000000000006080526006805460008290527f48302e310000000000000000000000000000000000000000000000000000000882556100b5907ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f602060026001841615610100026000190190931692909204601f01919091048101905b8082111561017957600081556001016100a1565b505060405161094b38038061094b83398101604052808051906020019091908051820191906020018051906020019091908051820191906020015050836000600050600033600160a060020a0316815260200190815260200160002060005081905550836002600050819055508260036000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017d57805160ff19168380011785555b506101ad9291506100a1565b5090565b8280016001018555821561016d579182015b8281111561016d57825182600050559160200191906001019061018f565b50506004805460ff19168317905560058054825160008390527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0602060026001851615610100026000190190941693909304601f90810184900482019386019083901061022d57805160ff19168380011785555b5061025d9291506100a1565b82800160010185558215610221579182015b8281111561022157825182600050559160200191906001019061023f565b5050505050506106da806102716000396000f36060604052361561008d5760e060020a600035046306fdde038114610095578063095ea7b3146100f357806318160ddd1461016857806323b872dd14610171578063313ce5671461025c57806354fd4d501461026857806370a08231146102c657806395d89b41146102f4578063a9059cbb14610352578063cae9ca51146103f7578063dd62ed3e146105be575b6105f2610002565b6040805160038054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03908116600081815260016020908152604080832094871680845294825280832086905580518681529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a35060015b92915050565b6102e260025481565b610662600435602435604435600160a060020a0383166000908152602081905260408120548290108015906101c4575060016020908152604080832033600160a060020a03168452909152812054829010155b80156101d05750600082115b156106bf57600160a060020a0383811660008181526020818152604080832080548801905588851680845281842080548990039055600183528184203390961684529482529182902080548790039055815186815291519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060016106c3565b61067660045460ff1681565b6040805160068054602060026001831615610100026000190190921691909104601f81018290048202840182019094528383526105f493908301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b600160a060020a03600435166000908152602081905260409020545b60408051918252519081900360200190f35b6105f46005805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156106b75780601f1061068c576101008083540402835291602001916106b7565b61066260043560243533600160a060020a03166000908152602081905260408120548290108015906103845750600082115b156106ca5733600160a060020a0390811660008181526020818152604080832080548890039055938716808352918490208054870190558351868152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a3506001610162565b604080516020604435600481810135601f810184900484028501840190955284845261066294813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a03908116600081815260016020908152604080832094881680845294825280832087905580518781529051929493927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925929181900390910190a383600160a060020a031660405180807f72656365697665417070726f76616c28616464726573732c75696e743235362c81526020017f616464726573732c627974657329000000000000000000000000000000000000815260200150602e019050604051809103902060e060020a9004338530866040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a031681526020018280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156105965780820380516001836020036101000a031916815260200191505b509450505050506000604051808303816000876161da5a03f19250505015156106d257610002565b6102e2600435602435600160a060020a03828116600090815260016020908152604080832093851683529290522054610162565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156106545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b604080519115158252519081900360200190f35b6040805160ff9092168252519081900360200190f35b820191906000526020600020905b81548152906001019060200180831161069a57829003601f168201915b505050505081565b5060005b9392505050565b506000610162565b5060016106c35600000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000754204275636b73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003544f54000000000000000000000000000000000000000000000000000000000026a042556c4f2a3f4e4e639cca524d1da70e60881417d4643e5382ed110a52719eafa0172f591a2a763d0bd6b13d042d8c5eb66e87f129c9dc77ada66b6041012db2b3"; + + { + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), expected); + ASSERT_EQ(hex(output.data()), hex(data)); + } +} + +TEST(TWAnySignerEthereum, SignERC20TransferAsERC20) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasPrice = store(uint256_t(42000000000)); // 0x09c7652400 + auto gasLimit = store(uint256_t(78009)); // 130B9 + auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; + auto token = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI + auto amount = uint256_t(2000000000000000000); + auto amountData = store(amount); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + // tx_mode not set, Legacy is the default + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_to_address(token); + input.set_private_key(key.data(), key.size()); + auto& erc20 = *input.mutable_transaction()->mutable_erc20_transfer(); + erc20.set_to(toAddress); + erc20.set_amount(amountData.data(), amountData.size()); + + // https://etherscan.io/tx/0x199a7829fc5149e49b452c2cab76d8fa5a9682fee6e4891b8acb697ac142513e + std::string expected = "f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98cea0032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931"; + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), expected); + + // expected payload + Data payload; + { + auto func = ABI::Function("transfer", std::vector>{ + std::make_shared(parse_hex(toAddress)), + std::make_shared(amount)}); + func.encode(payload); + } + ASSERT_EQ(hex(output.data()), hex(payload)); + ASSERT_EQ(hex(output.data()), "a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000"); +} + +TEST(TWAnySignerEthereum, SignERC20TransferAsGenericContract) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasPrice = store(uint256_t(42000000000)); // 0x09c7652400 + auto gasLimit = store(uint256_t(78009)); // 130B9 + auto toAddress = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI + // payload: transfer(0x5322b34c88ed0691971bf52a7047448f0f4efc84, 2000000000000000000) + auto data = parse_hex("0xa9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000"); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + // tx_mode not set, Legacy is the default + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_to_address(toAddress); + input.set_private_key(key.data(), key.size()); + auto& transfer = *input.mutable_transaction()->mutable_contract_generic(); + transfer.set_data(data.data(), data.size()); + + // https://etherscan.io/tx/0x199a7829fc5149e49b452c2cab76d8fa5a9682fee6e4891b8acb697ac142513e + std::string expected = "f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0724c62ad4fbf47346b02de06e603e013f26f26b56fdc0be7ba3d6273401d98cea0032131cae15da7ddcda66963e8bef51ca0d9962bfef0547d3f02597a4a58c931"; + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), expected); + ASSERT_EQ(hex(output.data()), hex(data)); +} + +TEST(TWAnySignerEthereum, SignERC20TransferInvalidAddress) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasPrice = store(uint256_t(42000000000)); // 0x09c7652400 + auto gasLimit = store(uint256_t(78009)); // 130B9 + auto invalidAddress = "0xdeadbeef"; + auto amount = store(uint256_t(2000000000000000000)); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + // tx_mode not set, Legacy is the default + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_to_address(invalidAddress); + input.set_private_key(key.data(), key.size()); + auto& erc20 = *input.mutable_transaction()->mutable_erc20_transfer(); + erc20.set_to(invalidAddress); + erc20.set_amount(amount.data(), amount.size()); + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), ""); +} + +TEST(TWAnySignerEthereum, SignERC20Approve) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasPrice = store(uint256_t(42000000000)); // 0x09c7652400 + auto gasLimit = store(uint256_t(78009)); // 130B9 + auto spenderAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; + auto token = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI + auto amount = store(uint256_t(2000000000000000000)); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + // tx_mode not set, Legacy is the default + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_to_address(token); + input.set_private_key(key.data(), key.size()); + auto& erc20 = *input.mutable_transaction()->mutable_erc20_approve(); + erc20.set_spender(spenderAddress); + erc20.set_amount(amount.data(), amount.size()); + + std::string expected = "f8aa808509c7652400830130b9946b175474e89094c44da98b954eedeac495271d0f80b844095ea7b30000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec8000025a0d8136d66da1e0ba8c7208d5c4f143167f54b89a0fe2e23440653bcca28b34dc1a049222a79339f1a9e4641cb4ad805c49c225ae704299ffc10627bf41c035c464a"; + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), expected); +} + +TEST(TWAnySignerEthereum, SignERC721Transfer) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasPrice = store(uint256_t(42000000000)); + auto gasLimit = store(uint256_t(78009)); + auto tokenContract = "0x4e45e92ed38f885d39a733c14f1817217a89d425"; + auto fromAddress = "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc"; + auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; + auto tokenId = parse_hex("23c47ee5"); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + // tx_mode not set, Legacy is the default + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_to_address(tokenContract); + input.set_private_key(key.data(), key.size()); + auto& erc721 = *input.mutable_transaction()->mutable_erc721_transfer(); + erc721.set_from(fromAddress); + erc721.set_to(toAddress); + erc721.set_token_id(tokenId.data(), tokenId.size()); + + std::string expected = "f8ca808509c7652400830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b86423b872dd000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee526a0d38440a4dc140a4100d301eb49fcc35b64439e27d1d8dd9b55823dca04e6e659a03b5f56a57feabc3406f123d6f8198cd7d7e2ced7e2d58d375f076952ecd9ce88"; + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), expected); + ASSERT_EQ(hex(output.data()), "23b872dd000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee5"); +} + +TEST(TWAnySignerEthereum, SignERC1155Transfer) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasPrice = store(uint256_t(42000000000)); + auto gasLimit = store(uint256_t(78009)); + auto tokenContract = "0x4e45e92ed38f885d39a733c14f1817217a89d425"; + auto fromAddress = "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc"; + auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; + auto tokenId = parse_hex("23c47ee5"); + auto value = uint256_t(2000000000000000000); + auto valueData = store(value); + auto data = parse_hex("01020304"); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + // tx_mode not set, Legacy is the default + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_to_address(tokenContract); + input.set_private_key(key.data(), key.size()); + auto& erc1155 = *input.mutable_transaction()->mutable_erc1155_transfer(); + erc1155.set_from(fromAddress); + erc1155.set_to(toAddress); + erc1155.set_token_id(tokenId.data(), tokenId.size()); + erc1155.set_value(valueData.data(), valueData.size()); + erc1155.set_data(data.data(), data.size()); + + std::string expected = "f9014a808509c7652400830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b8e4f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000004010203040000000000000000000000000000000000000000000000000000000026a010315488201ac801ce346bffd1570de147615462d7e7db3cf08cf558465c6b79a06643943b24593bc3904a9fda63bb169881730994c973ab80f07d66a698064573"; + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), expected); + + // expected payload + Data payload; + { + auto func = ABI::Function("safeTransferFrom", std::vector>{ + std::make_shared(parse_hex(fromAddress)), + std::make_shared(parse_hex(toAddress)), + std::make_shared(uint256_t(0x23c47ee5)), + std::make_shared(value), + std::make_shared(data)}); + func.encode(payload); + } + ASSERT_EQ(hex(output.data()), hex(payload)); + ASSERT_EQ(hex(output.data()), "f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000"); +} + +TEST(TWAnySignerEthereum, SignJSON) { + auto json = STRING(R"({"chainId":"AQ==","gasPrice":"1pOkAA==","gasLimit":"Ugg=","toAddress":"0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1","transaction":{"transfer":{"amount":"A0i8paFgAA=="}}})"); + auto key = DATA("17209af590a86462395d5881e60d11c7fa7d482cfb02b5a01b93c2eeef243543"); + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeEthereum)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeEthereum)); + assertStringsEqual(result, "f86a8084d693a400825208947d8bf18c7ce84b3e175b339c4ca93aed1dd166f1870348bca5a160008025a0fe5802b49e04c6b1705088310e133605ed8b549811a18968ad409ea02ad79f21a05bf845646fb1e1b9365f63a7fd5eb5e984094e3ed35c3bed7361aebbcbf41f10"); +} + +TEST(TWAnySignerEthereum, PlanNotSupported) { + // Ethereum does not use plan(), call it nonetheless + Proto::SigningInput input; + auto inputData = input.SerializeAsString(); + auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t*)inputData.data(), inputData.size())); + auto outputTWData = WRAPD(TWAnySignerPlan(inputTWData.get(), TWCoinTypeEthereum)); + EXPECT_EQ(TWDataSize(outputTWData.get()), 0ul); +} + +TEST(TWAnySignerEthereum, SignERC1559Transfer_1442) { + auto chainId = store(uint256_t(3)); + auto nonce = store(uint256_t(6)); + auto gasLimit = store(uint256_t(21100)); + auto maxInclusionFeePerGas = store(uint256_t(2000000000)); + auto maxFeePerGas = store(uint256_t(3000000000)); + auto toAddress = "0xB9F5771C27664bF2282D98E09D7F50cEc7cB01a7"; + auto value = uint256_t(543210987654321); + auto valueData = store(value); + auto key = parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::Enveloped); // EIP1559 + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_to_address(toAddress); + input.set_private_key(key.data(), key.size()); + auto& transfer = *input.mutable_transaction()->mutable_transfer(); + transfer.set_amount(valueData.data(), valueData.size()); + transfer.set_data(Data().data(), 0); + + // https://ropsten.etherscan.io/tx/0x14429509307efebfdaa05227d84c147450d168c68539351fbc01ed87c916ab2e + std::string expected = "02f8710306847735940084b2d05e0082526c94b9f5771c27664bf2282d98e09d7f50cec7cb01a78701ee0c29f50cb180c080a092c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c64a06487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"; + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + EXPECT_EQ(hex(output.encoded()), expected); + EXPECT_EQ(hex(output.v()), "00"); + EXPECT_EQ(hex(output.r()), "92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c64"); + EXPECT_EQ(hex(output.s()), "6487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"); + EXPECT_EQ(hex(output.data()), ""); +} + +TEST(TWAnySignerEthereum, SignERC20Transfer_1559) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasLimit = store(uint256_t(78009)); // 130B9 + auto maxInclusionFeePerGas = store(uint256_t(2000000000)); // 77359400 + auto maxFeePerGas = store(uint256_t(3000000000)); // B2D05E00 + auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; + auto token = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI + auto amount = uint256_t(2000000000000000000); + auto amountData = store(amount); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::Enveloped); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_to_address(token); + input.set_private_key(key.data(), key.size()); + auto& erc20 = *input.mutable_transaction()->mutable_erc20_transfer(); + erc20.set_to(toAddress); + erc20.set_amount(amountData.data(), amountData.size()); + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), "02f8b00180847735940084b2d05e00830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000c080a0adfcfdf98d4ed35a8967a0c1d78b42adb7c5d831cf5a3272654ec8f8bcd7be2ea011641e065684f6aa476f4fd250aa46cd0b44eccdb0a6e1650d658d1998684cdf"); +} + +TEST(TWAnySignerEthereum, SignERC20Approve_1559) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasLimit = store(uint256_t(78009)); // 130B9 + auto maxInclusionFeePerGas = store(uint256_t(2000000000)); + auto maxFeePerGas = store(uint256_t(3000000000)); + auto spenderAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; + auto token = "0x6b175474e89094c44da98b954eedeac495271d0f"; // DAI + auto amount = store(uint256_t(2000000000000000000)); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::Enveloped); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_to_address(token); + input.set_private_key(key.data(), key.size()); + auto& erc20 = *input.mutable_transaction()->mutable_erc20_approve(); + erc20.set_spender(spenderAddress); + erc20.set_amount(amount.data(), amount.size()); + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), "02f8b00180847735940084b2d05e00830130b9946b175474e89094c44da98b954eedeac495271d0f80b844095ea7b30000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000c080a05a43dda3dc193480ee532a5ed67ba8fbd2e3afb9eee218f4fb955b415d592925a01300e5b5f51c8cd5bf80f018cea3fb347fae589e65355068ac44ffc996313c60"); +} + +TEST(TWAnySignerEthereum, SignERC721Transfer_1559) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasLimit = store(uint256_t(78009)); + auto maxInclusionFeePerGas = store(uint256_t(2000000000)); + auto maxFeePerGas = store(uint256_t(3000000000)); + auto tokenContract = "0x4e45e92ed38f885d39a733c14f1817217a89d425"; + auto fromAddress = "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc"; + auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; + auto tokenId = parse_hex("23c47ee5"); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::Enveloped); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_to_address(tokenContract); + input.set_private_key(key.data(), key.size()); + auto& erc721 = *input.mutable_transaction()->mutable_erc721_transfer(); + erc721.set_from(fromAddress); + erc721.set_to(toAddress); + erc721.set_token_id(tokenId.data(), tokenId.size()); + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), "02f8d00180847735940084b2d05e00830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b86423b872dd000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee5c080a0dbd591d1eac39bad62d7c158d5e1d55e7014d2218998f8980462e2f283f42d4aa05acadb904484a0fb5526a4c64b8addb8aac4f6548f90199e40eb787b79faed4a"); +} + +TEST(TWAnySignerEthereum, SignERC1155Transfer_1559) { + auto chainId = store(uint256_t(1)); + auto nonce = store(uint256_t(0)); + auto gasLimit = store(uint256_t(78009)); + auto maxInclusionFeePerGas = store(uint256_t(2000000000)); + auto maxFeePerGas = store(uint256_t(3000000000)); + auto tokenContract = "0x4e45e92ed38f885d39a733c14f1817217a89d425"; + auto fromAddress = "0x718046867b5b1782379a14eA4fc0c9b724DA94Fc"; + auto toAddress = "0x5322b34c88ed0691971bf52a7047448f0f4efc84"; + auto tokenId = parse_hex("23c47ee5"); + auto value = uint256_t(2000000000000000000); + auto valueData = store(value); + auto data = parse_hex("01020304"); + auto key = parse_hex("0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151"); + + Proto::SigningInput input; + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_tx_mode(Proto::TransactionMode::Enveloped); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_max_inclusion_fee_per_gas(maxInclusionFeePerGas.data(), maxInclusionFeePerGas.size()); + input.set_max_fee_per_gas(maxFeePerGas.data(), maxFeePerGas.size()); + input.set_to_address(tokenContract); + input.set_private_key(key.data(), key.size()); + auto& erc1155 = *input.mutable_transaction()->mutable_erc1155_transfer(); + erc1155.set_from(fromAddress); + erc1155.set_to(toAddress); + erc1155.set_token_id(tokenId.data(), tokenId.size()); + erc1155.set_value(valueData.data(), valueData.size()); + erc1155.set_data(data.data(), data.size()); + + // sign test + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEthereum); + + ASSERT_EQ(hex(output.encoded()), "02f901500180847735940084b2d05e00830130b9944e45e92ed38f885d39a733c14f1817217a89d42580b8e4f242432a000000000000000000000000718046867b5b1782379a14ea4fc0c9b724da94fc0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000000000000023c47ee50000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000040102030400000000000000000000000000000000000000000000000000000000c080a0533df41dda5540c57257b7fe89c29cefff0155c333e063220df2bf9680fcc15aa036a844fd20de5a51de96ceaaf078558e87d86426a4a5d4b215ee1fd0fa397f8a"); +} + +} // namespace TW::Ethereum diff --git a/tests/chains/Ethereum/TWCoinTypeTests.cpp b/tests/chains/Ethereum/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..39878ade96c --- /dev/null +++ b/tests/chains/Ethereum/TWCoinTypeTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWEthereumCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeEthereum)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x9edaf0f7d9c6629c31bbf0471fc07d696c73b566b93783f7e25d8d5d2b62fa4f")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeEthereum, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x5bb497e8d9fe26e92dd1be01e32076c8e024d167")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeEthereum, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeEthereum)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeEthereum)); + const auto chainId = WRAPS(TWCoinTypeChainId(TWCoinTypeEthereum)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeEthereum), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeEthereum)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeEthereum)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeEthereum)); + assertStringsEqual(symbol, "ETH"); + assertStringsEqual(txUrl, "https://etherscan.io/tx/0x9edaf0f7d9c6629c31bbf0471fc07d696c73b566b93783f7e25d8d5d2b62fa4f"); + assertStringsEqual(accUrl, "https://etherscan.io/address/0x5bb497e8d9fe26e92dd1be01e32076c8e024d167"); + assertStringsEqual(id, "ethereum"); + assertStringsEqual(name, "Ethereum"); + assertStringsEqual(chainId, "1"); +} diff --git a/tests/Ethereum/TWEthereumAbiTests.cpp b/tests/chains/Ethereum/TWEthereumAbiTests.cpp similarity index 97% rename from tests/Ethereum/TWEthereumAbiTests.cpp rename to tests/chains/Ethereum/TWEthereumAbiTests.cpp index f64d3886540..9cb63bd7569 100644 --- a/tests/Ethereum/TWEthereumAbiTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiTests.cpp @@ -13,7 +13,7 @@ #include "HexCoding.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include @@ -41,7 +41,7 @@ TEST(TWEthereumAbi, FuncCreate1) { EXPECT_EQ(0, p2index); // check back get value auto p2val2 = TWEthereumAbiFunctionGetParamUInt64(func, p2index, true); - EXPECT_EQ(9, p2val2); + EXPECT_EQ(9ul, p2val2); auto type = WRAPS(TWEthereumAbiFunctionGetType(func)); std::string type2 = TWStringUTF8Bytes(type.get()); @@ -187,7 +187,7 @@ TEST(TWEthereumAbi, EncodeFuncMonster) { // check back out params EXPECT_EQ(1, TWEthereumAbiFunctionGetParamUInt8(func, 0, false)); - EXPECT_EQ(4, TWEthereumAbiFunctionGetParamUInt64(func, 3, false)); + EXPECT_EQ(4ul, TWEthereumAbiFunctionGetParamUInt64(func, 3, false)); EXPECT_EQ(true, TWEthereumAbiFunctionGetParamBool(func, 12, false)); EXPECT_EQ(std::string("Hello, world!"), std::string(TWStringUTF8Bytes(WRAPS(TWEthereumAbiFunctionGetParamString(func, 13, false)).get()))); WRAPD(TWEthereumAbiFunctionGetParamAddress(func, 14, false)); @@ -200,7 +200,7 @@ TEST(TWEthereumAbi, EncodeFuncMonster) { auto encoded = WRAPD(TWEthereumAbiEncode(func)); Data enc2 = data(TWDataBytes(encoded.get()), TWDataSize(encoded.get())); - EXPECT_EQ(4 + 76 * 32, enc2.size()); + EXPECT_EQ(4ul + 76 * 32, enc2.size()); // delete TWEthereumAbiFunctionDelete(func); @@ -216,14 +216,14 @@ TEST(TWEthereumAbi, DecodeOutputFuncCase1) { EXPECT_EQ(0, TWEthereumAbiFunctionAddParamUInt64(func, 0, true)); // original output value - EXPECT_EQ(0, TWEthereumAbiFunctionGetParamUInt64(func, 0, true)); + EXPECT_EQ(0ul, TWEthereumAbiFunctionGetParamUInt64(func, 0, true)); // decode auto encoded = WRAPD(TWDataCreateWithHexString(WRAPS(TWStringCreateWithUTF8Bytes("0000000000000000000000000000000000000000000000000000000000000045")).get())); EXPECT_EQ(true, TWEthereumAbiDecodeOutput(func, encoded.get())); // new output value - EXPECT_EQ(0x45, TWEthereumAbiFunctionGetParamUInt64(func, 0, true)); + EXPECT_EQ(0x45ul, TWEthereumAbiFunctionGetParamUInt64(func, 0, true)); // delete TWEthereumAbiFunctionDelete(func); @@ -238,11 +238,11 @@ TEST(TWEthereumAbi, GetParamWrongType) { // GetParameter with correct types EXPECT_EQ(1, TWEthereumAbiFunctionGetParamUInt8(func, 0, true)); - EXPECT_EQ(2, TWEthereumAbiFunctionGetParamUInt64(func, 1, true)); + EXPECT_EQ(2ul, TWEthereumAbiFunctionGetParamUInt64(func, 1, true)); // GetParameter with wrong type, default value (0) is returned EXPECT_EQ(0, TWEthereumAbiFunctionGetParamUInt8(func, 1, true)); - EXPECT_EQ(0, TWEthereumAbiFunctionGetParamUInt64(func, 0, true)); + EXPECT_EQ(0ul, TWEthereumAbiFunctionGetParamUInt64(func, 0, true)); EXPECT_EQ("00", hex(*(static_cast(WRAPD(TWEthereumAbiFunctionGetParamUInt256(func, 0, true)).get())))); EXPECT_EQ(false, TWEthereumAbiFunctionGetParamBool(func, 0, true)); EXPECT_EQ("", std::string(TWStringUTF8Bytes(WRAPS(TWEthereumAbiFunctionGetParamString(func, 0, true)).get()))); @@ -250,7 +250,7 @@ TEST(TWEthereumAbi, GetParamWrongType) { // GetParameter with wrong index, default value (0) is returned EXPECT_EQ(0, TWEthereumAbiFunctionGetParamUInt8(func, 99, true)); - EXPECT_EQ(0, TWEthereumAbiFunctionGetParamUInt64(func, 99, true)); + EXPECT_EQ(0ul, TWEthereumAbiFunctionGetParamUInt64(func, 99, true)); EXPECT_EQ("00", hex(*(static_cast(WRAPD(TWEthereumAbiFunctionGetParamUInt256(func, 99, true)).get())))); EXPECT_EQ(false, TWEthereumAbiFunctionGetParamBool(func, 99, true)); EXPECT_EQ("", std::string(TWStringUTF8Bytes(WRAPS(TWEthereumAbiFunctionGetParamString(func, 99, true)).get()))); diff --git a/tests/Ethereum/TWEthereumAbiValueDecoderTests.cpp b/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp similarity index 99% rename from tests/Ethereum/TWEthereumAbiValueDecoderTests.cpp rename to tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp index 2f84327195b..da8dcc4b728 100644 --- a/tests/Ethereum/TWEthereumAbiValueDecoderTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiValueDecoderTests.cpp @@ -8,7 +8,7 @@ #include "Data.h" #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Ethereum/TWEthereumAbiValueEncodeTests.cpp b/tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp similarity index 98% rename from tests/Ethereum/TWEthereumAbiValueEncodeTests.cpp rename to tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp index b4b120adbd3..7235666c804 100644 --- a/tests/Ethereum/TWEthereumAbiValueEncodeTests.cpp +++ b/tests/chains/Ethereum/TWEthereumAbiValueEncodeTests.cpp @@ -9,7 +9,7 @@ #include "Data.h" #include "HexCoding.h" #include "uint256.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/Ethereum/ValueDecoderTests.cpp b/tests/chains/Ethereum/ValueDecoderTests.cpp similarity index 83% rename from tests/Ethereum/ValueDecoderTests.cpp rename to tests/chains/Ethereum/ValueDecoderTests.cpp index 78d52a80f4c..c576971e9d4 100644 --- a/tests/Ethereum/ValueDecoderTests.cpp +++ b/tests/chains/Ethereum/ValueDecoderTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,8 +9,7 @@ #include -using namespace TW; -using namespace TW::Ethereum; +namespace TW::Ethereum::tests { uint256_t decodeFromHex(std::string s) { auto data = parse_hex(s); @@ -31,12 +30,12 @@ TEST(EthereumAbiValueDecoder, decodeValue) { EXPECT_EQ("24", ABI::ValueDecoder::decodeValue(parse_hex("0000000000000000000000000000000000000000000000000000000000000018"), "uint8")); EXPECT_EQ("123456", ABI::ValueDecoder::decodeValue(parse_hex("000000000000000000000000000000000000000000000000000000000001e240"), "uint256")); EXPECT_EQ("0xf784682c82526e245f50975190ef0fff4e4fc077", ABI::ValueDecoder::decodeValue(parse_hex("000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077"), "address")); - EXPECT_EQ("Hello World! Hello World! Hello World!", - ABI::ValueDecoder::decodeValue(parse_hex( - "000000000000000000000000000000000000000000000000000000000000002c" - "48656c6c6f20576f726c64212020202048656c6c6f20576f726c642120202020" - "48656c6c6f20576f726c64210000000000000000000000000000000000000000" - ), "string")); + EXPECT_EQ("Hello World! Hello World! Hello World!", + ABI::ValueDecoder::decodeValue(parse_hex( + "000000000000000000000000000000000000000000000000000000000000002c" + "48656c6c6f20576f726c64212020202048656c6c6f20576f726c642120202020" + "48656c6c6f20576f726c64210000000000000000000000000000000000000000"), + "string")); EXPECT_EQ("0x31323334353637383930", ABI::ValueDecoder::decodeValue(parse_hex("3132333435363738393000000000000000000000000000000000000000000000"), "bytes10")); } @@ -47,10 +46,9 @@ TEST(EthereumAbiValueDecoder, decodeArray) { "0000000000000000000000000000000000000000000000000000000000000003" "0000000000000000000000000000000000000000000000000000000000000031" "0000000000000000000000000000000000000000000000000000000000000032" - "0000000000000000000000000000000000000000000000000000000000000033" - ); + "0000000000000000000000000000000000000000000000000000000000000033"); auto res = ABI::ValueDecoder::decodeArray(input, "uint8[]"); - EXPECT_EQ(3, res.size()); + EXPECT_EQ(3ul, res.size()); EXPECT_EQ("49", res[0]); EXPECT_EQ("50", res[1]); EXPECT_EQ("51", res[2]); @@ -60,10 +58,9 @@ TEST(EthereumAbiValueDecoder, decodeArray) { Data input = parse_hex( "0000000000000000000000000000000000000000000000000000000000000002" "000000000000000000000000f784682c82526e245f50975190ef0fff4e4fc077" - "0000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3" - ); + "0000000000000000000000002e00cd222cb42b616d86d037cc494e8ab7f5c9a3"); auto res = ABI::ValueDecoder::decodeArray(input, "address[]"); - EXPECT_EQ(2, res.size()); + EXPECT_EQ(2ul, res.size()); EXPECT_EQ("0xf784682c82526e245f50975190ef0fff4e4fc077", res[0]); EXPECT_EQ("0x2e00cd222cb42b616d86d037cc494e8ab7f5c9a3", res[1]); } @@ -76,11 +73,12 @@ TEST(EthereumAbiValueDecoder, decodeArray) { "0000000000000000000000000000000000000000000000000000000000000002" "1011000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000003" - "1022220000000000000000000000000000000000000000000000000000000000" - ); + "1022220000000000000000000000000000000000000000000000000000000000"); auto res = ABI::ValueDecoder::decodeArray(input, "bytes[]"); - EXPECT_EQ(2, res.size()); + EXPECT_EQ(2ul, res.size()); EXPECT_EQ("0x1011", res[0]); EXPECT_EQ("0x102222", res[1]); } } + +} // namespace TW::Ethereum::tests diff --git a/tests/Ethereum/ValueEncoderTests.cpp b/tests/chains/Ethereum/ValueEncoderTests.cpp similarity index 87% rename from tests/Ethereum/ValueEncoderTests.cpp rename to tests/chains/Ethereum/ValueEncoderTests.cpp index bc6f8fd22a5..29a621d2a05 100644 --- a/tests/Ethereum/ValueEncoderTests.cpp +++ b/tests/chains/Ethereum/ValueEncoderTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,14 +9,12 @@ #include -using namespace TW; -using namespace TW::Ethereum; - -Data data; +namespace TW::Ethereum::tests { void checkLast32BytesEqual(const Data& data, const char* expected) { EXPECT_EQ(hex(subData(data, data.size() - 32, 32)), expected); } + TEST(EthereumAbiValueEncoder, encodeBool) { Data data; ABI::ValueEncoder::encodeBool(false, data); @@ -121,16 +119,18 @@ TEST(EthereumAbiValueEncoder, uint256FromInt256) { } TEST(EthereumAbiValueEncoder, pad32) { - EXPECT_EQ(64, ABI::ValueEncoder::paddedTo32(40)); - EXPECT_EQ(32, ABI::ValueEncoder::paddedTo32(32)); - EXPECT_EQ(64, ABI::ValueEncoder::paddedTo32(33)); - EXPECT_EQ(64, ABI::ValueEncoder::paddedTo32(63)); - EXPECT_EQ(64, ABI::ValueEncoder::paddedTo32(64)); - EXPECT_EQ(96, ABI::ValueEncoder::paddedTo32(65)); - EXPECT_EQ(24, ABI::ValueEncoder::padNeeded32(40)); - EXPECT_EQ(0, ABI::ValueEncoder::padNeeded32(32)); - EXPECT_EQ(31, ABI::ValueEncoder::padNeeded32(33)); - EXPECT_EQ(1, ABI::ValueEncoder::padNeeded32(63)); - EXPECT_EQ(0, ABI::ValueEncoder::padNeeded32(64)); - EXPECT_EQ(31, ABI::ValueEncoder::padNeeded32(65)); + EXPECT_EQ(64ul, ABI::ValueEncoder::paddedTo32(40)); + EXPECT_EQ(32ul, ABI::ValueEncoder::paddedTo32(32)); + EXPECT_EQ(64ul, ABI::ValueEncoder::paddedTo32(33)); + EXPECT_EQ(64ul, ABI::ValueEncoder::paddedTo32(63)); + EXPECT_EQ(64ul, ABI::ValueEncoder::paddedTo32(64)); + EXPECT_EQ(96ul, ABI::ValueEncoder::paddedTo32(65)); + EXPECT_EQ(24ul, ABI::ValueEncoder::padNeeded32(40)); + EXPECT_EQ(0ul, ABI::ValueEncoder::padNeeded32(32)); + EXPECT_EQ(31ul, ABI::ValueEncoder::padNeeded32(33)); + EXPECT_EQ(1ul, ABI::ValueEncoder::padNeeded32(63)); + EXPECT_EQ(0ul, ABI::ValueEncoder::padNeeded32(64)); + EXPECT_EQ(31ul, ABI::ValueEncoder::padNeeded32(65)); } + +} // namespace TW::Ethereum::tests diff --git a/tests/chains/EthereumClassic/TWCoinTypeTests.cpp b/tests/chains/EthereumClassic/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..6ac2854fa36 --- /dev/null +++ b/tests/chains/EthereumClassic/TWCoinTypeTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWEthereumClassicCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeEthereumClassic)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x66004165d3901819dc22e568931591d2e4287bda54995f4ce2701a12016f5997")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeEthereumClassic, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x9eab4b0fc468a7f5d46228bf5a76cb52370d068d")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeEthereumClassic, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeEthereumClassic)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeEthereumClassic)); + const auto chainId = WRAPS(TWCoinTypeChainId(TWCoinTypeEthereumClassic)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeEthereumClassic), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeEthereumClassic)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeEthereumClassic)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeEthereumClassic)); + assertStringsEqual(symbol, "ETC"); + assertStringsEqual(txUrl, "https://blockscout.com/etc/mainnet/tx/0x66004165d3901819dc22e568931591d2e4287bda54995f4ce2701a12016f5997"); + assertStringsEqual(accUrl, "https://blockscout.com/etc/mainnet/address/0x9eab4b0fc468a7f5d46228bf5a76cb52370d068d"); + assertStringsEqual(id, "classic"); + assertStringsEqual(name, "Ethereum Classic"); + assertStringsEqual(chainId, "61"); +} diff --git a/tests/chains/Everscale/AddressTests.cpp b/tests/chains/Everscale/AddressTests.cpp new file mode 100644 index 00000000000..5896183c21d --- /dev/null +++ b/tests/chains/Everscale/AddressTests.cpp @@ -0,0 +1,52 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Everscale/Address.h" +#include "Everscale/WorkchainType.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include +#include + +using namespace TW; + +namespace TW::Everscale { + +TEST(EverscaleAddress, Valid) { + ASSERT_TRUE(Address::isValid("0:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a")); +} + +TEST(EverscaleAddress, Invalid) { + ASSERT_FALSE(Address::isValid("hello world")); + ASSERT_FALSE(Address::isValid("83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a")); + ASSERT_FALSE(Address::isValid("1:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a")); + ASSERT_FALSE(Address::isValid("-2:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a")); + ASSERT_FALSE(Address::isValid("2147483648:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a")); + ASSERT_FALSE(Address::isValid("0:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469ab")); +} + +TEST(EverscaleAddress, FromString) { + auto address = Address("0:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a"); + ASSERT_EQ(address.string(), "0:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a"); + + auto address_uppercase = Address("0:83A0352908060FA87839195D8A763A8D9AB28F8FA41468832B398A719CC6469A"); + ASSERT_EQ(address_uppercase.string(), "0:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a"); +} + +TEST(EverscaleAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("5b59e0372d19b6355c73fa8cc708fa3301ae2ec21bb6277e8b79d386ccb7846f")); + auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519), WorkchainType::Basechain); + ASSERT_EQ(address.string(), "0:269fee242eb410786abe1777a14785c8bbeb1e34100c7570e17698b36ad66fb0"); +} + +TEST(EverscaleAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("e4925f9932df8d7fd0042efff3e2178a972028b644ded3a3b66f6d0577f82e78"), TWPublicKeyTypeED25519); + auto address = Address(publicKey, WorkchainType::Basechain); + ASSERT_EQ(address.string(), "0:269fee242eb410786abe1777a14785c8bbeb1e34100c7570e17698b36ad66fb0"); +} + +} // namespace TW::Everscale diff --git a/tests/chains/Everscale/CellBuilderTest.cpp b/tests/chains/Everscale/CellBuilderTest.cpp new file mode 100644 index 00000000000..860f3bac6e5 --- /dev/null +++ b/tests/chains/Everscale/CellBuilderTest.cpp @@ -0,0 +1,121 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "BinaryCoding.h" +#include "HexCoding.h" +#include "PublicKey.h" + +#include "Everscale/Cell.h" +#include "Everscale/CellBuilder.h" +#include "Everscale/Wallet.h" + +#include + +using namespace TW; +using boost::multiprecision::uint128_t; + +namespace TW::Everscale { + +void checkBuilder(const uint128_t& value, uint16_t bitLen, const std::string& hash) { + CellBuilder dataBuilder; + dataBuilder.appendU128(value); + const auto cell = dataBuilder.intoCell(); + ASSERT_EQ(cell->bitLen, bitLen); + ASSERT_EQ(hex(cell->hash), hash); +} + +TEST(EverscaleCell, BuilderVarUint16) { + const uint128_t oneEver{1'000'000'000u}; + + checkBuilder(0, 4, "5331fed036518120c7f345726537745c5929b8ea1fa37b99b2bb58f702671541"); + checkBuilder(1, 12, "d46edee086ccbace01f45c13d26d49b68f74cd1b7616f4662e699c82c6ec728b"); + checkBuilder(255, 12, "bd16b2d60c93163fbed832e91a5faec484715c48176857c57dcedf9f6e0f32f6"); + checkBuilder(256, 20, "16559011ce6f0f7aaa765179e73ef293f39610f5baa3838a1dc8c52da95793b3"); + checkBuilder(oneEver, 36, "e139b2d96d0bd76da98c3c23b0dc0481dcfe19562798fefbb7bf2e56d8ef37b5"); + checkBuilder(10 * oneEver, 44, "8882fead71f2deb3aa7b8dbd15bbb42c651fcaae8da82e6d5cf8e49825eed12b"); + checkBuilder(1000000 * oneEver, 60, "125f2f85da07f9d92148c067bc19aecbf4da65becdd6b51f17ae3a2aeb2c1bdd"); + checkBuilder(1'000'000'000'000u * oneEver, 76, "39bcb314cdb31de5159764d9c28779de27be44210ffcc52a27aa01bff1d82bf7"); +} + +TEST(EverscaleCell, ComputeContractAddress) { + const auto seqno = 0; + const auto walletId = WALLET_ID; + const auto publicKey = PublicKey(parse_hex("7dbe83e9b223157e85bed2628430e2cdb531d5c99ab428618b7dd29b567a0369"), TWPublicKeyTypeED25519); + + CellBuilder dataBuilder; + dataBuilder.appendU32(seqno); + dataBuilder.appendU32(walletId); + dataBuilder.appendRaw(publicKey.bytes, 256); + + const auto data = dataBuilder.intoCell(); + + // Builder should be empty after `intoCell` + { + const auto emptyCell = dataBuilder.intoCell(); + ASSERT_EQ(hex(emptyCell->hash), "96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7"); + } + + const auto code = Cell::deserialize(Wallet::code.data(), Wallet::code.size()); + + CellBuilder stateInitBuilder; + stateInitBuilder.appendBitZero(); // split_depth + stateInitBuilder.appendBitZero(); // special + stateInitBuilder.appendBitOne(); // code + stateInitBuilder.appendReferenceCell(code); + stateInitBuilder.appendBitOne(); // data + stateInitBuilder.appendReferenceCell(data); + stateInitBuilder.appendBitZero(); // library + + auto stateInit = stateInitBuilder.intoCell(); + + ASSERT_EQ(hex(stateInit->hash), "5a0f742c28067da91e05830f0b072a2069f0617a5f6529d295f6c517d63d67c6"); +} + +TEST(EverscaleCell, UnalignedRead) { + CellBuilder dataBuilder; + dataBuilder.appendU32(0x12312312); + + auto cell = dataBuilder.intoCell(); + auto slice = CellSlice(cell.get()); + + slice.dataOffset += 1; + const auto nextBytes = slice.getNextBytes(2); + ASSERT_TRUE(nextBytes.size() == 2 && nextBytes[0] == 0x24 && nextBytes[1] == 0x62); +} + +TEST(EverscaleCell, ReadZeroBytes) { + CellBuilder dataBuilder; + dataBuilder.appendU32(0x12312312); + + auto cell = dataBuilder.intoCell(); + auto slice = CellSlice(cell.get()); + + ASSERT_EQ(slice.getNextBytes(0), Data{}); +} + +TEST(EverscaleCell, InvalidBuilderData) { + CellBuilder dataBuilder; + ASSERT_ANY_THROW(dataBuilder.appendRaw(Data{}, 1)); +} + +TEST(EverscaleCell, DataOverflow) { + CellBuilder dataBuilder; + + Data data(128, 0x00); + ASSERT_ANY_THROW(dataBuilder.appendRaw(data, data.size() * 8)); +} + +TEST(EverscaleCell, DataUnderflow) { + CellBuilder dataBuilder; + dataBuilder.appendU32(0x12312312); + + auto cell = dataBuilder.intoCell(); + auto slice = CellSlice(cell.get()); + + ASSERT_ANY_THROW(slice.getNextBytes(100)); +} + +} // namespace TW::Everscale diff --git a/tests/chains/Everscale/CellTests.cpp b/tests/chains/Everscale/CellTests.cpp new file mode 100644 index 00000000000..c9b1b821cb9 --- /dev/null +++ b/tests/chains/Everscale/CellTests.cpp @@ -0,0 +1,106 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base64.h" +#include "Everscale/Cell.h" +#include "Everscale/Wallet.h" +#include "HexCoding.h" +#include +#include +#include +#include + +using namespace TW; + +namespace TW::Everscale { + +// All hashes could be verified using https://ever.bytie.moe/visualizer + +static constexpr auto TX = "te6ccgECDgEAAyUAA7V6uRyM7ESqbjssMUQyAqYyQTlEkfDkEhWjBiC1fvKLabAAAaKsEuhQGeqOSyMlWG32ehDzoVCXMh6ugfMLr6pOPj3b6KD4DR/wAAGirA4jnSYtmdCAADR6V/lIBQQBAg8MQQYcxc1EQAMCAG/JkrcETDHn2AAAAAAAAgAAAAAAAop8XDVxQ98+QpgCzzW0U0opAulbEzfySLp3wLLoHzboQRA6FACdROMjE4gAAAAAAAAAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIACCcgkc5v5RyBDaeDdbY8Q+SpmX5OOkhzxFyB/ug4o/bJwJXDMKjAeOEr9OvfcJlgyx80ukSBl43/FO2DXIt1+SuAQCAeAJBgEB3wcBsWgBVyORnYiVTcdlhiiGQFTGSCcokj4cgkK0YMQWr95RbTcAIXxdqfc1KmavZDEIGsfjdBuS4lE5Ox4rJZ+Z+rauOxDRZaC8AAYx6CQAADRVgl0KBMWzOhDACAF7C04VCAAAAAAvMK5pAAAAAAAAAAAAAAAAAA7xIIAFdGVusOGq5cSrATb2hH5h5turvUDrer4E0Mf51wPlefAMAd2IAVcjkZ2IlU3HZYYohkBUxkgnKJI+HIJCtGDEFq/eUW02AMrbR14n5UXrp1deXDU5rl8kQvDfSKbnA+d09dtQe2mo8t94jbtllx/DjCgucIpvywjhJBhWNQjtWXh4dP6qiDAAAAADFszqDukuhIYKAQTQAwsB42IAQvi7U+5qVM1eyGIQNY/G6DclxKJydjxWSz8z9W1cdiGiy0F4AAAAAAAAAAAAAAAAAAALThUIAAAAAC8wrmkAAAAAAAAAAAAAAAAADvEggAV0ZW6w4arlxKsBNvaEfmHm26u9QOt6vgTQx/nXA+V58AwBY4AUk5qcKxU0Kqq8xI6zxcnOzaRMAW8AGxI8jfJSPgiVJ6AAAAAAAAAAAAAARVadlK9wDQBDgBVyORnYiVTcdlhiiGQFTGSCcokj4cgkK0YMQWr95RbTcA=="; +static constexpr auto BIG_TX = "te6ccgECdQEAFD4AA7d61FeCHf8yG3+VsHLEilQ6UuYy8wcpWi1+/eB+QyLiYMAAAahJB7OYkL2Pl+S0sBbapRCERwSsVWKsZ/1WGbNouh5HwPhxSiGAAAGoP8TeAJYumenAAFSARBWHSAUEAQIbBIgLyR0Jj8WYgCdHLREDAgBxygFZfVBPmUqMAAAAAAAGAAIAAAAEW8033SNQ2/1TE9Nzuof+lf33h4/sRfFyiGy4DaJos2pa1CzcAJ5KDiw9CQAAAAAAAAAAATMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIJyjI1j6xocdf/ts8UpUE4PQBjq+eZ4ePLwpE1tDGf3pHcFUnuY03DQrIX8ExegcAhTl+187mhCPuegYR4tBS3ejwIB4HEGAgHdCgcBASAIAbFoAVqK8EO/5kNv8rYOWJFKh0pcxl5g5StFr9+8D8hkXEwZAB03C5ZU3g0onstArcVRQt2q942P0t/N68CNCNeI9IxfETHWk3wGKJa2AAA1CSD2cxbF0z04wAkBa2eguV8AAAAAAAAAAAAAAANFARhPgB374hjvHSKb7xHdyPtVEPFzv/+BeyMtxMrKJu6jDYpu8HMBASALArNoAVqK8EO/5kNv8rYOWJFKh0pcxl5g5StFr9+8D8hkXEwZAB03C5ZU3g0onstArcVRQt2q942P0t/N68CNCNeI9IxfEI8NGAAIA3C5RAAANQkg9nMUxdM9OeBTDAJTFaA4+wAAAAGAHfviGO8dIpvvEd3I+1UQ8XO//4F7Iy3Eysom7qMNim7wDg0AQ4AVmHZxK25XVSkUA1uAHGOMneWjMhSxqkQWdAyAImMM0RACBorbNXAPBCSK7VMg4wMgwP/jAiDA/uMC8gtNERB0A77tRNDXScMB+GaJ+Gkh2zzTAAGOGoECANcYIPkBAdMAAZTT/wMBkwL4QuL5EPKoldMAAfJ64tM/AfhDIbnytCD4I4ED6KiCCBt3QKC58rT4Y9MfAfgjvPK50x8B2zzyPGodEgR87UTQ10nDAfhmItDTA/pAMPhpqTgA+ER/b3GCCJiWgG9ybW9zcG90+GTjAiHHAOMCIdcNH/K8IeMDAds88jxKa2sSAiggghBnoLlfu+MCIIIQfW/yVLvjAh8TAzwgghBotV8/uuMCIIIQc+IhQ7rjAiCCEH1v8lS64wIcFhQDNjD4RvLgTPhCbuMAIZPU0dDe+kDR2zww2zzyAEwVUABo+Ev4SccF8uPo+Ev4TfhKcMjPhYDKAHPPQM5xzwtuVSDIz5BT9raCyx/OAcjOzc3JgED7AANOMPhG8uBM+EJu4wAhk9TR0N7Tf/pA03/U0dD6QNIA1NHbPDDbPPIATBdQBG74S/hJxwXy4+glwgDy5Bol+Ey78uQkJPpCbxPXC//DACX4S8cFs7Dy5AbbPHD7AlUD2zyJJcIAUTpqGAGajoCcIfkAyM+KAEDL/8nQ4jH4TCehtX/4bFUhAvhLVQZVBH/Iz4WAygBzz0DOcc8LblVAyM+RnoLlfst/zlUgyM7KAMzNzcmBAID7AFsZAQpUcVTbPBoCuPhL+E34QYjIz44rbNbMzslVBCD5APgo+kJvEsjPhkDKB8v/ydAGJsjPhYjOAfoCi9AAAAAAAAAAAAAAAAAHzxYh2zzMz4NVMMjPkFaA4+7Myx/OAcjOzc3JcfsAcBsANNDSAAGT0gQx3tIAAZPSATHe9AT0BPQE0V8DARww+EJu4wD4RvJz0fLAZB0CFu1E0NdJwgGOgOMNHkwDZnDtRND0BXEhgED0Do6A33IigED0Do6A33AgiPhu+G34bPhr+GqAQPQO8r3XC//4YnD4Y2lpdARQIIIQDwJYqrvjAiCCECDrx2274wIgghBGqdfsu+MCIIIQZ6C5X7vjAj0yKSAEUCCCEElpWH+64wIgghBWJUituuMCIIIQZl3On7rjAiCCEGeguV+64wInJSMhA0ow+Eby4Ez4Qm7jACGT1NHQ3tN/+kDU0dD6QNIA1NHbPDDbPPIATCJQAuT4SSTbPPkAyM+KAEDL/8nQxwXy5EzbPHL7AvhMJaC1f/hsAY41UwH4SVNW+Er4S3DIz4WAygBzz0DOcc8LblVQyM+Rw2J/Js7Lf1UwyM5VIMjOWcjOzM3Nzc2aIcjPhQjOgG/PQOLJgQCApgK1B/sAXwQ6UQPsMPhG8uBM+EJu4wDTH/hEWG91+GTR2zwhjiUj0NMB+kAwMcjPhyDOjQQAAAAAAAAAAAAAAAAOZdzp+M8WzMlwji74RCBvEyFvEvhJVQJvEchyz0DKAHPPQM4B+gL0AIBqz0D4RG8VzwsfzMn4RG8U4vsA4wDyAEwkSAE0+ERwb3KAQG90cG9x+GT4QYjIz44rbNbMzslwA0Yw+Eby4Ez4Qm7jACGT1NHQ3tN/+kDU0dD6QNTR2zww2zzyAEwmUAEW+Ev4SccF8uPo2zxCA/Aw+Eby4Ez4Qm7jANMf+ERYb3X4ZNHbPCGOJiPQ0wH6QDAxyM+HIM6NBAAAAAAAAAAAAAAAAAyWlYf4zxbLf8lwji/4RCBvEyFvEvhJVQJvEchyz0DKAHPPQM4B+gL0AIBqz0D4RG8Vzwsfy3/J+ERvFOL7AOMA8gBMKEgAIPhEcG9ygEBvdHBvcfhk+EwEUCCCEDIE7Cm64wIgghBDhPKYuuMCIIIQRFdChLrjAiCCEEap1+y64wIwLiwqA0ow+Eby4Ez4Qm7jACGT1NHQ3tN/+kDU0dD6QNIA1NHbPDDbPPIATCtQAcz4S/hJxwXy4+gkwgDy5Bok+Ey78uQkI/pCbxPXC//DACT4KMcFs7Dy5AbbPHD7AvhMJaG1f/hsAvhLVRN/yM+FgMoAc89AznHPC25VQMjPkZ6C5X7Lf85VIMjOygDMzc3JgQCA+wBRA+Iw+Eby4Ez4Qm7jANMf+ERYb3X4ZNHbPCGOHSPQ0wH6QDAxyM+HIM5xzwthAcjPkxFdChLOzclwjjH4RCBvEyFvEvhJVQJvEchyz0DKAHPPQM4B+gL0AHHPC2kByPhEbxXPCx/Ozcn4RG8U4vsA4wDyAEwtSAAg+ERwb3KAQG90cG9x+GT4SgNAMPhG8uBM+EJu4wAhk9TR0N7Tf/pA0gDU0ds8MNs88gBML1AB8PhK+EnHBfLj8ts8cvsC+EwkoLV/+GwBjjJUcBL4SvhLcMjPhYDKAHPPQM5xzwtuVTDIz5Hqe3iuzst/WcjOzM3NyYEAgKYCtQf7AI4oIfpCbxPXC//DACL4KMcFs7COFCHIz4UIzoBvz0DJgQCApgK1B/sA3uJfA1ED9DD4RvLgTPhCbuMA0x/4RFhvdfhk0x/R2zwhjiYj0NMB+kAwMcjPhyDOjQQAAAAAAAAAAAAAAAALIE7CmM8WygDJcI4v+EQgbxMhbxL4SVUCbxHIcs9AygBzz0DOAfoC9ACAas9A+ERvFc8LH8oAyfhEbxTi+wDjAPIATDFIAJr4RHBvcoBAb3Rwb3H4ZCCCEDIE7Cm6IYIQT0efo7oighAqSsQ+uiOCEFYlSK26JIIQDC/yDbolghB+3B03ulUFghAPAliqurGxsbGxsQRQIIIQEzKpMbrjAiCCEBWgOPu64wIgghAfATKRuuMCIIIQIOvHbbrjAjs3NTMDNDD4RvLgTPhCbuMAIZPU0dDe+kDR2zzjAPIATDRIAUL4S/hJxwXy4+jbPHD7AsjPhQjOgG/PQMmBAICmArUH+wBSA+Iw+Eby4Ez4Qm7jANMf+ERYb3X4ZNHbPCGOHSPQ0wH6QDAxyM+HIM5xzwthAcjPknwEykbOzclwjjH4RCBvEyFvEvhJVQJvEchyz0DKAHPPQM4B+gL0AHHPC2kByPhEbxXPCx/Ozcn4RG8U4vsA4wDyAEw2SAAg+ERwb3KAQG90cG9x+GT4SwNMMPhG8uBM+EJu4wAhltTTH9TR0JPU0x/i+kDU0dD6QNHbPOMA8gBMOEgCePhJ+ErHBSCOgN/y4GTbPHD7AiD6Qm8T1wv/wwAh+CjHBbOwjhQgyM+FCM6Ab89AyYEAgKYCtQf7AN5fBDlRASYwIds8+QDIz4oAQMv/ydD4SccFOgBUcMjL/3BtgED0Q/hKcViAQPQWAXJYgED0Fsj0AMn4TsjPhID0APQAz4HJA/Aw+Eby4Ez4Qm7jANMf+ERYb3X4ZNHbPCGOJiPQ0wH6QDAxyM+HIM6NBAAAAAAAAAAAAAAAAAkzKpMYzxbLH8lwji/4RCBvEyFvEvhJVQJvEchyz0DKAHPPQM4B+gL0AIBqz0D4RG8Vzwsfyx/J+ERvFOL7AOMA8gBMPEgAIPhEcG9ygEBvdHBvcfhk+E0ETCCCCIV++rrjAiCCCzaRmbrjAiCCEAwv8g264wIgghAPAliquuMCR0NAPgM2MPhG8uBM+EJu4wAhk9TR0N76QNHbPDDbPPIATD9QAEL4S/hJxwXy4+j4TPLULsjPhQjOgG/PQMmBAICmILUH+wADRjD4RvLgTPhCbuMAIZPU0dDe03/6QNTR0PpA1NHbPDDbPPIATEFQARb4SvhJxwXy4/LbPEIBmiPCAPLkGiP4TLvy5CTbPHD7AvhMJKG1f/hsAvhLVQP4Sn/Iz4WAygBzz0DOcc8LblVAyM+QZK1Gxst/zlUgyM5ZyM7Mzc3NyYEAgPsAUQNEMPhG8uBM+EJu4wAhltTTH9TR0JPU0x/i+kDR2zww2zzyAExEUAIo+Er4SccF8uPy+E0iuo6AjoDiXwNGRQFy+ErIzvhLAc74TAHLf/hNAcsfUiDLH1IQzvhOAcwj+wQj0CCLOK2zWMcFk9dN0N7XTNDtHu1Tyds8YgEy2zxw+wIgyM+FCM6Ab89AyYEAgKYCtQf7AFED7DD4RvLgTPhCbuMA0x/4RFhvdfhk0ds8IY4lI9DTAfpAMDHIz4cgzo0EAAAAAAAAAAAAAAAACAhX76jPFszJcI4u+EQgbxMhbxL4SVUCbxHIcs9AygBzz0DOAfoC9ACAas9A+ERvFc8LH8zJ+ERvFOL7AOMA8gBMSUgAKO1E0NP/0z8x+ENYyMv/yz/Oye1UACD4RHBvcoBAb3Rwb3H4ZPhOA7wh1h8x+Eby4Ez4Qm7jANs8cvsCINMfMiCCEGeguV+6jj0h038z+EwhoLV/+Gz4SQH4SvhLcMjPhYDKAHPPQM5xzwtuVSDIz5CfQjemzst/AcjOzc3JgQCApgK1B/sATFFLAYyOQCCCEBkrUbG6jjUh038z+EwhoLV/+Gz4SvhLcMjPhYDKAHPPQM5xzwtuWcjPkHDKgrbOy3/NyYEAgKYCtQf7AN7iW9s8UABK7UTQ0//TP9MAMfpA1NHQ+kDTf9Mf1NH4bvht+Gz4a/hq+GP4YgIK9KQg9KFObQQsoAAAAALbPHL7Aon4aon4a3D4bHD4bVFqak8Dpoj4bokB0CD6QPpA03/TH9Mf+kA3XkD4avhr+Gww+G0y1DD4biD6Qm8T1wv/wwAh+CjHBbOwjhQgyM+FCM6Ab89AyYEAgKYCtQf7AN4w2zz4D/IAdGpQAEb4TvhN+Ez4S/hK+EP4QsjL/8s/z4POVTDIzst/yx/MzcntVAEe+CdvEGim/mChtX/bPLYJUgAMghAF9eEAAgE0WlQBAcBVAgPPoFdWAENIAVmHZxK25XVSkUA1uAHGOMneWjMhSxqkQWdAyAImMM0RAgEgWVgAQyAE+QMzZwkbAX69TKqEV1UHZyfJCZqB4G/esE2ZHPnIDxQAQQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAIGits1cFsEJIrtUyDjAyDA/+MCIMD+4wLyC2xdXHQDiu1E0NdJwwH4Zon4aSHbPNMAAZ+BAgDXGCD5AVj4QvkQ8qje0z8B+EMhufK0IPgjgQPoqIIIG3dAoLnytPhj0x8B2zzyPGpmXgNS7UTQ10nDAfhmItDTA/pAMPhpqTgA3CHHAOMCIdcNH/K8IeMDAds88jxra14BFCCCEBWgOPu64wJfBJAw+EJu4wD4RvJzIZbU0x/U0dCT1NMf4vpA1NHQ+kDR+En4SscFII6A346AjhQgyM+FCM6Ab89AyYEAgKYgtQf7AOJfBNs88gBmY2BvAQhdIts8YQJ8+ErIzvhLAc5wAct/cAHLHxLLH874QYjIz44rbNbMzskBzCH7BAHQIIs4rbNYxwWT103Q3tdM0O0e7VPJ2zxwYgAE8AIBHjAh+kJvE9cL/8MAII6A3mQBEDAh2zz4SccFZQF+cMjL/3BtgED0Q/hKcViAQPQWAXJYgED0Fsj0AMn4QYjIz44rbNbMzsnIz4SA9AD0AM+ByfkAyM+KAEDL/8nQcAIW7UTQ10nCAY6A4w1oZwA07UTQ0//TP9MAMfpA1NHQ+kDR+Gv4avhj+GICVHDtRND0BXEhgED0Do6A33IigED0Do6A3/hr+GqAQPQO8r3XC//4YnD4Y2lpAQKJagBDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAK+Eby4EwCCvSkIPShbm0AFHNvbCAwLjU3LjEBGKAAAAACMNs8+A/yAG8ALPhK+EP4QsjL/8s/z4PO+EvIzs3J7VQADCD4Ye0e2QGxaAHfviGO8dIpvvEd3I+1UQ8XO//4F7Iy3Eysom7qMNim7wArUV4Id/zIbf5WwcsSKVDpS5jLzBylaLX794H5DIuJgxHQmPxYBisxYgAANQkg9nMQxdM9OMByAYtz4iFDAAAAAAAAAAAAAAADRQEYT4AVmHZxK25XVSkUA1uAHGOMneWjMhSxqkQWdAyAImMM0QAAAAAAAAAAAAAAAAR4aMAQcwFDgBWYdnErbldVKRQDW4AcY4yd5aMyFLGqRBZ0DIAiYwzRCHQAAA=="; + +TEST(EverscaleCell, DeserializeTransaction) { + const auto tx = Cell::fromBase64(TX); + ASSERT_EQ(hex(tx->hash), "88a02e7bd8833d384f37d63d4d01deef9a1806937b94a313cc5e8c3cc7643032"); + + const auto bigTx = Cell::fromBase64(BIG_TX); + ASSERT_EQ(hex(bigTx->hash), "37f29d9f3aa5c7cc783962d861c08705f245f5e5bfedd208dfe08d02e80a47d8"); +} + +TEST(EverscaleCell, DeserializeWallet) { + const auto cell = Cell::deserialize(Wallet::code.data(), Wallet::code.size()); + ASSERT_EQ(hex(cell->hash), "84dafa449f98a6987789ba232358072bc0f76dc4524002a5d0918b9a75d2d599"); +} + +TEST(EverscaleCell, SerializeTransaction) { + const auto cell = Cell::fromBase64(TX); + Data data; + cell->serialize(data); + + const auto decoded = Cell::deserialize(data.data(), data.size()); + ASSERT_EQ(hex(decoded->hash), "88a02e7bd8833d384f37d63d4d01deef9a1806937b94a313cc5e8c3cc7643032"); +} + +TEST(EverscaleCell, SerializeWallet) { + const auto cell = Cell::deserialize(Wallet::code.data(), Wallet::code.size()); + Data data; + cell->serialize(data); + + const auto decoded = Cell::deserialize(data.data(), data.size()); + ASSERT_EQ(hex(decoded->hash), "84dafa449f98a6987789ba232358072bc0f76dc4524002a5d0918b9a75d2d599"); +} + +TEST(EverscaleCell, EmptyCell) { + const auto EMPTY_CELL = "96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7"; + + const auto cell1 = Cell::fromBase64("te6ccgEBAQEAAgAAAA=="); + ASSERT_EQ(hex(cell1->hash), EMPTY_CELL); + + // With index + const auto cell2 = Cell::fromBase64("te6ccoEBAQEAAwACAAA="); + ASSERT_EQ(hex(cell2->hash), EMPTY_CELL); + + // With d2 > 0 && d2%8 != 0 but without end flag (computeBitLen should just return 0) + const auto cell3 = Cell::fromBase64("te6ccgEBAQEAAwAABQAAAA=="); + ASSERT_EQ(hex(cell3->hash), EMPTY_CELL); + + // With `storeHashes` (provided hash should be skipped) + const auto cell4 = Cell::fromBase64("te6ccgEBAQEAJAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); + ASSERT_EQ(hex(cell4->hash), EMPTY_CELL); +} + +TEST(EverscaleCell, InvalidCell) { + // unexpected eof + ASSERT_THROW(Cell::fromBase64("te4="), std::runtime_error); + // unknown magic + ASSERT_THROW(Cell::fromBase64("aGVsbG8gd29ybGQK"), std::runtime_error); + // refSize > 4 + ASSERT_THROW(Cell::fromBase64("te6ccgUCAAAAAAEAAAAAAQAAAAAAFD4AAAAAAAO3etQ="), std::runtime_error); + // unsupported root count + ASSERT_THROW(Cell::fromBase64("te6ccgECdRIAFD4AA7d61A=="), std::runtime_error); + // root count is greater than cell count + ASSERT_THROW(Cell::fromBase64("te6ccgECAAEAFD4AA7d61A=="), std::runtime_error); + // absent cells are not supported + ASSERT_THROW(Cell::fromBase64("te6ccgECAQESFD4AA7d61A=="), std::runtime_error); + // non-zero level is not supported + ASSERT_THROW(Cell::fromBase64("te6ccgECAQEAFD4AY7d61FeCHf8yG3+VsHLEilQ6UuYy8wcpWi1+/Q=="), std::runtime_error); + // exotic cells are not supported + ASSERT_THROW(Cell::fromBase64("te6ccgECAQEAFD4AC7d61FeCHf8yG3+VsHLEilQ6UuYy8wcpWi1+/Q=="), std::runtime_error); + // absent cells are not supported + ASSERT_THROW(Cell::fromBase64("te6ccgECAQEAFD4AF7d61FeCHf8yG3+VsHLEilQ6UuYy8wcpWi1+/Q=="), std::runtime_error); + // invalid ref count + ASSERT_THROW(Cell::fromBase64("te6ccgEBAQEAAwAFAA=="), std::runtime_error); + // invalid child index + ASSERT_THROW(Cell::fromBase64("te6ccgEBAQEAJAABAP8="), std::runtime_error); + // invalid child index (reference to itself) + ASSERT_THROW(Cell::fromBase64("te6ccgEBAQEAAgABAAA="), std::runtime_error); + // invalid root index + ASSERT_THROW(Cell::fromBase64("te6ccgEBAQEAAwEAAA"), std::runtime_error); + // child cell not found + ASSERT_THROW(Cell::fromBase64("te6ccgEBAQEAAgABAAE="), std::runtime_error); +} + +} // namespace TW::Everscale diff --git a/tests/chains/Everscale/SignerTests.cpp b/tests/chains/Everscale/SignerTests.cpp new file mode 100644 index 00000000000..80eb521a52d --- /dev/null +++ b/tests/chains/Everscale/SignerTests.cpp @@ -0,0 +1,88 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Everscale/Messages.h" +#include "Everscale/Signer.h" + +#include "Base64.h" +#include "HexCoding.h" + +#include + +using namespace TW; + +namespace TW::Everscale { + +TEST(EverscaleSigner, TransferWithDeploy) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_bounce(false); + transfer.set_behavior(Proto::MessageBehavior::SimpleTransfer); + transfer.set_amount(500000000); + transfer.set_expired_at(1680770631); + transfer.set_to("0:db18a67f4626f15ac0537a18445937f685f9b30184f0d7b28be4bdeb92d2fd90"); + + // NOTE: There is `set_encoded_contract_data` because contract was not deployed yet + + auto privateKey = parse_hex("542bd4288352f1c6b270046f153d406aec054a0a06000ab9b36b5c6dd3050ad4"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(Cell::fromBase64(output.encoded())->hash), "bfb18e56e9d00d783c7eb1726f08bf613dd0f01a110a130c0f8f91bb13390a39"); + + // Link to the message: https://everscan.io/messages/bfb18e56e9d00d783c7eb1726f08bf613dd0f01a110a130c0f8f91bb13390a39 + ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAAUoAAAPhiAG+Ilaz1wTyTEauoymMGl6o+NGqhszIlHS8BXAmXniYrBGMBTen55/RbfcIBoeCrPB1cxPMcHRx7xyBzJmdtewBPaTu/WuHgnqg09jQaxTEcii+Nuqm7p3b6iMq+/6598ggCXUlsUyF0MjgAAAAAHAAAwACAAEAaEIAbYxTP6MTeK1gKb0MIiyb+0L82YDCeGvZRfJe9clpfsgg7msoAAAAAAAAAAAAAAAAAAAAUAAAAABLqS2KOWKN+7Y5OSiKhKisiw6t/h2ovvR3WbapyAtrdctwupwA3v8AIN0gggFMl7ohggEznLqxn3Gw7UTQ0x/THzHXC//jBOCk8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/T/9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA=="); +} + +TEST(EverscaleSigner, Transfer1) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_bounce(false); + transfer.set_behavior(Proto::MessageBehavior::SimpleTransfer); + transfer.set_amount(100000000); + transfer.set_expired_at(1680770631); + transfer.set_to("0:db18a67f4626f15ac0537a18445937f685f9b30184f0d7b28be4bdeb92d2fd90"); + + transfer.set_encoded_contract_data("te6ccgEBAQEAKgAAUAAAAAFLqS2KOWKN+7Y5OSiKhKisiw6t/h2ovvR3WbapyAtrdctwupw="); + + auto privateKey = parse_hex("542bd4288352f1c6b270046f153d406aec054a0a06000ab9b36b5c6dd3050ad4"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(Cell::fromBase64(output.encoded())->hash), "73807b0a3ca2d8564c023dccd5b9da222a270f68338c6fc2c064dda376a2c59d"); + + // Link to the message: https://everscan.io/messages/73807b0a3ca2d8564c023dccd5b9da222a270f68338c6fc2c064dda376a2c59d + ASSERT_EQ(output.encoded(), "te6ccgICAAIAAQAAAKoAAAHfiAG+Ilaz1wTyTEauoymMGl6o+NGqhszIlHS8BXAmXniYrAImASIQKH2jIwoA65IGC6aua4gAA4fFo/Nuxgb3sIRELhZnSXIS7IsE2E4D+8hk3EWGVZX+ICqlN/ka9DvXduhaXUlsUyF0MjgAAAAIHAABAGhCAG2MUz+jE3itYCm9DCIsm/tC/NmAwnhr2UXyXvXJaX7IIC+vCAAAAAAAAAAAAAAAAAAA"); +} + +TEST(EverscaleSigner, Transfer2) { + auto input = Proto::SigningInput(); + + auto& transfer = *input.mutable_transfer(); + transfer.set_bounce(true); + transfer.set_behavior(Proto::MessageBehavior::SendAllBalance); + transfer.set_amount(200000000); + transfer.set_expired_at(1680770631); + transfer.set_to("0:df112b59eb82792623575194c60d2f547c68d54366644a3a5e02b8132f3c4c56"); + + transfer.set_encoded_contract_data("te6ccgEBAQEAKgAAUAAAAAJLqS2KOWKN+7Y5OSiKhKisiw6t/h2ovvR3WbapyAtrdctwupw="); + + auto privateKey = parse_hex("542bd4288352f1c6b270046f153d406aec054a0a06000ab9b36b5c6dd3050ad4"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(Cell::fromBase64(output.encoded())->hash), "e35616cfa88e115580f07c6b41ae3ded1902d2bab1efefb74f677b4aececef24"); + + // Link to the message: https://everscan.io/messages/e35616cfa88e115580f07c6b41ae3ded1902d2bab1efefb74f677b4aececef24 + ASSERT_EQ(output.encoded(), "te6ccgICAAIAAQAAAKoAAAHfiAG+Ilaz1wTyTEauoymMGl6o+NGqhszIlHS8BXAmXniYrANrT0ivIEpuMGjKoyS9J03Wbl24jowXvdzQdLD6L3USLETUyRGbbmbUfBcNtF1FwKtmIQd0lNR1qIX9K/eloMgaXUlsUyF0MjgAAAAUFAABAGhiAG+Ilaz1wTyTEauoymMGl6o+NGqhszIlHS8BXAmXniYrIF9eEAAAAAAAAAAAAAAAAAAA"); +} + +} // namespace TW::Everscale diff --git a/tests/chains/Everscale/TWAnyAddressTests.cpp b/tests/chains/Everscale/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..6cf7627b0f6 --- /dev/null +++ b/tests/chains/Everscale/TWAnyAddressTests.cpp @@ -0,0 +1,30 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include + +#include "TestUtilities.h" +#include + +namespace TW::Everscale { + +TEST(TWEverscale, HDWallet) { + auto mnemonic = + STRING("shoot island position soft burden budget tooth cruel issue economy destroy above"); + auto passphrase = STRING(""); + + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(mnemonic.get(), passphrase.get())); + + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKey(wallet.get(), TWCoinTypeEverscale, WRAPS(TWCoinTypeDerivationPath(TWCoinTypeEverscale)).get())); + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519(privateKey.get())); + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeEverscale)); + auto addressStr = WRAPS(TWAnyAddressDescription(address.get())); + + assertStringsEqual(addressStr, "0:0c39661089f86ec5926ea7d4ee4223d634ba4ed6dcc2e80c7b6a8e6d59f79b04"); +} + +} // namespace TW::Everscale diff --git a/tests/chains/Everscale/TWAnySignerTests.cpp b/tests/chains/Everscale/TWAnySignerTests.cpp new file mode 100644 index 00000000000..d404ce00210 --- /dev/null +++ b/tests/chains/Everscale/TWAnySignerTests.cpp @@ -0,0 +1,38 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base64.h" +#include "HexCoding.h" +#include "proto/Everscale.pb.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::Everscale { + +TEST(TWAnySignerEverscale, SignMessageToDeployWallet) { + Proto::SigningInput input; + + auto& transfer = *input.mutable_transfer(); + transfer.set_bounce(false); + transfer.set_behavior(Proto::MessageBehavior::SimpleTransfer); + transfer.set_amount(500000000); + transfer.set_expired_at(1680770631); + transfer.set_to("0:db18a67f4626f15ac0537a18445937f685f9b30184f0d7b28be4bdeb92d2fd90"); + + // NOTE: There is `set_encoded_contract_data` because contract was not deployed yet + + auto privateKey = parse_hex("542bd4288352f1c6b270046f153d406aec054a0a06000ab9b36b5c6dd3050ad4"); + input.set_private_key(privateKey.data(), privateKey.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeEverscale); + + ASSERT_EQ(output.encoded(), "te6ccgICAAQAAQAAAUoAAAPhiAG+Ilaz1wTyTEauoymMGl6o+NGqhszIlHS8BXAmXniYrBGMBTen55/RbfcIBoeCrPB1cxPMcHRx7xyBzJmdtewBPaTu/WuHgnqg09jQaxTEcii+Nuqm7p3b6iMq+/6598ggCXUlsUyF0MjgAAAAAHAAAwACAAEAaEIAbYxTP6MTeK1gKb0MIiyb+0L82YDCeGvZRfJe9clpfsgg7msoAAAAAAAAAAAAAAAAAAAAUAAAAABLqS2KOWKN+7Y5OSiKhKisiw6t/h2ovvR3WbapyAtrdctwupwA3v8AIN0gggFMl7ohggEznLqxn3Gw7UTQ0x/THzHXC//jBOCk8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/T/9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA=="); +} + +} // namespace TW::Everscale diff --git a/tests/chains/Everscale/TWCoinTypeTests.cpp b/tests/chains/Everscale/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..95a9297cef2 --- /dev/null +++ b/tests/chains/Everscale/TWCoinTypeTests.cpp @@ -0,0 +1,38 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + +namespace TW::Everscale { + +TEST(TWEverscaleCoinType, TWCoinType) { + const auto coin = TWCoinTypeEverscale; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("781238b2b0d15cd4cd2e2a0a142753750cd5e1b2c8b506fcede75a90e02f1268")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0:d2bf59964a05dee84a0dd1ddc0ad83ba44d49719cf843d689dc8b726d0fb59d8")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "everscale"); + assertStringsEqual(name, "Everscale"); + assertStringsEqual(symbol, "EVER"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 9); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEverscale); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(txUrl, "https://everscan.io/transactions/781238b2b0d15cd4cd2e2a0a142753750cd5e1b2c8b506fcede75a90e02f1268"); + assertStringsEqual(accUrl, "https://everscan.io/accounts/0:d2bf59964a05dee84a0dd1ddc0ad83ba44d49719cf843d689dc8b726d0fb59d8"); +} + +} // namespace TW::Everscale diff --git a/tests/chains/Evmos/SignerTests.cpp b/tests/chains/Evmos/SignerTests.cpp new file mode 100644 index 00000000000..0dc9d3e816c --- /dev/null +++ b/tests/chains/Evmos/SignerTests.cpp @@ -0,0 +1,126 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Base64.h" +#include "proto/Cosmos.pb.h" +#include "Cosmos/Address.h" +#include "Cosmos/Signer.h" +#include "TestUtilities.h" + +#include + +#include +#include + +namespace TW::Cosmos::evmos::tests { + +TEST(EvmosSigner, SignTxJsonEthermintKeyType) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::JSON); // obsolete + input.set_account_number(1037); + input.set_chain_id("evmos_9001-2"); + input.set_memo(""); + input.set_sequence(8); + + auto fromAddress = Address("evmos", parse_hex("BC2DA90C84049370D1B7C528BC164BC588833F21")); + auto toAddress = Address("evmos", parse_hex("12E8FE8B81ECC1F4F774EA6EC8DF267138B9F2D9")); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("muon"); + amountOfTx->set_amount("1"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("muon"); + amountOfFee->set_amount("200"); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeNativeEvmos); + auto anotherExpectedJson =R"( + { + "mode":"block", + "tx":{"fee":{"amount":[{"amount":"200","denom":"muon"}],"gas":"200000"}, + "memo":"", + "msg":[{"type":"cosmos-sdk/MsgSend", + "value":{"amount":[{"amount":"1","denom":"muon"}], + "from_address":"evmos1hsk6jryyqjfhp5dhc55tc9jtckygx0ep4mur4z", + "to_address":"evmos1zt50azupanqlfam5afhv3hexwyutnuke45f6ye"}}], + "signatures": + [ + { + "pub_key": + { + "type":"ethermint/PubKeyEthSecp256k1", + "value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F" + }, + "signature":"RWt8aaBxdMAeEjym8toWskJ6WaJpEF9Ciucz2lAHkvNnTicGpzxwTUzJbJXRirSnGkejhISaYtDw2RBiq0vg5w==" + } + ]} + })"_json; + + /// This tx is not broadcasted, we just want to test the signature format (ethermint/PubKeyEthSecp256k1) + EXPECT_EQ(anotherExpectedJson, nlohmann::json::parse(output.json())); + + auto signatures = nlohmann::json::parse(output.signature_json()); + + auto expectedSignatures = R"( + [ + { + "pub_key": + { + "type":"ethermint/PubKeyEthSecp256k1", + "value":"AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F" + }, + "signature":"RWt8aaBxdMAeEjym8toWskJ6WaJpEF9Ciucz2lAHkvNnTicGpzxwTUzJbJXRirSnGkejhISaYtDw2RBiq0vg5w==" + } + ])"_json; + EXPECT_EQ(signatures, expectedSignatures); +} + +TEST(EvmosSigner, CompoundingAuthz) { + // Successfully broadcasted https://www.mintscan.io/evmos/txs/8D811CEC078420C41220F0B584EA0AC037763380FAC31E0E352E4BB4D1D18B79 + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(2139877); + input.set_chain_id("evmos_9001-2"); + input.set_memo(""); + input.set_sequence(3); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_auth_grant(); + message.set_granter("evmos12m9grgas60yk0kult076vxnsrqz8xpjy9rpf3e"); + message.set_grantee("evmos18fzq4nac28gfma6gqfvkpwrgpm5ctar2z9mxf3"); + auto& grant_stake = *message.mutable_grant_stake(); + grant_stake.mutable_allow_list()->add_address("evmosvaloper1umk407eed7af6anvut6llg2zevnf0dn0feqqny"); + grant_stake.set_authorization_type(TW::Cosmos::Proto::Message_AuthorizationType_DELEGATE); + message.set_expiration(1692309600); + + auto& fee = *input.mutable_fee(); + fee.set_gas(180859); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("aevmos"); + amountOfFee->set_amount("4521475000000000"); + + auto privateKey = parse_hex("79bcbded1a5678ab34e6d9db9ad78e4e480e7b22723cc5fbf59e843732e1a8e5"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeNativeEvmos); + auto expected = R"( + { + "mode":"BROADCAST_MODE_BLOCK", + "tx_bytes":"CvUBCvIBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQSzwEKLGV2bW9zMTJtOWdyZ2FzNjB5azBrdWx0MDc2dnhuc3Jxejh4cGp5OXJwZjNlEixldm1vczE4ZnpxNG5hYzI4Z2ZtYTZncWZ2a3B3cmdwbTVjdGFyMno5bXhmMxpxCmcKKi9jb3Ntb3Muc3Rha2luZy52MWJldGExLlN0YWtlQXV0aG9yaXphdGlvbhI5EjUKM2V2bW9zdmFsb3BlcjF1bWs0MDdlZWQ3YWY2YW52dXQ2bGxnMnpldm5mMGRuMGZlcXFueSABEgYI4LD6pgYSfQpZCk8KKC9ldGhlcm1pbnQuY3J5cHRvLnYxLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohA4B2WHbj6sH/GWE7z/YW5PRnXYFGaGRAov7gZZI2Fv2nEgQKAggBGAMSIAoaCgZhZXZtb3MSEDQ1MjE0NzUwMDAwMDAwMDAQ+4QLGkAm17CZgB7m+CPVlITnrHosklMTL9zrUeGRs8FL8N0GcRami9zdJ+e3xuXOtJmwP7G6QNh85CRYjFj8a8lpmmJM" + })"; + assertJSONEqual(output.serialized(), expected); +} +} diff --git a/tests/chains/Evmos/TWAnyAddressTests.cpp b/tests/chains/Evmos/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..de3468d4cce --- /dev/null +++ b/tests/chains/Evmos/TWAnyAddressTests.cpp @@ -0,0 +1,39 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include + +#include + +namespace TW::Evmos::tests { + +TEST(EvmosAnyAddress, EvmosValidate) { + auto string = STRING("0x30627903124Aa1e71384bc52e1cb96E4AB3252b6"); + + EXPECT_TRUE(TWAnyAddressIsValid(string.get(), TWCoinTypeEvmos)); + + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeEvmos)); + + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "30627903124aa1e71384bc52e1cb96e4ab3252b6"); +} + +TEST(EvmosAnyAddress, EvmosCreate) { + auto publicKeyHex = "045a0c6b83b8bd9827e507270cadb499b7e3a9095246f6a2213281f783d877c98b256742741b0639f317768fe4f4c2762660c2112283a7685d815507dee3229173"; // shoot island position ... + const auto publicKey = WRAP(TWPublicKey, TWPublicKeyCreateWithData(DATA(publicKeyHex).get(), TWPublicKeyTypeSECP256k1Extended)); + + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeEvmos)); + + EXPECT_EQ(std::string(TWStringUTF8Bytes(WRAPS(TWAnyAddressDescription(addr.get())).get())), std::string("0x8f348F300873Fd5DA36950B2aC75a26584584feE")); + + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "8f348f300873fd5da36950b2ac75a26584584fee"); +} + +} // namespace TW::Evmos::tests diff --git a/tests/chains/Evmos/TWCoinTypeTests.cpp b/tests/chains/Evmos/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..346d9c98117 --- /dev/null +++ b/tests/chains/Evmos/TWCoinTypeTests.cpp @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include + +namespace TW::Evmos::tests { + +TEST(TWEvmosCoinType, TWCoinTypeEvmos) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeEvmos)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x24af42cf4977a96d62e3a82c3cd9b519c3e7c53dd83398b88f0cb435d867b422")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeEvmos, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x30627903124Aa1e71384bc52e1cb96E4AB3252b6")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeEvmos, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeEvmos)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeEvmos)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeEvmos), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeEvmos)); + + assertStringsEqual(symbol, "EVMOS"); + assertStringsEqual(txUrl, "https://evm.evmos.org/tx/0x24af42cf4977a96d62e3a82c3cd9b519c3e7c53dd83398b88f0cb435d867b422"); + assertStringsEqual(accUrl, "https://evm.evmos.org/address/0x30627903124Aa1e71384bc52e1cb96E4AB3252b6"); + assertStringsEqual(id, "evmos"); + assertStringsEqual(name, "Evmos"); +} + +} // namespace TW::Evmos::tests diff --git a/tests/chains/FIO/AddressTests.cpp b/tests/chains/FIO/AddressTests.cpp new file mode 100644 index 00000000000..aea76097f2e --- /dev/null +++ b/tests/chains/FIO/AddressTests.cpp @@ -0,0 +1,67 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "FIO/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include + +#include + +namespace TW::FIO::tests { + +TEST(FIOAddress, ValidateString) { + ASSERT_FALSE(Address::isValid("abc")); + ASSERT_FALSE(Address::isValid("65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF")); + ASSERT_FALSE(Address::isValid("EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjT")); + + ASSERT_TRUE(Address::isValid("FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o")); +} + +TEST(FIOAddress, ValidateData) { + Address address("FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); + EXPECT_EQ(address.bytes.size(), 37ul); + Data addrData = TW::data(address.bytes.data(), address.bytes.size()); + + EXPECT_EQ(Address::isValid(addrData), true); + + // create invalid data, too short + Data addressDataShort = subData(addrData, 0, addrData.size() - 1); + EXPECT_EQ(Address::isValid(addressDataShort), false); +} + +TEST(FIOAddress, FromString) { + Address addr("FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); + ASSERT_EQ(addr.string(), "FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); + ASSERT_EQ(hex(addr.bytes), "0271195c66ec2799e436757a70cd8431d4b17733a097b18a5f7f1b6b085978ff0f343fc54e"); +} + +TEST(FIOAddress, FromStringInvalid) { + try { + Address address("WRP5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); + } catch (std::invalid_argument&) { + return; // ok + } + ADD_FAILURE() << "Missed expected exeption"; +} + +TEST(FIOAddress, FromPublicKey) { + auto key = PrivateKey(parse_hex("ea8eb60b7e5868e218f248e032769020b4fea5dcfd02f2992861eaf4fb534854")); + auto publicKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(publicKey.bytes), "0271195c66ec2799e436757a70cd8431d4b17733a097b18a5f7f1b6b085978ff0f"); + auto address = Address(publicKey); + + ASSERT_EQ(address.string(), "FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o"); +} + +TEST(FIOAddress, GetPublicKey) { + const auto publicKeyHex = "0271195c66ec2799e436757a70cd8431d4b17733a097b18a5f7f1b6b085978ff0f"; + const PublicKey publicKey(parse_hex(publicKeyHex), TWPublicKeyTypeSECP256k1); + auto address = Address(publicKey); + EXPECT_EQ(hex(address.publicKey().bytes), publicKeyHex); +} + +} // namespace TW::FIO::tests diff --git a/tests/FIO/EncryptionTests.cpp b/tests/chains/FIO/EncryptionTests.cpp similarity index 97% rename from tests/FIO/EncryptionTests.cpp rename to tests/chains/FIO/EncryptionTests.cpp index 55aa29793f1..ac0c2ff9321 100644 --- a/tests/FIO/EncryptionTests.cpp +++ b/tests/chains/FIO/EncryptionTests.cpp @@ -15,8 +15,8 @@ #include -using namespace TW; -using namespace TW::FIO; +namespace TW::FIO::EncryptionTests { + using namespace std; TEST(FIOEncryption, checkEncrypt) { @@ -24,7 +24,7 @@ TEST(FIOEncryption, checkEncrypt) { const Data secret = parse_hex("02332627b9325cb70510a70f0f6be4bcb008fbbc7893ca51dedf5bf46aa740c0fc9d3fbd737d09a3c4046d221f4f1a323f515332c3fef46e7f075db561b1a2c9"); const Data plaintext = TW::data("secret message"); Data iv = parse_hex("f300888ca4f512cebdc0020ff0f7224c"); - + Data result = Encryption::checkEncrypt(secret, plaintext, iv); EXPECT_EQ(hex(result), "f300888ca4f512cebdc0020ff0f7224c7f896315e90e172bed65d005138f224da7301d5563614e3955750e4480aabf7753f44b4975308aeb8e23c31e114962ab"); } @@ -34,7 +34,7 @@ TEST(FIOEncryption, checkDecrypt) { const Data secret = parse_hex("02332627b9325cb70510a70f0f6be4bcb008fbbc7893ca51dedf5bf46aa740c0fc9d3fbd737d09a3c4046d221f4f1a323f515332c3fef46e7f075db561b1a2c9"); const Data encrypted = parse_hex("f300888ca4f512cebdc0020ff0f7224c7f896315e90e172bed65d005138f224da7301d5563614e3955750e4480aabf7753f44b4975308aeb8e23c31e114962ab"); const Data expectedPlaintext = TW::data("secret message"); - + Data result = Encryption::checkDecrypt(secret, encrypted); EXPECT_EQ(hex(result), hex(expectedPlaintext)); } @@ -53,7 +53,7 @@ TEST(FIOEncryption, checkEncryptInvalidIvLength) { TEST(FIOEncryption, checkDecryptInvalidMessageHMAC) { const Data secret = parse_hex("02332627b9325cb70510a70f0f6be4bcb008fbbc7893ca51dedf5bf46aa740c0fc9d3fbd737d09a3c4046d221f4f1a323f515332c3fef46e7f075db561b1a2c9"); const Data encrypted = parse_hex("f300888ca4f512cebdc0020ff0f7224c7f896315e90e172bed65d005138f224da7301d5563614e3955750e4480aabf7753f44b4975308aeb8e23c31e114962ab00"); - try { + try { Encryption::checkDecrypt(secret, encrypted); } catch (std::invalid_argument&) { // expected exception, OK @@ -64,7 +64,7 @@ TEST(FIOEncryption, checkDecryptInvalidMessageHMAC) { TEST(FIOEncryption, checkDecryptMessageTooShort) { const Data secret = parse_hex("02332627b9325cb70510a70f0f6be4bcb008fbbc7893ca51dedf5bf46aa740c0fc9d3fbd737d09a3c4046d221f4f1a323f515332c3fef46e7f075db561b1a2c9"); - try { + try { Encryption::checkDecrypt(secret, Data(60)); } catch (std::invalid_argument&) { // expected exception, OK @@ -75,7 +75,7 @@ TEST(FIOEncryption, checkDecryptMessageTooShort) { Data randomBuffer(size_t size) { Data d(size); - for (auto i = 0; i < size; ++i) { + for (auto i = 0ul; i < size; ++i) { d[i] = (TW::byte)(256.0 * rand() / RAND_MAX); } return d; @@ -116,21 +116,21 @@ TEST(FIOEncryption, getSharedSecret) { const PrivateKey privateKey(parse_hex("2bd806c97f0e00af1a1fc3328fa763a9269723c8db8fac4f93af71db186d6e90")); const PublicKey publicKey(parse_hex("024edfcf9dfe6c0b5c83d1ab3f78d1b39a46ebac6798e08e19761f5ed89ec83c10"), TWPublicKeyTypeSECP256k1); Data secret = Encryption::getSharedSecret(privateKey, publicKey); - EXPECT_EQ(secret.size(), 64); + EXPECT_EQ(secret.size(), 64ul); EXPECT_EQ(hex(secret), "a71b4ec5a9577926a1d2aa1d9d99327fd3b68f6a1ea597200a0d890bd3331df300a2d49fec0b2b3e6969ce9263c5d6cf47c191c1ef149373ecc9f0d98116b598"); } { const PrivateKey privateKey(parse_hex("81b637d8fcd2c6da6359e6963113a1170de795e4b725b84d1e0b4cfd9ec58ce9")); const PublicKey publicKey(parse_hex("039997a497d964fc1a62885b05a51166a65a90df00492c8d7cf61d6accf54803be"), TWPublicKeyTypeSECP256k1); Data secret = Encryption::getSharedSecret(privateKey, publicKey); - EXPECT_EQ(secret.size(), 64); + EXPECT_EQ(secret.size(), 64ul); EXPECT_EQ(hex(secret), "a71b4ec5a9577926a1d2aa1d9d99327fd3b68f6a1ea597200a0d890bd3331df300a2d49fec0b2b3e6969ce9263c5d6cf47c191c1ef149373ecc9f0d98116b598"); } { const PrivateKey privateKey(parse_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); const PublicKey publicKey(parse_hex("03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd"), TWPublicKeyTypeSECP256k1); Data secret = Encryption::getSharedSecret(privateKey, publicKey); - EXPECT_EQ(secret.size(), 64); + EXPECT_EQ(secret.size(), 64ul); EXPECT_EQ(hex(secret), "3f0840df1912e24d85f39008a56550c31403e096fce7fa9d7886fab8e5c2ceb66b4139c8f4f4172fd9f455e76c2e8913a3d734f51a1951090ce9ec660671957d"); } } @@ -170,7 +170,7 @@ TEST(FIOEncryption, encryptEncodeDecodeDecrypt) { EXPECT_EQ(addressBob.string(), "FIO5VE6Dgy9FUmd1mFotXwF88HkQN1KysCWLPqpVnDMjRvGRi1YrM"); const Data message = parse_hex("0b70757273652e616c69636501310a66696f2e7265716f6274000000"); const Data iv = parse_hex("f300888ca4f512cebdc0020ff0f7224c"); - + const Data encrypted = Encryption::encrypt(privateKeyAlice, publicKeyBob, message, iv); EXPECT_EQ(hex(encrypted), "f300888ca4f512cebdc0020ff0f7224c0db2984c4ad9afb12629f01a8c6a76328bbde17405655dc4e3cb30dad272996fb1dea8e662e640be193e25d41147a904c571b664a7381ab41ef062448ac1e205"); const string encoded = Encryption::encode(encrypted); @@ -182,3 +182,5 @@ TEST(FIOEncryption, encryptEncodeDecodeDecrypt) { // verify that decrypted is the same as the original EXPECT_EQ(hex(decrypted), hex(message)); } + +} // namespace TW::FIO::EncryptionTests diff --git a/tests/chains/FIO/SignerTests.cpp b/tests/chains/FIO/SignerTests.cpp new file mode 100644 index 00000000000..871db8d8091 --- /dev/null +++ b/tests/chains/FIO/SignerTests.cpp @@ -0,0 +1,78 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "FIO/Actor.h" +#include "FIO/Signer.h" + +#include "Base58.h" +#include "Hash.h" +#include "HexCoding.h" + +#include + +namespace TW::FIO::tests { + +using namespace std; + +TEST(FIOSigner, SignEncode) { + string sig1 = Signer::signatureToBase58(parse_hex("1f4fccc30bcba876963aef6de584daf7258306c02f4528fe25b116b517de8b349968bdc080cd6bef36f5a46d31a7c01ed0806ad215bb66a94f61e27a895d610983")); + + EXPECT_EQ("SIG_K1_K5hJTPeiV4bDkNR13mf66N2DY5AtVL4NU1iWE4G4dsczY2q68oCcUVxhzFdxjgV2eAeb2jNV1biqtCJ3SaNL8kkNgoZ43H", sig1); +} + +TEST(FIOSigner, SignInternals) { + // 5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf + PrivateKey pk = PrivateKey(parse_hex("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035")); + { + Data pk2 = parse_hex("80"); + append(pk2, pk.bytes); + EXPECT_EQ("5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri", Base58::bitcoin.encodeCheck(pk2)); + } + Data rawData = parse_hex("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd774a26285e19fac10ac5390000000001003056372503a85b0000c6eaa6645232017016f2cc12266c6b00000000a8ed3232bd010f6164616d4066696f746573746e657403034254432a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c7877703730643776034554482a30786365356342366339324461333762624261393142643430443443394434443732344133613846353103424e422a626e6231747333646735346170776c76723968757076326e306a366534367135347a6e6e75736a6b397300000000000000007016f2cc12266c6b0e726577617264734077616c6c6574000000000000000000000000000000000000000000000000000000000000000000"); + Data hash = Hash::sha256(rawData); + EXPECT_EQ("0f3cca0f50da4200b2858f65de1ea4530a9afd9e4bfc0b6b7196e36c25cc7a8b", hex(hash)); + Data sign2 = Signer::signData(pk, rawData); + EXPECT_EQ("1f4ae8d1b993f94d0de4b249d5185481770de0711863ad640b3aac21de598fcc02761c6e5395106bafb7b09aab1c7aa5ac0573dbd821c2d255725391a5105d30d1", hex(sign2)); + + string sigStr = Signer::signatureToBase58(sign2); + EXPECT_EQ("SIG_K1_K54CA1jmhgWrSdvrNrkokPyvqh7dwsSoQHNU9xgD3Ezf6cJySzhKeUubVRqmpYdnjoP1DM6SorroVAgrCu3qqvJ9coAQ6u", sigStr); + EXPECT_TRUE(Signer::verify(pk.getPublicKey(TWPublicKeyTypeSECP256k1), hash, sign2)); +} + +TEST(FIOSigner, Actor) { + { + const auto addr1 = "FIO6cDpi7vPnvRwMEdXtLnAmFwygaQ8CzD7vqKLBJ2GfgtHBQ4PPy"; + Address addr = Address(addr1); + EXPECT_EQ(addr1, addr.string()); + + uint64_t shortenedKey = Actor::shortenKey(addr.bytes); + EXPECT_EQ(1518832697283783336U, shortenedKey); + string name = Actor::name(shortenedKey); + EXPECT_EQ("2odzomo2v4pec", name); + } + const int n = 4; + const string addrArr[n] = { + "FIO6cDpi7vPnvRwMEdXtLnAmFwygaQ8CzD7vqKLBJ2GfgtHBQ4PPy", + "FIO7uMZoeei5HtXAD24C4yCkpWWbf24bjYtrRNjWdmGCXHZccwuiE", + "FIO7bxrQUTbQ4mqcoefhWPz1aFieN4fA9RQAiozRz7FrUChHZ7Rb8", + "FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf", + }; + const string actorArr[n] = { + "2odzomo2v4pe", + "hhq2g4qgycfb", + "5kmx4qbqlpld", + "qdfejz2a5wpl", + }; + for (int i = 0; i < n; ++i) { + Address addr = Address(addrArr[i]); + EXPECT_EQ(addrArr[i], addr.string()); + + string actor = Actor::actor(addr); + EXPECT_EQ(actorArr[i], actor); + } +} + +} // namespace TW::FIO::tests diff --git a/tests/chains/FIO/TWCoinTypeTests.cpp b/tests/chains/FIO/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..2dec77e3723 --- /dev/null +++ b/tests/chains/FIO/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWFIOCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeFIO)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("930d1d3cf8988b39b5f64b64e9d61314a3e05a155d9e3505bdf863aab1adddf3")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeFIO, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("f5axfpgffiqz")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeFIO, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeFIO)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeFIO)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeFIO), 9); + ASSERT_EQ(TWBlockchainFIO, TWCoinTypeBlockchain(TWCoinTypeFIO)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeFIO)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeFIO)); + assertStringsEqual(symbol, "FIO"); + assertStringsEqual(txUrl, "https://explorer.fioprotocol.io/transaction/930d1d3cf8988b39b5f64b64e9d61314a3e05a155d9e3505bdf863aab1adddf3"); + assertStringsEqual(accUrl, "https://explorer.fioprotocol.io/account/f5axfpgffiqz"); + assertStringsEqual(id, "fio"); + assertStringsEqual(name, "FIO"); +} diff --git a/tests/FIO/TWFIOAccountTests.cpp b/tests/chains/FIO/TWFIOAccountTests.cpp similarity index 97% rename from tests/FIO/TWFIOAccountTests.cpp rename to tests/chains/FIO/TWFIOAccountTests.cpp index 3c4764d4801..e9b0f948a09 100644 --- a/tests/FIO/TWFIOAccountTests.cpp +++ b/tests/chains/FIO/TWFIOAccountTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/FIO/TWFIOTests.cpp b/tests/chains/FIO/TWFIOTests.cpp similarity index 91% rename from tests/FIO/TWFIOTests.cpp rename to tests/chains/FIO/TWFIOTests.cpp index 81ea60022a7..9777e10f5c5 100644 --- a/tests/FIO/TWFIOTests.cpp +++ b/tests/chains/FIO/TWFIOTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,23 +10,22 @@ #include "proto/FIO.pb.h" #include "FIO/Address.h" #include "Data.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include "PrivateKey.h" #include -using namespace TW; -using namespace TW::FIO; -using namespace std; +namespace TW::FIO::TWFIOTests { +using namespace std; TEST(TWFIO, Address) { auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035").get())); ASSERT_NE(nullptr, privateKey.get()); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), false)); ASSERT_NE(nullptr, publicKey.get()); - ASSERT_EQ(65, publicKey.get()->impl.bytes.size()); + ASSERT_EQ(65ul, publicKey.get()->impl.bytes.size()); auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeFIO)); auto addressString = WRAPS(TWAnyAddressDescription(address.get())); assertStringsEqual(addressString, "FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf"); @@ -39,7 +38,7 @@ TEST(TWFIO, Address) { ASSERT_TRUE(TWAnyAddressEqual(address.get(), address2.get())); } -const Data chainId = parse_hex("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd77"); +const Data gChainId = parse_hex("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd77"); // 5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf const PrivateKey privKeyBA = PrivateKey(parse_hex("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035")); const PublicKey pubKey6M = privKeyBA.getPublicKey(TWPublicKeyTypeSECP256k1); @@ -48,7 +47,7 @@ const Address addr6M(pubKey6M); TEST(TWFIO, RegisterFioAddress) { Proto::SigningInput input; input.set_expiry(1579784511); - input.mutable_chain_params()->set_chain_id(string(chainId.begin(), chainId.end())); + input.mutable_chain_params()->set_chain_id(string(gChainId.begin(), gChainId.end())); input.mutable_chain_params()->set_head_block_number(39881); input.mutable_chain_params()->set_ref_block_prefix(4279583376); input.set_private_key(string(privKeyBA.bytes.begin(), privKeyBA.bytes.end())); @@ -66,7 +65,7 @@ TEST(TWFIO, RegisterFioAddress) { TEST(TWFIO, AddPubAddress) { Proto::SigningInput input; input.set_expiry(1579729429); - input.mutable_chain_params()->set_chain_id(string(chainId.begin(), chainId.end())); + input.mutable_chain_params()->set_chain_id(string(gChainId.begin(), gChainId.end())); input.mutable_chain_params()->set_head_block_number(11565); input.mutable_chain_params()->set_ref_block_prefix(4281229859); input.set_private_key(string(privKeyBA.bytes.begin(), privKeyBA.bytes.end())); @@ -93,7 +92,7 @@ TEST(TWFIO, AddPubAddress) { TEST(TWFIO, Transfer) { Proto::SigningInput input; input.set_expiry(1579790000); - input.mutable_chain_params()->set_chain_id(string(chainId.begin(), chainId.end())); + input.mutable_chain_params()->set_chain_id(string(gChainId.begin(), gChainId.end())); input.mutable_chain_params()->set_head_block_number(50000); input.mutable_chain_params()->set_ref_block_prefix(4000123456); input.set_private_key(string(privKeyBA.bytes.begin(), privKeyBA.bytes.end())); @@ -111,7 +110,7 @@ TEST(TWFIO, Transfer) { TEST(TWFIO, RenewFioAddress) { Proto::SigningInput input; input.set_expiry(1579785000); - input.mutable_chain_params()->set_chain_id(string(chainId.begin(), chainId.end())); + input.mutable_chain_params()->set_chain_id(string(gChainId.begin(), gChainId.end())); input.mutable_chain_params()->set_head_block_number(39881); input.mutable_chain_params()->set_ref_block_prefix(4279583376); input.set_private_key(string(privKeyBA.bytes.begin(), privKeyBA.bytes.end())); @@ -129,7 +128,7 @@ TEST(TWFIO, RenewFioAddress) { TEST(TWFIO, NewFundsRequest) { Proto::SigningInput input; input.set_expiry(1579785000); - input.mutable_chain_params()->set_chain_id(string(chainId.begin(), chainId.end())); + input.mutable_chain_params()->set_chain_id(string(gChainId.begin(), gChainId.end())); input.mutable_chain_params()->set_head_block_number(39881); input.mutable_chain_params()->set_ref_block_prefix(4279583376); input.set_private_key(string(privKeyBA.bytes.begin(), privKeyBA.bytes.end())); @@ -147,10 +146,11 @@ TEST(TWFIO, NewFundsRequest) { Proto::SigningOutput output; ANY_SIGN(input, TWCoinTypeFIO); - // Packed transacton varies, as there is no way to control encryption IV parameter from this level. + // Packed transaction varies, as there is no way to control encryption IV parameter from this level. // Therefore full equality cannot be checked, tail is cut off. The first N chars are checked, works in this case. EXPECT_EQ( R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff000000000100403ed4aa0ba85b00acba384dbdb89a01102b2f46fca756b200000000a8ed32328802106d6172696f4066696f746573746)", - output.json().substr(0, 195) - ); + output.json().substr(0, 195)); } + +} // namespace TW::FIO::TWFIOTests diff --git a/tests/FIO/TransactionBuilderTests.cpp b/tests/chains/FIO/TransactionBuilderTests.cpp similarity index 83% rename from tests/FIO/TransactionBuilderTests.cpp rename to tests/chains/FIO/TransactionBuilderTests.cpp index 04b36498cf2..ff7b73d9484 100644 --- a/tests/FIO/TransactionBuilderTests.cpp +++ b/tests/chains/FIO/TransactionBuilderTests.cpp @@ -1,32 +1,28 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. #include "FIO/Action.h" +#include "FIO/NewFundsRequest.h" #include "FIO/Transaction.h" #include "FIO/TransactionBuilder.h" -#include "FIO/NewFundsRequest.h" -#include "HexCoding.h" #include "BinaryCoding.h" +#include "HexCoding.h" #include - #include -#include -using namespace TW; -using namespace TW::FIO; +namespace TW::FIO::TransactionBuilderTests { using namespace std; - const Data chainId = parse_hex("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd77"); // 5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf -const PrivateKey privKeyBA = PrivateKey(parse_hex("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035")); -const PublicKey pubKey6M = privKeyBA.getPublicKey(TWPublicKeyTypeSECP256k1); -const Address addr6M(pubKey6M); +const PrivateKey gPrivKeyBA = PrivateKey(parse_hex("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035")); +const PublicKey gPubKey6MA = gPrivKeyBA.getPublicKey(TWPublicKeyTypeSECP256k1); +const Address gAddr6M(gPubKey6MA); TEST(FIOTransactionBuilder, RegisterFioAddressGeneric) { Proto::SigningInput input; @@ -34,10 +30,10 @@ TEST(FIOTransactionBuilder, RegisterFioAddressGeneric) { input.mutable_chain_params()->set_chain_id(string(chainId.begin(), chainId.end())); input.mutable_chain_params()->set_head_block_number(39881); input.mutable_chain_params()->set_ref_block_prefix(4279583376); - input.set_private_key(string(privKeyBA.bytes.begin(), privKeyBA.bytes.end())); + input.set_private_key(string(gPrivKeyBA.bytes.begin(), gPrivKeyBA.bytes.end())); input.set_tpid("rewards@wallet"); input.mutable_action()->mutable_register_fio_address_message()->set_fio_address("adam@fiotestnet"); - input.mutable_action()->mutable_register_fio_address_message()->set_owner_fio_public_key(addr6M.string()); + input.mutable_action()->mutable_register_fio_address_message()->set_owner_fio_public_key(gAddr6M.string()); input.mutable_action()->mutable_register_fio_address_message()->set_fee(5000000000); auto json = TransactionBuilder::sign(input); @@ -49,8 +45,8 @@ TEST(FIOTransactionBuilder, RegisterFioAddress) { ChainParams chainParams{chainId, 39881, 4279583376}; uint64_t fee = 5000000000; - string t = TransactionBuilder::createRegisterFioAddress(addr6M, privKeyBA, "adam@fiotestnet", - chainParams, fee, "rewards@wallet", 1579784511); + string t = TransactionBuilder::createRegisterFioAddress(gAddr6M, gPrivKeyBA, "adam@fiotestnet", + chainParams, fee, "rewards@wallet", 1579784511); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"3f99295ec99b904215ff0000000001003056372503a85b0000c6eaa66498ba01102b2f46fca756b200000000a8ed3232650f6164616d4066696f746573746e65743546494f366d31664d645470526b52426e6564765973685843784c4669433573755255384b44667838787874587032686e7478706e6600f2052a01000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K19ugLriG3ApYgjJCRDsy21p9xgsjbDtqBuZrmAEix9XYzndR1kNbJ6fXCngMJMAhxUHfwHAsPnh58otXiJZkazaM1EkS5"]})", t); } @@ -58,11 +54,8 @@ TEST(FIOTransactionBuilder, RegisterFioAddress) { TEST(FIOTransactionBuilder, AddPubAddress) { ChainParams chainParams{chainId, 11565, 4281229859}; - string t = TransactionBuilder::createAddPubAddress(addr6M, privKeyBA, "adam@fiotestnet", { - {"BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}, - {"ETH", "0xce5cB6c92Da37bbBa91Bd40D4C9D4D724A3a8F51"}, - {"BNB", "bnb1ts3dg54apwlvr9hupv2n0j6e46q54znnusjk9s"}}, - chainParams, 0, "rewards@wallet", 1579729429); + string t = TransactionBuilder::createAddPubAddress(gAddr6M, gPrivKeyBA, "adam@fiotestnet", {{"BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}, {"ETH", "0xce5cB6c92Da37bbBa91Bd40D4C9D4D724A3a8F51"}, {"BNB", "bnb1ts3dg54apwlvr9hupv2n0j6e46q54znnusjk9s"}}, + chainParams, 0, "rewards@wallet", 1579729429); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"15c2285e2d2d23622eff0000000001003056372503a85b0000c6eaa664523201102b2f46fca756b200000000a8ed3232c9010f6164616d4066696f746573746e65740303425443034254432a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c787770373064377603455448034554482a30786365356342366339324461333762624261393142643430443443394434443732344133613846353103424e4203424e422a626e6231747333646735346170776c76723968757076326e306a366534367135347a6e6e75736a6b39730000000000000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K3zimaMKU8cBhVRPw46KM2u7uQWaAKXrnoeYZ7MEbp6sVJcDQmQR2RtdavpUPwkAnYUkd8NqLun8H48tcxZBcTUgkiPGVJ"]})", t); } @@ -73,8 +66,8 @@ TEST(FIOTransactionBuilder, Transfer) { uint64_t amount = 1000000000; uint64_t fee = 250000000; - string t = TransactionBuilder::createTransfer(addr6M, privKeyBA, payee, amount, - chainParams, fee, "rewards@wallet", 1579790000); + string t = TransactionBuilder::createTransfer(gAddr6M, gPrivKeyBA, payee, amount, + chainParams, fee, "rewards@wallet", 1579790000); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"b0ae295e50c3400a6dee00000000010000980ad20ca85be0e1d195ba85e7cd01102b2f46fca756b200000000a8ed32325d3546494f37754d5a6f6565693548745841443234433479436b70575762663234626a597472524e6a57646d474358485a63637775694500ca9a3b0000000080b2e60e00000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K9VRCnvaTYN7vgcoVKVXgyJTdKUGV8hLXgFLoEbvqAcFxy7DXQ1rSnAfEuabi4ATkgmvnpaSBdVFN7TBtM1wrbZYqeJQw9"]})", t); } @@ -83,8 +76,8 @@ TEST(FIOTransactionBuilder, RenewFioAddress) { ChainParams chainParams{chainId, 39881, 4279583376}; uint64_t fee = 3000000000; - string t = TransactionBuilder::createRenewFioAddress(addr6M, privKeyBA, "nick@fiotestnet", - chainParams, fee, "rewards@wallet", 1579785000); + string t = TransactionBuilder::createRenewFioAddress(gAddr6M, gPrivKeyBA, "nick@fiotestnet", + chainParams, fee, "rewards@wallet", 1579785000); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff0000000001003056372503a85b80b1ba2919aea6ba01102b2f46fca756b200000000a8ed32322f0f6e69636b4066696f746573746e6574005ed0b200000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_Jxz7oCJ7Z4ECsxqb2utqBcyP3zPQCeQCBws9wWQjyptUKoWVk2AyCVEqtdMHJwqtLniio5Z7npMnaZB8E4pa2G75P9uGkb"]})", t); } @@ -94,7 +87,7 @@ TEST(FIOTransactionBuilder, NewFundsRequest) { ChainParams chainParams{chainId, 18484, 3712870657}; const Data iv = parse_hex("000102030405060708090a0b0c0d0e0f"); // use fixed iv for testability string t = TransactionBuilder::createNewFundsRequest( - Address("FIO5NMm9Vf3NjYFnhoc7yxTCrLW963KPUCzeMGv3SJ6zR3GMez4ub"), privKeyBA, + Address("FIO5NMm9Vf3NjYFnhoc7yxTCrLW963KPUCzeMGv3SJ6zR3GMez4ub"), gPrivKeyBA, "tag@fiotestnet", "FIO7iYHtDhs45smFgSqLyJ6Zi4D3YG8K5bZGyxmshLCDXXBPbbmJN", "dapixbp@fiotestnet", "14R4wEsGT58chmqo7nB65Dy4je6TiijDWc", "1", "BTC", "payment", "", "", chainParams, 800000000, "", 1583528215, iv); @@ -103,19 +96,19 @@ TEST(FIOTransactionBuilder, NewFundsRequest) { ChainParams chainParams{chainId, 39881, 4279583376}; uint64_t fee = 3000000000; - + const Data iv = parse_hex("000102030405060708090a0b0c0d0e0f"); // use fixed iv for testability - string t = TransactionBuilder::createNewFundsRequest(addr6M, privKeyBA, - "mario@fiotestnet", "FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o", "alice@fiotestnet", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v", - "5", "BTC", "Memo", "Hash", "https://trustwallet.com", - chainParams, fee, "rewards@wallet", 1579785000, iv); + string t = TransactionBuilder::createNewFundsRequest(gAddr6M, gPrivKeyBA, + "mario@fiotestnet", "FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o", "alice@fiotestnet", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v", + "5", "BTC", "Memo", "Hash", "https://trustwallet.com", + chainParams, fee, "rewards@wallet", 1579785000, iv); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff000000000100403ed4aa0ba85b00acba384dbdb89a01102b2f46fca756b200000000a8ed32328802106d6172696f4066696f746573746e657410616c6963654066696f746573746e6574c00141414543417751464267634943516f4c4441304f442f3575342f6b436b7042554c4a44682f546951334d31534f4e4938426668496c4e54766d39354249586d54396f616f7a55632f6c6c3942726e57426563464e767a76766f6d3751577a517250717241645035683433305732716b52355266416555446a704f514732364c347a6936767241553052764855474e382b685779736a6971506b2b7a455a444952534678426268796c69686d59334f4752342f5a46466358484967343241327834005ed0b2000000000c716466656a7a32613577706c0e726577617264734077616c6c657400","signatures":["SIG_K1_Kk79iVcQMpqpVgZwGTmC1rxgCTLy5BDFtHd8FvjRNm2FqNHR9dpeUmPTNqBKGMNG3BsPy4c5u26TuEDpS87SnyMpF43cZk"]})", t); } TEST(FIOTransaction, ActionRegisterFioAddressInternal) { - RegisterFioAddressData radata("adam@fiotestnet", addr6M.string(), - 5000000000, "rewards@wallet", "qdfejz2a5wpl"); + RegisterFioAddressData radata("adam@fiotestnet", gAddr6M.string(), + 5000000000, "rewards@wallet", "qdfejz2a5wpl"); Data ser1; radata.serialize(ser1); EXPECT_EQ( @@ -153,11 +146,8 @@ TEST(FIOTransaction, ActionRegisterFioAddressInternal) { } TEST(FIOTransaction, ActionAddPubAddressInternal) { - AddPubAddressData aadata("adam@fiotestnet", { - {"BTC", "BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}, - {"ETH", "ETH", "0xce5cB6c92Da37bbBa91Bd40D4C9D4D724A3a8F51"}, - {"BNB", "BNB", "bnb1ts3dg54apwlvr9hupv2n0j6e46q54znnusjk9s"}}, - 0, "rewards@wallet", "qdfejz2a5wpl"); + AddPubAddressData aadata("adam@fiotestnet", {{"BTC", "BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}, {"ETH", "ETH", "0xce5cB6c92Da37bbBa91Bd40D4C9D4D724A3a8F51"}, {"BNB", "BNB", "bnb1ts3dg54apwlvr9hupv2n0j6e46q54znnusjk9s"}}, + 0, "rewards@wallet", "qdfejz2a5wpl"); Data ser1; aadata.serialize(ser1); EXPECT_EQ( @@ -196,21 +186,21 @@ TEST(FIOTransaction, ActionAddPubAddressInternal) { TEST(FIONewFundsContent, serialize) { { - NewFundsContent newFunds {"bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v", "10", "BTC", "BTC", "Memo", "Hash", "https://trustwallet.com"}; + NewFundsContent newFunds{"bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v", "10", "BTC", "BTC", "Memo", "Hash", "https://trustwallet.com"}; Data ser; newFunds.serialize(ser); EXPECT_EQ(hex(ser), "2a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c78777037306437760231300342544303425443044d656d6f04486173681768747470733a2f2f747275737477616c6c65742e636f6d0000000000"); } { // empty struct - NewFundsContent newFunds {"", "", "", "", "", "", ""}; + NewFundsContent newFunds{"", "", "", "", "", "", ""}; Data ser; newFunds.serialize(ser); EXPECT_EQ(hex(ser), "000000000000000000000000"); } { // test from https://github.com/fioprotocol/fiojs/blob/master/src/tests/encryption-fio.test.ts - NewFundsContent newFunds {"purse.alice", "1", "", "fio.reqobt", "", "", ""}; + NewFundsContent newFunds{"purse.alice", "1", "", "fio.reqobt", "", "", ""}; Data ser; newFunds.serialize(ser); EXPECT_EQ(hex(ser), "0b70757273652e616c6963650131000a66696f2e7265716f62740000000000000000"); @@ -241,18 +231,17 @@ TEST(FIONewFundsContent, deserialize) { TEST(FIOTransactionBuilder, expirySetDefault) { uint32_t expiry = 1579790000; EXPECT_EQ(TransactionBuilder::expirySetDefaultIfNeeded(expiry), false); - EXPECT_EQ(expiry, 1579790000); + EXPECT_EQ(expiry, 1579790000ul); expiry = 0; - EXPECT_EQ(expiry, 0); + EXPECT_EQ(expiry, 0ul); EXPECT_EQ(TransactionBuilder::expirySetDefaultIfNeeded(expiry), true); EXPECT_TRUE(expiry > 1579790000); } // May throw nlohmann::json::type_error void createTxWithChainParam(const ChainParams& paramIn, ChainParams& paramOut) { - string tx = TransactionBuilder::createAddPubAddress(addr6M, privKeyBA, "adam@fiotestnet", { - {"BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}}, - paramIn, 0, "rewards@wallet", 1579729429); + string tx = TransactionBuilder::createAddPubAddress(gAddr6M, gPrivKeyBA, "adam@fiotestnet", {{"BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}}, + paramIn, 0, "rewards@wallet", 1579729429); // retrieve chain params from encoded tx; parse out packed tx try { nlohmann::json txJson = nlohmann::json::parse(tx); @@ -350,9 +339,11 @@ TEST(FIOTransactionBuilder, encodeString) { { Data data; const string text = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"; - EXPECT_EQ(text.length(), 130); + EXPECT_EQ(text.length(), 130ul); TW::FIO::encodeString(text, data); // length on 2 bytes EXPECT_EQ(hex(data), "8201" + hex(text)); } } + +} // namespace TW::FIO::TransactionBuilderTests diff --git a/tests/chains/Fantom/TWCoinTypeTests.cpp b/tests/chains/Fantom/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..15dcde31182 --- /dev/null +++ b/tests/chains/Fantom/TWCoinTypeTests.cpp @@ -0,0 +1,30 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWFantomCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeFantom)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb0a741d882291951de1fac72e90b9baf886ddb0c9c87658a0c206490dfaa5202")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeFantom, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x9474feb9917b87da6f0d830ba66ee0035835c0d3")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeFantom, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeFantom)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeFantom)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeFantom), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeFantom)); + + assertStringsEqual(symbol, "FTM"); + assertStringsEqual(txUrl, "https://ftmscan.com/tx/0xb0a741d882291951de1fac72e90b9baf886ddb0c9c87658a0c206490dfaa5202"); + assertStringsEqual(accUrl, "https://ftmscan.com/address/0x9474feb9917b87da6f0d830ba66ee0035835c0d3"); + assertStringsEqual(id, "fantom"); + assertStringsEqual(name, "Fantom"); +} diff --git a/tests/chains/Filecoin/AddressTests.cpp b/tests/chains/Filecoin/AddressTests.cpp new file mode 100644 index 00000000000..be2bd2355b9 --- /dev/null +++ b/tests/chains/Filecoin/AddressTests.cpp @@ -0,0 +1,101 @@ +// Copyright © 2017-2020 Trust. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Filecoin/Address.h" +#include "HexCoding.h" + +#include +#include + +using namespace TW; + +namespace TW::Filecoin::tests { +// clang-format off + +struct address_test { + std::string encoded; + const char* hex; +}; + +static const address_test validAddresses[] = { + // ID addresses + {"f00", "0000"}, + {"f01", "0001"}, + {"f010", "000a"}, + {"f0150", "009601"}, + {"f0499", "00f303"}, + {"f01024", "008008"}, + {"f01729", "00c10d"}, + {"f0999999", "00bf843d"}, + {"f018446744073709551615", "00ffffffffffffffffff01"}, + // secp256k1 addresses + {"f15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq", "01ea0f0ea039b291a0f08fd179e0556a8c3277c0d3"}, + {"f12fiakbhe2gwd5cnmrenekasyn6v5tnaxaqizq6a", "01d1500504e4d1ac3e89ac891a4502586fabd9b417"}, + {"f1wbxhu3ypkuo6eyp6hjx6davuelxaxrvwb2kuwva", "01b06e7a6f0f551de261fe3a6fe182b422ee0bc6b6"}, + {"f1xtwapqc6nh4si2hcwpr3656iotzmlwumogqbuaa", "01bcec07c05e69f92468e2b3e3bf77c874f2c5da8c"}, + {"f1xcbgdhkgkwht3hrrnui3jdopeejsoatkzmoltqy", "01b882619d46558f3d9e316d11b48dcf211327026a"}, + {"f17uoq6tp427uzv7fztkbsnn64iwotfrristwpryy", "01fd1d0f4dfcd7e99afcb99a8326b7dc459d32c628"}, + // Actor addresses + {"f24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as6i", "02e54dea4f9bc5b47d261819826d5e1fbf8bc5503b"}, + {"f25nml2cfbljvn4goqtclhifepvfnicv6g7mfmmvq", "02eb58bd08a15a6ade19d0989674148fa95a8157c6"}, + {"f2nuqrg7vuysaue2pistjjnt3fadsdzvyuatqtfei", "026d21137eb4c4814269e894d296cf6500e43cd714"}, + {"f24dd4ox4c2vpf5vk5wkadgyyn6qtuvgcpxxon64a", "02e0c7c75f82d55e5ed55db28033630df4274a984f"}, + {"f2gfvuyh7v2sx3patm5k23wdzmhyhtmqctasbr23y", "02316b4c1ff5d4afb7826ceab5bb0f2c3e0f364053"}, + // BLS addresses + {"f3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss4a","03ad58df696e2d4e91ea86c881e938ba4ea81b395e12797b84b9cf314b9546705e839c7a99d606b247ddb4f9ac7a3414dd"}, + {"f3wmuu6crofhqmm3v4enos73okk2l366ck6yc4owxwbdtkmpk42ohkqxfitcpa57pjdcftql4tojda2poeruwa","03b3294f0a2e29e0c66ebc235d2fedca5697bf784af605c75af608e6a63d5cd38ea85ca8989e0efde9188b382f9372460d"}, + {"f3s2q2hzhkpiknjgmf4zq3ejab2rh62qbndueslmsdzervrhapxr7dftie4kpnpdiv2n6tvkr743ndhrsw6d3a","0396a1a3e4ea7a14d49985e661b22401d44fed402d1d0925b243c923589c0fbc7e32cd04e29ed78d15d37d3aaa3fe6da33"}, + {"f3q22fijmmlckhl56rn5nkyamkph3mcfu5ed6dheq53c244hfmnq2i7efdma3cj5voxenwiummf2ajlsbxc65a","0386b454258c589475f7d16f5aac018a79f6c1169d20fc33921dd8b5ce1cac6c348f90a3603624f6aeb91b64518c2e8095"}, + {"f3u5zgwa4ael3vuocgc5mfgygo4yuqocrntuuhcklf4xzg5tcaqwbyfabxetwtj4tsam3pbhnwghyhijr5mixa","03a7726b038022f75a384617585360cee629070a2d9d28712965e5f26ecc40858382803724ed34f2720336f09db631f074"}, +}; + +static const std::string invalidAddresses[] = { + "", + "f0-1", // Negative :) + "f018446744073709551616", // Greater than max uint64_t + "f15ihq5ibzwki2b4ep2f46avlkr\0zhpqgtga7pdrq", // Embedded NUL + "t15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq", // Test net + "a15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq", // Unknown net + "f95ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7pdrq", // Unknown address type + // Invalid checksum cases + "f15ihq5ibzwki2b4ep2f46avlkrqzhpqgtga7rdrr", + "f24vg6ut43yw2h2jqydgbg2xq7x6f4kub3bg6as66", + "f3vvmn62lofvhjd2ugzca6sof2j2ubwok6cj4xxbfzz4yuxfkgobpihhd2thlanmsh3w2ptld2gqkn2jvlss44", +}; + +TEST(FilecoinAddress, IsValid) { + for (const auto& test : validAddresses) { + ASSERT_TRUE(Address::isValid(test.encoded)) + << "is_valid() != true: " << test.encoded << std::endl; + ASSERT_TRUE(Address::isValid(parse_hex(test.hex))) + << "is_valid() != true: " << test.hex << std::endl; + } + for (const auto& address : invalidAddresses) + ASSERT_FALSE(Address::isValid(address)) << "is_valid() != false: " << address << std::endl; + + ASSERT_FALSE(Address::isValid(parse_hex("00"))) << "Empty varuint"; + ASSERT_FALSE(Address::isValid(parse_hex("00ff"))) << "Short varuint"; + ASSERT_FALSE(Address::isValid(parse_hex("00ff00ff"))) << "Varuint with hole"; + ASSERT_FALSE(Address::isValid(parse_hex("000101"))) << "Long varuint"; + ASSERT_FALSE(Address::isValid(parse_hex("000000"))) << "Long varuint"; + ASSERT_FALSE(Address::isValid(parse_hex("00ffffffffffffffffff02"))) << "Overflow"; +} + +TEST(FilecoinAddress, String) { + for (const auto& test : validAddresses) { + Address a(parse_hex(test.hex)); + ASSERT_EQ(a.string(), test.encoded) << "Address(" << test.hex << ")"; + } +} + +TEST(FilecoinAddress, FromString) { + for (const auto& test : validAddresses) { + Address a(test.encoded); + ASSERT_EQ(hex(a.bytes), test.hex) << "Address(" << test.encoded << ")"; + } +} + +} diff --git a/tests/Filecoin/SignerTests.cpp b/tests/chains/Filecoin/SignerTests.cpp similarity index 100% rename from tests/Filecoin/SignerTests.cpp rename to tests/chains/Filecoin/SignerTests.cpp diff --git a/tests/chains/Filecoin/TWAnySignerTests.cpp b/tests/chains/Filecoin/TWAnySignerTests.cpp new file mode 100644 index 00000000000..a4af47a4332 --- /dev/null +++ b/tests/chains/Filecoin/TWAnySignerTests.cpp @@ -0,0 +1,72 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include + +#include "Data.h" +#include "HexCoding.h" +#include "proto/Filecoin.pb.h" +#include "uint256.h" + +#include + +using namespace TW; + +namespace TW::Filecoin::tests { + +TEST(TWAnySignerFilecoin, Sign) { + Proto::SigningInput input; + auto privateKey = parse_hex("1d969865e189957b9824bd34f26d5cbf357fda1a6d844cbf0c9ab1ed93fa7dbe"); + auto toAddress = + "f3um6uo3qt5of54xjbx3hsxbw5mbsc6auxzrvfxekn5bv3duewqyn2tg5rhrlx73qahzzpkhuj7a34iq7oifsq"; + uint64_t nonce = 2; + // 600 FIL + // auto value = parse_hex("2086ac351052600000"); + auto value = store(uint256_t(600) * uint256_t(1'000'000'000) * uint256_t(1'000'000'000)); + uint64_t gasLimit = 1000; + // auto gasFeeCap = parse_hex("25f273933db5700000"); + auto gasFeeCap = store(uint256_t(700) * uint256_t(1'000'000'000) * uint256_t(1'000'000'000)); + // auto gasPremium = parse_hex("2b5e3af16b18800000"); + auto gasPremium = store(uint256_t(800) * uint256_t(1'000'000'000) * uint256_t(1'000'000'000)); + + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_to(toAddress); + input.set_nonce(nonce); + input.set_value(value.data(), value.size()); + input.set_gas_limit(gasLimit); + input.set_gas_fee_cap(gasFeeCap.data(), gasFeeCap.size()); + input.set_gas_premium(gasPremium.data(), gasPremium.size()); + + auto inputString = input.SerializeAsString(); + auto inputData = WRAPD(TWDataCreateWithBytes((const byte*)inputString.data(), inputString.size())); + + auto outputData = WRAPD(TWAnySignerSign(inputData.get(), TWCoinTypeFilecoin)); + + Proto::SigningOutput output; + output.ParseFromArray(TWDataBytes(outputData.get()), static_cast(TWDataSize(outputData.get()))); + + ASSERT_EQ(output.json(), R"({"Message":{"From":"f1z4a36sc7mfbv4z3qwutblp2flycdui3baffytbq","GasFeeCap":"700000000000000000000","GasLimit":1000,"GasPremium":"800000000000000000000","Nonce":2,"To":"f3um6uo3qt5of54xjbx3hsxbw5mbsc6auxzrvfxekn5bv3duewqyn2tg5rhrlx73qahzzpkhuj7a34iq7oifsq","Value":"600000000000000000000"},"Signature":{"Data":"jMRu+OZ/lfppgmqSfGsntFrRLWZnUg3ZYmJTTRLsVt4V1310vR3VKGJpaE6S4sNvDOE6sEgmN9YmfTkPVK2qMgE=","Type":1}})"); +} + +TEST(TWAnySignerFilecoin, SignJSON) { + auto json = STRING( + R"({ + "to": "f3um6uo3qt5of54xjbx3hsxbw5mbsc6auxzrvfxekn5bv3duewqyn2tg5rhrlx73qahzzpkhuj7a34iq7oifsq", + "nonce": "2", + "value": "IIasNRBSYAAA", + "gasLimit": 1000, + "gasFeeCap": "JfJzkz21cAAA", + "gasPremium": "K1468WsYgAAA" + })"); + auto key = DATA("1d969865e189957b9824bd34f26d5cbf357fda1a6d844cbf0c9ab1ed93fa7dbe"); + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeFilecoin)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeFilecoin)); + assertStringsEqual(result, R"({"Message":{"From":"f1z4a36sc7mfbv4z3qwutblp2flycdui3baffytbq","GasFeeCap":"700000000000000000000","GasLimit":1000,"GasPremium":"800000000000000000000","Nonce":2,"To":"f3um6uo3qt5of54xjbx3hsxbw5mbsc6auxzrvfxekn5bv3duewqyn2tg5rhrlx73qahzzpkhuj7a34iq7oifsq","Value":"600000000000000000000"},"Signature":{"Data":"jMRu+OZ/lfppgmqSfGsntFrRLWZnUg3ZYmJTTRLsVt4V1310vR3VKGJpaE6S4sNvDOE6sEgmN9YmfTkPVK2qMgE=","Type":1}})"); +} + +} // namespace TW::Filecoin::tests diff --git a/tests/chains/Filecoin/TWCoinTypeTests.cpp b/tests/chains/Filecoin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..16d5499b5b1 --- /dev/null +++ b/tests/chains/Filecoin/TWCoinTypeTests.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + +TEST(TWFilecoinCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeFilecoin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("bafy2bzacedsgjcd6xfhrrymmfrqubb44otlyhvgqkgsh533d5j5hwniiqespm")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeFilecoin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("f1abjxfbp274xpdqcpuaykwkfb43omjotacm2p3za")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeFilecoin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeFilecoin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeFilecoin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeFilecoin), 18); + ASSERT_EQ(TWBlockchainFilecoin, TWCoinTypeBlockchain(TWCoinTypeFilecoin)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeFilecoin)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeFilecoin)); + assertStringsEqual(symbol, "FIL"); + assertStringsEqual(txUrl, "https://filfox.info/en/message/bafy2bzacedsgjcd6xfhrrymmfrqubb44otlyhvgqkgsh533d5j5hwniiqespm"); + assertStringsEqual(accUrl, "https://filfox.info/en/address/f1abjxfbp274xpdqcpuaykwkfb43omjotacm2p3za"); + assertStringsEqual(id, "filecoin"); + assertStringsEqual(name, "Filecoin"); +} diff --git a/tests/Filecoin/TransactionTests.cpp b/tests/chains/Filecoin/TransactionTests.cpp similarity index 100% rename from tests/Filecoin/TransactionTests.cpp rename to tests/chains/Filecoin/TransactionTests.cpp diff --git a/tests/chains/Firo/TWCoinTypeTests.cpp b/tests/chains/Firo/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..2f22a9c23ac --- /dev/null +++ b/tests/chains/Firo/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWFiroCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeFiro)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("09a60d58b3d17519a42a8eca60750c33b710ca8f3ca71994192e05c248a2a111")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeFiro, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a8ULhhDgfdSiXJhSZVdhb8EuDc6R3ogsaM")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeFiro, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeFiro)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeFiro)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeFiro), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeFiro)); + ASSERT_EQ(0x7, TWCoinTypeP2shPrefix(TWCoinTypeFiro)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeFiro)); + assertStringsEqual(symbol, "FIRO"); + assertStringsEqual(txUrl, "https://explorer.firo.org/tx/09a60d58b3d17519a42a8eca60750c33b710ca8f3ca71994192e05c248a2a111"); + assertStringsEqual(accUrl, "https://explorer.firo.org/address/a8ULhhDgfdSiXJhSZVdhb8EuDc6R3ogsaM"); + assertStringsEqual(id, "firo"); + assertStringsEqual(name, "Firo"); +} diff --git a/tests/Zcoin/TWZCoinAddressTests.cpp b/tests/chains/Firo/TWZCoinAddressTests.cpp similarity index 84% rename from tests/Zcoin/TWZCoinAddressTests.cpp rename to tests/chains/Firo/TWZCoinAddressTests.cpp index 1957520ac6e..33ccf2516e8 100644 --- a/tests/Zcoin/TWZCoinAddressTests.cpp +++ b/tests/chains/Firo/TWZCoinAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include @@ -19,7 +19,7 @@ TEST(TWZCoin, Address) { auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA("a22ddec5c567b4488bb00f69b6146c50da2ee883e2c096db098726394d585730").get())); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), true)); - auto address = WRAP(TWBitcoinAddress, TWBitcoinAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeP2pkhPrefix(TWCoinTypeZcoin))); + auto address = WRAP(TWBitcoinAddress, TWBitcoinAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeP2pkhPrefix(TWCoinTypeFiro))); auto addressString = WRAPS(TWBitcoinAddressDescription(address.get())); assertStringsEqual(addressString, "aAbqxogrjdy2YHVcnQxFHMzqpt2fhjCTVT"); } @@ -30,8 +30,8 @@ TEST(TWZCoin, ExtendedKeys) { STRING("TREZOR").get() )); - auto xpub = WRAPS(TWHDWalletGetExtendedPublicKey(wallet.get(), TWPurposeBIP44, TWCoinTypeZcoin, TWHDVersionXPUB)); - auto xprv = WRAPS(TWHDWalletGetExtendedPrivateKey(wallet.get(), TWPurposeBIP44, TWCoinTypeZcoin, TWHDVersionXPRV)); + auto xpub = WRAPS(TWHDWalletGetExtendedPublicKey(wallet.get(), TWPurposeBIP44, TWCoinTypeFiro, TWHDVersionXPUB)); + auto xprv = WRAPS(TWHDWalletGetExtendedPrivateKey(wallet.get(), TWPurposeBIP44, TWCoinTypeFiro, TWHDVersionXPRV)); assertStringsEqual(xpub, "xpub6Cb8Q6pDeS8PdKNbDv9Hvq4WpJXL3JvKvmHHwR1wD2H543hiCUE1f1tB5AXE6yg13k7xZ6PzEXMNUFHXk6kkx4RYte8VB1i4tCX9rwQVR4a"); assertStringsEqual(xprv, "xprv9ybmzbHKp4a6QqJ87tcHZh7nGGgqdrCUZYMh92cKegk6BFNZevum7DZhDuVDqqMdcBT9B4wJSEmwJW9JNdkMcUUjEWKqppxNrJjKFSyKsCr"); @@ -39,13 +39,13 @@ TEST(TWZCoin, ExtendedKeys) { TEST(TWZcoin, DeriveFromXpub) { auto xpub = STRING("xpub6Cb8Q6pDeS8PdKNbDv9Hvq4WpJXL3JvKvmHHwR1wD2H543hiCUE1f1tB5AXE6yg13k7xZ6PzEXMNUFHXk6kkx4RYte8VB1i4tCX9rwQVR4a"); - auto pubKey3 = WRAP(TWPublicKey, TWHDWalletGetPublicKeyFromExtended(xpub.get(), TWCoinTypeZcoin, STRING("m/44'/136'/0'/0/3").get())); - auto pubKey5 = WRAP(TWPublicKey, TWHDWalletGetPublicKeyFromExtended(xpub.get(), TWCoinTypeZcoin, STRING("m/44'/136'/0'/0/5").get())); + auto pubKey3 = WRAP(TWPublicKey, TWHDWalletGetPublicKeyFromExtended(xpub.get(), TWCoinTypeFiro, STRING("m/44'/136'/0'/0/3").get())); + auto pubKey5 = WRAP(TWPublicKey, TWHDWalletGetPublicKeyFromExtended(xpub.get(), TWCoinTypeFiro, STRING("m/44'/136'/0'/0/5").get())); - auto address3 = WRAP(TWBitcoinAddress, TWBitcoinAddressCreateWithPublicKey(pubKey3.get(), TWCoinTypeP2pkhPrefix(TWCoinTypeZcoin))); + auto address3 = WRAP(TWBitcoinAddress, TWBitcoinAddressCreateWithPublicKey(pubKey3.get(), TWCoinTypeP2pkhPrefix(TWCoinTypeFiro))); auto address3String = WRAPS(TWBitcoinAddressDescription(address3.get())); - auto address5 = WRAP(TWBitcoinAddress, TWBitcoinAddressCreateWithPublicKey(pubKey5.get(), TWCoinTypeP2pkhPrefix(TWCoinTypeZcoin))); + auto address5 = WRAP(TWBitcoinAddress, TWBitcoinAddressCreateWithPublicKey(pubKey5.get(), TWCoinTypeP2pkhPrefix(TWCoinTypeFiro))); auto address5String = WRAPS(TWBitcoinAddressDescription(address5.get())); assertStringsEqual(address3String, "aLnztJEbyACnxF9H7SFC8YjUxedwyQsgVm"); @@ -53,11 +53,11 @@ TEST(TWZcoin, DeriveFromXpub) { } TEST(TWZcoin, LockScripts) { - auto script2 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("a4YtT82mWWxHZhLmdx7e5aroW92dqJoRs3").get(), TWCoinTypeZcoin)); + auto script2 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("a4YtT82mWWxHZhLmdx7e5aroW92dqJoRs3").get(), TWCoinTypeFiro)); auto scriptData2 = WRAPD(TWBitcoinScriptData(script2.get())); assertHexEqual(scriptData2, "76a9142a10f88e30768d2712665c279922b9621ce58bc788ac"); - auto script3 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("4CFa4fnAQvFz4VpikGNzQ9XfCDXMmdk6sh").get(), TWCoinTypeZcoin)); + auto script3 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("4CFa4fnAQvFz4VpikGNzQ9XfCDXMmdk6sh").get(), TWCoinTypeFiro)); auto scriptData3 = WRAPD(TWBitcoinScriptData(script3.get())); assertHexEqual(scriptData3, "a914f010b17a9189e0f2737d71ae9790359eb5bbc13787"); } diff --git a/tests/chains/GoChain/TWCoinTypeTests.cpp b/tests/chains/GoChain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..70e8170b723 --- /dev/null +++ b/tests/chains/GoChain/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWGoChainCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeGoChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeGoChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeGoChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeGoChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeGoChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeGoChain), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeGoChain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeGoChain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeGoChain)); + assertStringsEqual(symbol, "GO"); + assertStringsEqual(txUrl, "https://explorer.gochain.io/tx/t123"); + assertStringsEqual(accUrl, "https://explorer.gochain.io/addr/a12"); + assertStringsEqual(id, "gochain"); + assertStringsEqual(name, "GoChain"); +} diff --git a/tests/chains/Groestlcoin/AddressTests.cpp b/tests/chains/Groestlcoin/AddressTests.cpp new file mode 100644 index 00000000000..a07719aaa0c --- /dev/null +++ b/tests/chains/Groestlcoin/AddressTests.cpp @@ -0,0 +1,46 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Groestlcoin/Address.h" + +#include "Coin.h" +#include "HDWallet.h" +#include "HexCoding.h" + +#include + +namespace TW::Groestlcoin::tests { + +TEST(GroestlcoinAddress, FromPublicKey) { + const auto publicKey = PublicKey(parse_hex("03b85cc59b67c35851eb5060cfc3a759a482254553c5857075c9e247d74d412c91"), TWPublicKeyTypeSECP256k1); + const auto address = Address(publicKey, 36); + ASSERT_EQ(address.string(), "Fj62rBJi8LvbmWu2jzkaUX1NFXLEqDLoZM"); +} + +TEST(GroestlcoinAddress, Valid) { + ASSERT_TRUE(Address::isValid(std::string("Fj62rBJi8LvbmWu2jzkaUX1NFXLEqDLoZM"))); +} + +TEST(GroestlcoinAddress, Invalid) { + ASSERT_FALSE(Address::isValid(std::string("1JAd7XCBzGudGpJQSDSfpmJhiygtLQWaGL"))); // Valid bitcoin address +} + +TEST(GroestlcoinAddress, FromString) { + const auto string = "Fj62rBJi8LvbmWu2jzkaUX1NFXLEqDLoZM"; + const auto address = Address(string); + + ASSERT_EQ(address.string(), string); +} + +TEST(GroestlcoinAddress, Derive) { + const auto mnemonic = "all all all all all all all all all all all all"; + const auto wallet = HDWallet(mnemonic, ""); + const auto path = TW::derivationPath(TWCoinTypeGroestlcoin); + const auto address = TW::deriveAddress(TWCoinTypeGroestlcoin, wallet.getKey(TWCoinTypeGroestlcoin, path)); + ASSERT_EQ(address, "grs1qw4teyraux2s77nhjdwh9ar8rl9dt7zww8r6lne"); +} + +} // namespace TW::Groestlcoin::tests diff --git a/tests/chains/Groestlcoin/TWCoinTypeTests.cpp b/tests/chains/Groestlcoin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..2cb15c927b2 --- /dev/null +++ b/tests/chains/Groestlcoin/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWGroestlcoinCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeGroestlcoin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeGroestlcoin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeGroestlcoin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeGroestlcoin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeGroestlcoin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeGroestlcoin), 8); + ASSERT_EQ(TWBlockchainGroestlcoin, TWCoinTypeBlockchain(TWCoinTypeGroestlcoin)); + ASSERT_EQ(0x5, TWCoinTypeP2shPrefix(TWCoinTypeGroestlcoin)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeGroestlcoin)); + assertStringsEqual(symbol, "GRS"); + assertStringsEqual(txUrl, "https://blockchair.com/groestlcoin/transaction/t123"); + assertStringsEqual(accUrl, "https://blockchair.com/groestlcoin/address/a12"); + assertStringsEqual(id, "groestlcoin"); + assertStringsEqual(name, "Groestlcoin"); +} diff --git a/tests/Groestlcoin/TWGroestlcoinSigningTests.cpp b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp similarity index 98% rename from tests/Groestlcoin/TWGroestlcoinSigningTests.cpp rename to tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp index 7e81ec429b1..994c8977942 100644 --- a/tests/Groestlcoin/TWGroestlcoinSigningTests.cpp +++ b/tests/chains/Groestlcoin/TWGroestlcoinSigningTests.cpp @@ -4,16 +4,13 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Bitcoin/OutPoint.h" #include "Bitcoin/Script.h" #include "Hash.h" #include "HexCoding.h" #include "PrivateKey.h" #include "proto/Bitcoin.pb.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "../Bitcoin/TxComparisonHelper.h" - -#include #include #include #include @@ -21,8 +18,7 @@ #include -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { TEST(GroestlcoinSigning, SignP2WPKH) { Proto::SigningInput input; @@ -187,3 +183,5 @@ TEST(GroestlcoinSigning, PlanP2WPKH) { EXPECT_TRUE(verifyPlan(plan, {4774}, 2500, 145)); EXPECT_EQ(plan.branch_id(), ""); } + +} // namespace TW::Bitcoin diff --git a/tests/Groestlcoin/TWGroestlcoinTests.cpp b/tests/chains/Groestlcoin/TWGroestlcoinTests.cpp similarity index 99% rename from tests/Groestlcoin/TWGroestlcoinTests.cpp rename to tests/chains/Groestlcoin/TWGroestlcoinTests.cpp index a00b684b019..e685bc59646 100644 --- a/tests/Groestlcoin/TWGroestlcoinTests.cpp +++ b/tests/chains/Groestlcoin/TWGroestlcoinTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/Harmony/AddressTests.cpp b/tests/chains/Harmony/AddressTests.cpp new file mode 100644 index 00000000000..eac8744c266 --- /dev/null +++ b/tests/chains/Harmony/AddressTests.cpp @@ -0,0 +1,47 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Harmony/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" + +#include + +using namespace TW; + +namespace TW::Harmony::tests { + +TEST(HarmonyAddress, FromString) { + Address sender; + ASSERT_TRUE(Address::decode("one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe", sender)); + Address receiver; + ASSERT_TRUE(Address::decode("one1tp7xdd9ewwnmyvws96au0e7e7mz6f8hjqr3g3p", receiver)); + + ASSERT_EQ("ed1ebe4fd1f73f86388f231997859ca42c07da5d", hex(sender.getKeyHash())); + ASSERT_EQ("587c66b4b973a7b231d02ebbc7e7d9f6c5a49ef2", hex(receiver.getKeyHash())); +} + +TEST(HarmonyAddress, FromData) { + const auto address = Address(parse_hex("0x587c66b4b973a7b231d02ebbc7e7d9f6c5a49ef2")); + const auto address_2 = Address(parse_hex("0xed1ebe4fd1f73f86388f231997859ca42c07da5d")); + ASSERT_EQ(address.string(), "one1tp7xdd9ewwnmyvws96au0e7e7mz6f8hjqr3g3p"); + ASSERT_EQ(address_2.string(), "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe"); +} + +TEST(HarmonyAddress, InvalidHarmonyAddress) { + ASSERT_FALSE(Address::isValid("one1a50tun737ulcvwy0yvve0pe")); + ASSERT_FALSE(Address::isValid("oe1tp7xdd9ewwnmyvws96au0ee7e7mz6f8hjqr3g3p")); +} + +TEST(HarmonyAddress, FromPrivateKey) { + const auto privateKey = + PrivateKey(parse_hex("e2f88b4974ae763ca1c2db49218802c2e441293a09eaa9ab681779e05d1b7b94")); + const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); + const auto address = Address(publicKey); + ASSERT_EQ(address.string(), "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe"); +} + +} // namespace TW::Harmony::tests diff --git a/tests/Harmony/SignerTests.cpp b/tests/chains/Harmony/SignerTests.cpp similarity index 100% rename from tests/Harmony/SignerTests.cpp rename to tests/chains/Harmony/SignerTests.cpp diff --git a/tests/Harmony/StakingTests.cpp b/tests/chains/Harmony/StakingTests.cpp similarity index 100% rename from tests/Harmony/StakingTests.cpp rename to tests/chains/Harmony/StakingTests.cpp diff --git a/tests/chains/Harmony/TWAnyAddressTests.cpp b/tests/chains/Harmony/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..a289284b229 --- /dev/null +++ b/tests/chains/Harmony/TWAnyAddressTests.cpp @@ -0,0 +1,23 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include + +#include + +TEST(HarmonyAnyAddress, Harmony) { + auto string = STRING("one1c8dpswxg2p50znzecnq0peuxlxtcm9je7q7yje"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeHarmony)); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + + EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); + + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "c1da1838c85068f14c59c4c0f0e786f9978d9659"); +} diff --git a/tests/chains/Harmony/TWAnySignerTests.cpp b/tests/chains/Harmony/TWAnySignerTests.cpp new file mode 100644 index 00000000000..3edae0c8247 --- /dev/null +++ b/tests/chains/Harmony/TWAnySignerTests.cpp @@ -0,0 +1,77 @@ + +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Harmony.pb.h" +#include "uint256.h" +#include "TestUtilities.h" +#include + +#include + +using namespace TW; + +namespace TW::Harmony::tests { + +static auto TEST_RECEIVER = "one129r9pj3sk0re76f7zs3qz92rggmdgjhtwge62k"; + +static uint256_t LOCAL_NET = 0x2; + +TEST(TWAnySignerHarmony, Sign) { + Proto::SigningInput input; + + auto transactionMessage = input.mutable_transaction_message(); + transactionMessage->set_to_address(TEST_RECEIVER); + const auto privateKey = parse_hex("4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48"); + + input.set_private_key(privateKey.data(), privateKey.size()); + + auto value = store(LOCAL_NET); + input.set_chain_id(value.data(), value.size()); + + value = store(uint256_t("0x1")); + transactionMessage->set_nonce(value.data(), value.size()); + + value = store(uint256_t("")); + transactionMessage->set_gas_price(value.data(), value.size()); + + value = store(uint256_t("0x5208")); + transactionMessage->set_gas_limit(value.data(), value.size()); + + value = store(uint256_t("0x1")); + transactionMessage->set_from_shard_id(value.data(), value.size()); + + value = store(uint256_t("0x0")); + transactionMessage->set_to_shard_id(value.data(), value.size()); + + value = store(uint256_t("0x6bfc8da5ee8220000")); + transactionMessage->set_amount(value.data(), value.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeHarmony); + + auto shouldBeV = "28"; + auto shouldBeR = "84cc200aab11f5e1b2f7ece0d56ec67385ac50cefb6e3dc2a2f3e036ed575a5c"; + auto shouldBeS = "643f18005b790cac8d8e7dc90e6147df0b83874b52db198864694ea28a79e6fc"; + + ASSERT_EQ(hex(output.v()), shouldBeV); + ASSERT_EQ(hex(output.r()), shouldBeR); + ASSERT_EQ(hex(output.s()), shouldBeS); + + ASSERT_EQ(hex(output.encoded()), "f86a0180825208018094514650ca30b3c79f693e14220115434236d44aeb8906bfc8da5ee82200008028a084cc200aab11f5e1b2f7ece0d56ec67385ac50cefb6e3dc2a2f3e036ed575a5ca0643f18005b790cac8d8e7dc90e6147df0b83874b52db198864694ea28a79e6fc"); +} + +TEST(TWAnySignerHarmony, SignJSON) { + auto json = STRING(R"({"chainId":"Ag==","transactionMessage":{"nonce":"AQ==","gasPrice":"AA==","gasLimit":"Ugg=","toAddress":"one129r9pj3sk0re76f7zs3qz92rggmdgjhtwge62k","amount":"Br/I2l7oIgAA","fromShardId":"AQ==","toShardId":"AA=="}})"); + auto key = DATA("4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48"); + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeHarmony)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeHarmony)); + assertStringsEqual(result, "f86a0180825208018094514650ca30b3c79f693e14220115434236d44aeb8906bfc8da5ee82200008028a084cc200aab11f5e1b2f7ece0d56ec67385ac50cefb6e3dc2a2f3e036ed575a5ca0643f18005b790cac8d8e7dc90e6147df0b83874b52db198864694ea28a79e6fc"); +} + +} // namespace TW::Harmony::tests diff --git a/tests/chains/Harmony/TWCoinTypeTests.cpp b/tests/chains/Harmony/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..1147cfeb362 --- /dev/null +++ b/tests/chains/Harmony/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWHarmonyCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeHarmony)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeHarmony, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeHarmony, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeHarmony)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeHarmony)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeHarmony), 18); + ASSERT_EQ(TWBlockchainHarmony, TWCoinTypeBlockchain(TWCoinTypeHarmony)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeHarmony)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeHarmony)); + assertStringsEqual(symbol, "ONE"); + assertStringsEqual(txUrl, "https://explorer.harmony.one/#/tx/t123"); + assertStringsEqual(accUrl, "https://explorer.harmony.one/#/address/a12"); + assertStringsEqual(id, "harmony"); + assertStringsEqual(name, "Harmony"); +} diff --git a/tests/Harmony/TWHarmonyStakingTests.cpp b/tests/chains/Harmony/TWHarmonyStakingTests.cpp similarity index 99% rename from tests/Harmony/TWHarmonyStakingTests.cpp rename to tests/chains/Harmony/TWHarmonyStakingTests.cpp index 81f6ae50108..164d37edacb 100644 --- a/tests/Harmony/TWHarmonyStakingTests.cpp +++ b/tests/chains/Harmony/TWHarmonyStakingTests.cpp @@ -9,7 +9,7 @@ #include "Harmony/Staking.h" #include "HexCoding.h" #include "PrivateKey.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "proto/Harmony.pb.h" #include "uint256.h" #include @@ -17,7 +17,7 @@ #include using namespace TW; -using namespace Harmony; +namespace TW::Harmony::tests { static auto TEST_ACCOUNT = "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9"; @@ -91,7 +91,6 @@ TEST(TWHarmonyStakingSigner, CreateValidator) { value = store(uint256_t("0x64")); stakingMessage->set_gas_limit(value.data(), value.size()); - Proto::SigningOutput output; ANY_SIGN(input, TWCoinTypeHarmony); @@ -104,7 +103,6 @@ TEST(TWHarmonyStakingSigner, CreateValidator) { ASSERT_EQ(hex(output.s()), shouldBeS); } - TEST(TWHarmonyStakingSigner, EditValidator) { auto input = Proto::SigningInput(); input.set_private_key(PRIVATE_KEY.bytes.data(), PRIVATE_KEY.bytes.size()); @@ -149,7 +147,7 @@ TEST(TWHarmonyStakingSigner, EditValidator) { "5c864771cafef7b96be541cb3c521a98f01838dd94"); editValidatorMsg->set_slot_key_to_add_sig(value.data(), value.size()); - //test active + // test active value = store(uint256_t("1")); editValidatorMsg->set_active(value.data(), value.size()); @@ -219,7 +217,7 @@ TEST(TWHarmonyStakingSigner, EditValidator2) { "5c864771cafef7b96be541cb3c521a98f01838dd94"); editValidatorMsg->set_slot_key_to_add_sig(value.data(), value.size()); - //test null + // test null value = store(uint256_t("0")); editValidatorMsg->set_active(value.data(), value.size()); @@ -289,7 +287,7 @@ TEST(TWHarmonyStakingSigner, EditValidator3) { "5c864771cafef7b96be541cb3c521a98f01838dd94"); editValidatorMsg->set_slot_key_to_add_sig(value.data(), value.size()); - //test inactive + // test inactive value = store(uint256_t("2")); editValidatorMsg->set_active(value.data(), value.size()); @@ -314,7 +312,6 @@ TEST(TWHarmonyStakingSigner, EditValidator3) { ASSERT_EQ(hex(output.s()), shouldBeS); } - TEST(TWHarmonyStakingSigner, Delegate) { auto input = Proto::SigningInput(); input.set_private_key(PRIVATE_KEY.bytes.data(), PRIVATE_KEY.bytes.size()); @@ -351,8 +348,6 @@ TEST(TWHarmonyStakingSigner, Delegate) { ASSERT_EQ(hex(output.s()), shouldBeS); } - - TEST(TWHarmonyStakingSigner, Undelegate) { auto input = Proto::SigningInput(); input.set_private_key(PRIVATE_KEY.bytes.data(), PRIVATE_KEY.bytes.size()); @@ -420,3 +415,5 @@ TEST(TWHarmonyStakingSigner, CollectRewards) { ASSERT_EQ(hex(output.r()), shouldBeR); ASSERT_EQ(hex(output.s()), shouldBeS); } + +} // namespace TW::Harmony::tests diff --git a/tests/chains/ICON/AddressTests.cpp b/tests/chains/ICON/AddressTests.cpp new file mode 100644 index 00000000000..a9ed8262e39 --- /dev/null +++ b/tests/chains/ICON/AddressTests.cpp @@ -0,0 +1,42 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Icon/Address.h" +#include "PrivateKey.h" + +#include + +using namespace TW; + +namespace TW::Icon::tests { + +TEST(IconAddress, Validation) { + ASSERT_TRUE(Address::isValid("cx116f042497e5f34268b1b91e742680f84cf4e9f3")); + ASSERT_TRUE(Address::isValid("hx116f042497e5f34268b1b91e742680f84cf4e9f3")); + + ASSERT_FALSE(Address::isValid("abc")); + ASSERT_FALSE(Address::isValid("dshadghasdghsadadsadjsad")); + ASSERT_FALSE(Address::isValid("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed")); +} + +TEST(IconAddress, String) { + const auto address = Address("hx116f042497e5f34268b1b91e742680f84cf4e9f3"); + ASSERT_EQ(address.string(), "hx116f042497e5f34268b1b91e742680f84cf4e9f3"); + + const auto address2 = Address("cx116f042497e5f34268b1b91e742680f84cf4e9f3"); + ASSERT_EQ(address2.string(), "cx116f042497e5f34268b1b91e742680f84cf4e9f3"); +} + +TEST(IconAddress, FromPrivateKey) { + const auto privateKey = PrivateKey(parse_hex("94d1a980d5e528067d44bf8a60d646f556e40ca71e17cd4ead2d56f89e4bd20f")); + const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); + const auto address = Address(publicKey, TypeAddress); + + ASSERT_EQ(address.string(), "hx98c0832ca5bd8e8bf355ca9491888aa9725c2c48"); +} + +} // namespace TW::Icon::tests diff --git a/tests/chains/ICON/TWAnySignerTests.cpp b/tests/chains/ICON/TWAnySignerTests.cpp new file mode 100644 index 00000000000..74a57537568 --- /dev/null +++ b/tests/chains/ICON/TWAnySignerTests.cpp @@ -0,0 +1,51 @@ + +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Data.h" +#include "HexCoding.h" +#include "proto/Icon.pb.h" +#include "uint256.h" +#include "TestUtilities.h" +#include + +#include + +using namespace TW; + +namespace TW::Icon::tests { + +TEST(TWAnySignerIcon, Sign) { + auto key = parse_hex("2d42994b2f7735bbc93a3e64381864d06747e574aa94655c516f9ad0a74eed79"); + auto input = Proto::SigningInput(); + + input.set_from_address("hxbe258ceb872e08851f1f59694dac2558708ece11"); + input.set_to_address("hx5bfdb090f43a808005ffc27c25b213145e80b7cd"); + + auto value = uint256_t(1000000000000000000); + auto valueData = store(value); + input.set_value(valueData.data(), valueData.size()); + + auto stepLimit = uint256_t("74565"); + auto stepLimitData = store(stepLimit); + input.set_step_limit(stepLimitData.data(), stepLimitData.size()); + + auto one = uint256_t("01"); + auto oneData = store(one); + input.set_network_id(oneData.data(), oneData.size()); + input.set_nonce(oneData.data(), oneData.size()); + + input.set_timestamp(1516942975500598); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeICON); + + auto expected = std::string("{\"from\":\"hxbe258ceb872e08851f1f59694dac2558708ece11\",\"nid\":\"0x1\",\"nonce\":\"0x1\",\"signature\":\"xR6wKs+IA+7E91bT8966jFKlK5mayutXCvayuSMCrx9KB7670CsWa0B7LQzgsxU0GLXaovlAT2MLs1XuDiSaZQE=\",\"stepLimit\":\"0x12345\",\"timestamp\":\"0x563a6cf330136\",\"to\":\"hx5bfdb090f43a808005ffc27c25b213145e80b7cd\",\"value\":\"0xde0b6b3a7640000\",\"version\":\"0x3\"}"); + ASSERT_EQ(output.encoded(), expected); +} + +} // namespace TW::Icon::tests diff --git a/tests/chains/ICON/TWCoinTypeTests.cpp b/tests/chains/ICON/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..5fc84a70033 --- /dev/null +++ b/tests/chains/ICON/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWICONCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeICON)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeICON, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeICON, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeICON)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeICON)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeICON), 18); + ASSERT_EQ(TWBlockchainIcon, TWCoinTypeBlockchain(TWCoinTypeICON)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeICON)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeICON)); + assertStringsEqual(symbol, "ICX"); + assertStringsEqual(txUrl, "https://tracker.icon.foundation/transaction/t123"); + assertStringsEqual(accUrl, "https://tracker.icon.foundation/address/a12"); + assertStringsEqual(id, "icon"); + assertStringsEqual(name, "ICON"); +} diff --git a/tests/chains/IoTeX/AddressTests.cpp b/tests/chains/IoTeX/AddressTests.cpp new file mode 100644 index 00000000000..36fd80b38d9 --- /dev/null +++ b/tests/chains/IoTeX/AddressTests.cpp @@ -0,0 +1,78 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" + +#include "IoTeX/Address.h" + +namespace TW::IoTeX { + +TEST(IoTeXAddress, Invalid) { + ASSERT_FALSE(Address::isValid("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8")); + ASSERT_FALSE(Address::isValid("io187wzp08vnhjpkydnr97qlh8kh0dpkkytfam8j")); + ASSERT_FALSE(Address::isValid("it187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j")); + ASSERT_FALSE(Address::isValid("ko187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j")); + ASSERT_FALSE(Address::isValid("io187wzp18vnhjjpkydnr97qlh8kh0dpkkytfam8j")); + ASSERT_FALSE(Address::isValid("io187wzp08vnhjbpkydnr97qlh8kh0dpkkytfam8j")); + ASSERT_FALSE(Address::isValid("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8k")); +} + +TEST(IoTeXAddress, Valid) { + ASSERT_TRUE(Address::isValid("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j")); +} + +TEST(IoTeXAddress, FromString) { + Address address; + ASSERT_TRUE(Address::decode("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", address)); + ASSERT_EQ("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", address.string()); + + ASSERT_FALSE(Address::decode("io187wzp08vnhjbpkydnr97qlh8kh0dpkkytfam8j", address)); +} + +TEST(IoTeXAddress, FromPrivateKey) { + const auto privateKey = PrivateKey(parse_hex("0806c458b262edd333a191e92f561aff338211ee3e18ab315a074a2d82aa343f")); + const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended)); + const auto address = Address(publicKey); + ASSERT_EQ(address.string(), "io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j"); + + EXPECT_THROW({ + try + { + const auto _publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + const auto _address = Address(_publicKey); + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ("address may only be an extended SECP256k1 public key", e.what()); + throw; + } + }, std::invalid_argument); +} + +TEST(IoTeXAddress, FromKeyHash) { + const auto keyHash = parse_hex("3f9c20bcec9de520d88d98cbe07ee7b5ded0dac4"); + const auto address = Address(keyHash); + ASSERT_EQ(address.string(), "io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j"); + + EXPECT_THROW({ + try + { + const auto _keyHash = parse_hex("3f9c20bcec9de520d88d98cbe07ee7b5ded0da"); + const auto _address = Address(_keyHash); + } + catch( const std::invalid_argument& e ) + { + EXPECT_STREQ("invalid address data", e.what()); + throw; + } + }, std::invalid_argument); +} + +} // namespace TW::IoTeX diff --git a/tests/IoTeX/SignerTests.cpp b/tests/chains/IoTeX/SignerTests.cpp similarity index 100% rename from tests/IoTeX/SignerTests.cpp rename to tests/chains/IoTeX/SignerTests.cpp diff --git a/tests/chains/IoTeX/StakingTests.cpp b/tests/chains/IoTeX/StakingTests.cpp new file mode 100644 index 00000000000..e5f37330dee --- /dev/null +++ b/tests/chains/IoTeX/StakingTests.cpp @@ -0,0 +1,332 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Data.h" +#include "HexCoding.h" +#include "IoTeX/Staking.h" +#include "proto/IoTeX.pb.h" +#include "TestUtilities.h" +#include +#include + +namespace TW::IoTeX::tests { + +TEST(TWIoTeXStaking, Create) { + std::string IOTEX_STAKING_CANDIDATE = "io19d0p3ah4g8ww9d7kcxfq87yxe7fnr8rpth5shj"; + std::string IOTEX_STAKING_PAYLOAD = "payload"; + std::string IOTEX_STAKING_AMOUNT = "100"; + Data candidate(IOTEX_STAKING_CANDIDATE.begin(), IOTEX_STAKING_CANDIDATE.end()); + Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); + Data amount(IOTEX_STAKING_AMOUNT.begin(), IOTEX_STAKING_AMOUNT.end()); + + auto stake = stakingCreate(candidate, amount, 10000, true, payload); + ASSERT_EQ(hex(stake), "0a29696f313964307033616834673877773964376b63786671383779786537666e723872" + "7074683573686a120331303018904e20012a077061796c6f6164"); +} + +TEST(TWIoTeXStaking, AddDeposit) { + std::string IOTEX_STAKING_PAYLOAD = "payload"; + std::string IOTEX_STAKING_AMOUNT = "10"; + Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); + Data amount(IOTEX_STAKING_AMOUNT.begin(), IOTEX_STAKING_AMOUNT.end()); + + auto stake = stakingAddDeposit(10, amount, payload); + + ASSERT_EQ(hex(stake), "080a120231301a077061796c6f6164"); +} + +TEST(TWIoTeXStaking, Unstake) { + std::string IOTEX_STAKING_PAYLOAD = "payload"; + Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); + + auto stake = stakingUnstake(10, payload); + + ASSERT_EQ(hex(stake), "080a12077061796c6f6164"); +} + +TEST(TWIoTeXStaking, Withdraw) { + std::string IOTEX_STAKING_PAYLOAD = "payload"; + Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); + + auto stake = stakingWithdraw(10, payload); + + ASSERT_EQ(hex(stake), "080a12077061796c6f6164"); +} + +TEST(TWIoTeXStaking, Restake) { + std::string IOTEX_STAKING_PAYLOAD = "payload"; + Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); + + auto stake = stakingRestake(10, 1000, true, payload); + + ASSERT_EQ(hex(stake), "080a10e807180122077061796c6f6164"); +} + +TEST(TWIoTeXStaking, ChangeCandidate) { + std::string IOTEX_STAKING_CANDIDATE = "io1xpq62aw85uqzrccg9y5hnryv8ld2nkpycc3gza"; + std::string IOTEX_STAKING_PAYLOAD = "payload"; + Data candidate(IOTEX_STAKING_CANDIDATE.begin(), IOTEX_STAKING_CANDIDATE.end()); + Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); + + auto stake = stakingChangeCandidate(10, candidate, payload); + + ASSERT_EQ(hex(stake), "080a1229696f3178707136326177383575717a72636367397935686e727976386c" + "64326e6b7079636333677a611a077061796c6f6164"); +} + +TEST(TWIoTeXStaking, Transfer) { + std::string IOTEX_STAKING_CANDIDATE = "io1xpq62aw85uqzrccg9y5hnryv8ld2nkpycc3gza"; + std::string IOTEX_STAKING_PAYLOAD = "payload"; + Data candidate(IOTEX_STAKING_CANDIDATE.begin(), IOTEX_STAKING_CANDIDATE.end()); + Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); + + auto stake = stakingTransfer(10, candidate, payload); + + ASSERT_EQ(hex(stake), "080a1229696f3178707136326177383575717a72636367397935686e727976386c6432" + "6e6b7079636333677a611a077061796c6f6164"); +} + +TEST(TWIoTeXStaking, CandidateRegister) { + std::string IOTEX_STAKING_NAME = "test"; + std::string IOTEX_STAKING_OPERATOR = "io10a298zmzvrt4guq79a9f4x7qedj59y7ery84he"; + std::string IOTEX_STAKING_REWARD = "io13sj9mzpewn25ymheukte4v39hvjdtrfp00mlyv"; + std::string IOTEX_STAKING_OWNER = "io19d0p3ah4g8ww9d7kcxfq87yxe7fnr8rpth5shj"; + std::string IOTEX_STAKING_AMOUNT = "100"; + std::string IOTEX_STAKING_PAYLOAD = "payload"; + Data name(IOTEX_STAKING_NAME.begin(), IOTEX_STAKING_NAME.end()); + Data operatorAddress(IOTEX_STAKING_OPERATOR.begin(), IOTEX_STAKING_OPERATOR.end()); + Data reward(IOTEX_STAKING_REWARD.begin(), IOTEX_STAKING_REWARD.end()); + Data amount(IOTEX_STAKING_AMOUNT.begin(), IOTEX_STAKING_AMOUNT.end()); + Data owner(IOTEX_STAKING_OWNER.begin(), IOTEX_STAKING_OWNER.end()); + Data payload(IOTEX_STAKING_PAYLOAD.begin(), IOTEX_STAKING_PAYLOAD.end()); + + auto stake = + candidateRegister(name, operatorAddress, reward, amount, 10000, false, owner, payload); + + ASSERT_EQ(hex(stake), + "0a5c0a04746573741229696f3130613239387a6d7a7672743467757137396139663478377165646a3539" + "7937657279383468651a29696f3133736a396d7a7065776e3235796d6865756b74653476333968766a64" + "7472667030306d6c7976120331303018904e2a29696f313964307033616834673877773964376b637866" + "71383779786537666e7238727074683573686a32077061796c6f6164"); +} + +TEST(TWIoTeXStaking, CandidateUpdate) { + std::string IOTEX_STAKING_NAME = "test"; + std::string IOTEX_STAKING_OPERATOR = "io1cl6rl2ev5dfa988qmgzg2x4hfazmp9vn2g66ng"; + std::string IOTEX_STAKING_REWARD = "io1juvx5g063eu4ts832nukp4vgcwk2gnc5cu9ayd"; + Data name(IOTEX_STAKING_NAME.begin(), IOTEX_STAKING_NAME.end()); + Data operatorAddress(IOTEX_STAKING_OPERATOR.begin(), IOTEX_STAKING_OPERATOR.end()); + Data reward(IOTEX_STAKING_REWARD.begin(), IOTEX_STAKING_REWARD.end()); + + auto stake = candidateUpdate(name, operatorAddress, reward); + + ASSERT_EQ(hex(stake), "0a04746573741229696f31636c36726c32657635646661393838716d677a6732783468" + "66617a6d7039766e326736366e671a29696f316a757678356730363365753474733833" + "326e756b7034766763776b32676e6335637539617964"); +} + +Proto::SigningInput createSigningInput() { + auto keyhex = parse_hex("cfa6ef757dee2e50351620dca002d32b9c090cfda55fb81f37f1d26b273743f1"); + auto input = Proto::SigningInput(); + input.set_version(1); + input.set_nonce(0); + input.set_gaslimit(1000000); + input.set_gasprice("10"); + input.set_privatekey(keyhex.data(), keyhex.size()); + return input; +} + +TEST(TWIoTeXStaking, SignAll) { + { // sign stakecreate + auto input = createSigningInput(); + Proto::SigningOutput output; + auto& action = *input.mutable_stakecreate(); + action.set_candidatename("io19d0p3ah4g8ww9d7kcxfq87yxe7fnr8rpth5shj"); + action.set_stakedamount("100"); + action.set_stakedduration(10000); + action.set_autostake(true); + action.set_payload("payload"); + ANY_SIGN(input, TWCoinTypeIoTeX); + ASSERT_EQ(hex(output.encoded()), + "0a4b080118c0843d22023130c2023e0a29696f313964307033616834673877773964376b63786671" + "3837797865" + "37666e7238727074683573686a120331303018904e20012a077061796c6f6164124104755ce6d890" + "3f6b3793bd" + "db4ea5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c" + "0bc76ef30d" + "d6a1038ed9da8daf331a412e8bac421bab88dcd99c26ac8ffbf27f11ee57a41e7d2537891bfed5ae" + "d8e2e026d4" + "6e55d1b856787bc1cd7c1216a6e2534c5b5d1097c3afe8e657aa27cbbb0801"); + // signed action's hash + ASSERT_EQ(hex(output.hash()), + "f1785e47b4200c752bb6518bd18097a41e075438b8c18c9cb00e1ae2f38ce767"); + } + { // sign stakeadddeposit + auto input = createSigningInput(); + Proto::SigningOutput output; + auto& action = *input.mutable_stakeadddeposit(); + action.set_bucketindex(10); + action.set_amount("10"); + action.set_payload("payload"); + ANY_SIGN(input, TWCoinTypeIoTeX); + ASSERT_EQ( + hex(output.encoded()), + "0a1c080118c0843d22023130da020f080a120231301a077061796c6f6164124104755ce6d8903f6b3793" + "bddb4ea5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0b" + "c76ef30dd6a1038ed9da8daf331a41a48ab1feba8181d760de946aefed7d815a89fd9b1ab503d2392bb5" + "5e1bb75eec42dddc8bd642f89accc3a37b3cf15a103a95d66695fdf0647b202869fdd66bcb01"); + // signed action's hash + ASSERT_EQ(hex(output.hash()), + "ca8937d6f224a4e4bf93cb5605581de2d26fb0481e1dfc1eef384ee7ccf94b73"); + } + { // sign stakeunstake + auto input = createSigningInput(); + Proto::SigningOutput output; + auto& action = *input.mutable_stakeunstake(); + action.set_bucketindex(10); + action.set_payload("payload"); + ANY_SIGN(input, TWCoinTypeIoTeX); + ASSERT_EQ( + hex(output.encoded()), + "0a18080118c0843d22023130ca020b080a12077061796c6f6164124104755ce6d8903f6b3793bddb4ea5" + "d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef30d" + "d6a1038ed9da8daf331a4100adee39b48e1d3dbbd65298a57c7889709fc4df39987130da306f6997374a" + "184b7e7c232a42f21e89b06e6e7ceab81303c6b7483152d08d19ac829b22eb81e601"); + // signed action's hash + ASSERT_EQ(hex(output.hash()), + "bed58b64a6c4e959eca60a86f0b2149ce0e1dd527ac5fd26aef725ebf7c22a7d"); + } + { // sign stakewithdraw + auto input = createSigningInput(); + Proto::SigningOutput output; + auto& action = *input.mutable_stakewithdraw(); + action.set_bucketindex(10); + action.set_payload("payload"); + ANY_SIGN(input, TWCoinTypeIoTeX); + ASSERT_EQ( + hex(output.encoded()), + "0a18080118c0843d22023130d2020b080a12077061796c6f6164124104755ce6d8903f6b3793bddb4ea5" + "d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef30d" + "d6a1038ed9da8daf331a4152644d102186be6640d46b517331f3402e24424b0d85129595421d28503d75" + "340b2922f5a0d4f667bbd6f576d9816770286b2ce032ba22eaec3952e24da4756b00"); + // signed action's hash + ASSERT_EQ(hex(output.hash()), + "28049348cf34f1aa927caa250e7a1b08778c44efaf73b565b6fa9abe843871b4"); + } + { // sign stakerestake + auto input = createSigningInput(); + Proto::SigningOutput output; + auto& action = *input.mutable_stakerestake(); + action.set_bucketindex(10); + action.set_stakedduration(1000); + action.set_autostake(true); + action.set_payload("payload"); + ANY_SIGN(input, TWCoinTypeIoTeX); + ASSERT_EQ( + hex(output.encoded()), + "0a1d080118c0843d22023130e20210080a10e807180122077061796c6f6164124104755ce6d8903f6b37" + "93bddb4ea5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c" + "0bc76ef30dd6a1038ed9da8daf331a41e2e763aed5b1fd1a8601de0f0ae34eb05162e34b0389ae3418ee" + "dbf762f64959634a968313a6516dba3a97b34efba4753bbed3a33d409ecbd45ac75007cd8e9101"); + // signed action's hash + ASSERT_EQ(hex(output.hash()), + "8816e8f784a1fce40b54d1cd172bb6976fd9552f1570c73d1d9fcdc5635424a9"); + } + { // sign stakechangecandidate + auto input = createSigningInput(); + Proto::SigningOutput output; + auto& action = *input.mutable_stakechangecandidate(); + action.set_bucketindex(10); + action.set_candidatename("io1xpq62aw85uqzrccg9y5hnryv8ld2nkpycc3gza"); + action.set_payload("payload"); + ANY_SIGN(input, TWCoinTypeIoTeX); + ASSERT_EQ( + hex(output.encoded()), + "0a43080118c0843d22023130ea0236080a1229696f3178707136326177383575717a7263636739793568" + "6e727976386c64326e6b7079636333677a611a077061796c6f6164124104755ce6d8903f6b3793bddb4e" + "a5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef3" + "0dd6a1038ed9da8daf331a41d519eb3747163b945b862989b7e82a7f8468001e9683757cb88d5ddd95f8" + "1895047429e858bd48f7d59a88bfec92de231d216293aeba1e4fbe11461d9c9fc99801"); + // signed action's hash + ASSERT_EQ(hex(output.hash()), + "186526b5b9fe74e25beb52c83c41780a69108160bef2ddaf3bffb9f1f1e5e73a"); + } + { // sign staketransfer + auto input = createSigningInput(); + Proto::SigningOutput output; + auto& action = *input.mutable_staketransferownership(); + action.set_bucketindex(10); + action.set_voteraddress("io1xpq62aw85uqzrccg9y5hnryv8ld2nkpycc3gza"); + action.set_payload("payload"); + ANY_SIGN(input, TWCoinTypeIoTeX); + ASSERT_EQ( + hex(output.encoded()), + "0a43080118c0843d22023130f20236080a1229696f3178707136326177383575717a7263636739793568" + "6e727976386c64326e6b7079636333677a611a077061796c6f6164124104755ce6d8903f6b3793bddb4e" + "a5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef3" + "0dd6a1038ed9da8daf331a41fa26db427ab87a56a129196c1604f2e22c4dd2a1f99b2217bc916260757d" + "00093d9e6dccdf53e3b0b64e41a69d71c238fbf9281625164694a74dfbeba075d0ce01"); + // signed action's hash + ASSERT_EQ(hex(output.hash()), + "74b2e1d6a09ba5d1298fa422d5850991ae516865077282196295a38f93c78b85"); + } + { // sign candidateupdate + auto input = createSigningInput(); + Proto::SigningOutput output; + auto& action = *input.mutable_candidateupdate(); + action.set_name("test"); + action.set_operatoraddress("io1cl6rl2ev5dfa988qmgzg2x4hfazmp9vn2g66ng"); + action.set_rewardaddress("io1juvx5g063eu4ts832nukp4vgcwk2gnc5cu9ayd"); + ANY_SIGN(input, TWCoinTypeIoTeX); + ASSERT_EQ( + hex(output.encoded()), + "0a69080118c0843d2202313082035c0a04746573741229696f31636c36726c3265763564666139383871" + "6d677a673278346866617a6d7039766e326736366e671a29696f316a7576783567303633657534747338" + "33326e756b7034766763776b32676e6335637539617964124104755ce6d8903f6b3793bddb4ea5d3589d" + "637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99a5c1335b583c0bc76ef30dd6a103" + "8ed9da8daf331a4101885c9c6684a4a8f2f5bf11f8326f27be48658f292e8f55ec8a11a604bb0c563a11" + "ebf12d995ca1c152e00f8e0f0edf288db711aa10dbdfd5b7d73b4a28e1f701"); + // signed action's hash + ASSERT_EQ(hex(output.hash()), + "ca1a28f0e9a58ffc67037cc75066dbdd8e024aa2b2e416e4d6ce16c3d86282e5"); + } + { // sign candidateregister + auto input = createSigningInput(); + Proto::SigningOutput output; + input.set_gasprice("1000"); + auto& cbi = *input.mutable_candidateregister()->mutable_candidate(); + cbi.set_name("test"); + cbi.set_operatoraddress("io10a298zmzvrt4guq79a9f4x7qedj59y7ery84he"); + cbi.set_rewardaddress("io13sj9mzpewn25ymheukte4v39hvjdtrfp00mlyv"); + + auto& action = *input.mutable_candidateregister(); + action.set_stakedamount("100"); + action.set_stakedduration(10000); + action.set_autostake(false); + action.set_owneraddress("io19d0p3ah4g8ww9d7kcxfq87yxe7fnr8rpth5shj"); + action.set_payload("payload"); + ANY_SIGN(input, TWCoinTypeIoTeX); + ASSERT_EQ(hex(output.encoded()), + "0aaa01080118c0843d220431303030fa029a010a5c0a04746573741229696f3130613239387a6d7a" + "7672743467" + "757137396139663478377165646a35397937657279383468651a29696f3133736a396d7a7065776e" + "3235796d68" + "65756b74653476333968766a647472667030306d6c7976120331303018904e2a29696f3139643070" + "3361683467" + "3877773964376b63786671383779786537666e7238727074683573686a32077061796c6f61641241" + "04755ce6d8" + "903f6b3793bddb4ea5d3589d637de2d209ae0ea930815c82db564ee8cc448886f639e8a0c7e94e99" + "a5c1335b58" + "3c0bc76ef30dd6a1038ed9da8daf331a417819b5bcb635e3577acc8ca757f2c3d6afa451c2b6ff8a" + "9179b141ac" + "68e2c50305679e5d09d288da6f0fb52876a86c74deab6a5247edc6d371de5c2f121e159400"); + // signed action's hash + ASSERT_EQ(hex(output.hash()), + "35f53a536e014b32b85df50483ef04849b80ad60635b3b1979c5ba1096b65237"); + } +} + +} // namespace TW::IoTeX::tests diff --git a/tests/chains/IoTeX/TWAnySignerTests.cpp b/tests/chains/IoTeX/TWAnySignerTests.cpp new file mode 100644 index 00000000000..4fbeafd20ac --- /dev/null +++ b/tests/chains/IoTeX/TWAnySignerTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include "HexCoding.h" +#include "proto/IoTeX.pb.h" + +#include + +namespace TW::IoTeX::tests { + +TEST(TWAnySignerIoTeX, Sign) { + auto key = parse_hex("68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748"); + Proto::SigningInput input; + input.set_version(1); + input.set_nonce(1); + input.set_gaslimit(1); + input.set_gasprice("1"); + input.set_privatekey(key.data(), key.size()); + auto& transfer = *input.mutable_transfer(); + transfer.set_amount("1"); + transfer.set_recipient("io1e2nqsyt7fkpzs5x7zf2uk0jj72teu5n6aku3tr"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeIoTeX); + + ASSERT_EQ(hex(output.encoded()), "0a39080110011801220131522e0a01311229696f3165326e7173797437666b707a733578377a6632756b306a6a3732746575356e36616b75337472124104fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5cf762f20459c9100eb9d4d7597f5817bf21e10b53a0120b9ec1ba5cddfdcb669b1a41ec9757ae6c9009315830faaab250b6db0e9535b00843277f596ae0b2b9efc0bd4e14138c056fc4cdfa285d13dd618052b3d1cb7a3f554722005a2941bfede96601"); +} + +} // namespace TW::IoTeX::tests diff --git a/tests/chains/IoTeX/TWCoinTypeTests.cpp b/tests/chains/IoTeX/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..4cdc2d68ad4 --- /dev/null +++ b/tests/chains/IoTeX/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWIoTeXCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeIoTeX)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeIoTeX, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeIoTeX, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeIoTeX)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeIoTeX)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeIoTeX), 18); + ASSERT_EQ(TWBlockchainIoTeX, TWCoinTypeBlockchain(TWCoinTypeIoTeX)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeIoTeX)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeIoTeX)); + assertStringsEqual(symbol, "IOTX"); + assertStringsEqual(txUrl, "https://iotexscan.io/action/t123"); + assertStringsEqual(accUrl, "https://iotexscan.io/address/a12"); + assertStringsEqual(id, "iotex"); + assertStringsEqual(name, "IoTeX"); +} diff --git a/tests/chains/Juno/TWAnyAddressTests.cpp b/tests/chains/Juno/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..992176b9510 --- /dev/null +++ b/tests/chains/Juno/TWAnyAddressTests.cpp @@ -0,0 +1,50 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "HexCoding.h" +#include "Hash.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(TWJunoAnyAddress, IsValid) { + EXPECT_TRUE(TWAnyAddressIsValidBech32(STRING("juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94").get(), TWCoinTypeCosmos, STRING("juno").get())); + EXPECT_FALSE(TWAnyAddressIsValidBech32(STRING("juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94").get(), TWCoinTypeBitcoin, STRING("juno").get())); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94").get(), TWCoinTypeCosmos)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("juno1gckvjxau7k56f8wg8c8xj80khyp83y8x8eqc94").get(), TWCoinTypeBitcoin)); + +} + +TEST(TWJunoAnyAddress, createFromPubKeyJuno) { + const auto hrp = STRING("juno"); + const auto data = DATA("02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc"); + const auto pubkey = TWPublicKeyCreateWithData(data.get(), TWPublicKeyTypeSECP256k1); + const auto twAddress = TWAnyAddressCreateBech32WithPublicKey(pubkey, TWCoinTypeCosmos, hrp.get()); + auto twData = TWAnyAddressData(twAddress); + auto hexData = hex(*reinterpret_cast(twData)); + ASSERT_EQ(hexData, "c494c4cb388e23fe24a93158d6cd1fbdca8ebb73"); + ASSERT_EQ(hex(Bech32Address("juno", TW::Hash::HasherSha256ripemd, pubkey->impl).getKeyHash()), hexData); + auto address = TWAnyAddressDescription(twAddress); + EXPECT_EQ("juno1cj2vfjec3c3luf9fx9vddnglhh9gawmncn4k5n", *reinterpret_cast(address)); + TWStringDelete(address); + TWAnyAddressDelete(twAddress); + TWDataDelete(twData); + TWPublicKeyDelete(pubkey); +} + +TEST(TWJunoAnyAddress, createFromStringJuno) { + const auto junoAddress = STRING("juno1cj2vfjec3c3luf9fx9vddnglhh9gawmncn4k5n"); + const auto hrp = STRING("juno"); + const auto anyAddr = TWAnyAddressCreateBech32(junoAddress.get(), TWCoinTypeCosmos, hrp.get()); + const auto addrDescription = TWAnyAddressDescription(anyAddr); + ASSERT_TRUE(TWAnyAddressIsValidBech32(addrDescription, TWCoinTypeCosmos, hrp.get())); + TWStringDelete(addrDescription); + TWAnyAddressDelete(anyAddr); + +} diff --git a/tests/chains/Kava/TWCoinTypeTests.cpp b/tests/chains/Kava/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..fd4a19b7412 --- /dev/null +++ b/tests/chains/Kava/TWCoinTypeTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWKavaCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeKava)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("2988DF83FCBFAA38179D583A96734CBD071541D6768221BB23111BC8136D5E6A")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeKava, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("kava1xd39avn2f008jmvua0eupg39zsp2xn3wf802vn")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeKava, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeKava)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeKava)); + const auto chainId = WRAPS(TWCoinTypeChainId(TWCoinTypeKava)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeKava), 6); + ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeKava)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeKava)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeKava)); + assertStringsEqual(chainId, "kava_2222-10"); + assertStringsEqual(symbol, "KAVA"); + assertStringsEqual(txUrl, "https://mintscan.io/kava/txs/2988DF83FCBFAA38179D583A96734CBD071541D6768221BB23111BC8136D5E6A"); + assertStringsEqual(accUrl, "https://mintscan.io/kava/account/kava1xd39avn2f008jmvua0eupg39zsp2xn3wf802vn"); + assertStringsEqual(id, "kava"); + assertStringsEqual(name, "Kava"); +} diff --git a/tests/chains/KavaEvm/TWCoinTypeTests.cpp b/tests/chains/KavaEvm/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..a658eba031a --- /dev/null +++ b/tests/chains/KavaEvm/TWCoinTypeTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWKavaEvmCoinType, TWCoinType) { + const auto coin = TWCoinTypeKavaEvm; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xa999bd5183568ba178795e6a9d1561566fbf4a9ccc813cc475168832bc4909b3")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xF92d3DB0d9f912f285b1ec69578A6201A78487d7")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "kavaevm"); + assertStringsEqual(name, "KavaEvm"); + assertStringsEqual(symbol, "KAVA"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "2222"); + assertStringsEqual(txUrl, "https://explorer.kava.io/tx/0xa999bd5183568ba178795e6a9d1561566fbf4a9ccc813cc475168832bc4909b3"); + assertStringsEqual(accUrl, "https://explorer.kava.io/address/0xF92d3DB0d9f912f285b1ec69578A6201A78487d7"); +} diff --git a/tests/chains/Kin/TWCoinTypeTests.cpp b/tests/chains/Kin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..8f37b7f3607 --- /dev/null +++ b/tests/chains/Kin/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWKinCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeKin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeKin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeKin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeKin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeKin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeKin), 5); + ASSERT_EQ(TWBlockchainStellar, TWCoinTypeBlockchain(TWCoinTypeKin)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeKin)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeKin)); + assertStringsEqual(symbol, "KIN"); + assertStringsEqual(txUrl, "https://www.kin.org/blockchainInfoPage/?&dataType=public&header=Transaction&id=t123"); + assertStringsEqual(accUrl, "https://www.kin.org/blockchainAccount/?&dataType=public&header=accountID&id=a12"); + assertStringsEqual(id, "kin"); + assertStringsEqual(name, "Kin"); +} diff --git a/tests/chains/Klaytn/TWCoinTypeTests.cpp b/tests/chains/Klaytn/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..d7eb5c71136 --- /dev/null +++ b/tests/chains/Klaytn/TWCoinTypeTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWKlaytnCoinType, TWCoinType) { + const auto coin = TWCoinTypeKlaytn; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x93ea92687845fe7bb6cacd69c76a16a2a3c2bbb85a8a93ff0e032d0098d583d7")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x2ad9656bf5b82caf10847b431012e28e301e83ba")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "klaytn"); + assertStringsEqual(name, "Klaytn"); + assertStringsEqual(symbol, "KLAY"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "8217"); + assertStringsEqual(txUrl, "https://scope.klaytn.com/tx/0x93ea92687845fe7bb6cacd69c76a16a2a3c2bbb85a8a93ff0e032d0098d583d7"); + assertStringsEqual(accUrl, "https://scope.klaytn.com/account/0x2ad9656bf5b82caf10847b431012e28e301e83ba"); +} diff --git a/tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp b/tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..354bc8b7ba1 --- /dev/null +++ b/tests/chains/KuCoinCommunityChain/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWKuCoinCommunityChainCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeKuCoinCommunityChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x2f0d79cd289a02f3181b68b9583a64c3809fe7387810b274275985c29d02c80d")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeKuCoinCommunityChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x4446fc4eb47f2f6586f9faab68b3498f86c07521")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeKuCoinCommunityChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeKuCoinCommunityChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeKuCoinCommunityChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeKuCoinCommunityChain), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeKuCoinCommunityChain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeKuCoinCommunityChain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeKuCoinCommunityChain)); + assertStringsEqual(symbol, "KCS"); + assertStringsEqual(txUrl, "https://explorer.kcc.io/en/tx/0x2f0d79cd289a02f3181b68b9583a64c3809fe7387810b274275985c29d02c80d"); + assertStringsEqual(accUrl, "https://explorer.kcc.io/en/address/0x4446fc4eb47f2f6586f9faab68b3498f86c07521"); + assertStringsEqual(id, "kcc"); + assertStringsEqual(name, "KuCoin Community Chain"); +} diff --git a/tests/chains/Kusama/AddressTests.cpp b/tests/chains/Kusama/AddressTests.cpp new file mode 100644 index 00000000000..10f6178d1df --- /dev/null +++ b/tests/chains/Kusama/AddressTests.cpp @@ -0,0 +1,52 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Kusama/Address.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include +#include + +namespace TW::Kusama::tests { + +TEST(KusamaAddress, Validation) { + // Substrate ed25519 + ASSERT_FALSE(Address::isValid("5FqqU2rytGPhcwQosKRtW1E3ha6BJKAjHgtcodh71dSyXhoZ")); + // Polkadot ed25519 + ASSERT_FALSE(Address::isValid("15AeCjMpcSt3Fwa47jJBd7JzQ395Kr2cuyF5Zp4UBf1g9ony")); + // Polkadot sr25519 + ASSERT_FALSE(Address::isValid("15AeCjMpcSt3Fwa47jJBd7JzQ395Kr2cuyF5Zp4UBf1g9ony")); + // Bitcoin + ASSERT_FALSE(Address::isValid("1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA")); + + // Kusama ed25519 + ASSERT_TRUE(Address::isValid("FHKAe66mnbk8ke8zVWE9hFVFrJN1mprFPVmD5rrevotkcDZ")); + // Kusama secp256k1 + ASSERT_TRUE(Address::isValid("FxQFyTorsjVsjjMyjdgq8w5vGx8LiA1qhWbRYcFijxKKchx")); + // Kusama sr25519 + ASSERT_TRUE(Address::isValid("EJ5UJ12GShfh7EWrcNZFLiYU79oogdtXFUuDDZzk7Wb2vCe")); +} + +TEST(KusamaAddress, FromPrivateKey) { + // from subkey: tiny escape drive pupil flavor endless love walk gadget match filter luxury + auto privateKey = PrivateKey(parse_hex("0xa21981f3bb990c40837df44df639541ff57c5e600f9eb4ac00ed8d1f718364e5")); + auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(address.string(), "CeVXtoU4py9e7F6upfM2ZarVave299TjcdaTSxhDDZrYgnM"); +} + +TEST(KusamaAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("0x032eb287017c5cde2940b5dd062d413f9d09f8aa44723fc80bf46b96c81ac23d"), TWPublicKeyTypeED25519); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "CeVXtoU4py9e7F6upfM2ZarVave299TjcdaTSxhDDZrYgnM"); +} + +TEST(KusamaAddress, FromString) { + auto address = Address("CeVXtoU4py9e7F6upfM2ZarVave299TjcdaTSxhDDZrYgnM"); + ASSERT_EQ(address.string(), "CeVXtoU4py9e7F6upfM2ZarVave299TjcdaTSxhDDZrYgnM"); +} + +} // namespace TW::Kusama::tests \ No newline at end of file diff --git a/tests/chains/Kusama/SignerTests.cpp b/tests/chains/Kusama/SignerTests.cpp new file mode 100644 index 00000000000..3af2df1c9ec --- /dev/null +++ b/tests/chains/Kusama/SignerTests.cpp @@ -0,0 +1,52 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Polkadot/Signer.h" +#include "Polkadot/Extrinsic.h" +#include "Polkadot/SS58Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "proto/Polkadot.pb.h" +#include "uint256.h" + +#include +#include + + +namespace TW::Polkadot::tests { + extern PrivateKey privateKey; + extern PublicKey toPublicKey; + auto genesisHashKSM = parse_hex("b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe"); + +TEST(PolkadotSigner, SignTransferKSM) { + auto blockHash = parse_hex("4955dd4813f3e91ef3fd5a825b928af2fc50a71380085f753ccef00bb1582891"); + auto toAddress = SS58Address(toPublicKey, TWSS58AddressTypeKusama); + + auto input = Proto::SigningInput(); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_genesis_hash(genesisHashKSM.data(), genesisHashKSM.size()); + input.set_nonce(0); + input.set_spec_version(2019); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_network(Proto::Network::KUSAMA); + input.set_transaction_version(2); + + auto balanceCall = input.mutable_balance_call(); + auto& transfer = *balanceCall->mutable_transfer(); + auto value = store(uint256_t(12345)); + transfer.set_to_address(toAddress.string()); + transfer.set_value(value.data(), value.size()); + + auto extrinsic = Extrinsic(input); + auto preimage = extrinsic.encodePayload(); + auto output = Signer::sign(input); + + ASSERT_EQ(hex(preimage), "04008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c0000000e307000002000000b0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe4955dd4813f3e91ef3fd5a825b928af2fc50a71380085f753ccef00bb1582891"); + ASSERT_EQ(hex(output.encoded()), "25028488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee000765cfa76cfe19499f4f19ef7dc4527652ec5b2e6b5ecfaf68725dafd48ae2694ad52e61f44152a544784e847de10ddb2c56bee4406574dcbcfdb5e5d35b6d0300000004008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c0"); +} + +} // namespace TW::Polkadot::tests diff --git a/tests/chains/Kusama/TWAnySignerTests.cpp b/tests/chains/Kusama/TWAnySignerTests.cpp new file mode 100644 index 00000000000..c23abdd9acb --- /dev/null +++ b/tests/chains/Kusama/TWAnySignerTests.cpp @@ -0,0 +1,42 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Polkadot.pb.h" +#include "uint256.h" +#include "TestUtilities.h" +#include + +#include + +namespace TW::Polkadot::tests { + +TEST(TWAnySignerKusama, Sign) { + auto key = parse_hex("0x8cdc538e96f460da9d639afc5c226f477ce98684d77fb31e88db74c1f1dd86b2"); + auto genesisHash = parse_hex("0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe"); + + Proto::SigningInput input; + input.set_block_hash(genesisHash.data(), genesisHash.size()); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_nonce(1); + input.set_spec_version(2019); + input.set_private_key(key.data(), key.size()); + input.set_network(Proto::Network::KUSAMA); + input.set_transaction_version(2); + + auto balanceCall = input.mutable_balance_call(); + auto& transfer = *balanceCall->mutable_transfer(); + auto value = store(uint256_t(10000000000)); + transfer.set_to_address("CtwdfrhECFs3FpvCGoiE4hwRC4UsSiM8WL899HjRdQbfYZY"); + transfer.set_value(value.data(), value.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeKusama); + + ASSERT_EQ(hex(output.encoded()), "350284f41296779fd61a5bed6c2f506cc6c9ea93d6aeb357b9c69717193f434ba24ae700cd78b46eff36c433e642d7e9830805aab4f43eef70067ef32c8b2a294c510673a841c5f8a6e8900c03be40cfa475ae53e6f8aa61961563cb7cc0fa169ef9630d00040004000e33fdfb980e4499e5c3576e742a563b6a4fc0f6f598b1917fd7a6fe393ffc720700e40b5402"); +} + +} // namespace TW::Polkadot::tests diff --git a/tests/chains/Kusama/TWCoinTypeTests.cpp b/tests/chains/Kusama/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..09cd2bf8c0f --- /dev/null +++ b/tests/chains/Kusama/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWKusamaCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeKusama)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xcbe0c2e2851c1245bedaae4d52f06eaa6b4784b786bea2f0bff11af7715973dd")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeKusama, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("DbCNECPna3k6MXFWWNZa5jGsuWycqEE6zcUxZYkxhVofrFk")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeKusama, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeKusama)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeKusama)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeKusama), 12); + ASSERT_EQ(TWBlockchainKusama, TWCoinTypeBlockchain(TWCoinTypeKusama)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeKusama)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeKusama)); + assertStringsEqual(symbol, "KSM"); + assertStringsEqual(txUrl, "https://kusama.subscan.io/extrinsic/0xcbe0c2e2851c1245bedaae4d52f06eaa6b4784b786bea2f0bff11af7715973dd"); + assertStringsEqual(accUrl, "https://kusama.subscan.io/account/DbCNECPna3k6MXFWWNZa5jGsuWycqEE6zcUxZYkxhVofrFk"); + assertStringsEqual(id, "kusama"); + assertStringsEqual(name, "Kusama"); +} diff --git a/tests/chains/Litecoin/LitecoinAddressTests.cpp b/tests/chains/Litecoin/LitecoinAddressTests.cpp new file mode 100644 index 00000000000..6858723ce13 --- /dev/null +++ b/tests/chains/Litecoin/LitecoinAddressTests.cpp @@ -0,0 +1,41 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Bitcoin/Address.h" +#include "Bitcoin/SegwitAddress.h" +#include "Coin.h" +#include "HexCoding.h" +#include "PublicKey.h" + +#include +#include + +#include +#include + +using namespace TW; + +namespace TW::Bitcoin::tests { + +TEST(LitecoinAddress, deriveAddress_legacy) { + const auto pubKey = PublicKey(parse_hex("03b49081a4d7ad24b20e209bc6fe10491aadb5607777baf0509a036cce96025db0"), TWPublicKeyTypeSECP256k1); + auto addrStr = deriveAddress(TWCoinTypeLitecoin, pubKey, TWDerivationLitecoinLegacy); + EXPECT_EQ(addrStr, "LW6HjAU6GL9fK2LZWUA6VZCzomTdrpx3nr"); + + const auto address = Address(pubKey, TWCoinTypeP2pkhPrefix(TWCoinTypeLitecoin)); + EXPECT_EQ(address.string(), addrStr); +} + +TEST(LitecoinAddress, deriveAddress_segwit) { + const auto pubKey = PublicKey(parse_hex("030fc2fdd1a0b5d43b31227a4b1cd57e7d35a6edc93fb12f9315e67762abeb8be0"), TWPublicKeyTypeSECP256k1); + auto addrStr = deriveAddress(TWCoinTypeLitecoin, pubKey, TWDerivationBitcoinSegwit); + EXPECT_EQ(addrStr, "ltc1q3m3ujh350qrqdl33pv7pjw0d0m9qnm6qjcjpga"); + + const auto address = SegwitAddress(pubKey, stringForHRP(TWCoinTypeHRP(TWCoinTypeLitecoin))); + EXPECT_EQ(address.string(), addrStr); +} + +} // namespace TW::Bitcoin::tests diff --git a/tests/chains/Litecoin/TWCoinTypeTests.cpp b/tests/chains/Litecoin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..441d729e66d --- /dev/null +++ b/tests/chains/Litecoin/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWLitecoinCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeLitecoin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeLitecoin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeLitecoin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeLitecoin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeLitecoin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeLitecoin), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeLitecoin)); + ASSERT_EQ(0x32, TWCoinTypeP2shPrefix(TWCoinTypeLitecoin)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeLitecoin)); + assertStringsEqual(symbol, "LTC"); + assertStringsEqual(txUrl, "https://blockchair.com/litecoin/transaction/t123"); + assertStringsEqual(accUrl, "https://blockchair.com/litecoin/address/a12"); + assertStringsEqual(id, "litecoin"); + assertStringsEqual(name, "Litecoin"); +} diff --git a/tests/Litecoin/TWLitecoinTests.cpp b/tests/chains/Litecoin/TWLitecoinTests.cpp similarity index 95% rename from tests/Litecoin/TWLitecoinTests.cpp rename to tests/chains/Litecoin/TWLitecoinTests.cpp index 46dd87b280f..0008fd375d1 100644 --- a/tests/Litecoin/TWLitecoinTests.cpp +++ b/tests/chains/Litecoin/TWLitecoinTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include @@ -15,6 +15,8 @@ #include +namespace TW::Litecoin::tests { + TEST(Litecoin, LegacyAddress) { auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA("a22ddec5c567b4488bb00f69b6146c50da2ee883e2c096db098726394d585730").get())); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), true)); @@ -46,9 +48,8 @@ TEST(Litecoin, LockScriptForAddressM) { TEST(Litecoin, ExtendedKeys) { auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic( - STRING("ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal").get(), - STRING("TREZOR").get() - )); + STRING("ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal").get(), + STRING("TREZOR").get())); // .bip44 auto lptv = WRAPS(TWHDWalletGetExtendedPrivateKey(wallet.get(), TWPurposeBIP44, TWCoinTypeLitecoin, TWHDVersionLTPV)); @@ -99,3 +100,5 @@ TEST(Litecoin, LockScripts) { auto scriptData3 = WRAPD(TWBitcoinScriptData(script3.get())); assertHexEqual(scriptData3, "76a914e771c6695c5dd189ccc4ef00cd0f3db3096d79bd88ac"); } + +} // namespace TW::Litecoin::tests diff --git a/tests/chains/Meter/TWCoinTypeTests.cpp b/tests/chains/Meter/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..b9c4df9d838 --- /dev/null +++ b/tests/chains/Meter/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWMeterCoinType, TWCoinType) { + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeMeter)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x8ea268d5dbb40217c763b800a75fc063cf28b56f40f2bc69dc043f5c4bbdc144")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeMeter, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xe5a273954d24eddf9ae9ea4cef2347d584cfa3dd")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeMeter, accId.get())); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeMeter)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeMeter)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeMeter), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeMeter)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeMeter)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeMeter)); + assertStringsEqual(symbol, "MTR"); + assertStringsEqual(txUrl, "https://scan.meter.io/tx/0x8ea268d5dbb40217c763b800a75fc063cf28b56f40f2bc69dc043f5c4bbdc144"); + assertStringsEqual(accUrl, "https://scan.meter.io/address/0xe5a273954d24eddf9ae9ea4cef2347d584cfa3dd"); + assertStringsEqual(id, "meter"); + assertStringsEqual(name, "Meter"); +} diff --git a/tests/chains/Metis/TWCoinTypeTests.cpp b/tests/chains/Metis/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..60733b44741 --- /dev/null +++ b/tests/chains/Metis/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWMetisCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeMetis)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x422f2ebbede32d4434ad0cf0ae55d44a84e14d3d5725a760133255b42676d8ce")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeMetis, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xBe9E8Ec25866B21bA34e97b9393BCabBcB4A5C86")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeMetis, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeMetis)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeMetis)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeMetis), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeMetis)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeMetis)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeMetis)); + assertStringsEqual(symbol, "METIS"); + assertStringsEqual(txUrl, "https://andromeda-explorer.metis.io/tx/0x422f2ebbede32d4434ad0cf0ae55d44a84e14d3d5725a760133255b42676d8ce"); + assertStringsEqual(accUrl, "https://andromeda-explorer.metis.io/address/0xBe9E8Ec25866B21bA34e97b9393BCabBcB4A5C86"); + assertStringsEqual(id, "metis"); + assertStringsEqual(name, "Metis"); +} diff --git a/tests/chains/Monacoin/TWCoinTypeTests.cpp b/tests/chains/Monacoin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..82fc75a0c68 --- /dev/null +++ b/tests/chains/Monacoin/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWMonacoinCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeMonacoin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeMonacoin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeMonacoin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeMonacoin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeMonacoin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeMonacoin), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeMonacoin)); + ASSERT_EQ(0x37, TWCoinTypeP2shPrefix(TWCoinTypeMonacoin)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeMonacoin)); + assertStringsEqual(symbol, "MONA"); + assertStringsEqual(txUrl, "https://blockbook.electrum-mona.org/tx/t123"); + assertStringsEqual(accUrl, "https://blockbook.electrum-mona.org/address/a12"); + assertStringsEqual(id, "monacoin"); + assertStringsEqual(name, "Monacoin"); +} diff --git a/tests/Monacoin/TWMonacoinAddressTests.cpp b/tests/chains/Monacoin/TWMonacoinAddressTests.cpp similarity index 99% rename from tests/Monacoin/TWMonacoinAddressTests.cpp rename to tests/chains/Monacoin/TWMonacoinAddressTests.cpp index e8f6c7c985d..d46964f25f5 100644 --- a/tests/Monacoin/TWMonacoinAddressTests.cpp +++ b/tests/chains/Monacoin/TWMonacoinAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Monacoin/TWMonacoinTransactionTests.cpp b/tests/chains/Monacoin/TWMonacoinTransactionTests.cpp similarity index 87% rename from tests/Monacoin/TWMonacoinTransactionTests.cpp rename to tests/chains/Monacoin/TWMonacoinTransactionTests.cpp index 5cc5da208b1..94f26493815 100644 --- a/tests/Monacoin/TWMonacoinTransactionTests.cpp +++ b/tests/chains/Monacoin/TWMonacoinTransactionTests.cpp @@ -1,11 +1,10 @@ - -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include "PublicKey.h" #include "proto/Bitcoin.pb.h" @@ -15,8 +14,7 @@ #include -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { TEST(MonacoinTransaction, SignTransaction) { /* @@ -67,8 +65,7 @@ TEST(MonacoinTransaction, SignTransaction) { ASSERT_EQ(output.error(), Common::Proto::OK); ASSERT_EQ(hex(output.encoded()), - "0100000001441a513dccc3b660c09c42ceaac147fcdc12b5de4b8b56a078fce5d5ce420aed000000006a473044022047789dc7483b178199439bbfce0ab0caf532fec51095ba099d0d9b0b2169033402201745a0160d8d327655a8ef0542367396ce86bbb13df6b183d58c922e422cfa10012102fc08693599fda741558613cd44a50fc65953b1be797637f8790a495b85554f3effffffff0280f0fa02000000001976a914076df984229a2731cbf465ec8fbd35b8da94380f88ac60a2fa02000000001976a914fea39370769d4fed2d8ab98dd5daa482cc56113b88ac00000000" - ); + "0100000001441a513dccc3b660c09c42ceaac147fcdc12b5de4b8b56a078fce5d5ce420aed000000006a473044022047789dc7483b178199439bbfce0ab0caf532fec51095ba099d0d9b0b2169033402201745a0160d8d327655a8ef0542367396ce86bbb13df6b183d58c922e422cfa10012102fc08693599fda741558613cd44a50fc65953b1be797637f8790a495b85554f3effffffff0280f0fa02000000001976a914076df984229a2731cbf465ec8fbd35b8da94380f88ac60a2fa02000000001976a914fea39370769d4fed2d8ab98dd5daa482cc56113b88ac00000000"); } TEST(MonacoinTransaction, LockScripts) { @@ -93,3 +90,5 @@ TEST(MonacoinTransaction, LockScripts) { auto scriptData3 = WRAPD(TWBitcoinScriptData(script3.get())); assertHexEqual(scriptData3, "001422e6014ad3631f1939281c3625bc98db808fbfb0"); } + +} // namespace TW::Bitcoin diff --git a/tests/chains/Moonbeam/TWCoinTypeTests.cpp b/tests/chains/Moonbeam/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..0c59369a597 --- /dev/null +++ b/tests/chains/Moonbeam/TWCoinTypeTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWMoonbeamCoinType, TWCoinType) { + const auto coin = TWCoinTypeMoonbeam; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb22a146c933e6e51affbfa5f712a266b5f5e92ae453cd2f252bcc3c36ff035a6")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x201bb4f276C765dF7225e5A4153E17edD23a67eC")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "moonbeam"); + assertStringsEqual(name, "Moonbeam"); + assertStringsEqual(symbol, "GLMR"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "1284"); + assertStringsEqual(txUrl, "https://moonscan.io/tx/0xb22a146c933e6e51affbfa5f712a266b5f5e92ae453cd2f252bcc3c36ff035a6"); + assertStringsEqual(accUrl, "https://moonscan.io/address/0x201bb4f276C765dF7225e5A4153E17edD23a67eC"); +} diff --git a/tests/chains/Moonriver/TWCoinTypeTests.cpp b/tests/chains/Moonriver/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..b19f8a84b7b --- /dev/null +++ b/tests/chains/Moonriver/TWCoinTypeTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWMoonriverCoinType, TWCoinType) { + const auto coin = TWCoinTypeMoonriver; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x2e2daa3943ba65d9bbb910a4f6765aa6a466a0ef8935090547ca9d30e201e032")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x899831D937937d011305E73EE782cce0455DF15a")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "moonriver"); + assertStringsEqual(name, "Moonriver"); + assertStringsEqual(symbol, "MOVR"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "1285"); + assertStringsEqual(txUrl, "https://moonriver.moonscan.io/tx/0x2e2daa3943ba65d9bbb910a4f6765aa6a466a0ef8935090547ca9d30e201e032"); + assertStringsEqual(accUrl, "https://moonriver.moonscan.io/address/0x899831D937937d011305E73EE782cce0455DF15a"); +} diff --git a/tests/chains/NEAR/AccountTests.cpp b/tests/chains/NEAR/AccountTests.cpp new file mode 100644 index 00000000000..f1294306588 --- /dev/null +++ b/tests/chains/NEAR/AccountTests.cpp @@ -0,0 +1,24 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "NEAR/Account.h" +#include "HexCoding.h" +#include + +namespace TW::NEAR::tests { + +TEST(NEARAccount, Validation) { + ASSERT_FALSE(Account::isValid("a")); + ASSERT_FALSE(Account::isValid("!?:")); + ASSERT_FALSE(Account::isValid("11111111111111111111111111111111222222222222222222222222222222223")); + + ASSERT_TRUE(Account::isValid("9902c136629fc630416e50d4f2fef6aff867ea7e.lockup.near")); + ASSERT_TRUE(Account::isValid("app_1.alice.near")); + ASSERT_TRUE(Account::isValid("test-trust.vlad.near")); + ASSERT_TRUE(Account::isValid("deadbeef")); +} + +} // namespace TW::NEAR::tests diff --git a/tests/chains/NEAR/AddressTests.cpp b/tests/chains/NEAR/AddressTests.cpp new file mode 100644 index 00000000000..214cd3fab2b --- /dev/null +++ b/tests/chains/NEAR/AddressTests.cpp @@ -0,0 +1,43 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "NEAR/Address.h" +#include "Base58.h" +#include "PrivateKey.h" +#include + +#include + +namespace TW::NEAR::tests { + +TEST(NEARAddress, Validation) { + ASSERT_FALSE(Address::isValid("abc")); + ASSERT_FALSE(Address::isValid("65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjTF")); + ASSERT_FALSE(Address::isValid("EOS65QzSGJ579GPNKtZoZkChTzsxR4B48RCfiS82m2ymJR6VZCjT")); + + ASSERT_TRUE(Address::isValid("NEAR2758Nk7CMUcxTwXdjVdSxNEidiZQWMZN3USJzj76q5ia3v2v2v")); + ASSERT_TRUE(Address::isValid("917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d")); +} + +TEST(NEARAddress, FromString) { + ASSERT_EQ( + Address("NEAR2758Nk7CMUcxTwXdjVdSxNEidiZQWMZN3USJzj76q5ia3v2v2v").string(), + "917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d"); + ASSERT_EQ( + Address("9685af3fe2dc231e5069ccff8ec6950eb961d42ebb9116a8ab9c0d38f9e45249").string(), + "9685af3fe2dc231e5069ccff8ec6950eb961d42ebb9116a8ab9c0d38f9e45249"); +} + +TEST(NEARAddress, FromPrivateKey) { + auto fullKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + auto key = PrivateKey(Data(fullKey.begin(), fullKey.begin() + 32)); + auto publicKey = key.getPublicKey(TWPublicKeyTypeED25519); + auto address = Address(publicKey); + + ASSERT_EQ(address.string(), "917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d"); +} + +} // namespace TW::NEAR::tests diff --git a/tests/chains/NEAR/SerializationTests.cpp b/tests/chains/NEAR/SerializationTests.cpp new file mode 100644 index 00000000000..7f6904988b6 --- /dev/null +++ b/tests/chains/NEAR/SerializationTests.cpp @@ -0,0 +1,278 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Base58.h" +#include "proto/NEAR.pb.h" +#include "NEAR/Serialization.h" + +#include +#include + +namespace TW::NEAR { + +TEST(NEARSerialization, SerializeTransferTransaction) { + auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + + auto input = Proto::SigningInput(); + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + + input.add_actions(); + auto& transfer = *input.mutable_actions(0)->mutable_transfer(); + Data deposit(16, 0); + deposit[0] = 1; + transfer.set_deposit(deposit.data(), deposit.size()); + + auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + input.set_private_key(privateKey.data(), 32); + + auto serialized = transactionData(input); + auto serializedHex = hex(serialized); + + ASSERT_EQ(serializedHex, "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000301000000000000000000000000000000"); +} + +TEST(NEARSerialization, SerializeFunctionCallTransaction) { + auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + + auto input = Proto::SigningInput(); + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + + input.add_actions(); + auto& functionCall = *input.mutable_actions(0)->mutable_function_call(); + + functionCall.set_method_name("qqq"); + functionCall.set_gas(1000); + + Data deposit(16, 0); + deposit[0] = 1; + functionCall.set_deposit(deposit.data(), deposit.size()); + + Data args(3, 0); + args[0] = 1; + args[1] = 2; + args[2] = 3; + functionCall.set_args(args.data(), args.size()); + + auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + input.set_private_key(privateKey.data(), 32); + + auto serialized = transactionData(input); + auto serializedHex = hex(serialized); + + ASSERT_EQ(serializedHex, "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef601000000020300000071717103000000010203e80300000000000001000000000000000000000000000000"); +} + +TEST(NEARSerialization, SerializeStakeTransaction) { + auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + + auto input = Proto::SigningInput(); + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + + input.add_actions(); + auto& stake = *input.mutable_actions(0)->mutable_stake(); + Data amount(16, 0); + amount[0] = 1; + stake.set_stake(amount.data(), amount.size()); + + auto& pKey = *stake.mutable_public_key(); + pKey.set_data(publicKey.data(), publicKey.size()); + pKey.set_key_type(0); + + auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + input.set_private_key(privateKey.data(), 32); + + auto serialized = transactionData(input); + auto serializedHex = hex(serialized); + + ASSERT_EQ(serializedHex, "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef601000000040100000000000000000000000000000000917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d"); +} + +TEST(NEARSerialization, SerializeStakeTransaction2) { + auto publicKey = Base58::bitcoin.decode("C2P7YcEmBv31vtCHLBcESteN4Yi4vSCkXEXMTANyB649"); + + auto input = Proto::SigningInput(); + input.set_signer_id("vdx.testnet"); + input.set_nonce(93128451000005); + input.set_receiver_id("vdx.testnet"); + + input.add_actions(); + auto& stake = *input.mutable_actions(0)->mutable_stake(); + // 2490000000000000000000000000 + auto amount = parse_hex("000000fa4f3f757902ae0b0800000000"); // little endian + stake.set_stake(amount.data(), amount.size()); + + auto& pKey = *stake.mutable_public_key(); + pKey.set_data(publicKey.data(), publicKey.size()); + pKey.set_key_type(0); + + auto blockHash = Base58::bitcoin.decode("ByDnm7c25npQXwNUX5yivbYbpjFcNuNumF6BJjaK3vhJ"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("5Cfk7QBnmDxxFxQk75FFq4ADrQS9gxHKe6vtuGH6JCCm8WV8aRPEGVqp579JHNmmHMUt49gkCVcH2t7NRnh2v7Qu"); + input.set_private_key(privateKey.data(), 32); + + auto serialized = transactionData(input); + auto serializedHex = hex(serialized); + + ASSERT_EQ(serializedHex, "0b0000007664782e746573746e657400a3cb23dbb9810abd4a6804328eec47a17236383b5c234cae903b064e9dc426dac5863d28b35400000b0000007664782e746573746e6574a2fbdae8a769c636d109952e4fe760b03688e629933cbf693aedfd97a470c7a50100000004000000fa4f3f757902ae0b080000000000a3cb23dbb9810abd4a6804328eec47a17236383b5c234cae903b064e9dc426da"); +} + +TEST(NEARSerialization, SerializeAddKeyFunctionCallTransaction) { + auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + + auto input = Proto::SigningInput(); + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + + input.add_actions(); + auto& addKey = *input.mutable_actions(0)->mutable_add_key(); + + auto& pKey = *addKey.mutable_public_key(); + pKey.set_data(publicKey.data(), publicKey.size()); + pKey.set_key_type(0); + + auto& accessKey = *addKey.mutable_access_key(); + accessKey.set_nonce(0); + auto& functionCallPermission = *accessKey.mutable_function_call(); + functionCallPermission.set_receiver_id("zzz"); + functionCallPermission.add_method_names("www"); + + auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + input.set_private_key(privateKey.data(), 32); + + auto serialized = transactionData(input); + auto serializedHex = hex(serialized); + + ASSERT_EQ(serializedHex, "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000500917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d00000000000000000000030000007a7a7a0100000003000000777777"); +} + +TEST(NEARSerialization, SerializeAddKeyFullAccessTransaction) { + auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + + auto input = Proto::SigningInput(); + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + + input.add_actions(); + auto& addKey = *input.mutable_actions(0)->mutable_add_key(); + + auto& pKey = *addKey.mutable_public_key(); + pKey.set_data(publicKey.data(), publicKey.size()); + pKey.set_key_type(0); + + auto& accessKey = *addKey.mutable_access_key(); + accessKey.set_nonce(0); + + accessKey.mutable_full_access(); + + auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + input.set_private_key(privateKey.data(), 32); + + auto serialized = transactionData(input); + auto serializedHex = hex(serialized); + + ASSERT_EQ(serializedHex, "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000500917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d000000000000000001"); +} + +TEST(NEARSerialization, SerializeDeleteKeyTransaction) { + auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + + auto input = Proto::SigningInput(); + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + + input.add_actions(); + auto& deleteKey = *input.mutable_actions(0)->mutable_delete_key(); + + auto& pKey = *deleteKey.mutable_public_key(); + pKey.set_data(publicKey.data(), publicKey.size()); + pKey.set_key_type(0); + + auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + input.set_private_key(privateKey.data(), 32); + + auto serialized = transactionData(input); + auto serializedHex = hex(serialized); + + ASSERT_EQ(serializedHex, "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000600917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d"); +} + +TEST(NEARSerialization, SerializeCreateAccountTransaction) { + auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + + auto input = Proto::SigningInput(); + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + + input.add_actions(); + input.mutable_actions(0)->mutable_create_account(); + + auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + input.set_private_key(privateKey.data(), 32); + + auto serialized = transactionData(input); + auto serializedHex = hex(serialized); + + ASSERT_EQ(serializedHex, "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef60100000000"); +} + +TEST(NEARSerialization, SerializeDeleteAccountTransaction) { + auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + + auto input = Proto::SigningInput(); + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + + input.add_actions(); + auto& deleteAccount = *input.mutable_actions(0)->mutable_delete_account(); + deleteAccount.set_beneficiary_id("123"); + + auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + input.set_private_key(privateKey.data(), 32); + + auto serialized = transactionData(input); + auto serializedHex = hex(serialized); + + ASSERT_EQ(serializedHex, "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6010000000703000000313233"); +} + +} diff --git a/tests/chains/NEAR/SignerTests.cpp b/tests/chains/NEAR/SignerTests.cpp new file mode 100644 index 00000000000..2a41296484c --- /dev/null +++ b/tests/chains/NEAR/SignerTests.cpp @@ -0,0 +1,48 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base64.h" +#include "Base58.h" +#include "HexCoding.h" +#include "proto/NEAR.pb.h" +#include "NEAR/Signer.h" + +#include +#include + +namespace TW::NEAR { + +TEST(NEARSigner, SignTx) { + auto publicKey = Base58::bitcoin.decode("Anu7LYDfpLtkP7E16LT9imXF694BdQaa9ufVkQiwTQxC"); + + auto input = Proto::SigningInput(); + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + + input.add_actions(); + auto& transfer = *input.mutable_actions(0)->mutable_transfer(); + Data deposit(16, 0); + deposit[0] = 1; + // uint128_t / little endian byte order + transfer.set_deposit(deposit.data(), deposit.size()); + + auto blockHash = Base58::bitcoin.decode("244ZQ9cgj3CQ6bWBdytfrJMuMQ1jdXLFGnr4HhvtCTnM"); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto privateKey = Base58::bitcoin.decode("3hoMW1HvnRLSFCLZnvPzWeoGwtdHzke34B2cTHM8rhcbG3TbuLKtShTv3DvyejnXKXKBiV7YPkLeqUHN1ghnqpFv"); + input.set_private_key(privateKey.data(), 32); + + auto output = Signer::sign(std::move(input)); + + auto signed_transaction = output.signed_transaction(); + auto outputInBase64 = Base64::encode(Data(signed_transaction.begin(), signed_transaction.end())); + + ASSERT_EQ(outputInBase64, "CQAAAHRlc3QubmVhcgCRez0mjUtY9/7BsVC9aNab4+5dTMOYVeNBU4Rlu3eGDQEAAAAAAAAADQAAAHdoYXRldmVyLm5lYXIPpHP9JpAd8pa+atxMxN800EDvokNSJLaYaRDmMML+9gEAAAADAQAAAAAAAAAAAAAAAAAAAACWmoMzIYbul1Xkg5MlUlgG4Ymj0tK7S0dg6URD6X4cTyLe7vAFmo6XExAO2m4ZFE2n6KDvflObIHCLodjQIb0B"); + ASSERT_EQ(hex(output.hash()), "eea6e680f3ea51a7f667e9a801d0bfadf66e03d41ed54975b3c6006351461b32"); +} + +} diff --git a/tests/chains/NEAR/TWAnySignerTests.cpp b/tests/chains/NEAR/TWAnySignerTests.cpp new file mode 100644 index 00000000000..afa81ba9a79 --- /dev/null +++ b/tests/chains/NEAR/TWAnySignerTests.cpp @@ -0,0 +1,71 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/NEAR.pb.h" +#include "TestUtilities.h" +#include +#include + +namespace TW::NEAR { + +TEST(TWAnySignerNEAR, SignTransfer) { + + auto privateKey = parse_hex("8737b99bf16fba78e1e753e23ba00c4b5423ac9c45d9b9caae9a519434786568"); + auto blockHash = parse_hex("0fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef6"); + // uint128_t / little endian byte order + auto deposit = parse_hex("01000000000000000000000000000000"); + + Proto::SigningInput input; + input.set_signer_id("test.near"); + input.set_nonce(1); + input.set_receiver_id("whatever.near"); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto& action = *input.add_actions(); + auto& transfer = *action.mutable_transfer(); + transfer.set_deposit(deposit.data(), deposit.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNEAR); + + ASSERT_EQ(hex(output.signed_transaction()), "09000000746573742e6e65617200917b3d268d4b58f7fec1b150bd68d69be3ee5d4cc39855e341538465bb77860d01000000000000000d00000077686174657665722e6e6561720fa473fd26901df296be6adc4cc4df34d040efa2435224b6986910e630c2fef601000000030100000000000000000000000000000000969a83332186ee9755e4839325525806e189a3d2d2bb4b4760e94443e97e1c4f22deeef0059a8e9713100eda6e19144da7e8a0ef7e539b20708ba1d8d021bd01"); + ASSERT_EQ(hex(output.hash()), "eea6e680f3ea51a7f667e9a801d0bfadf66e03d41ed54975b3c6006351461b32"); +} + +TEST(TWAnySignerNEAR, SignStake) { + + auto privateKey = parse_hex("d22149327ceb8e86f70962be0c7293f8308d85d0cbea2cc24e47c3033da7440f"); + auto publicKey = parse_hex("a3cb23dbb9810abd4a6804328eec47a17236383b5c234cae903b064e9dc426da"); + auto blockHash = parse_hex("a2fbdae8a769c636d109952e4fe760b03688e629933cbf693aedfd97a470c7a5"); + + // 2490000000000000000000000000 + auto amount = parse_hex("000000fa4f3f757902ae0b0800000000"); // little endian + + Proto::SigningInput input; + input.set_signer_id("vdx.testnet"); + input.set_nonce(93128451000005); + input.set_receiver_id("vdx.testnet"); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + + auto& action = *input.add_actions(); + auto& stake = *action.mutable_stake(); + stake.set_stake(amount.data(), amount.size()); + + auto& pubkey = *stake.mutable_public_key(); + pubkey.set_data(publicKey.data(), publicKey.size()); + pubkey.set_key_type(0); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNEAR); + + ASSERT_EQ(hex(output.signed_transaction()), "0b0000007664782e746573746e657400a3cb23dbb9810abd4a6804328eec47a17236383b5c234cae903b064e9dc426dac5863d28b35400000b0000007664782e746573746e6574a2fbdae8a769c636d109952e4fe760b03688e629933cbf693aedfd97a470c7a50100000004000000fa4f3f757902ae0b080000000000a3cb23dbb9810abd4a6804328eec47a17236383b5c234cae903b064e9dc426da0011fdbc234d4ce470ec7f2ac5e4d3d8f8fe1525f83e9a2425e7000aea52f7260ff4f5191beaa1a5ac29256e68c6acd368ada0d06ed033e9a204ee119f5ef1b104"); + ASSERT_EQ(hex(output.hash()), "c8aedbf75fcaa9b663a3959d27f1deae809e1923460791471e5219eafecc4ba8"); +} + +} // namespace TW::NEAR diff --git a/tests/chains/NEAR/TWCoinTypeTests.cpp b/tests/chains/NEAR/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..c5a04325d1f --- /dev/null +++ b/tests/chains/NEAR/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWNEARCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNEAR)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("FPQAMaVnvFHNwNBJWnTttXfdJhp5FvMGGDJEesB8gvbL")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNEAR, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("test-trust.vlad.near")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNEAR, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNEAR)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNEAR)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNEAR), 24); + ASSERT_EQ(TWBlockchainNEAR, TWCoinTypeBlockchain(TWCoinTypeNEAR)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNEAR)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNEAR)); + assertStringsEqual(symbol, "NEAR"); + assertStringsEqual(txUrl, "https://explorer.near.org/transactions/FPQAMaVnvFHNwNBJWnTttXfdJhp5FvMGGDJEesB8gvbL"); + assertStringsEqual(accUrl, "https://explorer.near.org/accounts/test-trust.vlad.near"); + assertStringsEqual(id, "near"); + assertStringsEqual(name, "NEAR"); +} diff --git a/tests/NEAR/TWNEARAccountTests.cpp b/tests/chains/NEAR/TWNEARAccountTests.cpp similarity index 96% rename from tests/NEAR/TWNEARAccountTests.cpp rename to tests/chains/NEAR/TWNEARAccountTests.cpp index c97ba7d5c0d..0be2cf6dbc7 100644 --- a/tests/NEAR/TWNEARAccountTests.cpp +++ b/tests/chains/NEAR/TWNEARAccountTests.cpp @@ -6,7 +6,7 @@ // // -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/NEO/AddressTests.cpp b/tests/chains/NEO/AddressTests.cpp new file mode 100644 index 00000000000..c17f9b8a5c9 --- /dev/null +++ b/tests/chains/NEO/AddressTests.cpp @@ -0,0 +1,74 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "NEO/Address.h" +#include "NEO/Signer.h" +#include "PublicKey.h" + +#include + +using namespace std; +using namespace TW; + +namespace TW::NEO::tests { + +TEST(NEOAddress, FromPublicKey) { + const auto publicKey = PublicKey(parse_hex("0222b2277d039d67f4197a638dd5a1d99c290b17aa8c4a16ccee5165fe612de66a"), TWPublicKeyTypeSECP256k1); + const auto address = Address(publicKey); + EXPECT_EQ(string("AKmrAHRD9ZDUnu4m3vWWonpsojo4vgSuqp"), address.string()); +} + +TEST(NEOAddress, FromString) { + string neoAddress = "AXkgwcMJTy9wTAXHsbyhauxh7t2Tt31MmC"; + const auto address = Address(neoAddress); + EXPECT_EQ(address.string(), neoAddress); +} + +TEST(NEOAddress, isValid) { + string neoAddress = "AQAsqiyHS4SSVWZ4CmMmnCxWg7vJ84GEj4"; + string bitcoinAddress = "1Ma2DrB78K7jmAwaomqZNRMCvgQrNjE2QC"; + + EXPECT_TRUE(Address::isValid(neoAddress)); + EXPECT_FALSE(Address::isValid(bitcoinAddress)); +} + +TEST(NEOAddress, validation) { + EXPECT_FALSE(Address::isValid("abc")); + EXPECT_FALSE(Address::isValid("abeb60f3e94c1b9a09f33669435e7ef12eacd")); + EXPECT_FALSE(Address::isValid("abcb60f3e94c9b9a09f33669435e7ef1beaedads")); + EXPECT_TRUE(Address::isValid("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD")); +} + +TEST(NEOAddress, fromPubKey) { + auto address = Address(PublicKey(parse_hex("031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"), TWPublicKeyTypeNIST256p1)); + EXPECT_EQ("AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5", address.string()); +} + +TEST(NEOAddress, fromString) { + auto b58Str = "AYTxeseHT5khTWhtWX1pFFP1mbQrd4q1zz"; + auto address = Address(b58Str); + EXPECT_EQ(b58Str, address.string()); + auto errB58Str = "AATxeseHT5khTWhtWX1pFFP1mbQrd4q1zz"; + EXPECT_THROW(new Address(errB58Str), std::invalid_argument); +} + +TEST(NEOAddress, Valid) { + ASSERT_TRUE(Address::isValid("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD")); +} + +TEST(NEOAddress, Invalid) { + ASSERT_FALSE(Address::isValid("ANDfjwrUr54515515155WKRMyxFwvVwnZD")); +} + +TEST(NEOAddress, FromPrivateKey) { + auto key = PrivateKey(parse_hex("0x2A9EAB0FEC93CD94FA0A209AC5604602C1F0105FB02EAB398E17B4517C2FFBAB")); + auto publicKey = key.getPublicKey(TWPublicKeyTypeNIST256p1); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "AQCSMB3oSDA1dHPn6GXN6KB4NHmdo1fX41"); +} + +} // namespace TW::NEO::tests diff --git a/tests/chains/NEO/CoinReferenceTests.cpp b/tests/chains/NEO/CoinReferenceTests.cpp new file mode 100644 index 00000000000..1101986a96f --- /dev/null +++ b/tests/chains/NEO/CoinReferenceTests.cpp @@ -0,0 +1,39 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "uint256.h" +#include "HexCoding.h" +#include "NEO/CoinReference.h" +#include + +namespace TW::NEO::tests { + +using namespace std; + +TEST(NEOCoinReference, Serialize) { + auto coinReference = CoinReference(); + string prevHash = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"; + coinReference.prevHash = load(parse_hex(prevHash)); + coinReference.prevIndex = 1; + EXPECT_EQ(prevHash + "0100", hex(coinReference.serialize())); +} + +TEST(NEOCoinReference, SerializeWithZeroLeading) { + auto coinReference = CoinReference(); + string prevHash = "0037ebf259ca5c6c43a5e7117c910858ea1146290e07d39e48554bc00d890b94"; + coinReference.prevHash = load(parse_hex(prevHash)); + coinReference.prevIndex = 1; + EXPECT_EQ(prevHash + "0100", hex(coinReference.serialize())); +} + +TEST(NEOCoinReference, Deserialize) { + auto coinReference = CoinReference(); + coinReference.deserialize(parse_hex("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a0100")); + EXPECT_EQ("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a", hex(store(coinReference.prevHash))); + EXPECT_EQ(1, coinReference.prevIndex); +} + +} // namespace TW::NEO::tests diff --git a/tests/chains/NEO/SignerTests.cpp b/tests/chains/NEO/SignerTests.cpp new file mode 100644 index 00000000000..fe33508c885 --- /dev/null +++ b/tests/chains/NEO/SignerTests.cpp @@ -0,0 +1,86 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "PublicKey.h" +#include "HexCoding.h" +#include "NEO/Address.h" +#include "NEO/Signer.h" + +#include + +namespace TW::NEO::tests { + +using namespace std; + +TEST(NEOSigner, FromPublicPrivateKey) { + auto hexPrvKey = "4646464646464646464646464646464646464646464646464646464646464646"; + auto hexPubKey = "031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"; + auto signer = Signer(PrivateKey(parse_hex(hexPrvKey))); + auto prvKey = signer.getPrivateKey(); + auto pubKey = signer.getPublicKey(); + + EXPECT_EQ(hexPrvKey, hex(prvKey.bytes)); + EXPECT_EQ(hexPubKey, hex(pubKey.bytes)); + + auto address = signer.getAddress(); + EXPECT_TRUE(Address::isValid(address.string())); + + EXPECT_EQ(Address(pubKey), address); +} + +TEST(NEOSigner, SigningData) { + auto signer = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); + auto verScript = "ba7908ddfe5a1177f2c9d3fa1d3dc71c9c289a3325b3bdd977e20c50136959ed02d1411efa5e8b897d970ef7e2325e6c0a3fdee4eb421223f0d86e455879a9ad"; + auto invocationScript = string("401642b3d538e138f34b32330e381a7fe3f5151fcf958f2030991e72e2e25043143e4a1ebd239634efba279c96fa0ab04a15aa15179d73a7ef5a886ac8a06af484401642b3d538e138f34b32330e381a7fe3f5151fcf958f2030991e72e2e25043143e4a1ebd239634efba279c96fa0ab04a15aa15179d73a7ef5a886ac8a06af484401642b3d538e138f34b32330e381a7fe3f5151fcf958f2030991e72e2e25043143e4a1ebd239634efba279c96fa0ab04a15aa15179d73a7ef5a886ac8a06af484"); + invocationScript = string(invocationScript.rbegin(), invocationScript.rend()); + + EXPECT_EQ(verScript, hex(signer.sign(parse_hex(invocationScript)))); +} + +TEST(NEOAccount, validity) { + auto hexPrvKey = "4646464646464646464646464646464646464646464646464646464646464646"; + auto hexPubKey = "031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"; + auto signer = Signer(PrivateKey(parse_hex(hexPrvKey))); + auto prvKey = signer.getPrivateKey(); + auto pubKey = signer.getPublicKey(); + EXPECT_EQ(hexPrvKey, hex(prvKey.bytes)); + EXPECT_EQ(hexPubKey, hex(pubKey.bytes)); +} + +TEST(NEOSigner, SigningTransaction) { + auto signer = Signer(PrivateKey(parse_hex("F18B2F726000E86B4950EBEA7BFF151F69635951BC4A31C44F28EE6AF7AEC128"))); + auto transaction = Transaction(); + transaction.type = TransactionType::TT_ContractTransaction; + transaction.version = 0x00; + + CoinReference coin; + coin.prevHash = load(parse_hex("9c85b39cd5677e2bfd6bf8a711e8da93a2f1d172b2a52c6ca87757a4bccc24de")); // reverse hash + coin.prevIndex = (uint16_t)1; + transaction.inInputs.push_back(coin); + + { + TransactionOutput out; + out.assetId = load(parse_hex("9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5")); + out.value = (int64_t)1 * 100000000; + auto scriptHash = TW::NEO::Address("Ad9A1xPbuA5YBFr1XPznDwBwQzdckAjCev").toScriptHash(); + out.scriptHash = load(scriptHash); + transaction.outputs.push_back(out); + } + + { + TransactionOutput out; + out.assetId = load(parse_hex("9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5")); + out.value = (int64_t)892 * 100000000; + auto scriptHash = TW::NEO::Address("AdtSLMBqACP4jv8tRWwyweXGpyGG46eMXV").toScriptHash(); + out.scriptHash = load(scriptHash); + transaction.outputs.push_back(out); + } + signer.sign(transaction); + auto signedTx = transaction.serialize(); + EXPECT_EQ(hex(signedTx), "800000019c85b39cd5677e2bfd6bf8a711e8da93a2f1d172b2a52c6ca87757a4bccc24de0100029b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500e1f50500000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500fcbbc414000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac0141405046619c8e20e1fdeec92ce95f3019f6e7cc057294eb16b2d5e55c105bf32eb27e1fc01c1858576228f1fef8c0945a8ad69688e52a4ed19f5b85f5eff7e961d7232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); +} + +} // namespace TW::NEO::tests diff --git a/tests/chains/NEO/TWAnySignerTests.cpp b/tests/chains/NEO/TWAnySignerTests.cpp new file mode 100644 index 00000000000..9a92c1133c0 --- /dev/null +++ b/tests/chains/NEO/TWAnySignerTests.cpp @@ -0,0 +1,104 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include "HexCoding.h" +#include "proto/NEO.pb.h" + +#include + +namespace TW::NEO::tests { + +Proto::SigningInput createInput() { + const std::string NEO_ASSET_ID = "9b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc5"; + const std::string GAS_ASSET_ID = "e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c60"; + + Proto::SigningInput input; + auto privateKey = parse_hex("F18B2F726000E86B4950EBEA7BFF151F69635951BC4A31C44F28EE6AF7AEC128"); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_fee(12345); // too low + input.set_gas_asset_id(GAS_ASSET_ID); + input.set_gas_change_address("AdtSLMBqACP4jv8tRWwyweXGpyGG46eMXV"); + +#define ADD_UTXO_INPUT(hash, index, value, assetId) \ + { \ + auto utxo = input.add_inputs(); \ + utxo->set_prev_hash(parse_hex(hash).data(), parse_hex(hash).size()); \ + utxo->set_prev_index(index); \ + utxo->set_asset_id(assetId); \ + utxo->set_value(value); \ + } + + ADD_UTXO_INPUT("c61508268c5d0343af1875c60e569493100824dbdba108b31789e0e33bcb50fb", 1, 98899890000, GAS_ASSET_ID); + ADD_UTXO_INPUT("4eb2f96937a0d4dc96b77ba69a29e1de9574cbd62b16d881f1ee2061a291d70b", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("3fee0109d155dcfab272176117306b45b176914c88e8c379933c246a9e29ea0b", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("6ea9ce8c578bfeeecdf281f498e2a764689df3b93d6855a3cc45bd6b5213c426", 0, 400000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("f75ad3cbd277d83ee240e08f99a97ffd7e42a82a868e0f7043414f6d6147262b", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("054734e98f442b3e73a940ca8f594859ece1c7ddac14130b0e2f5e2799b85931", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("8b0c42d448912fc28c674fdcf8e21e4667d7d2133666168eaa0570488a9c5036", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 1, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 2, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 3, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 4, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 5, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 6, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 7, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 8, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 9, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("cf83bce600626b6077e136581c1aecc78a0bbb7d7649b1f580b6be881087ec40", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("9bd7572ba8df685e262369897d24f7217b42be496b9eed16e16a889dd83b394e", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("b4ee250397dde2f1001d782d3c803c38992447d3b351cdc9bf20cfaa2cbf995b", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("e1019ca259a1615f77263324156a70007b76cb4f26b01b2956b8f85e6842ac62", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("bd379df2aca526ac600919aaba0e59d4a1ad4e2f22d18966063cf45e431d016f", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("164c3f843b9b7bfa6a7376a1548f343acb5cdfa0193b8f31e8c9a647ea63ea7d", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("4acec74a76161eafe70e0791b1f504b5ba1d175fd4f340d5bf56804e25505e92", 0, 300000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("895c6629a71c84cbdc8956abea9ca2d9d215e909e6173b1a1a96289186a67796", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("54828143c4c3a0e1b09102e4ed29220b141089c2bc4200b1042eeb12e5e49296", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("5345e4abc86f7ace47112f5a91c129175833bafcaf9f1e1bcbbaf4d019c1c69d", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("c83e19d0d4210df97b3bc7768dc7184ae3acfc1b5b3ac9b05d2be0fe5a636b9f", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("3456b03f5cb688ce26ab1d09b7a15799136c8c886ca7c3c6bcb2363e61bb1bb1", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("048f73d6cc82d9d92b08044eccef66c78a0c22e836988ed25d6f7ffe24fb5b38", 10, 34000000000, NEO_ASSET_ID); + // all inputs below must be unused in this tx + ADD_UTXO_INPUT("e5a7887521b8b3aaf2d5426617ddabe8ef8ea3eab31c80a977c3b8f339df5be0", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("1455e9dd3cd6a04d81cd47acc07a7335212029ebbdcd0abc3e52c33f8b77f6eb", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("da711260085211b5573801d0dfe064235c69e61a55f9c15449ac55cc02b9adee", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("04486cfed371103dd51a89205b2c8bcc45ad887c49a768a62465f35810437bef", 0, 500000000, NEO_ASSET_ID); + ADD_UTXO_INPUT("a5f27055a442db0e65103561900456d37af4233267960daded870c1ab2219ef4", 0, 500000000, NEO_ASSET_ID); + + { + auto output = input.add_outputs(); + output->set_asset_id(NEO_ASSET_ID); + output->set_to_address("Ad9A1xPbuA5YBFr1XPznDwBwQzdckAjCev"); + output->set_change_address("AdtSLMBqACP4jv8tRWwyweXGpyGG46eMXV"); + output->set_amount(25000000000); + } + + return input; +} + +TEST(TWAnySignerNEO, Sign) { + Proto::SigningInput input = createInput(); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNEO); + + // https://testnet-explorer.o3.network/transactions/0x7b138c753c24f474d0f70af30a9d79756e0ee9c1f38c12ed07fbdf6fc5132eaf + ASSERT_EQ(hex(output.encoded()), "8000001efb50cb3be3e08917b308a1dbdb2408109394560ec67518af43035d8c260815c601000bd791a26120eef181d8162bd6cb7495dee1299aa67bb796dcd4a03769f9b24e00000bea299e6a243c9379c3e8884c9176b1456b3017611772b2fadc55d10901ee3f000026c413526bbd45cca355683db9f39d6864a7e298f481f2cdeefe8b578ccea96e00002b2647616d4f4143700f8e862aa8427efd7fa9998fe040e23ed877d2cbd35af700003159b899275e2f0e0b1314acddc7e1ec5948598fca40a9733e2b448fe9344705000036509c8a487005aa8e16663613d2d767461ee2f8dc4f678cc22f9148d4420c8b0000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040100385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040200385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040300385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040400385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040500385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040600385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040700385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040800385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f04090040ec871088beb680f5b149767dbb0b8ac7ec1a1c5836e177606b6200e6bc83cf00004e393bd89d886ae116ed9e6b49be427b21f7247d896923265e68dfa82b57d79b00005b99bf2caacf20bfc9cd51b3d3472499383c803c2d781d00f1e2dd970325eeb4000062ac42685ef8b856291bb0264fcb767b00706a15243326775f61a159a29c01e100006f011d435ef43c066689d1222f4eada1d4590ebaaa190960ac26a5acf29d37bd00007dea63ea47a6c9e8318f3b19a0df5ccb3a348f54a176736afa7b9b3b843f4c160000925e50254e8056bfd540f3d45f171dbab504f5b191070ee7af1e16764ac7ce4a00009677a6869128961a1a3b17e609e915d2d9a29ceaab5689dccb841ca729665c8900009692e4e512eb2e04b10042bcc28910140b2229ede40291b0e1a0c3c44381825400009dc6c119d0f4bacb1b1e9faffcba33581729c1915a2f1147ce7a6fc8abe4455300009f6b635afee02b5db0c93a5b1bfcace34a18c78d76c73b7bf90d21d4d0193ec80000b11bbb613e36b2bcc6c3a76c888c6c139957a1b7091dab26ce88b65c3fb056340000385bfb24fe7f6f5dd28e9836e8220c8ac766efcc4e04082bd9d982ccd6738f040a00039b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc500ba1dd205000000ea610aa6db39bd8c8556c9569d94b5e5a5d0ad199b7cffdaa674beae0f930ebe6085af9093e5fe56b34a5c220ccdcf6efc336fc50083064905000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ace72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c605013cf0617000000f2908c7efc0c9e43ffa7e79170ba37e501e1b4ac014140dc261ac093a87640441bf0c3ad4a55ec727932b9175f600618bb5275f31aacf122956bc88746dc666759a2d67f120fe3ce1659f916d22a91e0b02421d3bddbd1232102a41c2aea8568864b106553729d32b1317ec463aa23e7a3521455d95992e17a7aac"); +} + +TEST(TWAnySignerNEO, Plan) { + Proto::SigningInput input = createInput(); + Proto::TransactionPlan plan; + ANY_PLAN(input, plan, TWCoinTypeNEO); + + EXPECT_EQ(plan.inputs_size(), 30); + EXPECT_EQ(plan.outputs_size(), 2); + EXPECT_EQ(plan.fee(), 1408000); + EXPECT_EQ(plan.error(), Common::Proto::OK); +} + +} // namespace TW::NEO::tests diff --git a/tests/chains/NEO/TWCoinTypeTests.cpp b/tests/chains/NEO/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..fa87f98119b --- /dev/null +++ b/tests/chains/NEO/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWNEOCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNEO)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("e0ddf7c81c732df26180aca0c36d5868ad009fdbbe6e7a56ebafc14bba41cd53")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNEO, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("AcxuqWhTureEQGeJgbmtSWNAtssjMLU7pb")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNEO, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNEO)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNEO)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNEO), 8); + ASSERT_EQ(TWBlockchainNEO, TWCoinTypeBlockchain(TWCoinTypeNEO)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNEO)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNEO)); + assertStringsEqual(symbol, "NEO"); + assertStringsEqual(txUrl, "https://neoscan.io/transaction/e0ddf7c81c732df26180aca0c36d5868ad009fdbbe6e7a56ebafc14bba41cd53"); + assertStringsEqual(accUrl, "https://neoscan.io/address/AcxuqWhTureEQGeJgbmtSWNAtssjMLU7pb"); + assertStringsEqual(id, "neo"); + assertStringsEqual(name, "NEO"); +} \ No newline at end of file diff --git a/tests/NEO/TWNEOAddressTests.cpp b/tests/chains/NEO/TWNEOAddressTests.cpp similarity index 96% rename from tests/NEO/TWNEOAddressTests.cpp rename to tests/chains/NEO/TWNEOAddressTests.cpp index 029f3bdb95b..cc36db2af5f 100644 --- a/tests/NEO/TWNEOAddressTests.cpp +++ b/tests/chains/NEO/TWNEOAddressTests.cpp @@ -9,7 +9,7 @@ #include #include "HexCoding.h" -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace TW; diff --git a/tests/chains/NEO/TransactionAttributeTests.cpp b/tests/chains/NEO/TransactionAttributeTests.cpp new file mode 100644 index 00000000000..73b64b14554 --- /dev/null +++ b/tests/chains/NEO/TransactionAttributeTests.cpp @@ -0,0 +1,93 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "uint256.h" +#include "HexCoding.h" +#include "NEO/ReadData.h" +#include "NEO/TransactionAttribute.h" +#include "NEO/TransactionAttributeUsage.h" +#include + +namespace TW::NEO::tests { + +using namespace std; + +TEST(NEOTransactionAttribute, Serialize) { + auto transactionAttribute = TransactionAttribute(); + string data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"; + transactionAttribute.usage = TransactionAttributeUsage::TAU_ContractHash; + transactionAttribute._data = parse_hex(data); + EXPECT_EQ("00" + data, hex(transactionAttribute.serialize())); + + data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4b"; + transactionAttribute.usage = TransactionAttributeUsage::TAU_Vote; + transactionAttribute._data = parse_hex(data); + EXPECT_EQ("30" + data, hex(transactionAttribute.serialize())); + + transactionAttribute.usage = TransactionAttributeUsage::TAU_ECDH02; + transactionAttribute._data = {(TW::byte)transactionAttribute.usage}; + auto d = parse_hex(data); + transactionAttribute._data.insert(transactionAttribute._data.end(), d.begin(), d.end()); + EXPECT_EQ("02" + data, hex(transactionAttribute.serialize())); + + data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af"; + transactionAttribute.usage = TransactionAttributeUsage::TAU_Script; + transactionAttribute._data = parse_hex(data); + EXPECT_EQ("20" + data, hex(transactionAttribute.serialize())); + + data = "bd"; + transactionAttribute.usage = TransactionAttributeUsage::TAU_DescriptionUrl; + transactionAttribute._data = parse_hex(data); + EXPECT_EQ("8101" + data, hex(transactionAttribute.serialize())); + + data = "bdecbb623eee6f9ade28d5a8ff5fb3ea"; + transactionAttribute.usage = TransactionAttributeUsage::TAU_Remark; + transactionAttribute._data = parse_hex(data); + EXPECT_EQ("f010" + data, hex(transactionAttribute.serialize())); +} + +TEST(NEOTransactionAttribute, Deserialize) { + auto transactionAttribute = TransactionAttribute(); + string data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"; + transactionAttribute.deserialize(parse_hex("00" + data)); + EXPECT_EQ(TransactionAttributeUsage::TAU_ContractHash, transactionAttribute.usage); + EXPECT_EQ(data, hex(transactionAttribute._data)); + + data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4b"; + transactionAttribute.deserialize(parse_hex("30" + data)); + EXPECT_EQ(TransactionAttributeUsage::TAU_Vote, transactionAttribute.usage); + EXPECT_EQ(data, hex(transactionAttribute._data)); + + transactionAttribute.deserialize(parse_hex("02" + data)); + EXPECT_EQ(TransactionAttributeUsage::TAU_ECDH02, transactionAttribute.usage); + EXPECT_EQ("02" + data, hex(transactionAttribute._data)); + + data = "bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af"; + transactionAttribute.deserialize(parse_hex("20" + data)); + EXPECT_EQ(TransactionAttributeUsage::TAU_Script, transactionAttribute.usage); + EXPECT_EQ(data, hex(transactionAttribute._data)); + + data = "bd"; + transactionAttribute.deserialize(parse_hex("8101" + data)); + EXPECT_EQ(TransactionAttributeUsage::TAU_DescriptionUrl, transactionAttribute.usage); + EXPECT_EQ(data, hex(transactionAttribute._data)); + + data = "bdecbb623eee6f9ade28d5a8ff5fb3ea"; + transactionAttribute.deserialize(parse_hex("f010" + data)); + EXPECT_EQ(TransactionAttributeUsage::TAU_Remark, transactionAttribute.usage); + EXPECT_EQ(data, hex(transactionAttribute._data)); + + EXPECT_THROW(transactionAttribute.deserialize(parse_hex("b1" + data)), std::invalid_argument); +} + +TEST(NEOTransactionAttribute, DeserializeInitialPositionAfterData) { + auto transactionAttribute = TransactionAttribute(); + EXPECT_THROW(transactionAttribute.deserialize(Data(), 1), std::invalid_argument); + EXPECT_THROW(transactionAttribute.deserialize(Data({1}), 2), std::invalid_argument); +} + + +} // namespace TW::NEO::tests diff --git a/tests/NEO/TransactionOutputTests.cpp b/tests/chains/NEO/TransactionOutputTests.cpp similarity index 94% rename from tests/NEO/TransactionOutputTests.cpp rename to tests/chains/NEO/TransactionOutputTests.cpp index 5c695cd9aab..84753ba029a 100644 --- a/tests/NEO/TransactionOutputTests.cpp +++ b/tests/chains/NEO/TransactionOutputTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -7,13 +7,11 @@ #include "uint256.h" #include "HexCoding.h" #include "NEO/TransactionOutput.h" - -#include #include +namespace TW::NEO::tests { + using namespace std; -using namespace TW; -using namespace TW::NEO; TEST(NEOTransactionOutput, Serialize) { auto transactionOutput = TransactionOutput(); @@ -42,3 +40,5 @@ TEST(NEOTransactionOutput, Deserialize) { EXPECT_EQ(assetId, hex(store(transactionOutput.assetId))); EXPECT_EQ(scriptHash, hex(store(transactionOutput.scriptHash))); } + +} // namespace TW::NEO::tests diff --git a/tests/chains/NEO/TransactionTests.cpp b/tests/chains/NEO/TransactionTests.cpp new file mode 100644 index 00000000000..fc973084e90 --- /dev/null +++ b/tests/chains/NEO/TransactionTests.cpp @@ -0,0 +1,249 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "uint256.h" +#include "HexCoding.h" +#include "NEO/Transaction.h" +#include "NEO/TransactionType.h" +#include "NEO/TransactionAttributeUsage.h" +#include "NEO/TransactionAttribute.h" +#include + +namespace TW::NEO::tests { + +using namespace std; + +TEST(NEOTransaction, SerializeDeserializeEmpty) { + auto transaction = Transaction(); + EXPECT_EQ(transaction, transaction); + + EXPECT_EQ(0ul, transaction.attributes.size()); + EXPECT_EQ(0ul, transaction.inInputs.size()); + EXPECT_EQ(0ul, transaction.outputs.size()); + auto serialized = transaction.serialize(); + + auto deserializedTransaction = Transaction(); + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); +} + +TEST(NEOTransaction, SerializeDeserializeEmptyCollections) { + auto transaction = Transaction(); + transaction.type = TransactionType::TT_EnrollmentTransaction; + transaction.version = 0x07; + const string zeroVarLong = "00"; + auto serialized = transaction.serialize(); + EXPECT_EQ("2007" + zeroVarLong + zeroVarLong + zeroVarLong, hex(serialized)); + + auto deserializedTransaction = Transaction(); + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); + EXPECT_EQ(transaction, transaction); +} + +TEST(NEOTransaction, SerializeDeserializeAttribute) { + auto transaction = Transaction(); + transaction.type = TransactionType::TT_ContractTransaction; + transaction.version = 0x07; + const string zeroVarLong = "00"; + const string oneVarLong = "01"; + transaction.attributes.push_back(TransactionAttribute()); + transaction.attributes[0].usage = TransactionAttributeUsage::TAU_ContractHash; + transaction.attributes[0]._data = parse_hex("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"); + auto serialized = transaction.serialize(); + EXPECT_EQ("8007" + oneVarLong + hex(transaction.attributes[0].serialize()) + zeroVarLong + zeroVarLong, hex(serialized)); + + auto deserializedTransaction = Transaction(); + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); + + transaction.attributes.push_back(TransactionAttribute()); + transaction.attributes[1].usage = TransactionAttributeUsage::TAU_ECDH02; + transaction.attributes[1]._data = parse_hex("02b7ecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a"); + serialized = transaction.serialize(); + const string twoVarLong = "02"; + string expectedSerialized = "8007" + twoVarLong; + expectedSerialized += hex(transaction.attributes[0].serialize()); + expectedSerialized += hex(transaction.attributes[1].serialize()); + expectedSerialized += zeroVarLong + zeroVarLong; + EXPECT_EQ(expectedSerialized, hex(serialized)); + + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); + EXPECT_EQ(transaction, transaction); +} + +TEST(NEOTransaction, SerializeDeserializeInputs) { + auto transaction = Transaction(); + transaction.type = TransactionType::TT_ContractTransaction; + transaction.version = 0x07; + const string zeroVarLong = "00"; + const string oneVarLong = "01"; + transaction.inInputs.push_back(CoinReference()); + transaction.inInputs[0].prevHash = load(parse_hex("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); + transaction.inInputs[0].prevIndex = 0xa; + auto serialized = transaction.serialize(); + EXPECT_EQ("8007" + zeroVarLong + oneVarLong + hex(transaction.inInputs[0].serialize()) + zeroVarLong, hex(serialized)); + + auto deserializedTransaction = Transaction(); + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); + + transaction.inInputs.push_back(CoinReference()); + transaction.inInputs[1].prevHash = load(parse_hex("bdecbb623eee4f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); + transaction.inInputs[1].prevIndex = 0xbc; + serialized = transaction.serialize(); + const string twoVarLong = "02"; + string expectedSerialized = "8007" + zeroVarLong + twoVarLong; + expectedSerialized += hex(transaction.inInputs[0].serialize()); + expectedSerialized += hex(transaction.inInputs[1].serialize()); + expectedSerialized += zeroVarLong; + EXPECT_EQ(expectedSerialized, hex(serialized)); + + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); + EXPECT_EQ(transaction, transaction); +} + +TEST(NEOTransaction, SerializeDeserializeOutputs) { + auto transaction = Transaction(); + transaction.type = TransactionType::TT_ContractTransaction; + transaction.version = 0x07; + const string zeroVarLong = "00"; + const string oneVarLong = "01"; + transaction.outputs.push_back(TransactionOutput()); + transaction.outputs[0].assetId = load(parse_hex("bdecbb623eee6f9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); + transaction.outputs[0].scriptHash = load(parse_hex("cbb23e6f9ade28d5a8ff3eac9d73af039e821b1b")); + transaction.outputs[0].value = 0x2; + auto serialized = transaction.serialize(); + EXPECT_EQ("8007" + zeroVarLong + zeroVarLong + oneVarLong + hex(transaction.outputs[0].serialize()), hex(serialized)); + + auto deserializedTransaction = Transaction(); + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); + + transaction.outputs.push_back(TransactionOutput()); + transaction.outputs[1].assetId = load(parse_hex("bdecbb623eee6a9ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); + transaction.outputs[1].scriptHash = load(parse_hex("cbb23e6f9a3e28d5a8ff3eac9d73af039e821b1b")); + transaction.outputs[1].value = 0x2; + serialized = transaction.serialize(); + const string twoVarLong = "02"; + string expectedSerialized = "8007" + zeroVarLong + zeroVarLong + twoVarLong; + expectedSerialized += hex(transaction.outputs[0].serialize()); + expectedSerialized += hex(transaction.outputs[1].serialize()); + EXPECT_EQ(expectedSerialized, hex(serialized)); + + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); +} + +TEST(NEOTransaction, SerializeDeserialize) { + auto transaction = Transaction(); + transaction.type = TransactionType::TT_ContractTransaction; + transaction.version = 0x07; + const string oneVarLong = "01"; + + transaction.attributes.push_back(TransactionAttribute()); + transaction.attributes[0].usage = TransactionAttributeUsage::TAU_ContractHash; + transaction.attributes[0]._data = parse_hex("bdecbb623eee6f9ade28d5a8ff5fbdea9c9d73af039e0286201b3b0291fb4d4a"); + + transaction.inInputs.push_back(CoinReference()); + transaction.inInputs[0].prevHash = load(parse_hex("bdecbb623eee679ade28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); + transaction.inInputs[0].prevIndex = 0xa; + + transaction.outputs.push_back(TransactionOutput()); + transaction.outputs[0].assetId = load(parse_hex("bdecbb623eee6f9ad328d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); + transaction.outputs[0].scriptHash = load(parse_hex("cbb23e6f9ade28a5a8ff3eac9d73af039e821b1b")); + transaction.outputs[0].value = 0x2; + + auto serialized = transaction.serialize(); + string expectedSerialized = "8007"; + expectedSerialized += oneVarLong + hex(transaction.attributes[0].serialize()); + expectedSerialized += oneVarLong + hex(transaction.inInputs[0].serialize()); + expectedSerialized += oneVarLong + hex(transaction.outputs[0].serialize()); + ASSERT_EQ(expectedSerialized, hex(serialized)); + + auto deserializedTransaction = Transaction(); + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); + + transaction.outputs.push_back(TransactionOutput()); + transaction.outputs[1].assetId = load(parse_hex("bdecbb623eee6a9a3e28d5a8ff5fb3ea9c9d73af039e0286201b3b0291fb4d4a")); + transaction.outputs[1].scriptHash = load(parse_hex("cbb23e6f9a3e28d5a8ff3eac9da3af039e821b1b")); + transaction.outputs[1].value = 0x2; + serialized = transaction.serialize(); + const string twoVarLong = "02"; + expectedSerialized = "8007"; + expectedSerialized += oneVarLong + hex(transaction.attributes[0].serialize()); + expectedSerialized += oneVarLong + hex(transaction.inInputs[0].serialize()); + expectedSerialized += twoVarLong + hex(transaction.outputs[0].serialize()); + expectedSerialized += hex(transaction.outputs[1].serialize()); + EXPECT_EQ(expectedSerialized, hex(serialized)); + + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); + + transaction.inInputs.push_back(CoinReference()); + transaction.inInputs[1].prevHash = load(parse_hex("bdecbb623e3e6f9ade28d5a8ff4fb3ea9c9d73af039e0286201b3b0291fb4d4a")); + transaction.inInputs[1].prevIndex = 0xbc; + transaction.inInputs.push_back(CoinReference()); + transaction.inInputs[2].prevHash = load(parse_hex("bdecbb624eee6f9ade28d5a8ff3fb3ea9c9d73af039e0286201b3b0291fb4d4a")); + transaction.inInputs[2].prevIndex = 0x1f; + + serialized = transaction.serialize(); + const string threeVarLong = "03"; + expectedSerialized = "8007"; + expectedSerialized += oneVarLong + hex(transaction.attributes[0].serialize()); + expectedSerialized += threeVarLong + hex(transaction.inInputs[0].serialize()); + expectedSerialized += hex(transaction.inInputs[1].serialize()); + expectedSerialized += hex(transaction.inInputs[2].serialize()); + expectedSerialized += twoVarLong + hex(transaction.outputs[0].serialize()); + expectedSerialized += hex(transaction.outputs[1].serialize()); + EXPECT_EQ(expectedSerialized, hex(serialized)); + + deserializedTransaction.deserialize(serialized); + EXPECT_EQ(transaction, deserializedTransaction); +} + +TEST(NEOTransaction, SerializeDeserializeMiner) { + string block2tn = "0000d11f7a2800000000"; + std::unique_ptr deserializedTransaction(Transaction::deserializeFrom(parse_hex(block2tn))); + auto serialized = deserializedTransaction->serialize(); + std::unique_ptr serializedTransaction(Transaction::deserializeFrom(serialized)); + + EXPECT_EQ(*deserializedTransaction, *serializedTransaction); + + string notMiner = "1000d11f7a2800000000"; + EXPECT_THROW( + std::unique_ptr _deserializedTransaction(Transaction::deserializeFrom(parse_hex(notMiner))), + std::invalid_argument); +} + +TEST(NEOTransaction, GetHash) { + string block2tn = "0000d11f7a2800000000"; + std::unique_ptr deserializedTransaction(Transaction::deserializeFrom(parse_hex(block2tn))); + + Data hash = parse_hex("8e3a32ba3a7e8bdb0ad9a2ad064713e45bd20eb0dab0d2e77df5b5ce985276d0"); + // It is flipped on the https://github.com/NeoResearch/neopt/blob/master/tests/ledger_Tests/Transaction.Test.cpp + hash = Data(hash.rbegin(), hash.rend()); + + EXPECT_EQ(hex(hash), hex(deserializedTransaction->getHash())); +} + +TEST(NEOTransaction, SerializeSize) { + auto transaction = Transaction(); + transaction.type = TransactionType::TT_EnrollmentTransaction; + transaction.version = 0x07; + const string zeroVarLong = "00"; + auto serialized = transaction.serialize(); + auto verSerialized = parse_hex("2007" + zeroVarLong + zeroVarLong + zeroVarLong); + EXPECT_EQ(hex(verSerialized), hex(serialized)); + EXPECT_EQ(verSerialized, serialized); + + EXPECT_EQ(serialized.size(), static_cast(transaction.size())); +} + +} // namespace TW::NEO::tests diff --git a/tests/NEO/WitnessTests.cpp b/tests/chains/NEO/WitnessTests.cpp similarity index 96% rename from tests/NEO/WitnessTests.cpp rename to tests/chains/NEO/WitnessTests.cpp index ff2a579846b..7bd68fda171 100644 --- a/tests/NEO/WitnessTests.cpp +++ b/tests/chains/NEO/WitnessTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,9 +11,9 @@ #include +namespace TW::NEO::tests { + using namespace std; -using namespace TW; -using namespace TW::NEO; TEST(NEOWitness, Serialize) { auto witness = Witness(); @@ -62,3 +62,5 @@ TEST(NEOWitness, SerializeDeserialize) { deWitness.deserialize(witness.serialize()); EXPECT_EQ(witness, deWitness); } + +} // namespace TW::NEO::tests diff --git a/tests/chains/NULS/AddressTests.cpp b/tests/chains/NULS/AddressTests.cpp new file mode 100644 index 00000000000..0082fac96fc --- /dev/null +++ b/tests/chains/NULS/AddressTests.cpp @@ -0,0 +1,62 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "NULS/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include + +namespace TW::NULS::tests { + +TEST(NULSAddress, StaticInvalid) { + ASSERT_FALSE(Address::isValid("abc")); + ASSERT_FALSE(Address::isValid("aaeb60f3e94c9b9a09f33669435e7ef1beaed")); + ASSERT_FALSE(Address::isValid("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z")); + ASSERT_TRUE(Address::isValid("NULSd6HgUxmcJWc88iELEJ7RH9XHsazBQqnJc")); + ASSERT_TRUE(Address::isValid("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2")); +} + +TEST(NULSAddress, ChainID) { + const auto address = Address("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2"); + ASSERT_TRUE(address.chainID() == 1); +} + +TEST(NULSAddress, Type) { + const auto address = Address("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2"); + ASSERT_TRUE(address.type() == 1); +} + +TEST(NULSAddress, FromString) { + const auto address = Address("NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2"); + ASSERT_EQ(address.string(), "NULSd6HgbwcM8wz48f6UkFYHLVriT1L81X9z2"); +} + +TEST(NULSAddress, FromPrivateKey) { + const auto privateKey = + PrivateKey(parse_hex("a1269039e4ffdf43687852d7247a295f0b5bc55e6dda031cffaa3295ca0a9d7a")); + const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + const auto address = Address(publicKey); + + ASSERT_EQ(address.string(), "NULSd6HghWa4CN5qdxqMwYVikQxRZyj57Jn4L"); +} + +TEST(NULSAddress, FromCompressedPublicKey) { + const auto publicKey = + PublicKey(parse_hex("0244d50ff36c3136b4bf81f0c74b066695bc2af43e28d7f0ca1d48fcfd084bea66"), TWPublicKeyTypeSECP256k1); + const auto address = Address(publicKey); + + ASSERT_EQ(address.string(), "NULSd6HgUiMKPNi221bPfqvvho8QpuYBvn1x3"); +} + +TEST(NULSAddress, FromPrivateKey33) { + const auto privateKey = PrivateKey(parse_hex("d77580833f0b3c35b7114c23d6b66790d726c308baf237ec8c369152f2c08d27")); + const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + const auto address = Address(publicKey); + + ASSERT_EQ(address.string(), "NULSd6HgXx8YkwEjePLWUmdRSZzPQzK6BXnsB"); +} + +} // namespace TW::NULS::tests diff --git a/tests/chains/NULS/TWAnySignerTests.cpp b/tests/chains/NULS/TWAnySignerTests.cpp new file mode 100644 index 00000000000..749de87342b --- /dev/null +++ b/tests/chains/NULS/TWAnySignerTests.cpp @@ -0,0 +1,40 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/NULS.pb.h" +#include +#include "uint256.h" + +#include "TestUtilities.h" +#include + +namespace TW::NULS::tests { + +TEST(TWAnySignerNULS, Sign) { + auto privateKey = parse_hex("0x9ce21dad67e0f0af2599b41b515a7f7018059418bab892a7b68f283d489abc4b"); + auto amount = store(uint256_t(10000000)); + auto balance = store(uint256_t(100000000)); + std::string nonce = "0000000000000000"; + Proto::SigningInput input; + + input.set_from("NULSd6Hgj7ZoVgsPN9ybB4C1N2TbvkgLc8Z9H"); + input.set_to("NULSd6Hgied7ym6qMEfVzZanMaa9qeqA6TZSe"); + input.set_amount(amount.data(), amount.size()); + input.set_chain_id(1); + input.set_idassets_id(1); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_balance(balance.data(), balance.size()); + input.set_timestamp(1569228280); + input.set_nonce(nonce.data(), nonce.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNULS); + + EXPECT_EQ(hex(output.encoded()), "0200f885885d00008c0117010001f7ec6473df12e751d64cf20a8baa7edd50810f8101000100201d9a0000000000000000000000000000000000000000000000000000000000080000000000000000000117010001f05e7878971f3374515eabb6f16d75219d8873120100010080969800000000000000000000000000000000000000000000000000000000000000000000000000692103958b790c331954ed367d37bac901de5c2f06ac8368b37d7bd6cd5ae143c1d7e3463044022028019c0099e2233c7adb84bb03a9a5666ece4a5b65a026a090fa460f3679654702204df0fcb8762b5944b3aba033fa1a287ccb098150035dd8b66f52dc58d3d0843a"); +} + +} // namespace TW::NULS::tests diff --git a/tests/chains/NULS/TWCoinTypeTests.cpp b/tests/chains/NULS/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..5618222dcb2 --- /dev/null +++ b/tests/chains/NULS/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWNULSCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNULS)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNULS, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNULS, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNULS)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNULS)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNULS), 8); + ASSERT_EQ(TWBlockchainNULS, TWCoinTypeBlockchain(TWCoinTypeNULS)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNULS)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNULS)); + assertStringsEqual(symbol, "NULS"); + assertStringsEqual(txUrl, "https://nulscan.io/transaction/info?hash=t123"); + assertStringsEqual(accUrl, "https://nulscan.io/address/info?address=a12"); + assertStringsEqual(id, "nuls"); + assertStringsEqual(name, "NULS"); +} diff --git a/tests/chains/Nano/AddressTests.cpp b/tests/chains/Nano/AddressTests.cpp new file mode 100644 index 00000000000..2c3435a11ee --- /dev/null +++ b/tests/chains/Nano/AddressTests.cpp @@ -0,0 +1,58 @@ +// Copyright © 2019 Mart Roosmaa. +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Nano/Address.h" + +#include + +using namespace std; +using namespace TW; + +namespace TW::Nano::tests { + +TEST(NanoAddress, FromPublicKey) { + { + const auto publicKey = PublicKey(parse_hex("5114aad86a390897d2a91b33b931b3a59a7df9e63eb3694f9430122f5622ae50"), TWPublicKeyTypeED25519Blake2b); + const auto address = Address(publicKey); + ASSERT_EQ(string("nano_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg"), address.string()); + } + + { + const auto publicKey = PublicKey(parse_hex("03e20ec6b4a39a629815ae02c0a1393b9225e3b890cae45b59f42fa29be9668d"), TWPublicKeyTypeED25519); + ASSERT_THROW(Address address(publicKey), std::invalid_argument); + } +} + +TEST(NanoAddress, FromString) { + { + string nanoAddress = "nano_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg"; + const auto address = Address(nanoAddress); + ASSERT_EQ(address.string(), nanoAddress); + ASSERT_EQ(hex(address.bytes), "5114aad86a390897d2a91b33b931b3a59a7df9e63eb3694f9430122f5622ae50"); + } + + { + string xrbAddress = "xrb_1111111111111111111111111111111111111111111111111111hifc8npp"; + string nanoAddress = "nano_1111111111111111111111111111111111111111111111111111hifc8npp"; + const auto address = Address(xrbAddress); + ASSERT_EQ(address.string(), nanoAddress); + ASSERT_EQ(hex(address.bytes), "0000000000000000000000000000000000000000000000000000000000000000"); + } +} + +TEST(NanoAddress, isValid) { + string nanodeAddress = "nano_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg"; + string faultyChecksumAddress = "xrb_1111111111111111111111111111111111111111111111111111hi111111"; + string bitcoinAddress = "1Ma2DrB78K7jmAwaomqZNRMCvgQrNjE2QC"; + + ASSERT_TRUE(Address::isValid(nanodeAddress)); + ASSERT_FALSE(Address::isValid(faultyChecksumAddress)); + ASSERT_FALSE(Address::isValid(bitcoinAddress)); +} + +} // namespace TW::Nano::tests diff --git a/tests/chains/Nano/SignerTests.cpp b/tests/chains/Nano/SignerTests.cpp new file mode 100644 index 00000000000..966198c3bc0 --- /dev/null +++ b/tests/chains/Nano/SignerTests.cpp @@ -0,0 +1,210 @@ +// Copyright © 2019 Mart Roosmaa. +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Nano/Signer.h" + +#include + +using namespace TW; + +namespace TW::Nano::tests { + +const std::string kPrivateKey{"173c40e97fe2afcd24187e74f6b603cb949a5365e72fbdd065a6b165e2189e34"}; +const std::string kRepOfficial1{"xrb_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4"}; +const std::string kRepNanode{"xrb_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg"}; + +TEST(NanoSigner, sign1) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); + + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_link_block(linkBlock.data(), linkBlock.size()); + input.set_representative(kRepOfficial1); + input.set_balance("96242336390000000000000000000"); + + // https://www.nanode.co/block/f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696 + const auto signer = Signer(input); + ASSERT_EQ(hex(signer.blockHash), "f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696"); + const auto signature = signer.sign(); + ASSERT_EQ(hex(signature), "d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09"); + const Proto::SigningOutput out = signer.build(); + EXPECT_EQ(hex(out.signature()), "d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09"); + EXPECT_EQ(hex(out.block_hash()), "f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696"); + EXPECT_EQ( + "{\"account\":\"nano_1bhbsc9yuh15anq3owu1izw1nk7bhhqefrkhfo954fyt8dk1q911buk1kk4c\"," + "\"balance\":\"96242336390000000000000000000\"," + "\"link\":\"491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507\"," + "\"link_as_account\":\"nano_1kazsap8mc481zbqbcqjytpf9mmigj87qr5k5fhf97579t4k8fa94octjx6d\"," + "\"previous\":\"0000000000000000000000000000000000000000000000000000000000000000\"," + "\"representative\":\"nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4\"," + "\"signature\":\"d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09\"," + "\"type\":\"state\"}", + out.json()); +} + +TEST(NanoSigner, sign2) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + const auto parentBlock = parse_hex("f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696"); + + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_parent_block(parentBlock.data(), parentBlock.size()); + input.set_representative(kRepNanode); + input.set_balance("96242336390000000000000000000"); + + // https://www.nanode.co/block/2568bf76336f7a415ca236dab97c1df9de951ca057a2e79df1322e647a259e7b + const auto signer = Signer(input); + ASSERT_EQ(hex(signer.blockHash), "2568bf76336f7a415ca236dab97c1df9de951ca057a2e79df1322e647a259e7b"); + const auto signature = signer.sign(); + ASSERT_EQ(hex(signature), "3a0687542405163d5623808052042b3482360a82cc003d178a0c0d8bfbca86450975d0faec60ae5ac37feba9a8e2205c8540317b26f2c589c2a6578b03870403"); +} + +TEST(NanoSigner, sign3) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + const auto parentBlock = parse_hex("2568bf76336f7a415ca236dab97c1df9de951ca057a2e79df1322e647a259e7b"); + const auto linkBlock = parse_hex("d7384845d2ae530b45a5dd50ee50757f988329f652781767af3f1bc2322f52b9"); + + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_parent_block(parentBlock.data(), parentBlock.size()); + input.set_link_block(linkBlock.data(), linkBlock.size()); + input.set_representative(kRepNanode); + input.set_balance("196242336390000000000000000000"); + input.set_work("123456789"); + + // https://www.nanode.co/block/1ca240212838d053ecaa9dceee598c52a6080067edecaeede3319eb0b7db6525 + const auto signer = Signer(input); + ASSERT_EQ(hex(signer.blockHash), "1ca240212838d053ecaa9dceee598c52a6080067edecaeede3319eb0b7db6525"); + const auto signature = signer.sign(); + ASSERT_EQ(hex(signature), "e980d45365ae2fb291950019f7c19a3d5fa5df2736ca7e7ca1984338b4686976cb7efdda2894ddcea480f82645b50f2340c9d0fc69a05621bdc355783a21820d"); + const Proto::SigningOutput out = signer.build(); + EXPECT_EQ( + "{\"account\":\"nano_1bhbsc9yuh15anq3owu1izw1nk7bhhqefrkhfo954fyt8dk1q911buk1kk4c\"," + "\"balance\":\"196242336390000000000000000000\"," + "\"link\":\"d7384845d2ae530b45a5dd50ee50757f988329f652781767af3f1bc2322f52b9\"," + "\"link_as_account\":\"nano_3osrb34x7dkm3f4tdqcixsa9czwrienzenmr4xmtyhruras4ynosarg1sdiq\"," + "\"previous\":\"2568bf76336f7a415ca236dab97c1df9de951ca057a2e79df1322e647a259e7b\"," + "\"representative\":\"nano_1nanode8ngaakzbck8smq6ru9bethqwyehomf79sae1k7xd47dkidjqzffeg\"," + "\"signature\":\"e980d45365ae2fb291950019f7c19a3d5fa5df2736ca7e7ca1984338b4686976cb7efdda2894ddcea480f82645b50f2340c9d0fc69a05621bdc355783a21820d\"," + "\"type\":\"state\",\"work\":\"123456789\"}", + out.json()); +} + +TEST(NanoSigner, sign4) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + const auto parentBlock = parse_hex("1ca240212838d053ecaa9dceee598c52a6080067edecaeede3319eb0b7db6525"); + + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_parent_block(parentBlock.data(), parentBlock.size()); + input.set_link_recipient("xrb_3wm37qz19zhei7nzscjcopbrbnnachs4p1gnwo5oroi3qonw6inwgoeuufdp"); + input.set_representative(kRepNanode); + input.set_balance("126242336390000000000000000000"); + + // https://www.nanode.co/block/32ac7d8f5a16a498abf203b8dfee623c9e111ff25e7339f8cd69ec7492b23edd + const auto signer = Signer(input); + ASSERT_EQ(hex(signer.blockHash), "32ac7d8f5a16a498abf203b8dfee623c9e111ff25e7339f8cd69ec7492b23edd"); + const auto signature = signer.sign(); + ASSERT_EQ(hex(signature), "bcb806e140c9e2bc71c51ebbd941b4d99cee3d97fd50e3006eabc5e325c712662e2dc163ee32660875d67815ce4721e122389d2e64f1c9ad4555a9d3d8c33802"); +} + +TEST(NanoSigner, signInvalid1) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + + // Missing link_block + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_representative(kRepOfficial1); + input.set_balance("96242336390000000000000000000"); + + ASSERT_THROW(Signer signer(input), std::invalid_argument); +} + +TEST(NanoSigner, signInvalid2) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); + + // Missing representative + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_link_block(linkBlock.data(), linkBlock.size()); + input.set_balance("96242336390000000000000000000"); + + ASSERT_THROW(Signer signer(input), std::invalid_argument); +} + +TEST(NanoSigner, signInvalid3) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); + + // Missing balance + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_link_block(linkBlock.data(), linkBlock.size()); + input.set_representative(kRepOfficial1); + + ASSERT_THROW(Signer signer(input), std::invalid_argument); +} + +TEST(NanoSigner, signInvalid4) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); + + // Account first block cannot be 0 balance + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_link_block(linkBlock.data(), linkBlock.size()); + input.set_representative(kRepOfficial1); + input.set_balance("0"); + + ASSERT_THROW(Signer signer(input), std::invalid_argument); +} + +TEST(NanoSigner, signInvalid5) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + + // First block must use link_block not link_recipient + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_link_recipient("xrb_3wm37qz19zhei7nzscjcopbrbnnachs4p1gnwo5oroi3qonw6inwgoeuufdp"); + input.set_representative(kRepOfficial1); + input.set_balance("96242336390000000000000000000"); + + ASSERT_THROW(Signer signer(input), std::invalid_argument); +} + +TEST(NanoSigner, signInvalid6) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); + + // Invalid representative value + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_link_block(linkBlock.data(), linkBlock.size()); + input.set_representative("xrb_4wm37qz19zhei7nzscjcopbrbnnachs4p1gnwo5oroi3qonw6inwgoeuufdp"); + input.set_balance("96242336390000000000000000000"); + + ASSERT_THROW(Signer signer(input), std::invalid_argument); +} + +TEST(NanoSigner, signInvalid7) { + const auto privateKey = PrivateKey(parse_hex(kPrivateKey)); + const auto parentBlock = parse_hex("f9a323153daefe041efb94d69b9669c882c935530ed953bbe8a665dfedda9696"); + + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_parent_block(parentBlock.data(), parentBlock.size()); + input.set_link_recipient("xrb_4wm37qz19zhei7nzscjcopbrbnnachs4p1gnwo5oroi3qonw6inwgoeuufdp"); + input.set_representative(kRepOfficial1); + input.set_balance("1.2.3"); + + ASSERT_THROW(Signer signer(input), std::invalid_argument); +} + +} // namespace TW::Nano::tests diff --git a/tests/chains/Nano/TWAnySignerTests.cpp b/tests/chains/Nano/TWAnySignerTests.cpp new file mode 100644 index 00000000000..aa76abc83f5 --- /dev/null +++ b/tests/chains/Nano/TWAnySignerTests.cpp @@ -0,0 +1,54 @@ +// Copyright © 2019 Mart Roosmaa. +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Nano.pb.h" +#include "TestUtilities.h" +#include +#include + +using namespace TW; + +namespace TW::Nano::tests { + +TEST(TWAnySignerNano, sign) { + const auto privateKey = parse_hex("173c40e97fe2afcd24187e74f6b603cb949a5365e72fbdd065a6b165e2189e34"); + const auto linkBlock = parse_hex("491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507"); + + auto input = Proto::SigningInput(); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_link_block(linkBlock.data(), linkBlock.size()); + input.set_representative("xrb_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4"); + input.set_balance("96242336390000000000000000000"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNano); + + EXPECT_EQ( + "{\"account\":\"nano_1bhbsc9yuh15anq3owu1izw1nk7bhhqefrkhfo954fyt8dk1q911buk1kk4c\"," + "\"balance\":\"96242336390000000000000000000\"," + "\"link\":\"491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507\"," + "\"link_as_account\":\"nano_1kazsap8mc481zbqbcqjytpf9mmigj87qr5k5fhf97579t4k8fa94octjx6d\"," + "\"previous\":\"0000000000000000000000000000000000000000000000000000000000000000\"," + "\"representative\":\"nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4\"," + "\"signature\":" + "\"d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5" + "ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09\"," + "\"type\":\"state\"}", + output.json()); +} + +TEST(TWAnySignerNano, SignJSON) { + auto json = STRING(R"({"link_block":"SR/KLGmoRgfTdKrx9qzTznB0TFvgchte05RlPoUjNQc=","representative":"nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4","balance":"96242336390000000000000000000"})"); + auto key = DATA("173c40e97fe2afcd24187e74f6b603cb949a5365e72fbdd065a6b165e2189e34"); + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeNano)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeNano)); + assertStringsEqual(result, R"({"account":"nano_1bhbsc9yuh15anq3owu1izw1nk7bhhqefrkhfo954fyt8dk1q911buk1kk4c","balance":"96242336390000000000000000000","link":"491fca2c69a84607d374aaf1f6acd3ce70744c5be0721b5ed394653e85233507","link_as_account":"nano_1kazsap8mc481zbqbcqjytpf9mmigj87qr5k5fhf97579t4k8fa94octjx6d","previous":"0000000000000000000000000000000000000000000000000000000000000000","representative":"nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4","signature":"d247f6b90383b24e612569c75a12f11242f6e03b4914eadc7d941577dcf54a3a7cb7f0a4aba4246a40d9ebb5ee1e00b4a0a834ad5a1e7bef24e11f62b95a9e09","type":"state"})"); +} + +} // namespace TW::Nano::tests diff --git a/tests/chains/Nano/TWCoinTypeTests.cpp b/tests/chains/Nano/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..c6458843538 --- /dev/null +++ b/tests/chains/Nano/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWNanoCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNano)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("C264DB7BF40738F0CEFF19B606746CB925B713E4B8699A055699E0DC8ABBC70F")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNano, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("nano_1wpj616kwhe1y38y1mspd8aub8i334cwybqco511iyuxm55zx8d67ptf1tsf")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNano, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNano)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNano)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNano), 30); + ASSERT_EQ(TWBlockchainNano, TWCoinTypeBlockchain(TWCoinTypeNano)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNano)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNano)); + assertStringsEqual(symbol, "XNO"); + assertStringsEqual(txUrl, "https://nanocrawler.cc/explorer/block/C264DB7BF40738F0CEFF19B606746CB925B713E4B8699A055699E0DC8ABBC70F"); + assertStringsEqual(accUrl, "https://nanocrawler.cc/explorer/account/nano_1wpj616kwhe1y38y1mspd8aub8i334cwybqco511iyuxm55zx8d67ptf1tsf"); + assertStringsEqual(id, "nano"); + assertStringsEqual(name, "Nano"); +} diff --git a/tests/Nano/TWNanoAddressTests.cpp b/tests/chains/Nano/TWNanoAddressTests.cpp similarity index 97% rename from tests/Nano/TWNanoAddressTests.cpp rename to tests/chains/Nano/TWNanoAddressTests.cpp index afcd1587cf4..44d653119aa 100644 --- a/tests/Nano/TWNanoAddressTests.cpp +++ b/tests/chains/Nano/TWNanoAddressTests.cpp @@ -5,7 +5,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include "PrivateKey.h" #include "PublicKey.h" diff --git a/tests/chains/NativeEvmos/TWAnyAddressTests.cpp b/tests/chains/NativeEvmos/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..ed2a02cd50f --- /dev/null +++ b/tests/chains/NativeEvmos/TWAnyAddressTests.cpp @@ -0,0 +1,39 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include + +#include + +namespace TW::NativeEvmos::tests { + +TEST(EvmosAnyAddress, NativeEvmosValidate) { + auto string = STRING("evmos1mwspdc5y6lj0glm90j9sg7vmr5ksmqcnzhg0h9"); + + EXPECT_TRUE(TWAnyAddressIsValid(string.get(), TWCoinTypeNativeEvmos)); + + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeNativeEvmos)); + + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "dba016e284d7e4f47f657c8b04799b1d2d0d8313"); +} + +TEST(EvmosAnyAddress, NativeEvmosCreate) { + auto publicKeyHex = "035a0c6b83b8bd9827e507270cadb499b7e3a9095246f6a2213281f783d877c98b"; // shoot island position ... + const auto publicKey = WRAP(TWPublicKey, TWPublicKeyCreateWithData(DATA(publicKeyHex).get(), TWPublicKeyTypeSECP256k1)); + + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeNativeEvmos)); + + EXPECT_EQ(std::string(TWStringUTF8Bytes(WRAPS(TWAnyAddressDescription(addr.get())).get())), std::string("evmos1mwspdc5y6lj0glm90j9sg7vmr5ksmqcnzhg0h9")); + + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "dba016e284d7e4f47f657c8b04799b1d2d0d8313"); +} + +} // namespace TW::NativeEvmos::tests diff --git a/tests/chains/NativeEvmos/TWCoinTypeTests.cpp b/tests/chains/NativeEvmos/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..6df42eaa394 --- /dev/null +++ b/tests/chains/NativeEvmos/TWCoinTypeTests.cpp @@ -0,0 +1,32 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include + +namespace TW::NativeEvmos::tests { + +TEST(TWEvmosCoinType, TWCoinTypeNativeEvmos) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNativeEvmos)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("A16C211C83AD1E684DE46F694FAAC17D8465C864BD7385A81EC062CDE0638811")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNativeEvmos, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNativeEvmos, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNativeEvmos)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNativeEvmos)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNativeEvmos), 18); + ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeNativeEvmos)); + + assertStringsEqual(symbol, "EVMOS"); + assertStringsEqual(txUrl, "https://mintscan.io/evmos/txs/A16C211C83AD1E684DE46F694FAAC17D8465C864BD7385A81EC062CDE0638811"); + assertStringsEqual(accUrl, "https://mintscan.io/evmos/account/evmos17xpfvakm2amg962yls6f84z3kell8c5ljcjw34"); + assertStringsEqual(id, "nativeevmos"); + assertStringsEqual(name, "Native Evmos"); +} + +} // namespace TW::NativeEvmos::tests diff --git a/tests/chains/Nebulas/AddressTests.cpp b/tests/chains/Nebulas/AddressTests.cpp new file mode 100644 index 00000000000..45a70df9a09 --- /dev/null +++ b/tests/chains/Nebulas/AddressTests.cpp @@ -0,0 +1,53 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Nebulas/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include + +namespace TW::Nebulas::tests { + +using namespace std; + +TEST(NebulasAddress, Invalid) { + ASSERT_FALSE(Address::isValid("abc")); + ASSERT_FALSE(Address::isValid("a1TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv")); + ASSERT_FALSE(Address::isValid("n2TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv")); + // normal address test + ASSERT_TRUE(Address::isValid("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY")); + // contract address test + ASSERT_TRUE(Address::isValid("n1zUNqeBPvsyrw5zxp9mKcDdLTjuaEL7s39")); +} + +TEST(NebulasAddress, String) { + ASSERT_THROW(Address("abc"), std::invalid_argument); + ASSERT_EQ(Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY").string(), + "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); + ASSERT_EQ(Address(Base58::bitcoin.decode("n1TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv")).string(), + "n1TgpFZWCMmFd2sphb6RKsCvsEyMCNa2Yyv"); + + const auto address = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); + ASSERT_EQ(address.string(), "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); +} + +TEST(NebulasAddress, Data) { + Data data; + EXPECT_THROW(Address(data).string(), std::invalid_argument); + ASSERT_EQ(Address(Base58::bitcoin.decode("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY")).string(), + "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); +} + +TEST(NebulasAddress, FromPrivateKey) { + const auto privateKey = PrivateKey(parse_hex("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + const auto address = Address(publicKey); + ASSERT_EQ(address.string(), "n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); + + EXPECT_THROW(Address(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)), std::invalid_argument); +} + +} // namespace TW::Nebulas::tests diff --git a/tests/Nebulas/SignerTests.cpp b/tests/chains/Nebulas/SignerTests.cpp similarity index 100% rename from tests/Nebulas/SignerTests.cpp rename to tests/chains/Nebulas/SignerTests.cpp diff --git a/tests/chains/Nebulas/TWAnySignerTests.cpp b/tests/chains/Nebulas/TWAnySignerTests.cpp new file mode 100644 index 00000000000..c850acf6935 --- /dev/null +++ b/tests/chains/Nebulas/TWAnySignerTests.cpp @@ -0,0 +1,45 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include "HexCoding.h" +#include "uint256.h" +#include "proto/Nebulas.pb.h" + +#include + +namespace TW::Nebulas::tests { + +TEST(TWAnySignerNebulas, Sign) { + Proto::SigningInput input; + input.set_from_address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); + input.set_to_address("n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"); + auto value = store(uint256_t(7)); + input.set_nonce(value.data(), value.size()); + value = store(uint256_t(1000000)); + input.set_gas_price(value.data(), value.size()); + value = store(uint256_t(200000)); + input.set_gas_limit(value.data(), value.size()); + value = store(uint256_t(11000000000000000000ULL)); + input.set_amount(value.data(), value.size()); + input.set_payload(""); + value = store(uint256_t(1560052938)); + input.set_timestamp(value.data(), value.size()); + + const auto privateKey = parse_hex("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b"); + input.set_private_key(privateKey.data(), privateKey.size()); + auto chainid = store(uint256_t(1)); + input.set_chain_id(chainid.data(), chainid.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNebulas); + + EXPECT_EQ(hex(output.signature()), "f53f4a9141ff8e462b094138eccd8c3a5d7865f9e9ab509626c78460a9e0b0fc35f7ed5ba1795ceb81a5e46b7580a6f7fb431d44fdba92515399cf6a8e47e71500"); + EXPECT_EQ(output.raw(), "CiBQXdR2neMqnEu21q/U+OHqZHSBX9Q0hNiRfL2eCZO4hRIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6CAoGYmluYXJ5QAFKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAADDUBYAWJB9T9KkUH/jkYrCUE47M2MOl14Zfnpq1CWJseEYKngsPw19+1boXlc64Gl5Gt1gKb3+0MdRP26klFTmc9qjkfnFQA="); +} + +} // namespace TW::Nebulas::tests diff --git a/tests/chains/Nebulas/TWCoinTypeTests.cpp b/tests/chains/Nebulas/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..d0f30bcc619 --- /dev/null +++ b/tests/chains/Nebulas/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWNebulasCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNebulas)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNebulas, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNebulas, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNebulas)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNebulas)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNebulas), 18); + ASSERT_EQ(TWBlockchainNebulas, TWCoinTypeBlockchain(TWCoinTypeNebulas)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNebulas)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNebulas)); + assertStringsEqual(symbol, "NAS"); + assertStringsEqual(txUrl, "https://explorer.nebulas.io/#/tx/t123"); + assertStringsEqual(accUrl, "https://explorer.nebulas.io/#/address/a12"); + assertStringsEqual(id, "nebulas"); + assertStringsEqual(name, "Nebulas"); +} diff --git a/tests/Nebulas/TWNebulasAddressTests.cpp b/tests/chains/Nebulas/TWNebulasAddressTests.cpp similarity index 98% rename from tests/Nebulas/TWNebulasAddressTests.cpp rename to tests/chains/Nebulas/TWNebulasAddressTests.cpp index 32e7ae08fa4..50a2cdcea9b 100644 --- a/tests/Nebulas/TWNebulasAddressTests.cpp +++ b/tests/chains/Nebulas/TWNebulasAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/Nebulas/TransactionTests.cpp b/tests/chains/Nebulas/TransactionTests.cpp new file mode 100644 index 00000000000..0651403fd2a --- /dev/null +++ b/tests/chains/Nebulas/TransactionTests.cpp @@ -0,0 +1,83 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Nebulas/Signer.h" +#include "HexCoding.h" +#include "Base64.h" +#include "PrivateKey.h" + +#include + +extern std::string htmlescape(const std::string& str); + +namespace TW::Nebulas::tests { + +using namespace std; + +TEST(NebulasTransaction, serialize) { + auto from = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); + auto to = Address("n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"); + auto transaction = Transaction( + /* to: */ from, + /* nonce: */ 7, + /* gasPrice: */ 1000000, + /* gasLimit: */ 200000, + /* to: */ to, + /* amount: */ 11000000000000000000ULL, + /* timestamp: */ 1560052938, + /* payload: */ std::string()); + + const auto privateKey = PrivateKey(parse_hex("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")); + auto signer = Signer(1); + signer.sign(privateKey, transaction); + transaction.serializeToRaw(); + + ASSERT_EQ(TW::Base64::encode(transaction.raw), "CiBQXdR2neMqnEu21q/U+OHqZHSBX9Q0hNiRfL2eCZO4hRIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6CAoGYmluYXJ5QAFKEAAAAAAAAAAAAAAAAAAPQkBSEAAAAAAAAAAAAAAAAAADDUBYAWJB9T9KkUH/jkYrCUE47M2MOl14Zfnpq1CWJseEYKngsPw19+1boXlc64Gl5Gt1gKb3+0MdRP26klFTmc9qjkfnFQA="); +} + +TEST(NebulasTransaction, binaryPayload) { + auto from = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); + auto to = Address("n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"); + auto transaction = Transaction( + /* to: */ from, + /* nonce: */ 7, + /* gasPrice: */ 1000000, + /* gasLimit: */ 200000, + /* to: */ to, + /* amount: */ 11000000000000000000ULL, + /* timestamp: */ 1560052938, + /* payload: */ std::string("{\"binary\":\"test\"}")); + + const auto privateKey = PrivateKey(parse_hex("d2fd0ec9f6268fc8d1f563e3e976436936708bdf0dc60c66f35890f5967a8d2b")); + auto signer = Signer(1); + signer.sign(privateKey, transaction); + ASSERT_EQ(TW::Base64::encode(transaction.raw), "CiB1Oqj7bxLQMHEoNyg/vFHmsTrGdkpTf/5qFDkYPB3bkxIaGVefwtw23wEobqA40/7aIwQHghETxH4r+50aGhlXf89CeLWgHFjKu9/6tn4KNbelsMDAIIi2IhAAAAAAAAAAAJin2bgxTAAAKAcwyony5wU6PQoGYmluYXJ5EjN7IkRhdGEiOnsiZGF0YSI6WzExNiwxMDEsMTE1LDExNl0sInR5cGUiOiJCdWZmZXIifX1AAUoQAAAAAAAAAAAAAAAAAA9CQFIQAAAAAAAAAAAAAAAAAAMNQFgBYkGHXq+JWPaEyeB19bqL3QB5jyM961WLq7PMTpnGM4iLtBjCkngjS81kgPM2TE4qKDcpzqjum/NccrZtUPQLGk0MAQ=="); +} + +TEST(NebulasTransaction, htmlescape) { + // test for escaped label + auto test = ("test&<>\x20\x28\x20\x29"); + auto result = htmlescape(test); + ASSERT_EQ(result, "test\\u0026\\u003c\\u003e\\u2028\\u2029"); +} + +TEST(NebulasTransaction, serializeUnsigned) { + auto from = Address("n1V5bB2tbaM3FUiL4eRwpBLgEredS5C2wLY"); + auto to = Address("n1SAeQRVn33bamxN4ehWUT7JGdxipwn8b17"); + auto transaction = Transaction( + /* to: */ from, + /* nonce: */ 7, + /* gasPrice: */ 1000000, + /* gasLimit: */ 200000, + /* to: */ to, + /* amount: */ 11000000000000000000ULL, + /* timestamp: */ 1560052938, + /* payload: */ std::string()); + + ASSERT_THROW(transaction.serializeToRaw(), std::logic_error); +} + +} // namespace TW::Nebulas::tests diff --git a/tests/chains/Nervos/AddressTests.cpp b/tests/chains/Nervos/AddressTests.cpp new file mode 100644 index 00000000000..15fb9cadfe0 --- /dev/null +++ b/tests/chains/Nervos/AddressTests.cpp @@ -0,0 +1,88 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HDWallet.h" +#include "HexCoding.h" +#include "Nervos/Address.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include +#include + +namespace TW::Nervos::tests { + +TEST(NervosAddress, Valid) { + ASSERT_TRUE(Address::isValid("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9e" + "rg8furras980hksatlslfaktks7epf25")); + ASSERT_TRUE(Address::isValid("ckb1qyqvfdgvtjxswncx8mq2wl0dp6hlp7nmvhdqcecnt6")); + ASSERT_TRUE(Address::isValid("ckb1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3394p3wg6" + "p60qclvpfmaa582lu860dja5h0fk0v")); +} + +TEST(NervosAddress, Invalid) { + ASSERT_FALSE(Address::isValid("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9" + "erg8furras980hksatlslfaktks7epf26")); + ASSERT_FALSE(Address::isValid("ckb1qyqvfdgvtjxswncx8mq2wl0dp6hlp7nmvhdqcecnt7")); + ASSERT_FALSE(Address::isValid("ckb1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3394p3wg" + "6p60qclvpfmaa582lu860dja5h0fk0w")); +} + +TEST(NervosAddress, FromPrivateKey) { + auto privateKey = + PrivateKey(parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb")); + auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + ASSERT_EQ(address.string(), "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9er" + "g8furras980hksatlslfaktks7epf25"); +} + +TEST(NervosAddress, FromPublicKey) { + auto publicKey = + PublicKey(parse_hex("026c9e4cbb95d4b3a123c1fc80795feacc38029683a1b3e16bccf49bba25fb2858"), + TWPublicKeyTypeSECP256k1); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9er" + "g8furras980hksatlslfaktks7epf25"); +} + +TEST(NervosAddress, FromString) { + auto address1 = Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8fu" + "rras980hksatlslfaktks7epf25"); + ASSERT_EQ(address1.string(), "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9e" + "rg8furras980hksatlslfaktks7epf25"); + auto address2 = Address("ckb1qyqvfdgvtjxswncx8mq2wl0dp6hlp7nmvhdqcecnt6"); + ASSERT_EQ(address2.string(), "ckb1qyqvfdgvtjxswncx8mq2wl0dp6hlp7nmvhdqcecnt6"); + auto address3 = Address("ckb1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3394p3wg6p60qc" + "lvpfmaa582lu860dja5h0fk0v"); + ASSERT_EQ(address3.string(), "ckb1qjda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xw3394p3wg6" + "p60qclvpfmaa582lu860dja5h0fk0v"); +} + +TEST(TWNervosAddress, AddressFromPublicKey) { + auto privateKey = + PrivateKey(parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb")); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + ASSERT_EQ(publicKey.bytes.size(), 33ul); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9er" + "g8furras980hksatlslfaktks7epf25"); +} + +TEST(NervosAddress, AddressFromString) { + auto address = Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8fur" + "ras980hksatlslfaktks7epf25"); + ASSERT_EQ(address.string(), "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9er" + "g8furras980hksatlslfaktks7epf25"); +} + +TEST(NervosAddress, AddressFromWallet) { + auto hdWallet = HDWallet( + "alpha draw toss question picnic endless recycle wrong enable roast success palm", ""); + auto addressString = hdWallet.deriveAddress(TWCoinTypeNervos); + ASSERT_EQ(addressString, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8f" + "urras980hksatlslfaktks7epf25"); +} + +} // namespace TW::Nervos::tests diff --git a/tests/chains/Nervos/SignerTests.cpp b/tests/chains/Nervos/SignerTests.cpp new file mode 100644 index 00000000000..1b8bdfefeb6 --- /dev/null +++ b/tests/chains/Nervos/SignerTests.cpp @@ -0,0 +1,929 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Nervos/Address.h" +#include "Nervos/Cell.h" +#include "Nervos/Serialization.h" +#include "Nervos/Signer.h" +#include "Nervos/Transaction.h" +#include "Nervos/TransactionPlan.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "uint256.h" +#include "TestUtilities.h" +#include + +#include + +namespace TW::Nervos::tests { + +std::vector getPrivateKeys(Proto::SigningInput& input) { + std::vector privateKeys; + privateKeys.reserve(input.private_key_size()); + for (auto&& privateKey : input.private_key()) { + privateKeys.emplace_back(privateKey); + } + return privateKeys; +} + +Proto::SigningInput getInput1() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_native_transfer(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02" + "wectaumxn0664yw2jd53lqk4mxg3"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78" + "yze6eyfyvd537z66ur22c9mmrgz82ama"); + operation.set_amount(10000000000); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(100000000000); + *cell1.mutable_out_point() = + OutPoint(parse_hex("71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3"), 1) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(20000000000); + *cell2.mutable_out_point() = + OutPoint(parse_hex("71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3"), 0) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto privateKey = parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey.begin(), privateKey.end())); + + return input; +} + +void checkOutput1(Transaction& tx) { + // https://explorer.nervos.org/transaction/0xf2c32afde7e72011985583873bc16c0a3c01fc01fc161eb4b914fcf84c53cdf8 + ASSERT_EQ(tx.hash(), + parse_hex("f2c32afde7e72011985583873bc16c0a3c01fc01fc161eb4b914fcf84c53cdf8")); + + ASSERT_EQ(tx.cellDeps.size(), 1ul); + + ASSERT_EQ(tx.cellDeps[0].outPoint.txHash, + parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c")); + ASSERT_EQ(tx.cellDeps[0].outPoint.index, 0ul); + ASSERT_EQ(tx.cellDeps[0].depType, DepType::DepGroup); + + ASSERT_EQ(tx.headerDeps.size(), 0ul); + + ASSERT_EQ(tx.inputs.size(), 1ul); + + ASSERT_EQ(tx.inputs[0].previousOutput.txHash, + parse_hex("71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3")); + ASSERT_EQ(tx.inputs[0].previousOutput.index, 0ul); + ASSERT_EQ(tx.inputs[0].since, 0ul); + + ASSERT_EQ(tx.outputs.size(), 2ul); + ASSERT_EQ(tx.outputsData.size(), 2ul); + + ASSERT_EQ(tx.outputs[0].capacity, 10000000000ul); + ASSERT_EQ(tx.outputs[0].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[0].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].lock.args, parse_hex("ab201f55b02f53b385f79b34dfad548e549b48fc")); + ASSERT_EQ(tx.outputs[0].type.codeHash.size(), 0ul); + ASSERT_EQ(tx.outputs[0].type.args.size(), 0ul); + ASSERT_EQ(tx.outputsData[0].size(), 0ul); + + ASSERT_EQ(tx.outputs[1].capacity, 9999999536ul); + ASSERT_EQ(tx.outputs[1].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[1].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[1].lock.args, parse_hex("b0d65be39059d6489231b48f85ad706a560bbd8d")); + ASSERT_EQ(tx.outputs[1].type.codeHash.size(), 0ul); + ASSERT_EQ(tx.outputs[1].type.args.size(), 0ul); + ASSERT_EQ(tx.outputsData[1].size(), 0ul); + + ASSERT_EQ(tx.serializedWitnesses.size(), 1ul); + ASSERT_EQ( + hex(tx.serializedWitnesses[0]), + "55000000100000005500000055000000410000002a9ef2ad7829e5ea0c7a32735d29a0cb2ec20434f6fd5bf6e2" + "9cda56b28e08140156191cbbf80313d3c9cae4b74607acce7b28eb21d52ef058ed8491cdde70b700"); +} + +void checkPlan1(TransactionPlan& txPlan) { + ASSERT_EQ(txPlan.error, Common::Proto::SigningError::OK); + + ASSERT_EQ(txPlan.cellDeps.size(), 1ul); + + ASSERT_EQ(txPlan.cellDeps[0].outPoint.txHash, + parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c")); + ASSERT_EQ(txPlan.cellDeps[0].outPoint.index, 0ul); + ASSERT_EQ(txPlan.cellDeps[0].depType, DepType::DepGroup); + + ASSERT_EQ(txPlan.headerDeps.size(), 0ul); + + ASSERT_EQ(txPlan.selectedCells.size(), 1ul); + + ASSERT_EQ(txPlan.selectedCells[0].outPoint.txHash, + parse_hex("71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3")); + ASSERT_EQ(txPlan.selectedCells[0].outPoint.index, 0ul); + ASSERT_EQ(txPlan.selectedCells[0].capacity, 20000000000ul); + ASSERT_EQ(txPlan.selectedCells[0].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(txPlan.selectedCells[0].lock.hashType, HashType::Type1); + ASSERT_EQ(txPlan.selectedCells[0].lock.args, + parse_hex("c4b50c5c8d074f063ec0a77ded0eaff0fa7b65da")); + ASSERT_EQ(txPlan.selectedCells[0].type.codeHash.size(), 0ul); + ASSERT_EQ(txPlan.selectedCells[0].type.args.size(), 0ul); + ASSERT_EQ(txPlan.selectedCells[0].data.size(), 0ul); + + ASSERT_EQ(txPlan.outputs.size(), 2ul); + ASSERT_EQ(txPlan.outputsData.size(), 2ul); + + ASSERT_EQ(txPlan.outputs[0].capacity, 10000000000ul); + ASSERT_EQ(txPlan.outputs[0].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(txPlan.outputs[0].lock.hashType, HashType::Type1); + ASSERT_EQ(txPlan.outputs[0].lock.args, parse_hex("ab201f55b02f53b385f79b34dfad548e549b48fc")); + ASSERT_EQ(txPlan.outputs[0].type.codeHash.size(), 0ul); + ASSERT_EQ(txPlan.outputs[0].type.args.size(), 0ul); + ASSERT_EQ(txPlan.outputsData[0].size(), 0ul); + + ASSERT_EQ(txPlan.outputs[1].capacity, 9999999536ul); + ASSERT_EQ(txPlan.outputs[1].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(txPlan.outputs[1].lock.hashType, HashType::Type1); + ASSERT_EQ(txPlan.outputs[1].lock.args, parse_hex("b0d65be39059d6489231b48f85ad706a560bbd8d")); + ASSERT_EQ(txPlan.outputs[1].type.codeHash.size(), 0ul); + ASSERT_EQ(txPlan.outputs[1].type.args.size(), 0ul); + ASSERT_EQ(txPlan.outputsData[1].size(), 0ul); +} + +TEST(NervosSigner, PlanAndSign_Native_Simple) { + auto input = getInput1(); + TransactionPlan txPlan; + txPlan.plan(input); + ASSERT_EQ(txPlan.error, Common::Proto::SigningError::OK); + checkPlan1(txPlan); + Transaction tx; + tx.build(txPlan); + auto error = tx.sign(getPrivateKeys(input)); + ASSERT_EQ(error, Common::Proto::SigningError::OK); + checkOutput1(tx); +} + +TEST(NervosSigner, Sign_NegativeMissingKey) { + auto input = getInput1(); + TransactionPlan txPlan; + txPlan.plan(input); + ASSERT_EQ(txPlan.error, Common::Proto::SigningError::OK); + Transaction tx; + tx.build(txPlan); + auto privateKey = parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61fec"); + *input.mutable_private_key(0) = std::string(privateKey.begin(), privateKey.end()); + auto error = tx.sign(getPrivateKeys(input)); + ASSERT_EQ(error, Common::Proto::Error_missing_private_key); +} + +TEST(NervosSigner, Sign_NegativeNotEnoughUtxos) { + auto input = getInput1(); + auto& operation = *input.mutable_native_transfer(); + operation.set_amount(1000000000000); + TransactionPlan txPlan; + txPlan.plan(input); + ASSERT_EQ(txPlan.error, Common::Proto::Error_not_enough_utxos); +} + +Proto::SigningInput getInput2() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_native_transfer(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02" + "wectaumxn0664yw2jd53lqk4mxg3"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78" + "yze6eyfyvd537z66ur22c9mmrgz82ama"); + operation.set_use_max_amount(true); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(11410040620); + *cell1.mutable_out_point() = + OutPoint(parse_hex("c75567c80dc9b97aaf4e5c23f4c7f37b077f2b33a50dd7abd952abfbd5beb247"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto privateKey = parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey.begin(), privateKey.end())); + + return input; +} + +TEST(NervosSigner, Sign_Native_SendMaximum) { + auto input = getInput2(); + TransactionPlan txPlan; + txPlan.plan(input); + ASSERT_EQ(txPlan.error, Common::Proto::SigningError::OK); + Transaction tx; + tx.build(txPlan); + auto error = tx.sign(getPrivateKeys(input)); + + ASSERT_EQ(error, Common::Proto::SigningError::OK); + + // https://explorer.nervos.org/transaction/0x298f5e04b6900796614b89062eb96cec63c3b2c460d01058736a793b567bc5c8 + ASSERT_EQ(tx.hash(), + parse_hex("298f5e04b6900796614b89062eb96cec63c3b2c460d01058736a793b567bc5c8")); + + ASSERT_EQ(tx.cellDeps.size(), 1ul); + + ASSERT_EQ(tx.cellDeps[0].outPoint.txHash, + parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c")); + ASSERT_EQ(tx.cellDeps[0].outPoint.index, 0ul); + ASSERT_EQ(tx.cellDeps[0].depType, DepType::DepGroup); + + ASSERT_EQ(tx.headerDeps.size(), 0ul); + + ASSERT_EQ(tx.inputs.size(), 1ul); + + ASSERT_EQ(tx.inputs[0].previousOutput.txHash, + parse_hex("c75567c80dc9b97aaf4e5c23f4c7f37b077f2b33a50dd7abd952abfbd5beb247")); + ASSERT_EQ(tx.inputs[0].previousOutput.index, 0ul); + ASSERT_EQ(tx.inputs[0].since, 0ul); + + ASSERT_EQ(tx.outputs.size(), 1ul); + ASSERT_EQ(tx.outputsData.size(), 1ul); + + ASSERT_EQ(tx.outputs[0].capacity, 11410040265ul); + ASSERT_EQ(tx.outputs[0].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[0].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].lock.args, parse_hex("ab201f55b02f53b385f79b34dfad548e549b48fc")); + ASSERT_EQ(tx.outputs[0].type.codeHash.size(), 0ul); + ASSERT_EQ(tx.outputs[0].type.args.size(), 0ul); + ASSERT_EQ(tx.outputsData[0].size(), 0ul); + + ASSERT_EQ(tx.serializedWitnesses.size(), 1ul); + ASSERT_EQ( + hex(tx.serializedWitnesses[0]), + "5500000010000000550000005500000041000000daf6e65e5a1fe447a4feb7199886b6635c44738e04ea594576" + "08fb1c447e068026529d57b02014ddc144622f886153df426853f22083f8891461eeb50b5ce97d01"); +} + +Proto::SigningInput getInput3() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_sudt_transfer(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02" + "wectaumxn0664yw2jd53lqk4mxg3"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78" + "yze6eyfyvd537z66ur22c9mmrgz82ama"); + uint256_t amount = 1000000000000000; + operation.set_amount(toString(amount)); + input.set_byte_fee(1); + auto sudtAddress = + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8"); + *operation.mutable_sudt_address() = std::string(sudtAddress.begin(), sudtAddress.end()); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(14399998906); + *cell1.mutable_out_point() = + OutPoint(parse_hex("5b12911e7413e011f251c1fb5fae4e76fd5fcae4f0d4c6412dcc5b0bfcece823"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(14400000000); + *cell2.mutable_out_point() = + OutPoint(parse_hex("e118bd11a73d381daf288381ce182d92b6cf2f52d25886bbda9e1a61525c7c4a"), 0) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + *cell2.mutable_type() = + Script(parse_hex("5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5"), + HashType::Type1, + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8")) + .proto(); + auto cell2Data = parse_hex("00e0e4c9b9f84f000000000000000000"); + *cell2.mutable_data() = std::string(cell2Data.begin(), cell2Data.end()); + + auto& cell3 = *input.add_cell(); + cell3.set_capacity(8210025567); + *cell3.mutable_out_point() = + OutPoint(parse_hex("09a45a15e48f985b554a0b6e5f0857913cc492ec061cc9b0b2befa4b24609a4a"), 1) + .proto(); + *cell3.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqds6ed78yze6eyfyvd537z66ur22c9mmrgz82ama")) + .proto(); + + auto privateKey1 = + parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey1.begin(), privateKey1.end())); + auto privateKey2 = + parse_hex("0c8859a9d9084a8c2b55963268b352e258756f9240f2a1f4645c610ed191dae9"); + input.add_private_key(std::string(privateKey2.begin(), privateKey2.end())); + + return input; +} + +TEST(NervosSigner, Sign_SUDT_Simple) { + auto input = getInput3(); + + TransactionPlan txPlan; + txPlan.plan(input); + ASSERT_EQ(txPlan.error, Common::Proto::SigningError::OK); + Transaction tx; + tx.build(txPlan); + auto error = tx.sign(getPrivateKeys(input)); + + ASSERT_EQ(error, Common::Proto::SigningError::OK); + + // https://explorer.nervos.org/transaction/0x9b15f2bea26b98201540d8e20e8b1c21d96dd77ad246520b405c6aabb7173371 + ASSERT_EQ(tx.hash(), + parse_hex("9b15f2bea26b98201540d8e20e8b1c21d96dd77ad246520b405c6aabb7173371")); + + ASSERT_EQ(tx.cellDeps.size(), 2ul); + + ASSERT_EQ(tx.cellDeps[0].outPoint.txHash, + parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c")); + ASSERT_EQ(tx.cellDeps[0].outPoint.index, 0ul); + ASSERT_EQ(tx.cellDeps[0].depType, DepType::DepGroup); + + ASSERT_EQ(tx.cellDeps[1].outPoint.txHash, + parse_hex("c7813f6a415144643970c2e88e0bb6ca6a8edc5dd7c1022746f628284a9936d5")); + ASSERT_EQ(tx.cellDeps[1].outPoint.index, 0ul); + ASSERT_EQ(tx.cellDeps[1].depType, DepType::Code); + + ASSERT_EQ(tx.headerDeps.size(), 0ul); + + ASSERT_EQ(tx.inputs.size(), 3ul); + + ASSERT_EQ(tx.inputs[0].previousOutput.txHash, + parse_hex("e118bd11a73d381daf288381ce182d92b6cf2f52d25886bbda9e1a61525c7c4a")); + ASSERT_EQ(tx.inputs[0].previousOutput.index, 0ul); + ASSERT_EQ(tx.inputs[0].since, 0ul); + + ASSERT_EQ(tx.inputs[1].previousOutput.txHash, + parse_hex("09a45a15e48f985b554a0b6e5f0857913cc492ec061cc9b0b2befa4b24609a4a")); + ASSERT_EQ(tx.inputs[1].previousOutput.index, 1ul); + ASSERT_EQ(tx.inputs[1].since, 0ul); + + ASSERT_EQ(tx.inputs[2].previousOutput.txHash, + parse_hex("5b12911e7413e011f251c1fb5fae4e76fd5fcae4f0d4c6412dcc5b0bfcece823")); + ASSERT_EQ(tx.inputs[2].previousOutput.index, 0ul); + ASSERT_EQ(tx.inputs[2].since, 0ul); + + ASSERT_EQ(tx.outputs.size(), 3ul); + ASSERT_EQ(tx.outputsData.size(), 3ul); + + ASSERT_EQ(tx.outputs[0].capacity, 14400000000ul); + ASSERT_EQ(tx.outputs[0].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[0].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].lock.args, parse_hex("ab201f55b02f53b385f79b34dfad548e549b48fc")); + ASSERT_EQ(tx.outputs[0].type.codeHash, + parse_hex("5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5")); + ASSERT_EQ(tx.outputs[0].type.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].type.args, + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8")); + ASSERT_EQ(hex(tx.outputsData[0]), "0080c6a47e8d03000000000000000000"); + + ASSERT_EQ(tx.outputs[1].capacity, 14400000000ul); + ASSERT_EQ(tx.outputs[1].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[1].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[1].lock.args, parse_hex("b0d65be39059d6489231b48f85ad706a560bbd8d")); + ASSERT_EQ(tx.outputs[1].type.codeHash, + parse_hex("5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5")); + ASSERT_EQ(tx.outputs[1].type.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[1].type.args, + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8")); + ASSERT_EQ(hex(tx.outputsData[1]), "00601e253b6b4c000000000000000000"); + + ASSERT_EQ(tx.outputs[2].capacity, 8210023387ul); + ASSERT_EQ(tx.outputs[2].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[2].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[2].lock.args, parse_hex("b0d65be39059d6489231b48f85ad706a560bbd8d")); + ASSERT_EQ(tx.outputs[2].type.codeHash.size(), 0ul); + ASSERT_EQ(tx.outputs[2].type.args.size(), 0ul); + ASSERT_EQ(tx.outputsData[2].size(), 0ul); + + ASSERT_EQ(tx.serializedWitnesses.size(), 3ul); + ASSERT_EQ( + hex(tx.serializedWitnesses[0]), + "550000001000000055000000550000004100000035d55fd46316f248552eb6af7ac9589c9ec533c4e5b71896b0" + "5cdf697e2d18551ceff54d7b860ebb2f4dd5f6c5bb4af1da15460a7621f5aa4bc7d5585a0504de00"); + ASSERT_EQ( + hex(tx.serializedWitnesses[1]), + "5500000010000000550000005500000041000000eaa4bf69126d3016ab786610f2f0668b2ef353915d623d0b01" + "84fc25cec3dcad6dc08a1504a2d7dd9faced17b041d79d4c21f1977e57859713360f5e3609583501"); + ASSERT_EQ(hex(tx.serializedWitnesses[2]), ""); +} + +Proto::SigningInput getInput4() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_sudt_transfer(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02" + "wectaumxn0664yw2jd53lqk4mxg3"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78" + "yze6eyfyvd537z66ur22c9mmrgz82ama"); + operation.set_use_max_amount(true); + input.set_byte_fee(1); + auto sudtAddress = + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8"); + *operation.mutable_sudt_address() = std::string(sudtAddress.begin(), sudtAddress.end()); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(8210026306); + *cell1.mutable_out_point() = + OutPoint(parse_hex("430cb60ee816e2631d6d9605659c18fec8eb3de94526f5fd4ad51feaad6f1664"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(14400000000); + *cell2.mutable_out_point() = + OutPoint(parse_hex("378b6bd2f7fc2b1599ee55be7e8fa17fdd6e0d25e2e146d5f46006e0292d6564"), 0) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + *cell2.mutable_type() = + Script(parse_hex("5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5"), + HashType::Type1, + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8")) + .proto(); + auto cell2Data = parse_hex("00601e253b6b4c000000000000000000"); + *cell2.mutable_data() = std::string(cell2Data.begin(), cell2Data.end()); + + auto privateKey = parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey.begin(), privateKey.end())); + + return input; +} + +TEST(NervosSigner, Sign_SUDT_SendMaximum) { + auto input = getInput4(); + TransactionPlan txPlan; + txPlan.plan(input); + ASSERT_EQ(txPlan.error, Common::Proto::SigningError::OK); + Transaction tx; + tx.build(txPlan); + auto error = tx.sign(getPrivateKeys(input)); + + ASSERT_EQ(error, Common::Proto::SigningError::OK); + + // https://explorer.nervos.org/transaction/0x09a45a15e48f985b554a0b6e5f0857913cc492ec061cc9b0b2befa4b24609a4a + ASSERT_EQ(tx.hash(), + parse_hex("09a45a15e48f985b554a0b6e5f0857913cc492ec061cc9b0b2befa4b24609a4a")); + + ASSERT_EQ(tx.cellDeps.size(), 2ul); + + ASSERT_EQ(tx.cellDeps[0].outPoint.txHash, + parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c")); + ASSERT_EQ(tx.cellDeps[0].outPoint.index, 0ul); + ASSERT_EQ(tx.cellDeps[0].depType, DepType::DepGroup); + + ASSERT_EQ(tx.cellDeps[1].outPoint.txHash, + parse_hex("c7813f6a415144643970c2e88e0bb6ca6a8edc5dd7c1022746f628284a9936d5")); + ASSERT_EQ(tx.cellDeps[1].outPoint.index, 0ul); + ASSERT_EQ(tx.cellDeps[1].depType, DepType::Code); + + ASSERT_EQ(tx.headerDeps.size(), 0ul); + + ASSERT_EQ(tx.inputs.size(), 2ul); + + ASSERT_EQ(tx.inputs[0].previousOutput.txHash, + parse_hex("378b6bd2f7fc2b1599ee55be7e8fa17fdd6e0d25e2e146d5f46006e0292d6564")); + ASSERT_EQ(tx.inputs[0].previousOutput.index, 0ul); + ASSERT_EQ(tx.inputs[0].since, 0ul); + + ASSERT_EQ(tx.inputs[1].previousOutput.txHash, + parse_hex("430cb60ee816e2631d6d9605659c18fec8eb3de94526f5fd4ad51feaad6f1664")); + ASSERT_EQ(tx.inputs[1].previousOutput.index, 0ul); + ASSERT_EQ(tx.inputs[1].since, 0ul); + + ASSERT_EQ(tx.outputs.size(), 2ul); + ASSERT_EQ(tx.outputsData.size(), 2ul); + + ASSERT_EQ(tx.outputs[0].capacity, 14400000000ul); + ASSERT_EQ(tx.outputs[0].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[0].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].lock.args, parse_hex("ab201f55b02f53b385f79b34dfad548e549b48fc")); + ASSERT_EQ(tx.outputs[0].type.codeHash, + parse_hex("5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5")); + ASSERT_EQ(tx.outputs[0].type.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].type.args, + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8")); + ASSERT_EQ(hex(tx.outputsData[0]), "00601e253b6b4c000000000000000000"); + + ASSERT_EQ(tx.outputs[1].capacity, 8210025567ul); + ASSERT_EQ(tx.outputs[1].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[1].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[1].lock.args, parse_hex("b0d65be39059d6489231b48f85ad706a560bbd8d")); + ASSERT_EQ(tx.outputs[1].type.codeHash.size(), 0ul); + ASSERT_EQ(tx.outputs[1].type.args.size(), 0ul); + ASSERT_EQ(tx.outputsData[1].size(), 0ul); + + ASSERT_EQ(tx.serializedWitnesses.size(), 2ul); + ASSERT_EQ( + hex(tx.serializedWitnesses[0]), + "5500000010000000550000005500000041000000da7c908bdf2cb091b7ff9bb682b762d1323c5e1ecf9b2ce0eb" + "edb9d55f6625c52ab14910ae401833112f2ea516ab11bc9ef691c3dff7886e3238c9348c3d73a701"); + ASSERT_EQ(hex(tx.serializedWitnesses[1]), ""); +} + +Proto::SigningInput getInput5() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_dao_deposit(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8f" + "urras980hksatlslfaktks7epf25"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9e" + "rg8furras980hksatlslfaktks7epf25"); + + operation.set_amount(10200000000); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(8210021909); + *cell1.mutable_out_point() = + OutPoint(parse_hex("c7dacd4aab49f5f9643e87752428cebde38eeb49c7726781c4d8b526822004a1"), 1) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqds6ed78yze6eyfyvd537z66ur22c9mmrgz82ama")) + .proto(); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(14399998167); + *cell2.mutable_out_point() = + OutPoint(parse_hex("d3c3263170815b326779e2fd8d548f846ae13eff9d9a82833c7071069a1d32bf"), 0) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto privateKey1 = + parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey1.begin(), privateKey1.end())); + auto privateKey2 = + parse_hex("0c8859a9d9084a8c2b55963268b352e258756f9240f2a1f4645c610ed191dae9"); + input.add_private_key(std::string(privateKey2.begin(), privateKey2.end())); + + return input; +} + +TEST(NervosSigner, Sign_DAO_Deposit) { + auto input = getInput5(); + + TransactionPlan txPlan; + txPlan.plan(input); + ASSERT_EQ(txPlan.error, Common::Proto::SigningError::OK); + Transaction tx; + tx.build(txPlan); + auto error = tx.sign(getPrivateKeys(input)); + + ASSERT_EQ(error, Common::Proto::SigningError::OK); + + // https://explorer.nervos.org/transaction/0x583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683 + ASSERT_EQ(tx.hash(), + parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683")); + + ASSERT_EQ(tx.cellDeps.size(), 2ul); + + ASSERT_EQ(tx.cellDeps[0].outPoint.txHash, + parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c")); + ASSERT_EQ(tx.cellDeps[0].outPoint.index, 0ul); + ASSERT_EQ(tx.cellDeps[0].depType, DepType::DepGroup); + + ASSERT_EQ(tx.cellDeps[1].outPoint.txHash, + parse_hex("e2fb199810d49a4d8beec56718ba2593b665db9d52299a0f9e6e75416d73ff5c")); + ASSERT_EQ(tx.cellDeps[1].outPoint.index, 2ul); + ASSERT_EQ(tx.cellDeps[1].depType, DepType::Code); + + ASSERT_EQ(tx.headerDeps.size(), 0ul); + + ASSERT_EQ(tx.inputs.size(), 2ul); + + ASSERT_EQ(tx.inputs[0].previousOutput.txHash, + parse_hex("c7dacd4aab49f5f9643e87752428cebde38eeb49c7726781c4d8b526822004a1")); + ASSERT_EQ(tx.inputs[0].previousOutput.index, 1ul); + ASSERT_EQ(tx.inputs[0].since, 0ul); + + ASSERT_EQ(tx.inputs[1].previousOutput.txHash, + parse_hex("d3c3263170815b326779e2fd8d548f846ae13eff9d9a82833c7071069a1d32bf")); + ASSERT_EQ(tx.inputs[1].previousOutput.index, 0ul); + ASSERT_EQ(tx.inputs[1].since, 0ul); + + ASSERT_EQ(tx.outputs.size(), 2ul); + ASSERT_EQ(tx.outputsData.size(), 2ul); + + ASSERT_EQ(tx.outputs[0].capacity, 10200000000ul); + ASSERT_EQ(tx.outputs[0].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[0].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].lock.args, parse_hex("c4b50c5c8d074f063ec0a77ded0eaff0fa7b65da")); + ASSERT_EQ(tx.outputs[0].type.codeHash, + parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e")); + ASSERT_EQ(tx.outputs[0].type.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].type.args, parse_hex("")); + ASSERT_EQ(hex(tx.outputsData[0]), "0000000000000000"); + + ASSERT_EQ(tx.outputs[1].capacity, 12410019377ul); + ASSERT_EQ(tx.outputs[1].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[1].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[1].lock.args, parse_hex("c4b50c5c8d074f063ec0a77ded0eaff0fa7b65da")); + ASSERT_EQ(tx.outputs[1].type.codeHash.size(), 0ul); + ASSERT_EQ(tx.outputs[1].type.args.size(), 0ul); + ASSERT_EQ(tx.outputsData[1].size(), 0ul); + + ASSERT_EQ(tx.serializedWitnesses.size(), 2ul); + ASSERT_EQ( + hex(tx.serializedWitnesses[0]), + "5500000010000000550000005500000041000000305d09c7de3f34a4d53bc4e0031ee59c95b9abc4fc3ff5548e" + "1a17ca726c069a232012c9c4be6ec4d4ffbe88613ca5e686e3e4b7d0b9bbd7038003e23ffdcdd601"); + ASSERT_EQ( + hex(tx.serializedWitnesses[1]), + "55000000100000005500000055000000410000007c514c77482dd1e1086f41a6d17364c9b5ed16364d61df6f7f" + "d8540f8bf7c131275c877943786b1b72fbf4f9d817ee5dd554a689808b7919543c691b5068e5be01"); +} + +Proto::SigningInput getInput6() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_dao_withdraw_phase1(); + + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9e" + "rg8furras980hksatlslfaktks7epf25"); + auto& depositCell = *operation.mutable_deposit_cell(); + depositCell.set_capacity(10200000000); + *depositCell.mutable_out_point() = + OutPoint(parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683"), 0) + .proto(); + *depositCell.mutable_lock() = + Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras9" + "80hksatlslfaktks7epf25")) + .proto(); + *depositCell.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto depositCellData = parse_hex("0000000000000000"); + *depositCell.mutable_data() = std::string(depositCellData.begin(), depositCellData.end()); + depositCell.set_block_number(7575466); + auto blockHashData = + parse_hex("3dfdb4b702a355a5593315016f8af0537d5a2f3292811b79420ded78a092be6a"); + *depositCell.mutable_block_hash() = std::string(blockHashData.begin(), blockHashData.end()); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(10200000000); + *cell1.mutable_out_point() = + OutPoint(parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + *cell1.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto cell1Data = parse_hex("0000000000000000"); + *cell1.mutable_data() = std::string(cell1Data.begin(), cell1Data.end()); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(12410019377); + *cell2.mutable_out_point() = + OutPoint(parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683"), 1) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto privateKey1 = + parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey1.begin(), privateKey1.end())); + + return input; +} + +TEST(NervosSigner, Sign_DAO_Withdraw_Phase1) { + auto input = getInput6(); + + TransactionPlan txPlan; + txPlan.plan(input); + ASSERT_EQ(txPlan.error, Common::Proto::SigningError::OK); + Transaction tx; + tx.build(txPlan); + auto error = tx.sign(getPrivateKeys(input)); + + ASSERT_EQ(error, Common::Proto::SigningError::OK); + + // https://explorer.nervos.org/transaction/0xb4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca + ASSERT_EQ(tx.hash(), + parse_hex("b4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca")); + + ASSERT_EQ(tx.cellDeps.size(), 2ul); + + ASSERT_EQ(tx.cellDeps[0].outPoint.txHash, + parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c")); + ASSERT_EQ(tx.cellDeps[0].outPoint.index, 0ul); + ASSERT_EQ(tx.cellDeps[0].depType, DepType::DepGroup); + + ASSERT_EQ(tx.cellDeps[1].outPoint.txHash, + parse_hex("e2fb199810d49a4d8beec56718ba2593b665db9d52299a0f9e6e75416d73ff5c")); + ASSERT_EQ(tx.cellDeps[1].outPoint.index, 2ul); + ASSERT_EQ(tx.cellDeps[1].depType, DepType::Code); + + ASSERT_EQ(tx.headerDeps.size(), 1ul); + ASSERT_EQ(tx.headerDeps[0], + parse_hex("3dfdb4b702a355a5593315016f8af0537d5a2f3292811b79420ded78a092be6a")); + + ASSERT_EQ(tx.inputs.size(), 2ul); + + ASSERT_EQ(tx.inputs[0].previousOutput.txHash, + parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683")); + ASSERT_EQ(tx.inputs[0].previousOutput.index, 0ul); + ASSERT_EQ(tx.inputs[0].since, 0ul); + + ASSERT_EQ(tx.inputs[1].previousOutput.txHash, + parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683")); + ASSERT_EQ(tx.inputs[1].previousOutput.index, 1ul); + ASSERT_EQ(tx.inputs[1].since, 0ul); + + ASSERT_EQ(tx.outputs.size(), 2ul); + ASSERT_EQ(tx.outputsData.size(), 2ul); + + ASSERT_EQ(tx.outputs[0].capacity, 10200000000ul); + ASSERT_EQ(tx.outputs[0].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[0].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].lock.args, parse_hex("c4b50c5c8d074f063ec0a77ded0eaff0fa7b65da")); + ASSERT_EQ(tx.outputs[0].type.codeHash, + parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e")); + ASSERT_EQ(tx.outputs[0].type.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].type.args, parse_hex("")); + ASSERT_EQ(hex(tx.outputsData[0]), "aa97730000000000"); + + ASSERT_EQ(tx.outputs[1].capacity, 12410018646ul); + ASSERT_EQ(tx.outputs[1].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[1].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[1].lock.args, parse_hex("c4b50c5c8d074f063ec0a77ded0eaff0fa7b65da")); + ASSERT_EQ(tx.outputs[1].type.codeHash.size(), 0ul); + ASSERT_EQ(tx.outputs[1].type.args.size(), 0ul); + ASSERT_EQ(tx.outputsData[1].size(), 0ul); + + ASSERT_EQ(tx.serializedWitnesses.size(), 2ul); + ASSERT_EQ( + hex(tx.serializedWitnesses[0]), + "5500000010000000550000005500000041000000d5131c1a6b8eca11e2c280b72c5db09ea00bb788fd3262eace" + "d861c39db2aad04a36f9d174b6f167a9c98b85d2bccf537a163c44459d23467dfa86408f48dd5f01"); + ASSERT_EQ(tx.serializedWitnesses[1].size(), 0ul); +} + +Proto::SigningInput getInput7() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_dao_withdraw_phase2(); + + auto& depositCell = *operation.mutable_deposit_cell(); + depositCell.set_capacity(10200000000); + *depositCell.mutable_out_point() = + OutPoint(parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683"), 0) + .proto(); + *depositCell.mutable_lock() = + Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras9" + "80hksatlslfaktks7epf25")) + .proto(); + *depositCell.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto depositCellData = parse_hex("0000000000000000"); + *depositCell.mutable_data() = std::string(depositCellData.begin(), depositCellData.end()); + depositCell.set_block_number(7575466); + auto blockHashData1 = + parse_hex("3dfdb4b702a355a5593315016f8af0537d5a2f3292811b79420ded78a092be6a"); + *depositCell.mutable_block_hash() = std::string(blockHashData1.begin(), blockHashData1.end()); + + auto& withdrawingCell = *operation.mutable_withdrawing_cell(); + withdrawingCell.set_capacity(10200000000); + *withdrawingCell.mutable_out_point() = + OutPoint(parse_hex("b4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca"), 0) + .proto(); + *withdrawingCell.mutable_lock() = + Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras9" + "80hksatlslfaktks7epf25")) + .proto(); + *withdrawingCell.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto withdrawingCellData = parse_hex("aa97730000000000"); + *withdrawingCell.mutable_data() = + std::string(withdrawingCellData.begin(), withdrawingCellData.end()); + withdrawingCell.set_block_number(7575534); + auto blockHashData2 = + parse_hex("b070d5364afd47c23fe267077d454009d6f665f200a915e68af1616e46f4aa52"); + *withdrawingCell.mutable_block_hash() = + std::string(blockHashData2.begin(), blockHashData2.end()); + withdrawingCell.set_since(0x20037c0000001731); + + operation.set_amount(10200000000); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(10200000000); + *cell1.mutable_out_point() = + OutPoint(parse_hex("b4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + *cell1.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto cell1Data = parse_hex("aa97730000000000"); + *cell1.mutable_data() = std::string(cell1Data.begin(), cell1Data.end()); + + auto privateKey1 = + parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey1.begin(), privateKey1.end())); + + return input; +} + +TEST(NervosSigner, Sign_DAO_Withdraw_Phase2) { + auto input = getInput7(); + + TransactionPlan txPlan; + txPlan.plan(input); + ASSERT_EQ(txPlan.error, Common::Proto::SigningError::OK); + Transaction tx; + tx.build(txPlan); + auto error = tx.sign(getPrivateKeys(input)); + + ASSERT_EQ(error, Common::Proto::SigningError::OK); + + ASSERT_EQ(tx.hash(), + parse_hex("4fde13c93fc5d24ab7f660070aaa0b1725809d585f6258605e595cdbd856ea1c")); + + ASSERT_EQ(tx.cellDeps.size(), 2ul); + + ASSERT_EQ(tx.cellDeps[0].outPoint.txHash, + parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c")); + ASSERT_EQ(tx.cellDeps[0].outPoint.index, 0ul); + ASSERT_EQ(tx.cellDeps[0].depType, DepType::DepGroup); + + ASSERT_EQ(tx.cellDeps[1].outPoint.txHash, + parse_hex("e2fb199810d49a4d8beec56718ba2593b665db9d52299a0f9e6e75416d73ff5c")); + ASSERT_EQ(tx.cellDeps[1].outPoint.index, 2ul); + ASSERT_EQ(tx.cellDeps[1].depType, DepType::Code); + + ASSERT_EQ(tx.headerDeps.size(), 2ul); + ASSERT_EQ(tx.headerDeps[0], + parse_hex("3dfdb4b702a355a5593315016f8af0537d5a2f3292811b79420ded78a092be6a")); + ASSERT_EQ(tx.headerDeps[1], + parse_hex("b070d5364afd47c23fe267077d454009d6f665f200a915e68af1616e46f4aa52")); + + ASSERT_EQ(tx.inputs.size(), 1ul); + + ASSERT_EQ(tx.inputs[0].previousOutput.txHash, + parse_hex("b4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca")); + ASSERT_EQ(tx.inputs[0].previousOutput.index, 0ul); + ASSERT_EQ(tx.inputs[0].since, 0x20037c0000001731ul); + + ASSERT_EQ(tx.outputs.size(), 1ul); + ASSERT_EQ(tx.outputsData.size(), 1ul); + + ASSERT_EQ(tx.outputs[0].capacity, 10199999532ul); + ASSERT_EQ(tx.outputs[0].lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(tx.outputs[0].lock.hashType, HashType::Type1); + ASSERT_EQ(tx.outputs[0].lock.args, parse_hex("c4b50c5c8d074f063ec0a77ded0eaff0fa7b65da")); + ASSERT_EQ(tx.outputs[0].type.codeHash.size(), 0ul); + ASSERT_EQ(tx.outputs[0].type.args.size(), 0ul); + ASSERT_EQ(tx.outputsData[0].size(), 0ul); + + ASSERT_EQ(tx.serializedWitnesses.size(), 1ul); + ASSERT_EQ(hex(tx.serializedWitnesses[0]), + "6100000010000000550000006100000041000000743f86c5557f4e2d3327f4d17e7bad27209b29c1e9cd" + "bab42ab03f7094af917b4b203ddd7f2e87102e09ae579f2fe7f6adb7900b7386b58c1183ba0011b7c421" + "00080000000000000000000000"); +} + +} // namespace TW::Nervos::tests diff --git a/tests/chains/Nervos/TWAnyAddressTests.cpp b/tests/chains/Nervos/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..c514723797c --- /dev/null +++ b/tests/chains/Nervos/TWAnyAddressTests.cpp @@ -0,0 +1,70 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "PrivateKey.h" +#include +#include +#include +#include +#include +#include + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(TWAnyAddressNervos, AddressFromPublicKey) { + auto privateKey = + WRAP(TWPrivateKey, + TWPrivateKeyCreateWithData( + DATA("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb").get())); + ASSERT_NE(nullptr, privateKey.get()); + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), true)); + ASSERT_NE(nullptr, publicKey.get()); + ASSERT_EQ(33ul, publicKey.get()->impl.bytes.size()); + auto address = + WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeNervos)); + auto addressString = WRAPS(TWAnyAddressDescription(address.get())); + assertStringsEqual(addressString, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwy" + "k5x9erg8furras980hksatlslfaktks7epf25"); +} + +TEST(TWAnyAddressNervos, AddressFromString) { + auto address = + WRAP(TWAnyAddress, + TWAnyAddressCreateWithString(STRING("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthy" + "waa50xwsqwyk5x9erg8furras980hksatlslfaktks7epf25") + .get(), + TWCoinTypeNervos)); + ASSERT_NE(nullptr, address.get()); + auto addressString = WRAPS(TWAnyAddressDescription(address.get())); + assertStringsEqual(addressString, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwy" + "k5x9erg8furras980hksatlslfaktks7epf25"); +} + +TEST(TWAnyAddressNervos, AddressFromWallet) { + auto wallet = WRAP( + TWHDWallet, + TWHDWalletCreateWithMnemonic( + STRING( + "alpha draw toss question picnic endless recycle wrong enable roast success palm") + .get(), + STRING("").get())); + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeNervos)); + auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); + EXPECT_EQ(TWDataSize(privateKeyData.get()), 32ul); + + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), true)); + auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); + EXPECT_EQ(TWDataSize(publicKeyData.get()), 33ul); + assertHexEqual(publicKeyData, + "026c9e4cbb95d4b3a123c1fc80795feacc38029683a1b3e16bccf49bba25fb2858"); + + auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeNervos, privateKey.get())); + assertStringsEqual(address, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9er" + "g8furras980hksatlslfaktks7epf25"); +} diff --git a/tests/chains/Nervos/TWAnySignerTests.cpp b/tests/chains/Nervos/TWAnySignerTests.cpp new file mode 100644 index 00000000000..c213e802d0a --- /dev/null +++ b/tests/chains/Nervos/TWAnySignerTests.cpp @@ -0,0 +1,665 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Nervos/Address.h" +#include "Nervos/Serialization.h" +#include "Nervos/TransactionPlan.h" +#include "PublicKey.h" +#include "proto/Nervos.pb.h" +#include "uint256.h" +#include "TestUtilities.h" +#include + +#include + +namespace TW::Nervos::tests { + +Proto::SigningInput getAnySignerInput1() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_native_transfer(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02" + "wectaumxn0664yw2jd53lqk4mxg3"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78" + "yze6eyfyvd537z66ur22c9mmrgz82ama"); + operation.set_amount(10000000000); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(100000000000); + *cell1.mutable_out_point() = + OutPoint(parse_hex("71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3"), 1) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(20000000000); + *cell2.mutable_out_point() = + OutPoint(parse_hex("71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3"), 0) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto privateKey = parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey.begin(), privateKey.end())); + + return input; +} + +void checkAnySignerOutput1(Proto::SigningOutput& output) { + ASSERT_EQ(output.error(), Common::Proto::OK); + // https://explorer.nervos.org/transaction/0xf2c32afde7e72011985583873bc16c0a3c01fc01fc161eb4b914fcf84c53cdf8 + ASSERT_EQ(output.transaction_id(), + "0xf2c32afde7e72011985583873bc16c0a3c01fc01fc161eb4b914fcf84c53cdf8"); + ASSERT_EQ( + output.transaction_json(), + "{\"cell_deps\":[{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c\"}}],\"header_deps\":" + "[],\"inputs\":[{\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3\"},\"since\":\"0x0\"}" + "],\"outputs\":[{\"capacity\":\"0x2540be400\",\"lock\":{\"args\":" + "\"0xab201f55b02f53b385f79b34dfad548e549b48fc\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":" + "\"type\"},\"type\":null},{\"capacity\":\"0x2540be230\",\"lock\":{\"args\":" + "\"0xb0d65be39059d6489231b48f85ad706a560bbd8d\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":" + "\"type\"},\"type\":null}],\"outputs_data\":[\"0x\",\"0x\"],\"version\":\"0x0\"," + "\"witnesses\":[" + "\"0x55000000100000005500000055000000410000002a9ef2ad7829e5ea0c7a32735d29a0cb2ec20434f6fd5b" + "f6e29cda56b28e08140156191cbbf80313d3c9cae4b74607acce7b28eb21d52ef058ed8491cdde70b700\"]}"); +} + +void checkPlan1(Proto::TransactionPlan& txPlanProto) { + ASSERT_EQ(txPlanProto.error(), Common::Proto::OK); + + ASSERT_EQ(txPlanProto.cell_deps_size(), 1); + + const auto cellDep1 = txPlanProto.cell_deps(0); + const auto cellDep1TxHash = + parse_hex("71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c"); + ASSERT_EQ(cellDep1.out_point().tx_hash(), + std::string(cellDep1TxHash.begin(), cellDep1TxHash.end())); + ASSERT_EQ(cellDep1.out_point().index(), 0ul); + ASSERT_EQ(cellDep1.dep_type(), "dep_group"); + + ASSERT_EQ(txPlanProto.header_deps_size(), 0); + + ASSERT_EQ(txPlanProto.selected_cells_size(), 1); + + auto cell1 = Cell(txPlanProto.selected_cells(0)); + ASSERT_EQ(cell1.outPoint.txHash, + parse_hex("71caea2d3ac9e3ea899643e3e67dd11eb587e7fe0d8c6e67255d0959fa0a1fa3")); + ASSERT_EQ(cell1.outPoint.index, 0ul); + ASSERT_EQ(cell1.capacity, 20000000000ul); + ASSERT_EQ(cell1.lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(cell1.lock.hashType, HashType::Type1); + ASSERT_EQ(cell1.lock.args, parse_hex("c4b50c5c8d074f063ec0a77ded0eaff0fa7b65da")); + ASSERT_EQ(cell1.type.codeHash.size(), 0ul); + ASSERT_EQ(cell1.type.args.size(), 0ul); + ASSERT_EQ(cell1.data.size(), 0ul); + + ASSERT_EQ(txPlanProto.outputs_size(), 2); + ASSERT_EQ(txPlanProto.outputs_data_size(), 2); + + auto cellOutput1 = CellOutput(txPlanProto.outputs(0)); + ASSERT_EQ(cellOutput1.capacity, 10000000000ul); + ASSERT_EQ(cellOutput1.lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(cellOutput1.lock.hashType, HashType::Type1); + ASSERT_EQ(cellOutput1.lock.args, parse_hex("ab201f55b02f53b385f79b34dfad548e549b48fc")); + ASSERT_EQ(cellOutput1.type.codeHash.size(), 0ul); + ASSERT_EQ(cellOutput1.type.args.size(), 0ul); + ASSERT_EQ(txPlanProto.outputs_data(0).length(), 0ul); + + auto cellOutput2 = CellOutput(txPlanProto.outputs(1)); + ASSERT_EQ(cellOutput2.capacity, 9999999536ul); + ASSERT_EQ(cellOutput2.lock.codeHash, + parse_hex("9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8")); + ASSERT_EQ(cellOutput2.lock.hashType, HashType::Type1); + ASSERT_EQ(cellOutput2.lock.args, parse_hex("b0d65be39059d6489231b48f85ad706a560bbd8d")); + ASSERT_EQ(cellOutput2.type.codeHash.size(), 0ul); + ASSERT_EQ(cellOutput2.type.args.size(), 0ul); + ASSERT_EQ(txPlanProto.outputs_data(1).length(), 0ul); +} + +TEST(TWAnySignerNervos, Sign_Native_Simple) { + auto input = getAnySignerInput1(); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNervos); + checkAnySignerOutput1(output); +} + +TEST(TWAnySignerNervos, PlanAndSign_Native_Simple) { + auto input = getAnySignerInput1(); + Proto::TransactionPlan txPlanProto; + ANY_PLAN(input, txPlanProto, TWCoinTypeNervos); + checkPlan1(txPlanProto); + *input.mutable_plan() = txPlanProto; + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNervos); + checkAnySignerOutput1(output); +} + +TEST(TWAnySignerNervos, Sign_NegativeMissingKey) { + auto input = getAnySignerInput1(); + input.clear_private_key(); + auto privateKey = parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61fec"); + input.add_private_key(std::string(privateKey.begin(), privateKey.end())); + Proto::SigningOutput output; + + ANY_SIGN(input, TWCoinTypeNervos); + + ASSERT_EQ(output.error(), Common::Proto::Error_missing_private_key); +} + +TEST(TWAnySignerNervos, Sign_NegativeNotEnoughUtxos) { + auto input = getAnySignerInput1(); + auto& operation = *input.mutable_native_transfer(); + operation.set_amount(1000000000000); + Proto::SigningOutput output; + + ANY_SIGN(input, TWCoinTypeNervos); + + ASSERT_EQ(output.error(), Common::Proto::Error_not_enough_utxos); +} + +Proto::SigningInput getAnySignerInput2() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_native_transfer(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02" + "wectaumxn0664yw2jd53lqk4mxg3"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78" + "yze6eyfyvd537z66ur22c9mmrgz82ama"); + operation.set_use_max_amount(true); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(11410040620); + *cell1.mutable_out_point() = + OutPoint(parse_hex("c75567c80dc9b97aaf4e5c23f4c7f37b077f2b33a50dd7abd952abfbd5beb247"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto privateKey = parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey.begin(), privateKey.end())); + + return input; +} + +TEST(TWAnySignerNervos, Sign_Native_SendMaximum) { + auto input = getAnySignerInput2(); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNervos); + ASSERT_EQ(output.error(), Common::Proto::OK); + // https://explorer.nervos.org/transaction/0x298f5e04b6900796614b89062eb96cec63c3b2c460d01058736a793b567bc5c8 + ASSERT_EQ(output.transaction_id(), + "0x298f5e04b6900796614b89062eb96cec63c3b2c460d01058736a793b567bc5c8"); + ASSERT_EQ( + output.transaction_json(), + "{\"cell_deps\":[{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c\"}}],\"header_deps\":" + "[],\"inputs\":[{\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0xc75567c80dc9b97aaf4e5c23f4c7f37b077f2b33a50dd7abd952abfbd5beb247\"},\"since\":\"0x0\"}" + "],\"outputs\":[{\"capacity\":\"0x2a81765c9\",\"lock\":{\"args\":" + "\"0xab201f55b02f53b385f79b34dfad548e549b48fc\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":" + "\"type\"},\"type\":null}],\"outputs_data\":[\"0x\"],\"version\":\"0x0\",\"witnesses\":[" + "\"0x5500000010000000550000005500000041000000daf6e65e5a1fe447a4feb7199886b6635c44738e04ea59" + "457608fb1c447e068026529d57b02014ddc144622f886153df426853f22083f8891461eeb50b5ce97d01\"]}"); +} + +Proto::SigningInput getAnySignerInput3() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_sudt_transfer(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02" + "wectaumxn0664yw2jd53lqk4mxg3"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78" + "yze6eyfyvd537z66ur22c9mmrgz82ama"); + uint256_t amount = 1000000000000000; + operation.set_amount(toString(amount)); + input.set_byte_fee(1); + auto sudtAddress = + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8"); + *operation.mutable_sudt_address() = std::string(sudtAddress.begin(), sudtAddress.end()); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(14399998906); + *cell1.mutable_out_point() = + OutPoint(parse_hex("5b12911e7413e011f251c1fb5fae4e76fd5fcae4f0d4c6412dcc5b0bfcece823"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(14400000000); + *cell2.mutable_out_point() = + OutPoint(parse_hex("e118bd11a73d381daf288381ce182d92b6cf2f52d25886bbda9e1a61525c7c4a"), 0) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + *cell2.mutable_type() = + Script(parse_hex("5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5"), + HashType::Type1, + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8")) + .proto(); + auto cell2Data = parse_hex("00e0e4c9b9f84f000000000000000000"); + *cell2.mutable_data() = std::string(cell2Data.begin(), cell2Data.end()); + + auto& cell3 = *input.add_cell(); + cell3.set_capacity(8210025567); + *cell3.mutable_out_point() = + OutPoint(parse_hex("09a45a15e48f985b554a0b6e5f0857913cc492ec061cc9b0b2befa4b24609a4a"), 1) + .proto(); + *cell3.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqds6ed78yze6eyfyvd537z66ur22c9mmrgz82ama")) + .proto(); + + auto privateKey1 = + parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey1.begin(), privateKey1.end())); + auto privateKey2 = + parse_hex("0c8859a9d9084a8c2b55963268b352e258756f9240f2a1f4645c610ed191dae9"); + input.add_private_key(std::string(privateKey2.begin(), privateKey2.end())); + + return input; +} + +TEST(TWAnySignerNervos, Sign_SUDT_Simple) { + auto input = getAnySignerInput3(); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNervos); + ASSERT_EQ(output.error(), Common::Proto::OK); + // https://explorer.nervos.org/transaction/0x9b15f2bea26b98201540d8e20e8b1c21d96dd77ad246520b405c6aabb7173371 + ASSERT_EQ(output.transaction_id(), + "0x9b15f2bea26b98201540d8e20e8b1c21d96dd77ad246520b405c6aabb7173371"); + ASSERT_EQ( + output.transaction_json(), + "{\"cell_deps\":[{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c\"}},{\"dep_type\":" + "\"code\",\"out_point\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0xc7813f6a415144643970c2e88e0bb6ca6a8edc5dd7c1022746f628284a9936d5\"}}],\"header_deps\":" + "[],\"inputs\":[{\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0xe118bd11a73d381daf288381ce182d92b6cf2f52d25886bbda9e1a61525c7c4a\"},\"since\":\"0x0\"}" + ",{\"previous_output\":{\"index\":\"0x1\",\"tx_hash\":" + "\"0x09a45a15e48f985b554a0b6e5f0857913cc492ec061cc9b0b2befa4b24609a4a\"},\"since\":\"0x0\"}" + ",{\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x5b12911e7413e011f251c1fb5fae4e76fd5fcae4f0d4c6412dcc5b0bfcece823\"},\"since\":\"0x0\"}" + "],\"outputs\":[{\"capacity\":\"0x35a4e9000\",\"lock\":{\"args\":" + "\"0xab201f55b02f53b385f79b34dfad548e549b48fc\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":" + "\"type\"},\"type\":{\"args\":" + "\"0x9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8\",\"code_hash\":" + "\"0x5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5\",\"hash_type\":" + "\"type\"}},{\"capacity\":\"0x35a4e9000\",\"lock\":{\"args\":" + "\"0xb0d65be39059d6489231b48f85ad706a560bbd8d\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":" + "\"type\"},\"type\":{\"args\":" + "\"0x9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8\",\"code_hash\":" + "\"0x5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5\",\"hash_type\":" + "\"type\"}},{\"capacity\":\"0x1e95b03db\",\"lock\":{\"args\":" + "\"0xb0d65be39059d6489231b48f85ad706a560bbd8d\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":" + "\"type\"},\"type\":null}],\"outputs_data\":[\"0x0080c6a47e8d03000000000000000000\"," + "\"0x00601e253b6b4c000000000000000000\",\"0x\"],\"version\":\"0x0\",\"witnesses\":[" + "\"0x550000001000000055000000550000004100000035d55fd46316f248552eb6af7ac9589c9ec533c4e5b718" + "96b05cdf697e2d18551ceff54d7b860ebb2f4dd5f6c5bb4af1da15460a7621f5aa4bc7d5585a0504de00\"," + "\"0x5500000010000000550000005500000041000000eaa4bf69126d3016ab786610f2f0668b2ef353915d623d" + "0b0184fc25cec3dcad6dc08a1504a2d7dd9faced17b041d79d4c21f1977e57859713360f5e3609583501\"," + "\"0x\"]}"); +} + +Proto::SigningInput getAnySignerInput4() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_sudt_transfer(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqdtyq04tvp02" + "wectaumxn0664yw2jd53lqk4mxg3"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqds6ed78" + "yze6eyfyvd537z66ur22c9mmrgz82ama"); + operation.set_use_max_amount(true); + input.set_byte_fee(1); + auto sudtAddress = + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8"); + *operation.mutable_sudt_address() = std::string(sudtAddress.begin(), sudtAddress.end()); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(8210026306); + *cell1.mutable_out_point() = + OutPoint(parse_hex("430cb60ee816e2631d6d9605659c18fec8eb3de94526f5fd4ad51feaad6f1664"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(14400000000); + *cell2.mutable_out_point() = + OutPoint(parse_hex("378b6bd2f7fc2b1599ee55be7e8fa17fdd6e0d25e2e146d5f46006e0292d6564"), 0) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + *cell2.mutable_type() = + Script(parse_hex("5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5"), + HashType::Type1, + parse_hex("9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8")) + .proto(); + auto cell2Data = parse_hex("00601e253b6b4c000000000000000000"); + *cell2.mutable_data() = std::string(cell2Data.begin(), cell2Data.end()); + + auto privateKey = parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey.begin(), privateKey.end())); + + return input; +} + +TEST(TWAnySignerNervos, Sign_SUDT_SendMaximum) { + auto input = getAnySignerInput4(); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNervos); + ASSERT_EQ(output.error(), Common::Proto::OK); + // https://explorer.nervos.org/transaction/0x09a45a15e48f985b554a0b6e5f0857913cc492ec061cc9b0b2befa4b24609a4a + ASSERT_EQ(output.transaction_id(), + "0x09a45a15e48f985b554a0b6e5f0857913cc492ec061cc9b0b2befa4b24609a4a"); + ASSERT_EQ(output.transaction_json(), + "{\"cell_deps\":[{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x0\",\"tx_" + "hash\":\"0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c\"}},{" + "\"dep_type\":\"code\",\"out_point\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0xc7813f6a415144643970c2e88e0bb6ca6a8edc5dd7c1022746f628284a9936d5\"}}],\"header_" + "deps\":[],\"inputs\":[{\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x378b6bd2f7fc2b1599ee55be7e8fa17fdd6e0d25e2e146d5f46006e0292d6564\"},\"since\":" + "\"0x0\"},{\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x430cb60ee816e2631d6d9605659c18fec8eb3de94526f5fd4ad51feaad6f1664\"},\"since\":" + "\"0x0\"}],\"outputs\":[{\"capacity\":\"0x35a4e9000\",\"lock\":{\"args\":" + "\"0xab201f55b02f53b385f79b34dfad548e549b48fc\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_" + "type\":\"type\"},\"type\":{\"args\":" + "\"0x9657b32fcdc463e13ec9205914fd91c443822a949937ae94add9869e7f2e1de8\",\"code_" + "hash\":\"0x5e7a36a77e68eecc013dfa2fe6a23f3b6c344b04005808694ae6dd45eea4cfd5\"," + "\"hash_type\":\"type\"}},{\"capacity\":\"0x1e95b0c5f\",\"lock\":{\"args\":" + "\"0xb0d65be39059d6489231b48f85ad706a560bbd8d\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_" + "type\":\"type\"},\"type\":null}],\"outputs_data\":[" + "\"0x00601e253b6b4c000000000000000000\",\"0x\"],\"version\":\"0x0\",\"witnesses\":[" + "\"0x5500000010000000550000005500000041000000da7c908bdf2cb091b7ff9bb682b762d1323c5e1e" + "cf9b2ce0ebedb9d55f6625c52ab14910ae401833112f2ea516ab11bc9ef691c3dff7886e3238c9348c3d" + "73a701\",\"0x\"]}"); +} + +Proto::SigningInput getAnySignerInput5() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_dao_deposit(); + + operation.set_to_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8f" + "urras980hksatlslfaktks7epf25"); + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9e" + "rg8furras980hksatlslfaktks7epf25"); + operation.set_amount(10200000000); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(8210021909); + *cell1.mutable_out_point() = + OutPoint(parse_hex("c7dacd4aab49f5f9643e87752428cebde38eeb49c7726781c4d8b526822004a1"), 1) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqds6ed78yze6eyfyvd537z66ur22c9mmrgz82ama")) + .proto(); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(14399998167); + *cell2.mutable_out_point() = + OutPoint(parse_hex("d3c3263170815b326779e2fd8d548f846ae13eff9d9a82833c7071069a1d32bf"), 0) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto privateKey1 = + parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey1.begin(), privateKey1.end())); + auto privateKey2 = + parse_hex("0c8859a9d9084a8c2b55963268b352e258756f9240f2a1f4645c610ed191dae9"); + input.add_private_key(std::string(privateKey2.begin(), privateKey2.end())); + + return input; +} + +TEST(TWAnySignerNervos, Sign_DAO_Deposit) { + auto input = getAnySignerInput5(); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNervos); + ASSERT_EQ(output.error(), Common::Proto::OK); + // https://explorer.nervos.org/transaction/0x583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683 + ASSERT_EQ(output.transaction_id(), + "0x583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683"); + ASSERT_EQ( + output.transaction_json(), + "{\"cell_deps\":[{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c\"}},{\"dep_type\":" + "\"code\",\"out_point\":{\"index\":\"0x2\",\"tx_hash\":" + "\"0xe2fb199810d49a4d8beec56718ba2593b665db9d52299a0f9e6e75416d73ff5c\"}}],\"header_deps\":" + "[],\"inputs\":[{\"previous_output\":{\"index\":\"0x1\",\"tx_hash\":" + "\"0xc7dacd4aab49f5f9643e87752428cebde38eeb49c7726781c4d8b526822004a1\"},\"since\":\"0x0\"}" + ",{\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0xd3c3263170815b326779e2fd8d548f846ae13eff9d9a82833c7071069a1d32bf\"},\"since\":\"0x0\"}" + "],\"outputs\":[{\"capacity\":\"0x25ff7a600\",\"lock\":{\"args\":" + "\"0xc4b50c5c8d074f063ec0a77ded0eaff0fa7b65da\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":" + "\"type\"},\"type\":{\"args\":\"0x\",\"code_hash\":" + "\"0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e\",\"hash_type\":" + "\"type\"}},{\"capacity\":\"0x2e3b1de31\",\"lock\":{\"args\":" + "\"0xc4b50c5c8d074f063ec0a77ded0eaff0fa7b65da\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":" + "\"type\"},\"type\":null}],\"outputs_data\":[\"0x0000000000000000\",\"0x\"],\"version\":" + "\"0x0\",\"witnesses\":[" + "\"0x5500000010000000550000005500000041000000305d09c7de3f34a4d53bc4e0031ee59c95b9abc4fc3ff5" + "548e1a17ca726c069a232012c9c4be6ec4d4ffbe88613ca5e686e3e4b7d0b9bbd7038003e23ffdcdd601\"," + "\"0x55000000100000005500000055000000410000007c514c77482dd1e1086f41a6d17364c9b5ed16364d61df" + "6f7fd8540f8bf7c131275c877943786b1b72fbf4f9d817ee5dd554a689808b7919543c691b5068e5be01\"]}"); +} + +Proto::SigningInput getAnySignerInput6() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_dao_withdraw_phase1(); + + operation.set_change_address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9e" + "rg8furras980hksatlslfaktks7epf25"); + auto& depositCell = *operation.mutable_deposit_cell(); + depositCell.set_capacity(10200000000); + *depositCell.mutable_out_point() = + OutPoint(parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683"), 0) + .proto(); + *depositCell.mutable_lock() = + Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras9" + "80hksatlslfaktks7epf25")) + .proto(); + *depositCell.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto depositCellData = parse_hex("0000000000000000"); + *depositCell.mutable_data() = std::string(depositCellData.begin(), depositCellData.end()); + depositCell.set_block_number(7575466); + auto blockHashData = + parse_hex("3dfdb4b702a355a5593315016f8af0537d5a2f3292811b79420ded78a092be6a"); + *depositCell.mutable_block_hash() = std::string(blockHashData.begin(), blockHashData.end()); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(10200000000); + *cell1.mutable_out_point() = + OutPoint(parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + *cell1.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto cell1Data = parse_hex("0000000000000000"); + *cell1.mutable_data() = std::string(cell1Data.begin(), cell1Data.end()); + + auto& cell2 = *input.add_cell(); + cell2.set_capacity(12410019377); + *cell2.mutable_out_point() = + OutPoint(parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683"), 1) + .proto(); + *cell2.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + + auto privateKey1 = + parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey1.begin(), privateKey1.end())); + + return input; +} + +TEST(TWAnySignerNervos, Sign_DAO_Withdraw_Phase1) { + auto input = getAnySignerInput6(); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNervos); + ASSERT_EQ(output.error(), Common::Proto::OK); + // https://explorer.nervos.org/transaction/0xb4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca + ASSERT_EQ(output.transaction_id(), + "0xb4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca"); + ASSERT_EQ(output.transaction_json(), + "{\"cell_deps\":[{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x0\",\"tx_" + "hash\":\"0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c\"}},{" + "\"dep_type\":\"code\",\"out_point\":{\"index\":\"0x2\",\"tx_hash\":" + "\"0xe2fb199810d49a4d8beec56718ba2593b665db9d52299a0f9e6e75416d73ff5c\"}}],\"header_" + "deps\":[\"0x3dfdb4b702a355a5593315016f8af0537d5a2f3292811b79420ded78a092be6a\"]," + "\"inputs\":[{\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683\"},\"since\":" + "\"0x0\"},{\"previous_output\":{\"index\":\"0x1\",\"tx_hash\":" + "\"0x583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683\"},\"since\":" + "\"0x0\"}],\"outputs\":[{\"capacity\":\"0x25ff7a600\",\"lock\":{\"args\":" + "\"0xc4b50c5c8d074f063ec0a77ded0eaff0fa7b65da\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_" + "type\":\"type\"},\"type\":{\"args\":\"0x\",\"code_hash\":" + "\"0x82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e\",\"hash_" + "type\":\"type\"}},{\"capacity\":\"0x2e3b1db56\",\"lock\":{\"args\":" + "\"0xc4b50c5c8d074f063ec0a77ded0eaff0fa7b65da\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_" + "type\":\"type\"},\"type\":null}],\"outputs_data\":[\"0xaa97730000000000\",\"0x\"]," + "\"version\":\"0x0\",\"witnesses\":[" + "\"0x5500000010000000550000005500000041000000d5131c1a6b8eca11e2c280b72c5db09ea00bb788" + "fd3262eaced861c39db2aad04a36f9d174b6f167a9c98b85d2bccf537a163c44459d23467dfa86408f48" + "dd5f01\",\"0x\"]}"); +} + +Proto::SigningInput getAnySignerInput7() { + auto input = Proto::SigningInput(); + auto& operation = *input.mutable_dao_withdraw_phase2(); + + auto& depositCell = *operation.mutable_deposit_cell(); + depositCell.set_capacity(10200000000); + *depositCell.mutable_out_point() = + OutPoint(parse_hex("583d77a037e86155b7ab79ac59fc9bb01640e2427e859467ea10c4a6f222b683"), 0) + .proto(); + *depositCell.mutable_lock() = + Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras9" + "80hksatlslfaktks7epf25")) + .proto(); + *depositCell.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto depositCellData = parse_hex("0000000000000000"); + *depositCell.mutable_data() = std::string(depositCellData.begin(), depositCellData.end()); + depositCell.set_block_number(7575466); + auto blockHashData1 = + parse_hex("3dfdb4b702a355a5593315016f8af0537d5a2f3292811b79420ded78a092be6a"); + *depositCell.mutable_block_hash() = std::string(blockHashData1.begin(), blockHashData1.end()); + + auto& withdrawingCell = *operation.mutable_withdrawing_cell(); + withdrawingCell.set_capacity(10200000000); + *withdrawingCell.mutable_out_point() = + OutPoint(parse_hex("b4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca"), 0) + .proto(); + *withdrawingCell.mutable_lock() = + Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras9" + "80hksatlslfaktks7epf25")) + .proto(); + *withdrawingCell.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto withdrawingCellData = parse_hex("aa97730000000000"); + *withdrawingCell.mutable_data() = + std::string(withdrawingCellData.begin(), withdrawingCellData.end()); + withdrawingCell.set_block_number(7575534); + auto blockHashData2 = + parse_hex("b070d5364afd47c23fe267077d454009d6f665f200a915e68af1616e46f4aa52"); + *withdrawingCell.mutable_block_hash() = + std::string(blockHashData2.begin(), blockHashData2.end()); + withdrawingCell.set_since(0x20037c0000001731); + + operation.set_amount(10200000000); + input.set_byte_fee(1); + + auto& cell1 = *input.add_cell(); + cell1.set_capacity(10200000000); + *cell1.mutable_out_point() = + OutPoint(parse_hex("b4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca"), 0) + .proto(); + *cell1.mutable_lock() = Script(Address("ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50x" + "wsqwyk5x9erg8furras980hksatlslfaktks7epf25")) + .proto(); + *cell1.mutable_type() = + Script(parse_hex("82d76d1b75fe2fd9a27dfbaa65a039221a380d76c926f378d3f81cf3e7e13f2e"), + HashType::Type1, Data()) + .proto(); + auto cell1Data = parse_hex("aa97730000000000"); + *cell1.mutable_data() = std::string(cell1Data.begin(), cell1Data.end()); + + auto privateKey1 = + parse_hex("8a2a726c44e46d1efaa0f9c2a8efed932f0e96d6050b914fde762ee285e61feb"); + input.add_private_key(std::string(privateKey1.begin(), privateKey1.end())); + + return input; +} + +TEST(TWAnySignerNervos, Sign_DAO_Withdraw_Phase2) { + auto input = getAnySignerInput7(); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNervos); + ASSERT_EQ(output.error(), Common::Proto::OK); + ASSERT_EQ(output.transaction_id(), + "0x4fde13c93fc5d24ab7f660070aaa0b1725809d585f6258605e595cdbd856ea1c"); + ASSERT_EQ( + output.transaction_json(), + "{\"cell_deps\":[{\"dep_type\":\"dep_group\",\"out_point\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0x71a7ba8fc96349fea0ed3a5c47992e3b4084b031a42264a018e0072e8172e46c\"}},{\"dep_type\":" + "\"code\",\"out_point\":{\"index\":\"0x2\",\"tx_hash\":" + "\"0xe2fb199810d49a4d8beec56718ba2593b665db9d52299a0f9e6e75416d73ff5c\"}}],\"header_deps\":" + "[\"0x3dfdb4b702a355a5593315016f8af0537d5a2f3292811b79420ded78a092be6a\"," + "\"0xb070d5364afd47c23fe267077d454009d6f665f200a915e68af1616e46f4aa52\"],\"inputs\":[{" + "\"previous_output\":{\"index\":\"0x0\",\"tx_hash\":" + "\"0xb4e62bc5f5108275b0ef3da8f8cc3fb0172843c4a2a9cdfef3b04d6c65e9acca\"},\"since\":" + "\"0x20037c0000001731\"}],\"outputs\":[{\"capacity\":\"0x25ff7a42c\",\"lock\":{\"args\":" + "\"0xc4b50c5c8d074f063ec0a77ded0eaff0fa7b65da\",\"code_hash\":" + "\"0x9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8\",\"hash_type\":" + "\"type\"},\"type\":null}],\"outputs_data\":[\"0x\"],\"version\":\"0x0\",\"witnesses\":[" + "\"0x6100000010000000550000006100000041000000743f86c5557f4e2d3327f4d17e7bad27209b29c1e9cdba" + "b42ab03f7094af917b4b203ddd7f2e87102e09ae579f2fe7f6adb7900b7386b58c1183ba0011b7c42100080000" + "000000000000000000\"]}"); +} + +} // namespace TW::Nervos::tests \ No newline at end of file diff --git a/tests/chains/Nervos/TWCoinTypeTests.cpp b/tests/chains/Nervos/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..1150536151a --- /dev/null +++ b/tests/chains/Nervos/TWCoinTypeTests.cpp @@ -0,0 +1,35 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWNervosCoinType, TWCoinType) { + const auto coin = TWCoinTypeNervos; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "nervos"); + assertStringsEqual(name, "Nervos"); + assertStringsEqual(symbol, "CKB"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 8); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainNervos); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(txUrl, "https://explorer.nervos.org/transaction/t123"); + assertStringsEqual(accUrl, "https://explorer.nervos.org/address/a12"); +} diff --git a/tests/chains/Nervos/TWNervosAddressTests.cpp b/tests/chains/Nervos/TWNervosAddressTests.cpp new file mode 100644 index 00000000000..cf7a8560e0d --- /dev/null +++ b/tests/chains/Nervos/TWNervosAddressTests.cpp @@ -0,0 +1,30 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PrivateKey.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::Nervos::tests { + +TEST(TWNervosAddress, Create) { + const auto ckbAddress = "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqwyk5x9erg8furras980hksatlslfaktks7epf25"; + const auto addr = WRAP(TWNervosAddress, TWNervosAddressCreateWithString(STRING(ckbAddress).get())); + + const auto codeCash = WRAPD(TWNervosAddressCodeHash(addr.get())); + const auto args = WRAPD(TWNervosAddressArgs(addr.get())); + const auto hashType = WRAPS(TWNervosAddressHashType(addr.get())); + + EXPECT_TRUE(TWNervosAddressIsValidString(STRING(ckbAddress).get())); + assertHexEqual(codeCash, "9bd7e06f3ecf4be0f2fcd2188b23f1b9fcc88e5d4b65a8637b17723bbda3cce8"); + assertHexEqual(args, "c4b50c5c8d074f063ec0a77ded0eaff0fa7b65da"); + assertStringsEqual(hashType, "type"); +} + +} // namespace TW::Nervos::tests diff --git a/tests/chains/Nimiq/AddressTests.cpp b/tests/chains/Nimiq/AddressTests.cpp new file mode 100644 index 00000000000..b25fda815cb --- /dev/null +++ b/tests/chains/Nimiq/AddressTests.cpp @@ -0,0 +1,57 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Nimiq/Address.h" +#include "Nimiq/Signer.h" + +#include +#include + +using namespace TW; + +namespace TW::Nimiq::tests { + +TEST(NimiqAddress, IsValid) { + // No address + ASSERT_FALSE(Address::isValid("")); + // Invalid country code + ASSERT_FALSE(Address::isValid("DE86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA")); + // Invalid checksum + ASSERT_FALSE(Address::isValid("NQ42 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA")); + // Too short + ASSERT_FALSE(Address::isValid("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0ML")); + // Too long + ASSERT_FALSE(Address::isValid("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA 0MLA")); + // Valid, without spaces + ASSERT_TRUE(Address::isValid("NQ862H8FYGU5RM77QSN9LYLHC56ACYYR0MLA")); + // Valid, normal format + ASSERT_TRUE(Address::isValid("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA")); +} + +TEST(NimiqAddress, String) { + // Address to string + ASSERT_EQ( + Address(parse_hex("5b3e9e5f32b89abafc3708765dc8f00216cefbb1")).string(), + "NQ61 BCY9 UPRJ P2DB MY1P 11T5 TJ7G 08BC VXVH"); + // Without spaces + ASSERT_EQ( + Address("NQ862H8FYGU5RM77QSN9LYLHC56ACYYR0MLA").string(), + "NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA"); + // With spaces + ASSERT_EQ( + Address("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA").string(), + "NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA"); +} + +TEST(NimiqAddress, FromPublicKey) { + const auto publicKey = Signer::publicKeyFromBytes( + parse_hex("70c7492aaa9c9ac7a05bc0d9c5db2dae9372029654f71f0c7f95deed5099b702")); + const auto address = Address(publicKey); + ASSERT_EQ(address.string(), "NQ27 GBAY EVHP HK5X 6JHV JGFJ 5M3H BF4Y G7GD"); +} + +} // namespace TW::Nimiq::tests diff --git a/tests/Nimiq/SignerTests.cpp b/tests/chains/Nimiq/SignerTests.cpp similarity index 100% rename from tests/Nimiq/SignerTests.cpp rename to tests/chains/Nimiq/SignerTests.cpp diff --git a/tests/chains/Nimiq/TWAnySignerTests.cpp b/tests/chains/Nimiq/TWAnySignerTests.cpp new file mode 100644 index 00000000000..b9a9ee32b24 --- /dev/null +++ b/tests/chains/Nimiq/TWAnySignerTests.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Nimiq.pb.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::Nimiq::tests { + +TEST(TWAnySignerNimiq, Sign) { + auto privateKey = parse_hex("e3cc33575834add098f8487123cd4bca543ee859b3e8cfe624e7e6a97202b756"); + + Proto::SigningInput input; + + input.set_destination("NQ86 2H8F YGU5 RM77 QSN9 LYLH C56A CYYR 0MLA"); + input.set_fee(1000); + input.set_value(42042042); + input.set_validity_start_height(314159); + input.set_private_key(privateKey.data(), privateKey.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeNimiq); + + EXPECT_EQ(hex(output.encoded()), "0070c7492aaa9c9ac7a05bc0d9c5db2dae9372029654f71f0c7f95deed5099b7021450ffc385cd4e7c6ac9a7e91614ca67ff90568a00000000028182ba00000000000003e80004cb2f2a74dc7f6e0ab58a0bf52cc6e8801b0cca132dd4229d9a3e3a3d2f90e4d8f045d981b771bf5fc3851a98f3c617b1a943228f963e910e061808a721cfa0e3cad50b"); +} + +} // namespace TW::Nimiq::tests diff --git a/tests/chains/Nimiq/TWCoinTypeTests.cpp b/tests/chains/Nimiq/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..f973c71604e --- /dev/null +++ b/tests/chains/Nimiq/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWNimiqCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeNimiq)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeNimiq, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeNimiq, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeNimiq)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeNimiq)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeNimiq), 5); + ASSERT_EQ(TWBlockchainNimiq, TWCoinTypeBlockchain(TWCoinTypeNimiq)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeNimiq)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeNimiq)); + assertStringsEqual(symbol, "NIM"); + assertStringsEqual(txUrl, "https://nimiq.watch/#t123"); + assertStringsEqual(accUrl, "https://nimiq.watch/#a12"); + assertStringsEqual(id, "nimiq"); + assertStringsEqual(name, "Nimiq"); +} diff --git a/tests/Nimiq/TransactionTests.cpp b/tests/chains/Nimiq/TransactionTests.cpp similarity index 100% rename from tests/Nimiq/TransactionTests.cpp rename to tests/chains/Nimiq/TransactionTests.cpp diff --git a/tests/chains/OKXChain/TWCoinTypeTests.cpp b/tests/chains/OKXChain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..af4eed1e128 --- /dev/null +++ b/tests/chains/OKXChain/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWCoinTypeOKXChain, TWCoinType) { + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOKXChain)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x46C3A947E8248570FBD28E4FE456CC8F80DFD90716533878FB67857B95FA3D37")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeOKXChain, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x074faafd0b20fad2efa115b8ed7e75993e580b85")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeOKXChain, accId.get())); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeOKXChain)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeOKXChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeOKXChain), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeOKXChain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeOKXChain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeOKXChain)); + assertStringsEqual(symbol, "OKT"); + assertStringsEqual(txUrl, "https://www.oklink.com/en/okc/tx/0x46C3A947E8248570FBD28E4FE456CC8F80DFD90716533878FB67857B95FA3D37"); + assertStringsEqual(accUrl, "https://www.oklink.com/en/okc/address/0x074faafd0b20fad2efa115b8ed7e75993e580b85"); + assertStringsEqual(id, "okc"); + assertStringsEqual(name, "OKX Chain"); +} diff --git a/tests/chains/Oasis/AddressTests.cpp b/tests/chains/Oasis/AddressTests.cpp new file mode 100644 index 00000000000..ce1436525f5 --- /dev/null +++ b/tests/chains/Oasis/AddressTests.cpp @@ -0,0 +1,77 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Oasis/Address.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include +#include + +using namespace TW; + +namespace TW::Oasis::tests { + +TEST(OasisAddress, Valid) { + ASSERT_TRUE(Address::isValid("oasis1qp0cnmkjl22gky6p6qeghjytt4v7dkxsrsmueweh")); +} + +TEST(OasisAddress, Invalid) { + ASSERT_FALSE(Address::isValid("oasis1qp0cnmkjl22gky6p6qeghjytt4v7dkxsrsmuewehj")); + ASSERT_FALSE(Address::isValid("oasi1qp0cnmkjl22gky6p6qeghjytt4v7dkxsrsmueweh")); +} + +TEST(OasisAddress, ForceInvalid) { + try { + auto addressString = "oasis1qp0cnmkjl22gky6p6qeghjytt4v7dkxsrsmuewehj"; + auto address = Address(addressString); + } catch (std::invalid_argument& e1) { + return; + } + FAIL() << "This test should generate an exception as it an invalid address"; +} + +TEST(OasisAddress, FromWrongData) { + try { + auto dataString = "asdadfasdfsdfwrwrsadasdasdsad"; + auto address = Address(data(dataString)); + } catch (std::invalid_argument& e1) { + return; + } + FAIL() << "This test should generate an exception as it an invalid data"; +} + +TEST(OasisAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("4f8b5676990b00e23d9904a92deb8d8f428ff289c8939926358f1d20537c21a0")); + auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(address.string(), "oasis1qzawzy5kaa2xgphenf3r0f5enpr3mx5dps559yxm"); +} + +TEST(OasisAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("aba52c0dcb80c2fe96ed4c3741af40c573a0500c0d73acda22795c37cb0f1739"), TWPublicKeyTypeED25519); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "oasis1qphdkldpttpsj2j3l9sde9h26cwpfwqwwuhvruyu"); +} + +TEST(OasisAddress, WrongPublicKeyType) { + try { + auto publicKey = PublicKey(parse_hex("aba52c0dcb80c2fe96ed4c3741af40c573a0500c0d73acda22795c37cb0f1739"), TWPublicKeyTypeED25519Cardano); + auto address = Address(publicKey); + } catch (std::invalid_argument& e1) { + return; + } + FAIL() << "TWPublicKeyTypeED25519Cardano should generate an exception as it an invalid publicKey type"; +} + +TEST(OasisAddress, FromString) { + Address address; + ASSERT_TRUE(Address::decode("oasis1hts399h023jqd7v6vgm6dxvcguwe4rgvqqgvq38n", address)); + ASSERT_EQ(address.string(), "oasis1hts399h023jqd7v6vgm6dxvcguwe4rgvqqgvq38n"); + + ASSERT_FALSE(Address::decode("oasis1hts399h023jqd7v6vgm6dxvcguwe4rgvqqgvq38ng", address)); +} + +} // namespace TW::Oasis::tests diff --git a/tests/chains/Oasis/SignerTests.cpp b/tests/chains/Oasis/SignerTests.cpp new file mode 100644 index 00000000000..b4cc6ee2a6b --- /dev/null +++ b/tests/chains/Oasis/SignerTests.cpp @@ -0,0 +1,40 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Oasis/Address.h" +#include "Oasis/Signer.h" +#include "PrivateKey.h" +#include "PublicKey.h" + +#include + +using namespace TW; + +namespace TW::Oasis::tests { + +TEST(OasisSigner, Sign) { + auto input = Proto::SigningInput(); + auto& transfer = *input.mutable_transfer(); + + transfer.set_gas_price(0); + transfer.set_gas_amount("0"); + transfer.set_nonce(0); + transfer.set_to("oasis1qrrnesqpgc6rfy2m50eew5d7klqfqk69avhv4ak5"); + transfer.set_amount("10000000"); + + // The use of this context thing is explained here --> https://docs.oasis.dev/oasis-core/common-functionality/crypto#domain-separation + transfer.set_context("oasis-core/consensus: tx for chain a245619497e580dd3bc1aa3256c07f68b8dcc13f92da115eadc3b231b083d3c4"); + + auto key = parse_hex("4f8b5676990b00e23d9904a92deb8d8f428ff289c8939926358f1d20537c21a0"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output = Signer::sign(input); + + ASSERT_EQ(hex(output.encoded()), "a2697369676e6174757265a2697369676e617475726558406e51c18c9b2015c9b49414b3307336597f51ff331873d214ce2db81c9651a34d99529ccaa294a39ccd01c6b0bc2c2239d87c624e5ba4840cf99ac8f9283e240c6a7075626c69635f6b6579582093d8f8a455f50527976a8aa87ebde38d5606efa86cb985d3fb466aff37000e3b73756e747275737465645f7261775f76616c7565585ea463666565a2636761730066616d6f756e74410064626f6479a262746f5500c73cc001463434915ba3f39751beb7c0905b45eb66616d6f756e744400989680656e6f6e636500666d6574686f64707374616b696e672e5472616e73666572"); +} + +} // namespace TW::Oasis::tests diff --git a/tests/chains/Oasis/TWAnySignerTests.cpp b/tests/chains/Oasis/TWAnySignerTests.cpp new file mode 100644 index 00000000000..d390b051db5 --- /dev/null +++ b/tests/chains/Oasis/TWAnySignerTests.cpp @@ -0,0 +1,38 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "HexCoding.h" +#include "proto/Oasis.pb.h" + +#include "TestUtilities.h" +#include + +using namespace TW; +namespace TW::Oasis::tests { + +TEST(TWAnySignerOasis, Sign) { + auto input = Proto::SigningInput(); + auto output = Proto::SigningOutput(); + auto& transfer = *input.mutable_transfer(); + + transfer.set_gas_price(0); + transfer.set_gas_amount("0"); + transfer.set_nonce(0); + transfer.set_to("oasis1qrrnesqpgc6rfy2m50eew5d7klqfqk69avhv4ak5"); + transfer.set_amount("10000000"); + transfer.set_context("oasis-core/consensus: tx for chain a245619497e580dd3bc1aa3256c07f68b8dcc13f92da115eadc3b231b083d3c4"); + + auto key = parse_hex("4f8b5676990b00e23d9904a92deb8d8f428ff289c8939926358f1d20537c21a0"); + input.set_private_key(key.data(), key.size()); + + ANY_SIGN(input, TWCoinTypeOasis); + + EXPECT_EQ("a2697369676e6174757265a2697369676e617475726558406e51c18c9b2015c9b49414b3307336597f51ff331873d214ce2db81c9651a34d99529ccaa294a39ccd01c6b0bc2c2239d87c624e5ba4840cf99ac8f9283e240c6a7075626c69635f6b6579582093d8f8a455f50527976a8aa87ebde38d5606efa86cb985d3fb466aff37000e3b73756e747275737465645f7261775f76616c7565585ea463666565a2636761730066616d6f756e74410064626f6479a262746f5500c73cc001463434915ba3f39751beb7c0905b45eb66616d6f756e744400989680656e6f6e636500666d6574686f64707374616b696e672e5472616e73666572", + hex(output.encoded())); +} + +} // namespace TW::Oasis::tests diff --git a/tests/chains/Oasis/TWCoinTypeTests.cpp b/tests/chains/Oasis/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..86ff3764832 --- /dev/null +++ b/tests/chains/Oasis/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWOasisCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOasis)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0b9bd4983f1c88a1c71bf33562b6ba02b3064e01697d15a0de4bfe1922ec74b8")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeOasis, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("oasis1qrx376dmwuckmruzn9vq64n49clw72lywctvxdf4")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeOasis, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeOasis)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeOasis)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeOasis), 9); + ASSERT_EQ(TWBlockchainOasisNetwork, TWCoinTypeBlockchain(TWCoinTypeOasis)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeOasis)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeOasis)); + assertStringsEqual(symbol, "ROSE"); + assertStringsEqual(txUrl, "https://oasisscan.com/transactions/0b9bd4983f1c88a1c71bf33562b6ba02b3064e01697d15a0de4bfe1922ec74b8"); + assertStringsEqual(accUrl, "https://oasisscan.com/accounts/detail/oasis1qrx376dmwuckmruzn9vq64n49clw72lywctvxdf4"); + assertStringsEqual(id, "oasis"); + assertStringsEqual(name, "Oasis"); +} diff --git a/tests/chains/Ontology/AccountTests.cpp b/tests/chains/Ontology/AccountTests.cpp new file mode 100644 index 00000000000..1fdf49f4806 --- /dev/null +++ b/tests/chains/Ontology/AccountTests.cpp @@ -0,0 +1,28 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Hash.h" +#include "HexCoding.h" +#include "PrivateKey.h" + +#include "Ontology/Signer.h" + +#include + +using namespace TW; +namespace TW::Ontology::tests { + +TEST(OntologyAccount, validity) { + auto hexPrvKey = "4646464646464646464646464646464646464646464646464646464646464646"; + auto hexPubKey = "031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"; + auto signer = Signer(PrivateKey(parse_hex(hexPrvKey))); + auto prvKey = signer.getPrivateKey(); + auto pubKey = signer.getPublicKey(); + EXPECT_EQ(hexPrvKey, hex(prvKey.bytes)); + EXPECT_EQ(hexPubKey, hex(pubKey.bytes)); +} + +} // namespace TW::Ontology::tests diff --git a/tests/chains/Ontology/AddressTests.cpp b/tests/chains/Ontology/AddressTests.cpp new file mode 100644 index 00000000000..f6917282e9b --- /dev/null +++ b/tests/chains/Ontology/AddressTests.cpp @@ -0,0 +1,48 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PublicKey.h" + +#include "Ontology/Address.h" +#include "Ontology/Signer.h" + +#include + +namespace TW::Ontology::tests { + +TEST(OntologyAddress, validation) { + ASSERT_FALSE(Address::isValid("abc")); + ASSERT_FALSE(Address::isValid("abeb60f3e94c1b9a09f33669435e7ef12eacd")); + ASSERT_FALSE(Address::isValid("abcb60f3e94c9b9a09f33669435e7ef1beaedads")); + ASSERT_TRUE(Address::isValid("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD")); +} + +TEST(OntologyAddress, fromPubKey) { + auto address = Address( + PublicKey(parse_hex("031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486"), TWPublicKeyTypeSECP256k1)); + EXPECT_EQ("AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5", address.string()); +} + +TEST(OntologyAddress, fromString) { + auto b58Str = "AYTxeseHT5khTWhtWX1pFFP1mbQrd4q1zz"; + auto address = Address(b58Str); + EXPECT_EQ(b58Str, address.string()); + auto errB58Str = "AATxeseHT5khTWhtWX1pFFP1mbQrd4q1zz"; + ASSERT_THROW(new Address(errB58Str), std::runtime_error); +} + +TEST(OntologyAddress, fromMultiPubKeys) { + auto signer1 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); + auto signer2 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); + auto signer3 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464658"))); + std::vector pubKeys{signer1.getPublicKey().bytes, signer2.getPublicKey().bytes, signer3.getPublicKey().bytes}; + uint8_t m = 2; + auto multiAddress = Address(m, pubKeys); + EXPECT_EQ("AYGWgijVZnrUa2tRoCcydsHUXR1111DgdW", multiAddress.string()); +} + +} // namespace TW::Ontology::tests diff --git a/tests/chains/Ontology/Oep4Tests.cpp b/tests/chains/Ontology/Oep4Tests.cpp new file mode 100644 index 00000000000..be49303bd69 --- /dev/null +++ b/tests/chains/Ontology/Oep4Tests.cpp @@ -0,0 +1,140 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" + +#include "Ontology/Oep4.h" +#include "Ontology/Signer.h" +#include +#include + +namespace TW::Ontology::tests { + +TEST(OntologyOep4, name) { + std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + Oep4 wing(wing_addr); + + uint32_t nonce = 0x1234; + auto tx = wing.name(nonce); + auto rawTx = hex(tx.serialize()); + EXPECT_EQ("00d1341200000000000000000000000000000000000000000000000000000000000000000000000000001c00c1046e616d656733def739225d0f93dd2aed457d7b1fd074ec31ff0000", rawTx); +} + +TEST(OntologyOep4, symbol) { + std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + Oep4 wing(wing_addr); + + uint32_t nonce = 0x1234; + auto tx = wing.symbol(nonce); + auto rawTx = hex(tx.serialize()); + EXPECT_EQ("00d1341200000000000000000000000000000000000000000000000000000000000000000000000000001e00c10673796d626f6c6733def739225d0f93dd2aed457d7b1fd074ec31ff0000", rawTx); +} + +TEST(OntologyOep4, decimals) { + std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + Oep4 wing(wing_addr); + + uint32_t nonce = 0x1234; + auto tx = wing.decimals(nonce); + auto rawTx = hex(tx.serialize()); + EXPECT_EQ("00d1341200000000000000000000000000000000000000000000000000000000000000000000000000002000c108646563696d616c736733def739225d0f93dd2aed457d7b1fd074ec31ff0000", rawTx); +} + +TEST(OntologyOep4, totalSupply) { + std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + Oep4 wing(wing_addr); + + uint32_t nonce = 0x1234; + auto tx = wing.totalSupply(nonce); + auto rawTx = hex(tx.serialize()); + EXPECT_EQ("00d1341200000000000000000000000000000000000000000000000000000000000000000000000000002300c10b746f74616c537570706c796733def739225d0f93dd2aed457d7b1fd074ec31ff0000", rawTx); +} + +TEST(OntologyOep4, balanceOf) { + std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + Oep4 wing(wing_addr); + auto user = Address("AeaThtPwh5kAYnjHavzwmvxPd725nVTvbM"); + + uint32_t nonce = 0x1234; + auto tx = wing.balanceOf(user, nonce); + auto rawTx = hex(tx.serialize()); + EXPECT_EQ("00d1341200000000000000000000000000000000000000000000000000000000000000000000000000003614fa2254ffaee3c3e1172e8e98f800e4105c74988e51c10962616c616e63654f666733def739225d0f93dd2aed457d7b1fd074ec31ff0000", rawTx); +} + +TEST(OntologyOep4, addressHack) { + auto ownerbin = parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + auto payerbin = parse_hex("4646464646464646464646464646464646464646464646464646464646464652"); + + PrivateKey owner(ownerbin); + PrivateKey payer(payerbin); + + auto pubKey = owner.getPublicKey(TWPublicKeyTypeNIST256p1); + Address addr(pubKey); + + EXPECT_EQ("AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5", addr.string()); + + auto payerpub_key = payer.getPublicKey(TWPublicKeyTypeNIST256p1); + Address payer_addr(payerpub_key); + EXPECT_EQ("APniYDGozkhUh8Tk7pe35aah2HGJ4fJfVd", payer_addr.string()); +} + +TEST(OntologyOep4, transfer) { + auto from = Signer( + PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); + + auto payer = Signer( + PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); + + auto toAddress = Address("AVY6LfvxauVQAVHDV9hC3ZCv7cQqzfDotH"); + + uint32_t nonce = 0x1234; + uint64_t amount = 233; + uint64_t gasPrice = 2500; + uint64_t gasLimit = 50000; + + std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + Oep4 wing(wing_addr); + + auto tx = wing.transfer(from, toAddress, amount, payer, gasPrice, gasLimit, nonce); + auto rawTx = hex(tx.serialize()); + // Transaction Hex tab + // https://explorer.ont.io/testnet/tx/710266b8d497e794ecd47e01e269e4aeb6f4ff2b01eaeafc4cd371e062b13757 + EXPECT_EQ("00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e9001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff00024140bd2923854d7b84b97a107bb3cddf18c8e3dddd2f36b41a1f5f5b23366484daa22871cfb819923fe01e9cb1e9ed16baa2b05c2feb76bcbe2ec125f72701c5e965232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41406d638653597774ce45812ea2653250806b657b32b7c6ad3e027ddeba91e9a9da4bb5dacd23dafba868cb31bacb38b4a6ff2607682a426c1dc09b05a1e158d6cd2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac", rawTx); +} + +TEST(OntologyOep4, transferMainnet) { + auto from = Signer( + PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); + + auto payer = Signer( + PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); + + auto toAddress = Address("AUJJhwRNi4RsNfvuexLETxXEb6szu9D5Ad"); + + uint32_t nonce = 0x1234; + uint64_t amount = 233; + uint64_t gasPrice = 2500; + uint64_t gasLimit = 50000; + + // wing oep4 mainnet address + std::string wing_hex{"00c59fcd27a562d6397883eab1f2fff56e58ef80"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + Oep4 wing(wing_addr); + + auto tx = wing.transfer(from, toAddress, amount, payer, gasPrice, gasLimit, nonce); + auto rawTx = hex(tx.serialize()); + // Transaction Hex tab + // https://explorer.ont.io/tx/70b276aaeb6b4578237390ec339b6a196f4620bdef8df1717032d32576ccef4a + EXPECT_EQ("00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e900148962e81f62cb76068b5f204ea5425d64d57147191457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726780ef586ef5fff2b1ea837839d662a527cd9fc500000241403c3a5e738f99e8f98ac4f59e225e549e2483bb60aee1771ef8ef189255e1670825d6a4c401f2e103348877393d8355c4d295b21fdfaf3dc4fea9b0459f1e1507232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41409501ccaab299dc660da9084dd6e8f22658f7687e77319b17b97149c3f023806d04b300baa52874eae57ccde935bb64e2c16c59e00e0efe7086ae93c1153b80722321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac", rawTx); +} + +} // namespace TW::Ontology::tests diff --git a/tests/Ontology/OngTests.cpp b/tests/chains/Ontology/OngTests.cpp similarity index 97% rename from tests/Ontology/OngTests.cpp rename to tests/chains/Ontology/OngTests.cpp index afb5ba4fd67..d4b4994ca50 100644 --- a/tests/Ontology/OngTests.cpp +++ b/tests/chains/Ontology/OngTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -11,8 +11,7 @@ #include #include -using namespace TW; -using namespace TW::Ontology; +namespace TW::Ontology::tests { TEST(OntologyOng, decimals) { uint32_t nonce = 0; @@ -78,4 +77,6 @@ TEST(OntologyOng, withdraw) { "0ea6435f6f2b1335192a5d1b346fd431e8af912bfa4e1a23ad7d0ab7fc5b808655af5c9043232103d9fd62df33" "2403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", rawTx); -} \ No newline at end of file +} + +} // namespace TW::Ontology::tests diff --git a/tests/Ontology/OntTests.cpp b/tests/chains/Ontology/OntTests.cpp similarity index 96% rename from tests/Ontology/OntTests.cpp rename to tests/chains/Ontology/OntTests.cpp index e2d26a06895..20e9c3bbeb6 100644 --- a/tests/Ontology/OntTests.cpp +++ b/tests/chains/Ontology/OntTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -13,8 +13,7 @@ #include #include -using namespace TW; -using namespace TW::Ontology; +namespace TW::Ontology::tests { TEST(OntologyOnt, decimals) { uint32_t nonce = 0; @@ -57,4 +56,6 @@ TEST(OntologyOnt, transfer) { "0576d7b092fabafd0913a67ccf8b2f8e3d2bd708f768c2bb67e2d2f759805608232103d9fd62df332403" "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", rawTx); -} \ No newline at end of file +} + +} // namespace TW::Ontology::tests diff --git a/tests/chains/Ontology/ParamsBuilderTests.cpp b/tests/chains/Ontology/ParamsBuilderTests.cpp new file mode 100644 index 00000000000..0534472852d --- /dev/null +++ b/tests/chains/Ontology/ParamsBuilderTests.cpp @@ -0,0 +1,127 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PublicKey.h" + +#include "Ontology/Address.h" +#include "Ontology/Ont.h" +#include "Ontology/ParamsBuilder.h" + +#include + +namespace TW::Ontology::tests { + +TEST(ParamsBuilder, pushInt) { + std::vector numVector{0, + 1, + 2, + 127, + 128, + 129, + 65534, + 65535, + 65536, + 65537, + 4294967294, + 4294967295, + 4294967296, + 68719476735, + 68719476736, + 72057594037927935, + 1152921504606846975}; + std::vector codeVector{"00", + "51", + "52", + "017f", + "028000", + "028100", + "03feff00", + "03ffff00", + "03000001", + "03010001", + "05feffffff00", + "05ffffffff00", + "050000000001", + "05ffffffff0f", + "050000000010", + "08ffffffffffffff00", + "08ffffffffffffff0f"}; + for (auto index = 0ul; index < numVector.size(); index++) { + auto builder = ParamsBuilder(); + builder.push(numVector[index]); + EXPECT_EQ(codeVector[index], hex(builder.getBytes())); + } +} + +TEST(ParamsBuilder, balanceInvokeCode) { + auto balanceParam = Address("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD")._data; + auto invokeCode = ParamsBuilder::buildNativeInvokeCode(Ont().contractAddress(), 0x00, + "balanceOf", {balanceParam}); + auto hexInvokeCode = + "1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f661400000000000000000000000000" + "000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b65"; + EXPECT_EQ(hexInvokeCode, hex(invokeCode)); +} + +TEST(ParamsBuilder, transferInvokeCode) { + auto fromAddress = Address("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD")._data; + auto toAddress = Address("Af1n2cZHhMZumNqKgw9sfCNoTWu9de4NDn")._data; + uint64_t amount = 1; + NeoVmParamValue::ParamList transferParam{fromAddress, toAddress, amount}; + NeoVmParamValue::ParamArray args{transferParam}; + auto invokeCode = + ParamsBuilder::buildNativeInvokeCode(Ont().contractAddress(), 0x00, "transfer", {args}); + auto hexInvokeCode = + "00c66b1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b76a7cc814feec06b79ed299ea06fcb94abac41aaf3e" + "ad76586a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068" + "164f6e746f6c6f67792e4e61746976652e496e766f6b65"; + EXPECT_EQ(hexInvokeCode, hex(invokeCode)); +} + +TEST(ParamsBuilder, invokeOep4Code) { + std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + + NeoVmParamValue::ParamArray args{}; + std::string method{"name"}; + auto invokeCode = ParamsBuilder::buildOep4InvokeCode(wing_addr, method, {args}); + + auto expectCode = "00c1046e616d656733def739225d0f93dd2aed457d7b1fd074ec31ff"; + EXPECT_EQ(expectCode, hex(invokeCode)); +} + +TEST(ParamsBuilder, invokeOep4CodeBalanceOf) { + std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto user_addr = Address("AeaThtPwh5kAYnjHavzwmvxPd725nVTvbM"); + Data d(std::begin(user_addr._data), std::end(user_addr._data)); + + NeoVmParamValue::ParamArray args{d}; + std::string method{"balanceOf"}; + auto invokeCode = ParamsBuilder::buildOep4InvokeCode(wing_addr, method, {args}); + + auto expectCode = "14fa2254ffaee3c3e1172e8e98f800e4105c74988e51c10962616c616e63654f666733def739225d0f93dd2aed457d7b1fd074ec31ff"; + EXPECT_EQ(expectCode, hex(invokeCode)); +} + +TEST(OntologyOep4, invokeOep4CodeTransfer) { + std::string wing_hex{"ff31ec74d01f7b7d45ed2add930f5d2239f7de33"}; + auto wing_addr = Address(parse_hex(wing_hex.begin(), wing_hex.end())); + auto from = Address("APniYDGozkhUh8Tk7pe35aah2HGJ4fJfVd"); + auto to = Address("AVY6LfvxauVQAVHDV9hC3ZCv7cQqzfDotH"); + uint64_t amount = 253; + + NeoVmParamValue::ParamArray args{from._data, to._data, amount}; + std::reverse(args.begin(), args.end()); + std::string method{"transfer"}; + auto invokeCode = ParamsBuilder::buildOep4InvokeCode(wing_addr, method, {args}); + + auto expectCode = "02fd001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff"; + EXPECT_EQ(expectCode, hex(invokeCode)); +} + +} // namespace TW::Ontology::tests diff --git a/tests/chains/Ontology/TWAnySignerTests.cpp b/tests/chains/Ontology/TWAnySignerTests.cpp new file mode 100644 index 00000000000..99f2c8b1559 --- /dev/null +++ b/tests/chains/Ontology/TWAnySignerTests.cpp @@ -0,0 +1,240 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "TestUtilities.h" + +#include "Ontology/Oep4TxBuilder.h" +#include "Ontology/OngTxBuilder.h" +#include "Ontology/OntTxBuilder.h" + +#include + +#include + +using namespace TW; + +namespace TW::Ontology::tests { + +TEST(TWAnySingerOntology, OntBalanceOf) { + // curl -H "Content-Type: application/json" -X POST -d '{"Action":"sendrawtransaction", + // "Version":"1.0.0","00d1885602ec0000000000000000000000000000000000000000000000000000000000000000000000004d1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f661400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b650000"}' + // http://polaris2.ont.io:20334/api/v1/transaction?preExec=1 + // + // {"Action":"sendrawtransaction","Desc":"SUCCESS","Error":0,"Result":{"State":1,"Gas":20000,"Result":"00","Notify":[]},"Version":"1.0.0"} + auto input = Proto::SigningInput(); + input.set_contract("ONT"); + input.set_method("balanceOf"); + input.set_query_address("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD"); + input.set_nonce(3959576200); + auto data = OntTxBuilder::build(input); + auto rawTx = hex(data); + EXPECT_EQ("00d1885602ec000000000000000000000000000000000000000000000000000000000000000000000000" + "4d1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f6614000000000000000000" + "00000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b650000", + rawTx); +} + +TEST(TWAnySingerOntology, OntDecimals) { + // curl -H "Content-Type: application/json" -X POST -d '{"Action":"sendrawtransaction", + // "Version":"1.0.0","Data":"00d1bdc12a48000000000000000000000000000000000000000000000000000000000000000000000000380008646563696d616c731400000000000000000000000000000000000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b650000"}' + // http://polaris2.ont.io:20334/api/v1/transaction?preExec=1 + // + //{"Action":"sendrawtransaction","Desc":"SUCCESS","Error":0,"Result":{"State":1,"Gas":20000,"Result":"","Notify":[]},"Version":"1.0.0"} + auto input = Proto::SigningInput(); + input.set_contract("ONT"); + input.set_method("decimals"); + input.set_nonce(1210761661); + auto data = OntTxBuilder::build(input); + auto rawTx = hex(data); + EXPECT_EQ("00d1bdc12a48000000000000000000000000000000000000000000000000000000000000000000000000" + "380008646563696d616c731400000000000000000000000000000000000000010068164f6e746f6c6f67" + "792e4e61746976652e496e766f6b650000", + rawTx); +} + +TEST(TWAnySingerOntology, OntTransfer) { + // tx on polaris test net. + // https://explorer.ont.io/transaction/4a672ce813d3fac9042e9472cf9b470f8a5e59a2deb41fd7b23a1f7479a155d5/testnet + auto ownerPrivateKey = + parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + auto payerPrivateKey = + parse_hex("4646464646464646464646464646464646464646464646464646464646464652"); + auto input = Proto::SigningInput(); + input.set_contract("ONT"); + input.set_method("transfer"); + input.set_nonce(2338116610); + input.set_owner_private_key(ownerPrivateKey.data(), ownerPrivateKey.size()); + input.set_payer_private_key(payerPrivateKey.data(), payerPrivateKey.size()); + input.set_to_address("Af1n2cZHhMZumNqKgw9sfCNoTWu9de4NDn"); + input.set_amount(1); + input.set_gas_price(500); + input.set_gas_limit(20000); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeOntology); + + EXPECT_EQ("00d102d45c8bf401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" + "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" + "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" + "00000000010068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140301766d925382a6e" + "bb2ebeb18d3741954c9370dcf6d9c45b34ce7b18bc42dcdb7cff28ddaf7f1048822c0ca21a0c4926323a" + "2497875b963f3b8cbd3717aa6e7c2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e4" + "47125f927b7486ac414038466b25ac49a22ba8c301328ef049a61711b257987e85e25d63e0444a14e860" + "305a4cd3bb6ea2fe80fd293abb3c592e679c42c546cbf3baa051a07b28b374a6232103d9fd62df332403" + "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + hex(output.encoded())); +} + +TEST(TWAnySingerOntology, OngDecimals) { + // curl -H "Content-Type: application/json" -X POST -d '{"Action":"sendrawtransaction", + // "Version":"1.0.0","Data":"00d1e3f2e679000000000000000000000000000000000000000000000000000000000000000000000000380008646563696d616c731400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b650000"}' + // http://polaris2.ont.io:20334/api/v1/transaction?preExec=1 + // + // {"Action":"sendrawtransaction","Desc":"SUCCESS","Error":0,"Result":{"State":1,"Gas":20000,"Result":"09","Notify":[]},"Version":"1.0.0"} + auto input = Proto::SigningInput(); + input.set_contract("ONG"); + input.set_method("decimals"); + input.set_nonce(2045178595); + auto data = OngTxBuilder::build(input); + auto rawTx = hex(data); + EXPECT_EQ("00d1e3f2e679000000000000000000000000000000000000000000000000000000000000000000000000" + "380008646563696d616c731400000000000000000000000000000000000000020068164f6e746f6c6f67" + "792e4e61746976652e496e766f6b650000", + rawTx); +} + +TEST(TWAnySingerOntology, OngBalanceOf) { + // curl -H "Content-Type: application/json" -X POST -d '{"Action":"sendrawtransaction", + // "Version":"1.0.0","Data":"00d1ab1ad0cf0000000000000000000000000000000000000000000000000000000000000000000000004d1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f661400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b650000"}' + // http://polaris2.ont.io:20334/api/v1/transaction?preExec=1 + // + //{"Action":"sendrawtransaction","Desc":"SUCCESS","Error":0,"Result":{"State":1,"Gas":20000,"Result":"27e74d240609","Notify":[]},"Version":"1.0.0"} + auto input = Proto::SigningInput(); + input.set_contract("ONG"); + input.set_method("balanceOf"); + input.set_query_address("ANDfjwrUroaVtvBguDtrWKRMyxFwvVwnZD"); + input.set_nonce(3486522027); + auto data = OngTxBuilder::build(input); + auto rawTx = hex(data); + EXPECT_EQ("00d1ab1ad0cf000000000000000000000000000000000000000000000000000000000000000000000000" + "4d1446b1a18af6b7c9f8a4602f9f73eeb3030f0c29b70962616c616e63654f6614000000000000000000" + "00000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b650000", + rawTx); +} + +TEST(TWAnySingerOntology, OngTransfer) { + // tx on polaris test net. + // https://explorer.ont.io/transaction/8a1e59396dcb72d9095088f50d1023294bf9c7b79ba693bd641578f748cbd4e6/testnet + auto ownerPrivateKey = + parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + auto payerPrivateKey = + parse_hex("4646464646464646464646464646464646464646464646464646464646464652"); + auto input = Proto::SigningInput(); + input.set_contract("ONG"); + input.set_method("transfer"); + input.set_owner_private_key(ownerPrivateKey.data(), ownerPrivateKey.size()); + input.set_payer_private_key(payerPrivateKey.data(), payerPrivateKey.size()); + input.set_to_address("Af1n2cZHhMZumNqKgw9sfCNoTWu9de4NDn"); + input.set_amount(1); + input.set_gas_price(500); + input.set_gas_limit(20000); + input.set_nonce(2827104669); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeOntology); + + EXPECT_EQ("00d19d3182a8f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df6" + "7100c66b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc814feec06b79ed299ea06fcb94aba" + "c41aaf3ead76586a7cc8516a7cc86c51c1087472616e7366657214000000000000000000000000000000" + "00000000020068164f6e746f6c6f67792e4e61746976652e496e766f6b6500024140e27e935b87855efa" + "d62bb76b21c7b591f445f867eff86f888ca6ee1870ecd80f73b8ab199a4d757b4c7b9ed46c4ff8cfa8ae" + "faa90b7fb6485e358034448cba752321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e4" + "47125f927b7486ac4140450047b2efb384129a16ec4c707790e9379b978cc7085170071d8d7c5c037d74" + "3b078bd4e21bb4404c0182a32ee05260e22454dffb34dacccf458dfbee6d32db232103d9fd62df332403" + "d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + hex(output.encoded())); +} + +TEST(TWAnySingerOntology, OngWithdraw) { + // tx on polaris test net. + // https://explorer.ont.io/transaction/433cb7ed4dec32d55be0db104aaa7ade4c7dbe0f62ef94f7b17829f7ac7cd75b/testnet + auto ownerPrivateKey = + parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + auto payerPrivateKey = + parse_hex("4646464646464646464646464646464646464646464646464646464646464652"); + auto input = Proto::SigningInput(); + input.set_contract("ONG"); + input.set_method("withdraw"); + input.set_owner_private_key(ownerPrivateKey.data(), ownerPrivateKey.size()); + input.set_payer_private_key(payerPrivateKey.data(), payerPrivateKey.size()); + input.set_to_address("AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5"); + input.set_amount(1); + input.set_gas_price(500); + input.set_gas_limit(20000); + input.set_nonce(3784713724); + auto data = OngTxBuilder::build(input); + auto rawTx = hex(data); + EXPECT_EQ( + "00d1fc2596e1f401000000000000204e00000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df68b00c6" + "6b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc81400000000000000000000000000000000000000" + "016a7cc814fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc8516a7cc86c0c7472616e7366657246726f" + "6d1400000000000000000000000000000000000000020068164f6e746f6c6f67792e4e61746976652e496e766f" + "6b65000241400ef868766eeafce71b6ff2a4332aa4363980e66c55ef70aea80e3baee1daf02b43ae6d4c7c8a17" + "8b92f523602426eaa4205ab0ae5944b0fdae0abcbabaefbc4c2321031bec1250aa8f78275f99a6663688f31085" + "848d0ed92f1203e447125f927b7486ac4140c49c23092cd9003247a55792211d816010c7d6204c6e07a6e017da" + "70007b25ee2ab3665103f846300cd03512040275b78ae46812d40cd611058decdff5551e1f232103d9fd62df33" + "2403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac", + rawTx); +} + +TEST(TWAnySingerOntology, Oep4Decimal) { + auto input = Proto::SigningInput(); + input.set_contract("ff31ec74d01f7b7d45ed2add930f5d2239f7de33"); + input.set_method("decimals"); + input.set_nonce(0x1234); + auto data = Oep4TxBuilder::build(input); + auto rawTx = hex(data); + EXPECT_EQ("00d1341200000000000000000000000000000000000000000000000000000000000000000000000000002000c108646563696d616c736733def739225d0f93dd2aed457d7b1fd074ec31ff0000", rawTx); +} + +TEST(TWAnySingerOntology, Oep4BalanceOf) { + // read only method don't need signer + auto input = Proto::SigningInput(); + input.set_contract("ff31ec74d01f7b7d45ed2add930f5d2239f7de33"); + input.set_method("balanceOf"); + input.set_query_address("AeaThtPwh5kAYnjHavzwmvxPd725nVTvbM"); + input.set_nonce(0x1234); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeOntology); + EXPECT_EQ("00d1341200000000000000000000000000000000000000000000000000000000000000000000000000003614fa2254ffaee3c3e1172e8e98f800e4105c74988e51c10962616c616e63654f666733def739225d0f93dd2aed457d7b1fd074ec31ff0000", hex(output.encoded())); +} + +TEST(TWAnySingerOntology, Oep4Transfer) { + // https://explorer.ont.io/testnet/tx/710266b8d497e794ecd47e01e269e4aeb6f4ff2b01eaeafc4cd371e062b13757 + auto ownerPrivateKey = + parse_hex("4646464646464646464646464646464646464646464646464646464646464652"); + auto payerPrivateKey = + parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + auto input = Proto::SigningInput(); + input.set_contract("ff31ec74d01f7b7d45ed2add930f5d2239f7de33"); + input.set_method("transfer"); + input.set_owner_private_key(ownerPrivateKey.data(), ownerPrivateKey.size()); + input.set_payer_private_key(payerPrivateKey.data(), payerPrivateKey.size()); + input.set_to_address("AVY6LfvxauVQAVHDV9hC3ZCv7cQqzfDotH"); + input.set_amount(233); + input.set_gas_price(2500); + input.set_gas_limit(50000); + input.set_nonce(0x1234); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeOntology); + + EXPECT_EQ("00d134120000c40900000000000050c3000000000000fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a4d02e9001496f688657b95be51c11a87b51adfda4ab69e9cbb1457e9d1a61f9aafa798b6c7fbeae35639681d7df653c1087472616e736665726733def739225d0f93dd2aed457d7b1fd074ec31ff00024140bd2923854d7b84b97a107bb3cddf18c8e3dddd2f36b41a1f5f5b23366484daa22871cfb819923fe01e9cb1e9ed16baa2b05c2feb76bcbe2ec125f72701c5e965232103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac41406d638653597774ce45812ea2653250806b657b32b7c6ad3e027ddeba91e9a9da4bb5dacd23dafba868cb31bacb38b4a6ff2607682a426c1dc09b05a1e158d6cd2321031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac", hex(output.encoded())); +} + +} // namespace TW::Ontology::tests diff --git a/tests/chains/Ontology/TWCoinTypeTests.cpp b/tests/chains/Ontology/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..4bada2c0a47 --- /dev/null +++ b/tests/chains/Ontology/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWOntologyCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOntology)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeOntology, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeOntology, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeOntology)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeOntology)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeOntology), 0); + ASSERT_EQ(TWBlockchainOntology, TWCoinTypeBlockchain(TWCoinTypeOntology)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeOntology)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeOntology)); + assertStringsEqual(symbol, "ONT"); + assertStringsEqual(txUrl, "https://explorer.ont.io/transaction/t123"); + assertStringsEqual(accUrl, "https://explorer.ont.io/address/a12"); + assertStringsEqual(id, "ontology"); + assertStringsEqual(name, "Ontology"); +} diff --git a/tests/chains/Ontology/TransactionTests.cpp b/tests/chains/Ontology/TransactionTests.cpp new file mode 100644 index 00000000000..ccf7fc14094 --- /dev/null +++ b/tests/chains/Ontology/TransactionTests.cpp @@ -0,0 +1,66 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PrivateKey.h" + +#include "Ontology/ParamsBuilder.h" +#include "Ontology/Signer.h" +#include "Ontology/Transaction.h" + +#include + +#include +#include + +namespace TW::Ontology::tests { + +TEST(OntologyTransaction, validity) { + std::vector ontContract{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + auto fromAddress = Address("AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5"); + auto toAddress = Address("APniYDGozkhUh8Tk7pe35aah2HGJ4fJfVd"); + uint64_t amount = 1; + NeoVmParamValue::ParamList transferParam{fromAddress._data, toAddress._data, amount}; + NeoVmParamValue::ParamArray args{transferParam}; + auto invokeCode = ParamsBuilder::buildNativeInvokeCode(ontContract, 0x00, "transfer", {args}); + uint8_t version = 0; + uint8_t txType = 0xd1; + uint32_t nonce = 1552759011; + uint64_t gasPrice = 600; + uint64_t gasLimit = 300000; + auto tx = + Transaction(version, txType, nonce, gasPrice, gasLimit, toAddress.string(), invokeCode); + std::string hexTx = + "00d1e3388d5c5802000000000000e09304000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c6" + "6b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc81457e9d1a61f9aafa798b6c7fbeae35639681d7d" + "f66a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f" + "6e746f6c6f67792e4e61746976652e496e766f6b650000"; + EXPECT_EQ(hexTx, hex(tx.serialize())); + auto signer1 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464646"))); + signer1.sign(tx); + hexTx = + "00d1e3388d5c5802000000000000e09304000000000057e9d1a61f9aafa798b6c7fbeae35639681d7df67100c6" + "6b14fbacc8214765d457c8e3f2b5a1d3c4981a2e9d2a6a7cc81457e9d1a61f9aafa798b6c7fbeae35639681d7d" + "f66a7cc8516a7cc86c51c1087472616e736665721400000000000000000000000000000000000000010068164f" + "6e746f6c6f67792e4e61746976652e496e766f6b6500014140e03a09d85f56d2ceb5817a1f3a430bab9bf0f469" + "da38afe4a5b33de258a06236d8e0a59d25918a49825455c99f91de9caf8071e38a589a530519705af9081eca23" + "21031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac"; + EXPECT_EQ(520ul, hex(tx.serialize()).length()); + EXPECT_EQ(hexTx.substr(0, 20), hex(tx.serialize()).substr(0, 20)); + auto signer2 = Signer(PrivateKey(parse_hex("4646464646464646464646464646464646464646464646464646464646464652"))); + signer2.addSign(tx); + auto result = tx.serialize(); + auto verifyPosition1 = + hex(result).find("21031bec1250aa8f78275f99a6663688f31085848d0ed92f1203e447125f927b7486ac"); + auto verifyPosition2 = + hex(result).find("2103d9fd62df332403d9114f3fa3da0d5aec9dfa42948c2f50738d52470469a1a1eeac"); + EXPECT_EQ(450ul, verifyPosition1); + EXPECT_EQ(654ul, verifyPosition2); + EXPECT_EQ(724ul, hex(result).length()); +} + +} // namespace TW::Ontology::tests diff --git a/tests/chains/Optimism/TWCoinTypeTests.cpp b/tests/chains/Optimism/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..1cc6ff0e4c3 --- /dev/null +++ b/tests/chains/Optimism/TWCoinTypeTests.cpp @@ -0,0 +1,30 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWOptimismCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOptimism)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x6fd99288be9bf71eb002bb31da10a4fb0fbbb3c45ae73693b212f49c9db7df8f")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeOptimism, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x1f932361e31d206b4f6b2478123a9d0f8c761031")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeOptimism, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeOptimism)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeOptimism)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeOptimism), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeOptimism)); + + assertStringsEqual(symbol, "ETH"); + assertStringsEqual(txUrl, "https://optimistic.etherscan.io/tx/0x6fd99288be9bf71eb002bb31da10a4fb0fbbb3c45ae73693b212f49c9db7df8f"); + assertStringsEqual(accUrl, "https://optimistic.etherscan.io/address/0x1f932361e31d206b4f6b2478123a9d0f8c761031"); + assertStringsEqual(id, "optimism"); + assertStringsEqual(name, "Optimistic Ethereum"); +} diff --git a/tests/chains/Osmosis/AddressTests.cpp b/tests/chains/Osmosis/AddressTests.cpp new file mode 100644 index 00000000000..68011e0dc75 --- /dev/null +++ b/tests/chains/Osmosis/AddressTests.cpp @@ -0,0 +1,46 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cosmos/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include +#include + +namespace TW::Cosmos::tests { + +TEST(OsmosisAddress, Valid) { + EXPECT_TRUE(Address::isValid(TWCoinTypeOsmosis, "osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5")); + EXPECT_TRUE(Address::isValid(TWCoinTypeOsmosis, "osmo18s0hdnsllgcclweu9aymw4ngktr2k0rkvn7jmn")); +} + +TEST(OsmosisAddress, Invalid) { + EXPECT_FALSE(Address::isValid(TWCoinTypeOsmosis, "osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f6")); + EXPECT_FALSE(Address::isValid(TWCoinTypeOsmosis, "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02")); // valid cosmos +} + +TEST(OsmosisAddress, FromPrivateKey) { + auto privateKey = PrivateKey(parse_hex("8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af")); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + auto address = Address(TWCoinTypeOsmosis, publicKey); + ASSERT_EQ(address.string(), "osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5"); +} + +TEST(OsmosisAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("02ecef5ce437a302c67f95468de4b31f36e911f467d7e6a52b41c1e13e1d563649"), TWPublicKeyTypeSECP256k1); + auto address = Address(TWCoinTypeOsmosis, publicKey); + ASSERT_EQ(address.string(), "osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5"); +} + +TEST(OsmosisAddress, FromString) { + Address address; + EXPECT_TRUE(Address::decode("osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5", address)); + EXPECT_EQ(address.string(), "osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5"); + EXPECT_EQ(hex(address.getKeyHash()), "dd89a2e267cd96e23cf5a33382f030686f65996f"); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Osmosis/SignerTests.cpp b/tests/chains/Osmosis/SignerTests.cpp new file mode 100644 index 00000000000..91dd335f25e --- /dev/null +++ b/tests/chains/Osmosis/SignerTests.cpp @@ -0,0 +1,59 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cosmos/Address.h" +#include "Cosmos/Signer.h" +#include "HexCoding.h" +#include "PublicKey.h" +#include "proto/Cosmos.pb.h" +#include "TestUtilities.h" + +#include + +namespace TW::Cosmos::tests { + +TEST(OsmosisSigner, SignTransfer_81B4) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(124703); + input.set_chain_id("osmosis-1"); + input.set_memo(""); + input.set_sequence(0); + + Address fromAddress; + Address toAddress; + EXPECT_TRUE(Address::decode("osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5", fromAddress)); + EXPECT_TRUE(Address::decode("osmo18s0hdnsllgcclweu9aymw4ngktr2k0rkvn7jmn", toAddress)); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("uosmo"); + amountOfTx->set_amount("99800"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uosmo"); + amountOfFee->set_amount("200"); + + auto privateKey = parse_hex("8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeOsmosis); + + // https://www.mintscan.io/osmosis/txs/81B4F01BDE72AF7FF4536E5D7E66EB218E9FC9ACAA7C5EB5DB237DD0595D5F5F + // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "Co0B...rYVj", "mode": "BROADCAST_MODE_BLOCK"}' https://lcd-osmosis.keplr.app/cosmos/tx/v1beta1/txs + + assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"Co0BCooBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmoKK29zbW8xbWt5Njljbjhla3R3eTA4NDV2ZWM5dXBzZHBoa3R4dDBlbjk3ZjUSK29zbW8xOHMwaGRuc2xsZ2NjbHdldTlheW13NG5na3RyMmswcmt2bjdqbW4aDgoFdW9zbW8SBTk5ODAwEmQKTgpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQLs71zkN6MCxn+VRo3ksx826RH0Z9fmpStBweE+HVY2SRIECgIIARISCgwKBXVvc21vEgMyMDAQwJoMGkAMY//Md5GRUR4lVZhk558hFS3kii9QZYoYKfg4+ac/xgNeyoiEweVDhcmEvlH1orVwjLUOnYs4ly2a/yIurYVj\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "0c63ffcc779191511e25559864e79f21152de48a2f50658a1829f838f9a73fc6035eca8884c1e54385c984be51f5a2b5708cb50e9d8b38972d9aff222ead8563"); + EXPECT_EQ(output.error(), ""); + EXPECT_EQ(output.json(), ""); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Osmosis/TWAnyAddressTests.cpp b/tests/chains/Osmosis/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..40793a73acf --- /dev/null +++ b/tests/chains/Osmosis/TWAnyAddressTests.cpp @@ -0,0 +1,28 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(TWOsmosisAnyAddress, IsValid) { + EXPECT_TRUE(TWAnyAddressIsValid(STRING("osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5").get(), TWCoinTypeOsmosis)); + EXPECT_TRUE(TWAnyAddressIsValid(STRING("osmo18s0hdnsllgcclweu9aymw4ngktr2k0rkvn7jmn").get(), TWCoinTypeOsmosis)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02").get(), TWCoinTypeOsmosis)); +} + +TEST(TWOsmosisAnyAddress, Create) { + auto string = STRING("osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeOsmosis)); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "dd89a2e267cd96e23cf5a33382f030686f65996f"); +} diff --git a/tests/chains/Osmosis/TWAnySignerTests.cpp b/tests/chains/Osmosis/TWAnySignerTests.cpp new file mode 100644 index 00000000000..95f230551a8 --- /dev/null +++ b/tests/chains/Osmosis/TWAnySignerTests.cpp @@ -0,0 +1,57 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cosmos/Address.h" +#include "HexCoding.h" +#include "proto/Cosmos.pb.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::Cosmos::tests { + +TEST(TWAnySignerOsmosis, Sign) { + auto privateKey = parse_hex("8bbec3772ddb4df68f3186440380c301af116d1422001c1877d6f5e4dba8c8af"); + Proto::SigningInput input; + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(124703); + input.set_chain_id("osmosis-1"); + input.set_memo(""); + input.set_sequence(0); + input.set_private_key(privateKey.data(), privateKey.size()); + + Address fromAddress; + Address toAddress; + EXPECT_TRUE(Address::decode("osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5", fromAddress)); + EXPECT_TRUE(Address::decode("osmo18s0hdnsllgcclweu9aymw4ngktr2k0rkvn7jmn", toAddress)); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("uosmo"); + amountOfTx->set_amount("99800"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uosmo"); + amountOfFee->set_amount("200"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeOsmosis); + + // https://www.mintscan.io/osmosis/txs/81B4F01BDE72AF7FF4536E5D7E66EB218E9FC9ACAA7C5EB5DB237DD0595D5F5F + // curl -H 'Content-Type: application/json' --data-binary '{"tx_bytes": "Co0B...rYVj", "mode": "BROADCAST_MODE_BLOCK"}' https://lcd-osmosis.keplr.app/cosmos/tx/v1beta1/txs + assertJSONEqual(output.serialized(), "{\"tx_bytes\":\"Co0BCooBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmoKK29zbW8xbWt5Njljbjhla3R3eTA4NDV2ZWM5dXBzZHBoa3R4dDBlbjk3ZjUSK29zbW8xOHMwaGRuc2xsZ2NjbHdldTlheW13NG5na3RyMmswcmt2bjdqbW4aDgoFdW9zbW8SBTk5ODAwEmQKTgpGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQLs71zkN6MCxn+VRo3ksx826RH0Z9fmpStBweE+HVY2SRIECgIIARISCgwKBXVvc21vEgMyMDAQwJoMGkAMY//Md5GRUR4lVZhk558hFS3kii9QZYoYKfg4+ac/xgNeyoiEweVDhcmEvlH1orVwjLUOnYs4ly2a/yIurYVj\",\"mode\":\"BROADCAST_MODE_BLOCK\"}"); + EXPECT_EQ(hex(output.signature()), "0c63ffcc779191511e25559864e79f21152de48a2f50658a1829f838f9a73fc6035eca8884c1e54385c984be51f5a2b5708cb50e9d8b38972d9aff222ead8563"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Osmosis/TWCoinTypeTests.cpp b/tests/chains/Osmosis/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..d42a4106616 --- /dev/null +++ b/tests/chains/Osmosis/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWOsmosisCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeOsmosis)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("5A6E50A6F2927E4B8C87BB094D5FBF15F1287429A09111806FC44B3CD86CACA8")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeOsmosis, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeOsmosis, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeOsmosis)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeOsmosis)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeOsmosis), 6); + ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeOsmosis)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeOsmosis)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeOsmosis)); + assertStringsEqual(symbol, "OSMO"); + assertStringsEqual(txUrl, "https://mintscan.io/osmosis/txs/5A6E50A6F2927E4B8C87BB094D5FBF15F1287429A09111806FC44B3CD86CACA8"); + assertStringsEqual(accUrl, "https://mintscan.io/osmosis/account/osmo1mky69cn8ektwy0845vec9upsdphktxt0en97f5"); + assertStringsEqual(id, "osmosis"); + assertStringsEqual(name, "Osmosis"); +} diff --git a/tests/chains/POANetwork/TWCoinTypeTests.cpp b/tests/chains/POANetwork/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..76581a52843 --- /dev/null +++ b/tests/chains/POANetwork/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWPOANetworkCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypePOANetwork)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypePOANetwork, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypePOANetwork, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypePOANetwork)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypePOANetwork)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypePOANetwork), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypePOANetwork)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypePOANetwork)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypePOANetwork)); + assertStringsEqual(symbol, "POA"); + assertStringsEqual(txUrl, "https://blockscout.com/poa/core/tx/t123"); + assertStringsEqual(accUrl, "https://blockscout.com/poa/core/address/a12"); + assertStringsEqual(id, "poa"); + assertStringsEqual(name, "POA Network"); +} diff --git a/tests/chains/Polkadot/AddressTests.cpp b/tests/chains/Polkadot/AddressTests.cpp new file mode 100644 index 00000000000..d2b4b74a230 --- /dev/null +++ b/tests/chains/Polkadot/AddressTests.cpp @@ -0,0 +1,52 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Polkadot/Address.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include +#include + +namespace TW::Polkadot::tests { + +TEST(PolkadotAddress, Validation) { + // Substrate ed25519 + ASSERT_FALSE(Address::isValid("5FqqU2rytGPhcwQosKRtW1E3ha6BJKAjHgtcodh71dSyXhoZ")); + // Bitcoin + ASSERT_FALSE(Address::isValid("1ES14c7qLb5CYhLMUekctxLgc1FV2Ti9DA")); + // Kusama ed25519 + ASSERT_FALSE(Address::isValid("FHKAe66mnbk8ke8zVWE9hFVFrJN1mprFPVmD5rrevotkcDZ")); + // Kusama secp256k1 + ASSERT_FALSE(Address::isValid("FxQFyTorsjVsjjMyjdgq8w5vGx8LiA1qhWbRYcFijxKKchx")); + // Kusama sr25519 + ASSERT_FALSE(Address::isValid("EJ5UJ12GShfh7EWrcNZFLiYU79oogdtXFUuDDZzk7Wb2vCe")); + + // Polkadot ed25519 + ASSERT_TRUE(Address::isValid("15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu")); + // Polkadot sr25519 + ASSERT_TRUE(Address::isValid("15AeCjMpcSt3Fwa47jJBd7JzQ395Kr2cuyF5Zp4UBf1g9ony")); +} + +TEST(PolkadotAddress, FromPrivateKey) { + // subkey phrase `chief menu kingdom stereo hope hazard into island bag trick egg route` + auto privateKey = PrivateKey(parse_hex("0x612d82bc053d1b4729057688ecb1ebf62745d817ddd9b595bc822f5f2ba0e41a")); + auto address = Address(privateKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(address.string(), "15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu"); +} + +TEST(PolkadotAddress, FromPublicKey) { + auto publicKey = PublicKey(parse_hex("0xbeff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519); + auto address = Address(publicKey); + ASSERT_EQ(address.string(), "15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu"); +} + +TEST(PolkadotAddress, FromString) { + auto address = Address("15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu"); + ASSERT_EQ(address.string(), "15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu"); +} + +} // namespace TW::Polkadot::tests diff --git a/tests/chains/Polkadot/SS58AddressTests.cpp b/tests/chains/Polkadot/SS58AddressTests.cpp new file mode 100644 index 00000000000..701938d697b --- /dev/null +++ b/tests/chains/Polkadot/SS58AddressTests.cpp @@ -0,0 +1,146 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Polkadot/SS58Address.h" +#include "HexCoding.h" +#include "PublicKey.h" +#include "TestUtilities.h" + +#include +#include + +using namespace TW; + +namespace TW::Polkadot::tests { + +TEST(SS58Address, IsValid) { + EXPECT_TRUE(SS58Address::isValid("15KRsCq9LLNmCxNFhGk55s5bEyazKefunDxUH24GFZwsTxyu", 0)); + + EXPECT_TRUE(SS58Address::isValid("ZG2d3dH5zfqNchsqReS6x4nBJuJCW7Z6Fh5eLvdA3ZXGkPd", 5)); + EXPECT_FALSE(SS58Address::isValid("ZG2d3dH5zfqNchsqReS6x4nBJuJCW7Z6Fh5eLvdA3ZXGkPd", 6)); + + EXPECT_TRUE(SS58Address::isValid("Fu3r514w83euSVV7q1MyFGWErUR2xDzXS2goHzimUn4S12D", 2)); + EXPECT_FALSE(SS58Address::isValid("Fu3r514w83euSVV7q1MyFGWErUR2xDzXS2goHzimUn4S12D", 5)); + + EXPECT_TRUE(SS58Address::isValid("cEYtw6AVMB27hFUs4gVukajLM7GqxwxUfJkbPY3rNToHMcCgb", 64)); + EXPECT_FALSE(SS58Address::isValid("cEYtw6AVMB27hFUs4gVukajLM7GqxwxUfJkbPY3rNToHMcCgb", 65)); + EXPECT_FALSE(SS58Address::isValid("JCViCkwMdGWKpf7Wogb8EFtDmaYTEZGEg6ah4svUPGnnpc7A", 64)); + + EXPECT_TRUE(SS58Address::isValid("p8EGHjWt7e1MYoD7V6WXvbPZWK9GSJiiK85kv2R7Ur7FisPUL", 172)); + EXPECT_FALSE(SS58Address::isValid("p8EGHjWt7e1MYoD7V6WXvbPZWK9GSJiiK85kv2R7Ur7FisPUL", 171)); + + EXPECT_TRUE(SS58Address::isValid("VDSyeURSP7ykE1zJPJGeqx6GcDZQF2DT3hAKhPMuwM5FuN9HE", 4096)); + EXPECT_FALSE(SS58Address::isValid("VDSyeURSP7ykE1zJPJGeqx6GcDZQF2DT3hAKhPMuwM5FuN9HE", 64)); + + EXPECT_TRUE(SS58Address::isValid("YDTv3GdhXPP3pQMqQtntGVg5hMno4jqanfYUgMPX2rLGJBKX6", 8219)); + EXPECT_FALSE(SS58Address::isValid("YDTv3GdhXPP3pQMqQtntGVg5hMno4jqanfYUgMPX2rLGJBKX6", 322)); +} + +const auto pubkeyString1 = "92fd9c237030356e26cfcc4568dc71055d5ec92dfe0ff903767e00611971bad3"; + +TEST(SS58Address, FromPublicKey) { + auto publicKey = PublicKey(parse_hex(pubkeyString1), TWPublicKeyTypeED25519); + auto addressPolkadot = SS58Address(publicKey, 0); + EXPECT_EQ(addressPolkadot.string(), "14KjL5vGAYJCbKgZJmFKDSjewtBpvaxx9YvRZvi7qmb5s8CC"); + EXPECT_EQ(hex(addressPolkadot.keyBytes()), pubkeyString1); + + auto addressAstar = SS58Address(publicKey, 5); + EXPECT_EQ(addressAstar.string(), "ZG2d3dH5zfqNchsqReS6x4nBJuJCW7Z6Fh5eLvdA3ZXGkPd"); + EXPECT_EQ(hex(addressAstar.keyBytes()), pubkeyString1); + + auto addressParallel = SS58Address(publicKey, 172); + EXPECT_EQ(addressParallel.string(), "p8EGHjWt7e1MYoD7V6WXvbPZWK9GSJiiK85kv2R7Ur7FisPUL"); + EXPECT_EQ(hex(addressParallel.keyBytes()), pubkeyString1); +} + +TEST(SS58Address, FromPublicKeyInvalid) { + auto publicKey = PublicKey(parse_hex(pubkeyString1), TWPublicKeyTypeED25519); + EXPECT_EXCEPTION(SS58Address(publicKey, 32771), "network out of range 32771"); +} + +TEST(SS58Address, FromString) { + auto addressKusama = SS58Address("Fu3r514w83euSVV7q1MyFGWErUR2xDzXS2goHzimUn4S12D", 2); + EXPECT_EQ(addressKusama.string(), "Fu3r514w83euSVV7q1MyFGWErUR2xDzXS2goHzimUn4S12D"); + + auto addressParallel = SS58Address("p8EGHjWt7e1MYoD7V6WXvbPZWK9GSJiiK85kv2R7Ur7FisPUL", 172); + EXPECT_EQ(addressParallel.string(), "p8EGHjWt7e1MYoD7V6WXvbPZWK9GSJiiK85kv2R7Ur7FisPUL"); +} + +TEST(SS58Address, FromStringInvalid) { + EXPECT_EXCEPTION(SS58Address("p8EGHjWt7e1MYoD7V6WXvbPZWK9GSJiiK85kv2R7Ur7FisPUL", 130), "Invalid address string"); +} + +std::map networkData = { + {0x00, "00"}, + {0x01, "01"}, + {0x02, "02"}, + {0x03, "03"}, + {0x04, "04"}, + {0x08, "08"}, + {0x0b, "0b"}, + {0x10, "10"}, + {0x20, "20"}, + {0x23, "23"}, + {0x30, "30"}, + {0x3f, "3f"}, + {0x40, "5000"}, + {0x41, "5040"}, + {0x80, "6000"}, + {0x0100, "4001"}, + {0x0123, "48c1"}, + {0x0200, "4002"}, + {0x0300, "4003"}, + {0x0400, "4004"}, + {0x0800, "4008"}, + {0x0fff, "7fcf"}, + {0x1000, "4010"}, + {0x1003, "40d0"}, + {0x2000, "4020"}, + {0x3000, "4030"}, + {0x3fff, "7fff"}, +}; + +TEST(SS58Address, DecodeNetwork) { + byte networkSize = 0; + uint32_t network = 0; + for (auto& d: networkData) { + std::string input = d.second + std::string("000102030405"); + EXPECT_TRUE(SS58Address::decodeNetwork(parse_hex(input), networkSize, network)); + EXPECT_EQ(network, d.first); + if (d.first < 64) { + EXPECT_EQ(networkSize, 1); + } else { + EXPECT_EQ(networkSize, 2); + } + } + + // 1. byte from invalid range + EXPECT_FALSE(SS58Address::decodeNetwork(parse_hex("ab" "000102030405"), networkSize, network)); + EXPECT_FALSE(SS58Address::decodeNetwork(parse_hex("8000" "000102030405"), networkSize, network)); + + // 2-byte, but decoded network is < 64 + EXPECT_FALSE(SS58Address::decodeNetwork(parse_hex("4000" "000102030405"), networkSize, network)); + EXPECT_FALSE(SS58Address::decodeNetwork(parse_hex("4040" "000102030405"), networkSize, network)); + EXPECT_FALSE(SS58Address::decodeNetwork(parse_hex("4080" "000102030405"), networkSize, network)); + EXPECT_FALSE(SS58Address::decodeNetwork(parse_hex("4100" "000102030405"), networkSize, network)); + EXPECT_FALSE(SS58Address::decodeNetwork(parse_hex("4200" "000102030405"), networkSize, network)); + EXPECT_FALSE(SS58Address::decodeNetwork(parse_hex("4400" "000102030405"), networkSize, network)); + EXPECT_FALSE(SS58Address::decodeNetwork(parse_hex("4800" "000102030405"), networkSize, network)); +} + +TEST(SS58Address, EncodeNetwork) { + Data data; + for (auto& d: networkData) { + EXPECT_TRUE(SS58Address::encodeNetwork(d.first, data)); + EXPECT_EQ(hex(data), d.second); + } + + // network > 16383 + EXPECT_FALSE(SS58Address::encodeNetwork(0x4000, data)); + EXPECT_FALSE(SS58Address::encodeNetwork(0x8000, data)); +} + +} // namespace TW::Polkadot::tests diff --git a/tests/Polkadot/ScaleCodecTests.cpp b/tests/chains/Polkadot/ScaleCodecTests.cpp similarity index 98% rename from tests/Polkadot/ScaleCodecTests.cpp rename to tests/chains/Polkadot/ScaleCodecTests.cpp index efdd07515c8..c31892fddea 100644 --- a/tests/Polkadot/ScaleCodecTests.cpp +++ b/tests/chains/Polkadot/ScaleCodecTests.cpp @@ -12,8 +12,7 @@ #include using namespace TW; -using namespace TW::Polkadot; - +namespace TW::Polkadot::tests { TEST(PolkadotCodec, EncodeCompact) { ASSERT_EQ(hex(encodeCompact(0)), "00"); @@ -76,3 +75,5 @@ TEST(PolkadotCodec, EncodeEra) { ASSERT_EQ(hex(era1), "7200"); ASSERT_EQ(hex(era2), "1100"); } + +} // namespace TW::Polkadot::tests diff --git a/tests/chains/Polkadot/SignerTests.cpp b/tests/chains/Polkadot/SignerTests.cpp new file mode 100644 index 00000000000..59d51714c23 --- /dev/null +++ b/tests/chains/Polkadot/SignerTests.cpp @@ -0,0 +1,340 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Polkadot/Signer.h" +#include "Polkadot/Extrinsic.h" +#include "Polkadot/Address.h" +#include "Polkadot/SS58Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "proto/Polkadot.pb.h" +#include "uint256.h" + +#include +#include + + +namespace TW::Polkadot::tests { + auto privateKey = PrivateKey(parse_hex("0xabf8e5bdbe30c65656c0a3cbd181ff8a56294a69dfedd27982aace4a76909115")); + auto privateKeyIOS = PrivateKey(parse_hex("37932b086586a6675e66e562fe68bd3eeea4177d066619c602fe3efc290ada62")); + auto privateKeyThrow2 = PrivateKey(parse_hex("70a794d4f1019c3ce002f33062f45029c4f930a56b3d20ec477f7668c6bbc37f")); + auto privateKeyPolkadot = PrivateKey(parse_hex("298fcced2b497ed48367261d8340f647b3fca2d9415d57c2e3c5ef90482a2266")); + auto addressThrow2 = "14Ztd3KJDaB9xyJtRkREtSZDdhLSbm7UUKt8Z7AwSv7q85G2"; + auto toPublicKey = PublicKey(parse_hex("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"), TWPublicKeyTypeED25519); + auto genesisHash = parse_hex("91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"); + auto controller1 = "14xKzzU1ZYDnzFj7FgdtDAYSMJNARjDc2gNw4XAFDgr4uXgp"; + +TEST(PolkadotSigner, SignTransfer_9fd062) { + auto toAddress = Address("13ZLCqJNPsRZYEbwjtZZFpWt9GyFzg5WahXCVWKpWdUJqrQ5"); + + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + auto blockHash = parse_hex("0x5d2143bb808626d63ad7e1cda70fa8697059d670a992e82cd440fbb95ea40351"); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(3); + input.set_spec_version(26); + { + PublicKey publicKey = privateKeyThrow2.getPublicKey(TWPublicKeyTypeED25519); + Address address = Address(publicKey); + EXPECT_EQ(address.string(), addressThrow2); + } + input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(5); + + // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true + auto era = input.mutable_era(); + era->set_block_number(3541050); + era->set_period(64); + + auto balanceCall = input.mutable_balance_call(); + auto transfer = balanceCall->mutable_transfer(); + auto value = store(uint256_t(2000000000)); // 0.2 + transfer->set_to_address(toAddress.string()); + transfer->set_value(value.data(), value.size()); + + auto extrinsic = Extrinsic(input); + auto preimage = extrinsic.encodePayload(); + EXPECT_EQ(hex(preimage), "05007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0300943577a5030c001a0000000500000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c35d2143bb808626d63ad7e1cda70fa8697059d670a992e82cd440fbb95ea40351"); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/0x9fd06208a6023e489147d8d93f0182b0cb7e45a40165247319b87278e08362d8 + EXPECT_EQ(hex(output.encoded()), "3502849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f7830073e59cef381aedf56d7af076bafff9857ffc1e3bd7d1d7484176ff5b58b73f1211a518e1ed1fd2ea201bd31869c0798bba4ffe753998c409d098b65d25dff801a5030c0005007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0300943577"); +} + +TEST(PolkadotSigner, SignTransferDOT) { + + auto blockHash = parse_hex("0x343a3f4258fd92f5ca6ca5abdf473d86a78b0bcd0dc09c568ca594245cc8c642"); + auto toAddress = SS58Address(toPublicKey, TWSS58AddressTypePolkadot); + + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + + input.set_nonce(0); + input.set_spec_version(17); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(3); + + auto& era = *input.mutable_era(); + era.set_block_number(927699); + era.set_period(8); + + auto balanceCall = input.mutable_balance_call(); + auto& transfer = *balanceCall->mutable_transfer(); + auto value = store(uint256_t(12345)); + transfer.set_to_address(toAddress.string()); + transfer.set_value(value.data(), value.size()); + + auto extrinsic = Extrinsic(input); + auto preimage = extrinsic.encodePayload(); + auto output = Signer::sign(input); + + ASSERT_EQ(hex(preimage), "05008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c032000000110000000300000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3343a3f4258fd92f5ca6ca5abdf473d86a78b0bcd0dc09c568ca594245cc8c642"); + ASSERT_EQ(hex(output.encoded()), "29028488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee003d91a06263956d8ce3ce5c55455baefff299d9cb2bb3f76866b6828ee4083770b6c03b05d7b6eb510ac78d047002c1fe5c6ee4b37c9c5a8b09ea07677f12e50d3200000005008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c0"); +} + +TEST(PolkadotSigner, SignTransfer_72dd5b) { + + auto blockHash = parse_hex("7d5fa17b70251d0806f26156b1b698dfd09e040642fa092595ce0a78e9e84fcd"); + + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + + input.set_nonce(1); + input.set_spec_version(28); + input.set_private_key(privateKeyIOS.bytes.data(), privateKeyIOS.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(6); + + auto& era = *input.mutable_era(); + era.set_block_number(3910736); + era.set_period(64); + + auto balanceCall = input.mutable_balance_call(); + auto& transfer = *balanceCall->mutable_transfer(); + auto value = store(uint256_t(10000000000)); + transfer.set_to_address("13ZLCqJNPsRZYEbwjtZZFpWt9GyFzg5WahXCVWKpWdUJqrQ5"); + transfer.set_value(value.data(), value.size()); + + auto extrinsic = Extrinsic(input); + auto preimage = extrinsic.encodePayload(); + auto output = Signer::sign(input); + + ASSERT_EQ(hex(preimage), "0500007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b5402050104001c0000000600000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c37d5fa17b70251d0806f26156b1b698dfd09e040642fa092595ce0a78e9e84fcd"); + ASSERT_EQ(hex(output.encoded()), "410284008d96660f14babe708b5e61853c9f5929bc90dd9874485bf4d6dc32d3e6f22eaa0038ec4973ab9773dfcbf170b8d27d36d89b85c3145e038d68914de83cf1f7aca24af64c55ec51ba9f45c5a4d74a9917dee380e9171108921c3e5546e05be15206050104000500007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b5402"); +} + +TEST(PolkadotSigner, SignBond_8da66d) { + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + auto blockHash = parse_hex("0xf1eee612825f29abd3299b486e401299df2faa55b7ce1e34bf2243bd591905fc"); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(0); + input.set_spec_version(26); + { + PublicKey publicKey = privateKeyThrow2.getPublicKey(TWPublicKeyTypeED25519); + Address address = Address(publicKey); + EXPECT_EQ(address.string(), addressThrow2); + } + input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(5); + + // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true + auto era = input.mutable_era(); + era->set_block_number(3540912); + era->set_period(64); + + auto stakingCall = input.mutable_staking_call(); + auto bond = stakingCall->mutable_bond(); + auto value = store(uint256_t(11000000000)); // 1.1 + bond->set_controller(addressThrow2); // myself + bond->set_value(value.data(), value.size()); + bond->set_reward_destination(Proto::RewardDestination::STASH); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/0x8da66d3fe0f592cff714ec107289370365117a1abdb72a19ac91181fdcf62bba + ASSERT_EQ(hex(output.encoded()), "3d02849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f783009025843bc49c1c4fbc99dbbd290c92f9879665d55b02f110abfb4800f0e7630877d2cffd853deae7466c22fbc8616a609e1b92615bb365ea8adccba5ef7624050503000007009dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f7830700aea68f0201"); +} + +TEST(PolkadotSigner, SignBondAndNominate_4955314_2) { + + auto key = parse_hex("7f44b19b391a8015ca4c7d94097b3695867a448d1391e7f3243f06987bdb6858"); + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(genesisHash.data(), genesisHash.size()); + input.set_nonce(4); + input.set_spec_version(30); + input.set_private_key(key.data(), key.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(7); + + auto stakingCall = input.mutable_staking_call(); + auto bondnom = stakingCall->mutable_bond_and_nominate(); + auto value = store(uint256_t(10000000000)); // 1 DOT + bondnom->set_controller("13ZLCqJNPsRZYEbwjtZZFpWt9GyFzg5WahXCVWKpWdUJqrQ5"); + bondnom->set_value(value.data(), value.size()); + bondnom->set_reward_destination(Proto::RewardDestination::STASH); + bondnom->add_nominators("1zugcavYA9yCuYwiEYeMHNJm9gXznYjNfXQjZsZukF1Mpow"); + bondnom->add_nominators("15oKi7HoBQbwwdQc47k71q4sJJWnu5opn1pqoGx4NAEYZSHs"); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/4955314-2 + ASSERT_EQ(hex(output.encoded()), "6103840036092fac541e0e5feda19e537c679b487566d7101141c203ac8322c27e5f076a00a8b1f859d788f11a958e98b731358f89cf3fdd41a667ea992522e8d4f46915f4c03a1896f2ac54bdc5f16e2ce8a2a3bf233d02aad8192332afd2113ed6688e0d0010001a02080700007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b540201070508002c2a55b5ffdca266bd0207df97565b03255f70783ca1a349be5ed9f44589c36000d44533a4d21fd9d6f5d57c8cd05c61a6f23f9131cec8ae386b6b437db399ec3d"); +} + +TEST(PolkadotSigner, SignNominate_452522) { + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + auto blockHash = parse_hex("0x211787d016e39007ac054547737a10542620013e73648b3134541d536cb44e2c"); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(1); + input.set_spec_version(26); + input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(5); + + // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true + auto era = input.mutable_era(); + era->set_block_number(3540945); + era->set_period(64); + + auto stakingCall = input.mutable_staking_call(); + auto nominate = stakingCall->mutable_nominate(); + + nominate->add_nominators(controller1); + nominate->add_nominators("1REAJ1k691g5Eqqg9gL7vvZCBG7FCCZ8zgQkZWd4va5ESih"); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/0x4525224b7d8f3e58de3a54a9fbfd071401c2b737f314c972a2bb087a0ff508a6 + ASSERT_EQ(hex(output.encoded()), "a502849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f78300d73ff0dc456704743f70173a56e6c13e88a6e1dddb38a23552a066e44fb64e2c9d8a5e9a76afb9489b8540365f668bddd34b7d9c8dbdc4600e6316080e55a30315010400070508aee72821ca00e62304e4f0d858122a65b87c8df4f0eae224ae064b951d39f610127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a"); +} + +TEST(PolkadotSigner, SignNominate2) { + auto blockHash = parse_hex("d22a6b2e3e61325050718bd04a14da9efca1f41c9f0a525c375d36106e25af68"); + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(0); + input.set_spec_version(17); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(3); + + auto stakingCall = input.mutable_staking_call(); + auto& nominate = *stakingCall->mutable_nominate(); + // payload size larger than 256, will be hashed + nominate.add_nominators("1zugcabYjgfQdMLC3cAzQ8tJZMo45tMnGpivpAzpxB4CZyK"); + nominate.add_nominators("1REAJ1k691g5Eqqg9gL7vvZCBG7FCCZ8zgQkZWd4va5ESih"); + nominate.add_nominators("1WG3jyNqniQMRZGQUc7QD2kVLT8hkRPGMSqAb5XYQM1UDxN"); + nominate.add_nominators("16QFrtU6kDdBjxY8qEKz5EEfuDkHxqG8pix3wSGKQzRcuWHo"); + nominate.add_nominators("14ShUZUYUR35RBZW6uVVt1zXDxmSQddkeDdXf1JkMA6P721N"); + nominate.add_nominators("15MUBwP6dyVw5CXF9PjSSv7SdXQuDSwjX86v1kBodCSWVR7c"); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.encoded()), "a1048488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee00135bbc68b67fffadaf7e98b6402c4fc60382765f543225083a024b0e0ff8071d4ec4ddd67a65828113cc76f3208765608be010d2fcfdcd47e8fe342872704c000000000705182c2a55b5a116a4c88aff57e8f2b70ba72dda72dda4b78630e16ad0ca69006f18127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a1650c532ed1a8641e8922aa24ade0ff411d03edd9ed1c6b7fe42f1a801cee37ceee9d5d071a418b51c02b456d5f5cefd6231041ad59b0e8379c59c11ba4a2439984e16482c99cfad1436111e321a86d87d0fac203bf64538f888e45d793b5413c08d5de7a5d97bea2c7ddf516d0635bddc43f326ae2f80e2595b49d4a08c4619"); +} + +TEST(PolkadotSigner, SignChill) { + auto blockHash = parse_hex("1d4a1ecc8b1c37bf0ba5d3e0bf14ec5402fbb035eeaf6d8042c07ca5f8c57429"); + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(0); + input.set_spec_version(17); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(3); + + auto stakingCall = input.mutable_staking_call(); + //auto __attribute__((unused)) &chill = *stakingCall->mutable_chill(); + [[maybe_unused]] auto &chill = *stakingCall->mutable_chill(); //win + auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.encoded()), "9d018488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee0088b5e1cd93ba74b82e329f95e1b22660385970182172b2ae280801fdd1ee5652cf7bf319e5e176ccc299dd8eb1e7fccb0ea7717efaf4aacd7640789dd09c1e070000000706"); +} + +TEST(PolkadotSigner, SignWithdraw) { + auto blockHash = parse_hex("7b4d1d1e2573eabcc90a3e96058eb0d8d21d7a0b636e8030d152d9179a345dda"); + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(0); + input.set_spec_version(17); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(3); + + auto stakingCall = input.mutable_staking_call(); + auto& withdraw = *stakingCall->mutable_withdraw_unbonded(); + withdraw.set_slashing_spans(10); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.encoded()), "ad018488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee002e49bf0dec9bef01dd3bd25419e2147dc983613d0860108f889f9ff2d062c5e3267e309e2dbc35dd2fc2b877b57d86a5f12cbeb8217485be32be3c34d2507d0e00000007030a000000"); +} + +TEST(PolkadotSigner, SignUnbond_070957) { + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + auto blockHash = parse_hex("0x53040c71c6061bd256346b81fcb3545c13b5c34c7cd0c2c25f00aa6e564b16d5"); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(2); + input.set_spec_version(26); + input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(5); + + auto era = input.mutable_era(); + era->set_block_number(3540983); + era->set_period(64); + + auto stakingCall = input.mutable_staking_call(); + auto unbond = stakingCall->mutable_unbond(); + auto value = store(uint256_t(4000000000)); + unbond->set_value(value.data(), value.size()); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/0x070957ab697adbe11f7d72a1314d0a81d272a747d2e6880818073317125f980a + ASSERT_EQ(hex(output.encoded()), "b501849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f783003a762d9dc3f2aba8922c4babf7e6622ca1d74da17ab3f152d8f29b0ffee53c7e5e150915912a9dfd98ef115d272e096543eef9f513207dd606eea97d023a64087503080007020300286bee"); +} + +TEST(PolkadotSigner, SignChillAndUnbond) { + auto blockHash = parse_hex("0x35ba668bb19453e8da6334cadcef2a27c8d4141bfc8b49e78e853c3d73e1ecd0"); + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(6); + input.set_spec_version(9200); + input.set_private_key(privateKeyPolkadot.bytes.data(), privateKeyPolkadot.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(12); + + auto era = input.mutable_era(); + era->set_block_number(10541373); + era->set_period(64); + + auto stakingCall = input.mutable_staking_call(); + auto chillBond = stakingCall->mutable_chill_and_unbond(); + auto value = store(uint256_t(100500000000)); // 10.05 DOT + chillBond->set_value(value.data(), value.size()); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/10541383-2 + ASSERT_EQ(hex(output.encoded()), "d10184008361bd08ddca5fda28b5e2aa84dc2621de566e23e089e555a42194c3eaf2da7900c891ba102db672e378945d74cf7f399226a76b43cab502436971599255451597fc2599902e4b62c7ce85ecc3f653c693fef3232be620984b5bb5bcecbbd7b209d50318001a02080706070207004d446617"); +} + +} // namespace TW::Polkadot::tests diff --git a/tests/chains/Polkadot/TWCoinTypeTests.cpp b/tests/chains/Polkadot/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..b35b069a115 --- /dev/null +++ b/tests/chains/Polkadot/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + +TEST(TWPolkadotCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypePolkadot)); + + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb96f97d8ee508f420e606e1a6dcc74b88844713ddec2bd7cf4e3aa6b1d6beef4")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypePolkadot, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("13hJFqnkqQbmgnGQteGntjMjTdmTBRE8Z93JqxsrpgT7Yjd2")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypePolkadot, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypePolkadot)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypePolkadot)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypePolkadot), 10); + ASSERT_EQ(TWBlockchainPolkadot, TWCoinTypeBlockchain(TWCoinTypePolkadot)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypePolkadot)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypePolkadot)); + assertStringsEqual(symbol, "DOT"); + assertStringsEqual(txUrl, "https://polkadot.subscan.io/extrinsic/0xb96f97d8ee508f420e606e1a6dcc74b88844713ddec2bd7cf4e3aa6b1d6beef4"); + assertStringsEqual(accUrl, "https://polkadot.subscan.io/account/13hJFqnkqQbmgnGQteGntjMjTdmTBRE8Z93JqxsrpgT7Yjd2"); + assertStringsEqual(id, "polkadot"); + assertStringsEqual(name, "Polkadot"); +} diff --git a/tests/chains/Polygon/TWCoinTypeTests.cpp b/tests/chains/Polygon/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..3ffc1203d40 --- /dev/null +++ b/tests/chains/Polygon/TWCoinTypeTests.cpp @@ -0,0 +1,31 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWPolygonCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypePolygon)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xe26ed1470d5bf99a53d687843e7acdf7e4ba6620af93b4d672e714de90476e8e")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypePolygon, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x720E1fa107A1Df39Db4E78A3633121ac36Bec132")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypePolygon, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypePolygon)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypePolygon)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypePolygon), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypePolygon)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypePolygon)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypePolygon)); + assertStringsEqual(symbol, "MATIC"); + assertStringsEqual(txUrl, "https://polygonscan.com/tx/0xe26ed1470d5bf99a53d687843e7acdf7e4ba6620af93b4d672e714de90476e8e"); + assertStringsEqual(accUrl, "https://polygonscan.com/address/0x720E1fa107A1Df39Db4E78A3633121ac36Bec132"); + assertStringsEqual(id, "polygon"); + assertStringsEqual(name, "Polygon"); +} diff --git a/tests/chains/Qtum/TWCoinTypeTests.cpp b/tests/chains/Qtum/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..97814e04ab3 --- /dev/null +++ b/tests/chains/Qtum/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWQtumCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeQtum)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeQtum, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeQtum, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeQtum)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeQtum)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeQtum), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeQtum)); + ASSERT_EQ(0x32, TWCoinTypeP2shPrefix(TWCoinTypeQtum)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeQtum)); + assertStringsEqual(symbol, "QTUM"); + assertStringsEqual(txUrl, "https://qtum.info/tx/t123"); + assertStringsEqual(accUrl, "https://qtum.info/address/a12"); + assertStringsEqual(id, "qtum"); + assertStringsEqual(name, "Qtum"); +} diff --git a/tests/Qtum/TWQtumAddressTests.cpp b/tests/chains/Qtum/TWQtumAddressTests.cpp similarity index 99% rename from tests/Qtum/TWQtumAddressTests.cpp rename to tests/chains/Qtum/TWQtumAddressTests.cpp index 8d658cf096c..7da7788ce7e 100644 --- a/tests/Qtum/TWQtumAddressTests.cpp +++ b/tests/chains/Qtum/TWQtumAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/Ravencoin/TWCoinTypeTests.cpp b/tests/chains/Ravencoin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..9f90f507883 --- /dev/null +++ b/tests/chains/Ravencoin/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWRavencoinCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeRavencoin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeRavencoin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeRavencoin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeRavencoin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeRavencoin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeRavencoin), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeRavencoin)); + ASSERT_EQ(0x7a, TWCoinTypeP2shPrefix(TWCoinTypeRavencoin)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeRavencoin)); + assertStringsEqual(symbol, "RVN"); + assertStringsEqual(txUrl, "https://ravencoin.network/tx/t123"); + assertStringsEqual(accUrl, "https://ravencoin.network/address/a12"); + assertStringsEqual(id, "ravencoin"); + assertStringsEqual(name, "Ravencoin"); +} diff --git a/tests/Ravencoin/TWRavencoinTransactionTests.cpp b/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp similarity index 95% rename from tests/Ravencoin/TWRavencoinTransactionTests.cpp rename to tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp index b9c220fdb0d..d3927772a2c 100644 --- a/tests/Ravencoin/TWRavencoinTransactionTests.cpp +++ b/tests/chains/Ravencoin/TWRavencoinTransactionTests.cpp @@ -1,11 +1,10 @@ - -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/OutPoint.h" #include "Bitcoin/TransactionBuilder.h" @@ -18,8 +17,7 @@ #include -using namespace TW; -using namespace TW::Bitcoin; +namespace TW::Bitcoin { TEST(RavencoinTransaction, SignTransaction) { /* @@ -73,22 +71,23 @@ TEST(RavencoinTransaction, SignTransaction) { signedTx.encode(serialized, Transaction::SegwitFormatMode::NonSegwit); ASSERT_EQ( hex(serialized), - "0100000001445560237d8093da3487eb90bc7ff826fab43cdbe213c034d671ec4eb4827e0c000000006b483045022100d790bdaa3c44eb5e3a422365ca5fc009c4512625222e3378f2f16e7e6ef1732a0220688c1bb995b7ff2f12729e101d7c24b6314430317e7717911fdc35c0d84f2f0d012102138724e702d25b0fdce73372ccea9734f9349442d5a9681a5f4d831036cd9429ffffffff0280f0fa02000000001976a9149451f4546e09fc2e49ef9b5303924712ec2b038e88ac006cdc02000000001976a9145d6e33f3a108bbcc586cbbe90994d5baf5a9cce488ac00000000" - ); + "0100000001445560237d8093da3487eb90bc7ff826fab43cdbe213c034d671ec4eb4827e0c000000006b483045022100d790bdaa3c44eb5e3a422365ca5fc009c4512625222e3378f2f16e7e6ef1732a0220688c1bb995b7ff2f12729e101d7c24b6314430317e7717911fdc35c0d84f2f0d012102138724e702d25b0fdce73372ccea9734f9349442d5a9681a5f4d831036cd9429ffffffff0280f0fa02000000001976a9149451f4546e09fc2e49ef9b5303924712ec2b038e88ac006cdc02000000001976a9145d6e33f3a108bbcc586cbbe90994d5baf5a9cce488ac00000000"); } TEST(RavencoinTransaction, LockScripts) { - // P2PKH + // P2PKH // https://blockbook.ravencoin.org/tx/3717b528eb4925461d9de5a596d2eefe175985740b4fda153255e10135f236a6 - + auto script = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("RNoSGCX8SPFscj8epDaJjqEpuZa2B5in88").get(), TWCoinTypeRavencoin)); auto scriptData = WRAPD(TWBitcoinScriptData(script.get())); assertHexEqual(scriptData, "76a9149451f4546e09fc2e49ef9b5303924712ec2b038e88ac"); // P2SH // https://ravencoin.network/api/tx/f600d07814677d1f60545c8f7f71260238595c4928d6fb87caa0f9dd732e9bb5 - + auto script2 = WRAP(TWBitcoinScript, TWBitcoinScriptLockScriptForAddress(STRING("rPWwn5h4QFZNaz1XmY39rc73sdYGGDdmq1").get(), TWCoinTypeRavencoin)); auto scriptData2 = WRAPD(TWBitcoinScriptData(script2.get())); assertHexEqual(scriptData2, "a914bd92088bb7e82d611a9b94fbb74a0908152b784f87"); } + +} // namespace TW::Bitcoin diff --git a/tests/chains/Ronin/TWAnyAddressTests.cpp b/tests/chains/Ronin/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..8bdcd75d68f --- /dev/null +++ b/tests/chains/Ronin/TWAnyAddressTests.cpp @@ -0,0 +1,58 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include + +#include "Ronin/Address.h" +#include "Ronin/Entry.h" + +#include + +const auto roninPrefixChecksummed = "ronin:EC49280228b0D05Aa8e8b756503254e1eE7835ab"; + +const auto gTests = { + roninPrefixChecksummed, + "ronin:ec49280228b0d05aa8e8b756503254e1ee7835ab", + "0xEC49280228b0D05Aa8e8b756503254e1eE7835ab", + "ronin:0xEC49280228b0D05Aa8e8b756503254e1eE7835ab", +}; + +TEST(RoninAnyAddress, Validate) { + for (const auto& t: gTests) { + EXPECT_TRUE(TWAnyAddressIsValid(STRING(t).get(), TWCoinTypeRonin)); + } +} + +TEST(RoninAnyAddress, Normalize) { + for (const auto& t: gTests) { + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(STRING(t).get(), TWCoinTypeRonin)); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + EXPECT_TRUE(TWStringEqual(string2.get(), STRING(roninPrefixChecksummed).get())); + + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "ec49280228b0d05aa8e8b756503254e1ee7835ab"); + } +} + +TEST(RoninAnyAddress, Invalid) { + const auto tests = { + "EC49280228b0D05Aa8e8b756503254e1eE7835ab", // no prefix + "ec49280228b0d05aa8e8b756503254e1ee7835ab", // no prefix + "roni:EC49280228b0D05Aa8e8b756503254e1eE7835ab", // wrong prefix + "ronin=EC49280228b0D05Aa8e8b756503254e1eE7835ab", // wrong prefix + "0xronin:EC49280228b0D05Aa8e8b756503254e1eE7835ab", // wrong prefix + "EC49280228b0D05Aa8e8b756503254e1eE7835", // too short + "ronin:EC49280228b0D05Aa8e8b756503254e1eE7835", // too short + "ronin:ec49280228b0d05aa8e8b756503254e1ee7835", // too short + }; + + for (const auto& t: tests) { + EXPECT_FALSE(TWAnyAddressIsValid(STRING(t).get(), TWCoinTypeRonin)); + } +} diff --git a/tests/chains/Ronin/TWAnySignerTests.cpp b/tests/chains/Ronin/TWAnySignerTests.cpp new file mode 100644 index 00000000000..cabaf560b68 --- /dev/null +++ b/tests/chains/Ronin/TWAnySignerTests.cpp @@ -0,0 +1,48 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Ethereum.pb.h" +#include "uint256.h" +#include "TestUtilities.h" +#include + +#include + +namespace TW::Ronin::tests { + +TEST(TWAnySignerRonin, Sign) { + // https://explorer.roninchain.com/tx/0xf13a2c4421700f8782ca73eaf16bb8baf82bcf093e23570a1ff062cdd8dbf6c3 + Ethereum::Proto::SigningInput input; + auto chainId = store(uint256_t(2020)); + auto nonce = store(uint256_t(0)); + auto gasPrice = store(uint256_t(1000000000)); + auto gasLimit = store(uint256_t(21000)); + auto key = parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646"); + + input.set_chain_id(chainId.data(), chainId.size()); + input.set_nonce(nonce.data(), nonce.size()); + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(gasLimit.data(), gasLimit.size()); + input.set_private_key(key.data(), key.size()); + input.set_to_address("ronin:c36edf48e21cf395b206352a1819de658fd7f988"); + + auto& transfer = *input.mutable_transaction()->mutable_transfer(); + auto amount = store(uint256_t(276447)); + transfer.set_amount(amount.data(), amount.size()); + + std::string expected = "f86880843b9aca0082520894c36edf48e21cf395b206352a1819de658fd7f988830437df80820feca0442aa06b0d0465bfecf84b28e2ce614a32a1ccc12735dc03a5799517d6659d7aa004e1bf2efa30743f1b6d49dbec2671e9fb5ead1e7da15e352ca1df6fb86a8ba7"; + + // sign test + Ethereum::Proto::SigningOutput output; + + ANY_SIGN(input, TWCoinTypeRonin); + + ASSERT_EQ(hex(output.encoded()), expected); + EXPECT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeRonin)); +} + +} // namespace TW::Ronin::tests diff --git a/tests/chains/Ronin/TWCoinTypeTests.cpp b/tests/chains/Ronin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..3549ef495a0 --- /dev/null +++ b/tests/chains/Ronin/TWCoinTypeTests.cpp @@ -0,0 +1,30 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWRoninCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeRonin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xc09835aaf9c1cacea8ce322865583c791d3a4dfbd9a3b72f79539db88d3697ab")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeRonin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xdc05ecd5fbdb64058d94f3182d66f44342b9adcb")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeRonin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeRonin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeRonin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeRonin), 18); + ASSERT_EQ(TWBlockchainRonin, TWCoinTypeBlockchain(TWCoinTypeRonin)); + + assertStringsEqual(symbol, "RON"); + assertStringsEqual(txUrl, "https://explorer.roninchain.com/tx/0xc09835aaf9c1cacea8ce322865583c791d3a4dfbd9a3b72f79539db88d3697ab"); + assertStringsEqual(accUrl, "https://explorer.roninchain.com/address/0xdc05ecd5fbdb64058d94f3182d66f44342b9adcb"); + assertStringsEqual(id, "ronin"); + assertStringsEqual(name, "Ronin"); +} diff --git a/tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp b/tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..f08f7466371 --- /dev/null +++ b/tests/chains/SmartBitcoinCash/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWSmartBitcoinCashCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeSmartBitcoinCash)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x6413466b455b17d03c7a8ce2d7f99fec34bcd338628bdd2d0580a21e3197a4d9")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeSmartBitcoinCash, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xFeEc227410E1DF9f3b4e6e2E284DC83051ae468F")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeSmartBitcoinCash, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeSmartBitcoinCash)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeSmartBitcoinCash)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeSmartBitcoinCash), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeSmartBitcoinCash)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeSmartBitcoinCash)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeSmartBitcoinCash)); + assertStringsEqual(symbol, "BCH"); + assertStringsEqual(txUrl, "https://www.smartscan.cash/tx/0x6413466b455b17d03c7a8ce2d7f99fec34bcd338628bdd2d0580a21e3197a4d9"); + assertStringsEqual(accUrl, "https://www.smartscan.cash/address/0xFeEc227410E1DF9f3b4e6e2E284DC83051ae468F"); + assertStringsEqual(id, "smartbch"); + assertStringsEqual(name, "Smart Bitcoin Cash"); +} diff --git a/tests/chains/Solana/AddressTests.cpp b/tests/chains/Solana/AddressTests.cpp new file mode 100644 index 00000000000..15f011299f9 --- /dev/null +++ b/tests/chains/Solana/AddressTests.cpp @@ -0,0 +1,62 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base58.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "Solana/Address.h" +#include "Solana/Program.h" + +#include + +using namespace std; +using namespace TW; + +namespace TW::Solana::tests { + +TEST(SolanaAddress, FromPublicKey) { + const auto addressString = "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"; + const auto publicKey = PublicKey(Base58::bitcoin.decode(addressString), TWPublicKeyTypeED25519); + const auto address = Address(publicKey); + ASSERT_EQ(addressString, address.string()); +} + +TEST(SolanaAddress, FromString) { + string addressString = "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"; + const auto address = Address(addressString); + ASSERT_EQ(address.string(), addressString); +} + +TEST(SolanaAddress, isValid) { + ASSERT_TRUE(Address::isValid("2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST")); + ASSERT_FALSE(Address::isValid( + "2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdSl")); // Contains invalid base-58 character + ASSERT_FALSE( + Address::isValid("2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpd")); // Is invalid length +} + +TEST(SolanaAddress, isValidOnCurve) { + EXPECT_TRUE(PublicKey(Base58::bitcoin.decode("HzqnaMjWFbK2io6WgV2Z5uBguCBU21RMUS16wsDUHkon"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_TRUE(PublicKey(Base58::bitcoin.decode("68io7dTfyeWua1wD1YcCMka4y5iiChceaFRCBjqCM5PK"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_TRUE(PublicKey(Base58::bitcoin.decode("Dra34QLFCjxnk8tUNcBwxs6pgb5spF4oseQYF2xn7ABZ"), TWPublicKeyTypeED25519).isValidED25519()); + // negative case + EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_FALSE(PublicKey(Base58::bitcoin.decode("AbygL37RheNZv327cMvZPqKYLLkZ6wqWYexRxgNiZyeP"), TWPublicKeyTypeED25519).isValidED25519()); +} + +TEST(SolanaAddress, defaultTokenAddress) { + const Address serumToken = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + EXPECT_EQ(Address("HBYC51YrGFAZ8rM7Sj8e9uqKggpSrDYrinQDZzvMtqQp").defaultTokenAddress(serumToken).string(), + "6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"); + EXPECT_EQ(Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V").defaultTokenAddress(serumToken).string(), + "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + EXPECT_EQ(Address("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ").defaultTokenAddress(serumToken).string(), + "ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); +} + +} // namespace TW::Solana::tests diff --git a/tests/Solana/ProgramTests.cpp b/tests/chains/Solana/ProgramTests.cpp similarity index 76% rename from tests/Solana/ProgramTests.cpp rename to tests/chains/Solana/ProgramTests.cpp index 0a5c413dafd..f943622e28b 100644 --- a/tests/Solana/ProgramTests.cpp +++ b/tests/chains/Solana/ProgramTests.cpp @@ -4,16 +4,17 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "Solana/Address.h" -#include "Solana/Program.h" #include "Base58.h" #include "PrivateKey.h" +#include "Solana/Address.h" +#include "Solana/Program.h" #include using namespace std; using namespace TW; -using namespace TW::Solana; + +namespace TW::Solana::tests { TEST(SolanaStakeProgram, addressFromValidatorSeed) { auto user = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); @@ -43,7 +44,7 @@ TEST(SolanaStakeProgram, addressFromRecentBlockhash) { TEST(SolanaTokenProgram, defaultTokenAddress) { const Address serumToken = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); EXPECT_EQ(TokenProgram::defaultTokenAddress(Address("HBYC51YrGFAZ8rM7Sj8e9uqKggpSrDYrinQDZzvMtqQp"), serumToken).string(), - "6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"); + "6X4X1Ae24mkoWeCEpktevySVG9jzeCufut5vtUW3wFrD"); } TEST(SolanaTokenProgram, findProgramAddress) { @@ -52,21 +53,30 @@ TEST(SolanaTokenProgram, findProgramAddress) { Base58::bitcoin.decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), Base58::bitcoin.decode("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), }; - Address address = TokenProgram::findProgramAddress(seeds, Address("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")); - EXPECT_EQ(address.string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + { + Address address = TokenProgram::findProgramAddress(seeds, Address("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")); + EXPECT_EQ(address.string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + } + { + Address address = TokenProgram::findProgramAddress(seeds, Address("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")); + EXPECT_EQ(address.string(), "CuS1kE1wvGTmwGk3FYNQK85g4jU7gMySWwFRQQ9LFunp"); + } } TEST(SolanaTokenProgram, createProgramAddress) { + std::vector seeds4 = { + Base58::bitcoin.decode("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"), + Base58::bitcoin.decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), + Base58::bitcoin.decode("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), + Data{255}}; { - std::vector seeds = { - Base58::bitcoin.decode("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"), - Base58::bitcoin.decode("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"), - Base58::bitcoin.decode("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"), - Data{255} - }; - Address address = TokenProgram::createProgramAddress(seeds, Address("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")); + Address address = TokenProgram::createProgramAddress(seeds4, Address("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")); EXPECT_EQ(address.string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); } + { + Address address = TokenProgram::createProgramAddress(seeds4, Address("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr")); + EXPECT_EQ(address.string(), "CuS1kE1wvGTmwGk3FYNQK85g4jU7gMySWwFRQQ9LFunp"); + } // https://github.com/solana-labs/solana/blob/f25c969ad87e64e6d1fd07d2d37096ac71cf8d06/sdk/program/src/pubkey.rs#L353-L435 { std::vector seeds = {TW::data(""), {1}}; @@ -84,3 +94,5 @@ TEST(SolanaTokenProgram, createProgramAddress) { EXPECT_EQ(address.string(), "GUs5qLUfsEHkcMB9T38vjr18ypEhRuNWiePW2LoK4E3K"); } } + +} // namespace TW::Solana::tests diff --git a/tests/chains/Solana/SignerTests.cpp b/tests/chains/Solana/SignerTests.cpp new file mode 100644 index 00000000000..875300fb5e2 --- /dev/null +++ b/tests/chains/Solana/SignerTests.cpp @@ -0,0 +1,417 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Solana/Signer.h" +#include "Solana/Transaction.h" +#include "Solana/Program.h" +#include "HexCoding.h" +#include "PublicKey.h" + +#include + +using namespace TW; +namespace TW::Solana::tests { + +TEST(SolanaSigner, CompiledInstruction) { + const auto privateKey0 = + PrivateKey(Base58::bitcoin.decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); + const auto publicKey0 = privateKey0.getPublicKey(TWPublicKeyTypeED25519); + const auto address0 = Address(publicKey0); + ASSERT_EQ(Data(publicKey0.bytes.begin(), publicKey0.bytes.end()), + Base58::bitcoin.decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); + const auto privateKey1 = + PrivateKey(Base58::bitcoin.decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); + const auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeED25519); + const auto address1 = Address(publicKey1); + ASSERT_EQ(Data(publicKey1.bytes.begin(), publicKey1.bytes.end()), + Base58::bitcoin.decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); + Address programId("11111111111111111111111111111111"); + + std::vector
addresses = {address0, address1, programId}; + + std::vector instrAddresses = { + AccountMeta(address1, false, false), + AccountMeta(address0, false, false), + AccountMeta(programId, false, false), + AccountMeta(address1, false, false), + AccountMeta(address0, false, false), + }; + Data data = {0, 1, 2, 4}; + Instruction instruction(programId, instrAddresses, data); + + auto compiledInstruction = CompiledInstruction(instruction, addresses); + + EXPECT_EQ(compiledInstruction.programIdIndex, 2); + ASSERT_EQ(compiledInstruction.accounts.size(), 5ul); + EXPECT_EQ(compiledInstruction.accounts[0], 1); + EXPECT_EQ(compiledInstruction.accounts[1], 0); + EXPECT_EQ(compiledInstruction.accounts[2], 2); + EXPECT_EQ(compiledInstruction.accounts[3], 1); + EXPECT_EQ(compiledInstruction.accounts[4], 0); + ASSERT_EQ(compiledInstruction.data.size(), 4ul); +} + +TEST(SolanaSigner, CompiledInstructionFindAccount) { + Address address1 = Address(parse_hex("0102030405060708090a0102030405060708090a0102030405060708090a0101")); + Address address2 = Address(parse_hex("0102030405060708090a0102030405060708090a0102030405060708090a0102")); + Address address3 = Address(parse_hex("0102030405060708090a0102030405060708090a0102030405060708090a0103")); + Address programId("11111111111111111111111111111111"); + // clang-format off + Instruction instruction(programId, std::vector{ + AccountMeta(address1, true, false), + AccountMeta(address2, false, false), + }, Data{1, 2, 3, 4}); + // clang-format on + std::vector
addresses = { + address1, + address2, + programId, + }; + CompiledInstruction compiledInstruction = CompiledInstruction(instruction, addresses); + ASSERT_EQ(compiledInstruction.findAccount(address1), 0); + ASSERT_EQ(compiledInstruction.findAccount(address2), 1); + ASSERT_EQ(compiledInstruction.findAccount(programId), 2); + // negative case + try { + compiledInstruction.findAccount(address3); + FAIL() << "Missing expected exception"; + } catch (...) { + // ok + } +} + +TEST(SolanaSigner, SingleSignTransaction) { + const auto privateKey = + PrivateKey(Base58::bitcoin.decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), + Base58::bitcoin.decode("7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q")); + + const auto from = Address(publicKey); + auto to = Address("EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto transaction = Transaction(from, to, 42, recentBlockhash); + + std::vector signerKeys; + signerKeys.push_back(privateKey); + Signer::sign(signerKeys, transaction); + + std::vector expectedSignatures; + Signature expectedSignature( + "5T6uZBHnHFd8uWErDBTFRVkbKuhbcm94K5MJ2beTYDruzqv4FjS7EMKvC94ZfxNAiWUXZ6bZxS3WXUbhJwYNPWn"); + expectedSignatures.push_back(expectedSignature); + ASSERT_EQ(transaction.signatures, expectedSignatures); + + auto expectedString = + "3p2kzZ1DvquqC6LApPuxpTg5CCDVPqJFokGSnGhnBHrta4uq7S2EyehV1XNUVXp51D69GxGzQZU" + "jikfDzbWBG2aFtG3gHT1QfLzyFKHM4HQtMQMNXqay1NAeiiYZjNhx9UvMX4uAQZ4Q6rx6m2AYfQ" + "7aoMUrejq298q1wBFdtS9XVB5QTiStnzC7zs97FUEK2T4XapjF1519EyFBViTfHpGpnf5bfizDz" + "sW9kYUtRDW1UC2LgHr7npgq5W9TBmHf9hSmRgM9XXucjXLqubNWE7HUMhbKjuBqkirRM"; + ASSERT_EQ(transaction.serialize(), expectedString); + + const auto additionalPrivateKey = + PrivateKey(Base58::bitcoin.decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); + signerKeys.push_back(additionalPrivateKey); + try { + Signer::sign(signerKeys, transaction); + FAIL() << "publicKey not found in message.accountKeys"; + } catch (std::invalid_argument const& err) { + EXPECT_EQ(err.what(), std::string("publicKey not found in message.accountKeys")); + } +} + +TEST(SolanaSigner, SignTransactionToSelf) { + const auto privateKey = + PrivateKey(Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), + Base58::bitcoin.decode("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu")); + + const auto from = Address(publicKey); + auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto transaction = Transaction(from, to, 42, recentBlockhash); + + std::vector signerKeys; + signerKeys.push_back(privateKey); + Signer::sign(signerKeys, transaction); + + std::vector expectedSignatures; + Signature expectedSignature( + "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); + expectedSignatures.push_back(expectedSignature); + ASSERT_EQ(transaction.signatures, expectedSignatures); + + auto expectedString = + "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqe" + "EFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8n" + "UkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"; + ASSERT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaSigner, MultipleSignTransaction) { + const auto privateKey0 = + PrivateKey(Base58::bitcoin.decode("96PKHuMPtniu1T74RvUNkbDPXPPRZ8Mg1zXwciCAyaDq")); + const auto publicKey0 = privateKey0.getPublicKey(TWPublicKeyTypeED25519); + const auto address0 = Address(publicKey0); + ASSERT_EQ(Data(publicKey0.bytes.begin(), publicKey0.bytes.end()), + Base58::bitcoin.decode("GymAh18wHuFTytfSJWi8eYTA9x5S3sNb9CJSGBWoPRE3")); + const auto privateKey1 = + PrivateKey(Base58::bitcoin.decode("GvGmNPMQLZE2VNx3KG2GdiC4ndS8uCqd7PjioPgm9Qhi")); + const auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeED25519); + const auto address1 = Address(publicKey1); + ASSERT_EQ(Data(publicKey1.bytes.begin(), publicKey1.bytes.end()), + Base58::bitcoin.decode("2oKoYSAHgveX91917v4DUEuN8BNKXDg8KJWpaGyEay9V")); + + Data data = {0, 0, 0, 0}; + Address programId("11111111111111111111111111111111"); + std::vector instrAddresses = { + AccountMeta(address0, true, false), + AccountMeta(address1, false, false), + }; + Instruction instruction(programId, instrAddresses, data); + std::vector instructions = {instruction}; + + MessageHeader header = {2, 0, 1}; + std::vector
accountKeys = {address0, address1, programId}; + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + Message message; + message.header = header; + message.accountKeys = accountKeys; + message.recentBlockhash = recentBlockhash; + message.instructions = instructions; + message.compileInstructions(); + + auto transaction = Transaction(message); + + std::vector signerKeys; + // Sign order should not matter + signerKeys.push_back(privateKey1); + signerKeys.push_back(privateKey0); + Signer::sign(signerKeys, transaction); + + std::vector expectedSignatures; + Signature expectedSignature0( + "37beWPhNMfWUz75Tb24TX3PCS89FZscbCgwwLpFnzVfZYPqDpAWruvqzc9eeQYft35H23Vm9Tv1dPwEKWT3vAVPb"); + expectedSignatures.push_back(expectedSignature0); + Signature expectedSignature1( + "5NxQshVaAXtQ8YVdcBtCanT62KbxnRfhubjGndFvetgn9AiaoLVZvRGutR5D7FJebRxq8bd6nQXn59LFzavEUrdQ"); + expectedSignatures.push_back(expectedSignature1); + ASSERT_EQ(transaction.signatures, expectedSignatures); + + auto expectedString = + "oL2CmkcP9xf2DiU7eo6hh3JdHnX3NGjunheXYo6SjVchzc8LtFJpPs4jccWUd7oPZUPQNTcR7Ee" + "Hn259ror9A7aXgJdP4djhntoD8irF1kuBZCj7pubtoWfiAKzagSL4hChQsTSe7e9jaGtoXu58mP" + "HCMKTz55TLjhdmCj7ixoWRowWEzkrF49MxXnurb4yf6ASru1XdHPFn3DdzkRHgypYwvRM6ci8p2" + "7trQvXFukhWX6qG6JkxqsWYSzACcAAGGWfAxSi63Yx1RxkxGUzyxy5f2thQhWZ6Nx6pR1im65yV" + "YMYPXj94kgtHxXw9h5V4p7xSAwRpmhw4jewYyQVX4jmnfro3gFNdX9AqpqMs4uGHA4rZM"; + ASSERT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaSigner, SignUpdateBlockhash) { + const auto privateKey = + PrivateKey(Base58::bitcoin.decode("G4VSzrknPBWZ1z2YwUnWTxD1td7wmqR5jMPEJRN6wm8S")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), + Base58::bitcoin.decode("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5")); + + const auto from = Address(publicKey); + auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto transaction = Transaction(from, to, 42, recentBlockhash); + + std::vector signerKeys; + signerKeys.push_back(privateKey); + Signer::sign(signerKeys, transaction); + + Solana::Hash newBlockhash("GgBaCs3NCBuZN12kCJgAW63ydqohFkHEdfdEXBPzLHq"); + Signer::signUpdateBlockhash(signerKeys, transaction, newBlockhash); + + std::vector expectedSignatures; + Signature expectedSignature( + "5AFhXjvGdENXCAe9MPvUA2qjoL4XtZwZKG7kK2HmZf1ibpxjx5kzogHZjN39uYB9J33UFJN15KhSggBZhzyNQmta"); + expectedSignatures.push_back(expectedSignature); + ASSERT_EQ(transaction.signatures, expectedSignatures); + + auto expectedString = + "62ABadDCoPfGGRnhLoBhfcPekMHyN5ee8DgTY8wD4iwKDjyFAsNbsaahTcqMWxmwa61q9iAGCQB" + "v1bETcYzWsTwLKMVGLoEpwqA84mPjqHyr5sQD5dcghyQiQ1ckYNub9K7s8FspVwwowK8gJG69xe" + "DEaqi7G1zrChBVbQYTmVUwJETyDmP1Vs8QU3CaxBs8qwcxoziU52KWLBpRj9o38QVBdxJtJ7hig" + "hgPKJubfqUfTWdN94PzqEfyPqwoCpFD39nvBn8C5xe1caPKivicg6U7Lzm9s8RYTLCEB"; + ASSERT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaSigner, SignRawMessage) { + const auto privateKey = + PrivateKey(Base58::bitcoin.decode("GjXseuD8JavBjKMdd6GEsPYZPV7tMMa46GS2JRS5tHRq")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + ASSERT_EQ(Data(publicKey.bytes.begin(), publicKey.bytes.end()), + Base58::bitcoin.decode("3BocAWPm1oNXN5qkAV4QeDUmAPpkTcN1rrmCMWAfsXJY")); + + auto rawMessageData = + "01000203207be13c43c4528592eaf3fd34e064c641c5be3cb6691877d7ade94dff36734108eaea30723c33b525" + "07bc54024910612f885e4c80c10b99a047fd42c0acbace00000000000000000000000000000000000000000000" + "000000000000000000000404040404040404040404040404040404040404040404040404040404040404010202" + "00010c020000002a00000000000000"; + + std::vector signerKeys; + signerKeys.push_back(privateKey); + Data rawTransaction = Signer::signRawMessage(signerKeys, parse_hex(rawMessageData)); + + auto expectedHex = + "016e7f8349977b482bccf0bfc202ad917295803831e59ccb865b97d657464791ebfe3336879b84b9f165e464a3" + "4751fe30d54b01f3c9f33f969aafe1e85951b10901000203207be13c43c4528592eaf3fd34e064c641c5be3cb6" + "691877d7ade94dff36734108eaea30723c33b52507bc54024910612f885e4c80c10b99a047fd42c0acbace0000" + "000000000000000000000000000000000000000000000000000000000000040404040404040404040404040404" + "040404040404040404040404040404040401020200010c020000002a00000000000000"; + ASSERT_EQ(hex(rawTransaction), expectedHex); +} + +TEST(SolanaSigner, SignDelegateStakeV2) { + const auto privateKeySigner = + PrivateKey(Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); + const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); + auto signer = Address(publicKeySigner); + ASSERT_EQ(signer.string(), "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + + auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); + auto programId = Address("Stake11111111111111111111111111111111111111"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); + + auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); + auto transaction = Transaction(message); + + std::vector signerKeys; + signerKeys.push_back(privateKeySigner); + Signer::sign(signerKeys, transaction); + + std::vector expectedSignatures; + Signature expectedSignature("58iogHzSJZmvTxi71W8k2yZXSPVfGAgtgqrk1RaBtfVFewU9yiJCkvSF1Hhjyax5DuexzR7ryWZDAWKQ73pyqvMs"); + expectedSignatures.push_back(expectedSignature); + EXPECT_EQ(transaction.signatures, expectedSignatures); + + auto expectedString = "j24mVM9Zgu5vDZhPLGGuCRXQnP9djNtxdHh4txN3S7dwJsNNL5fbhzGpPgSUAcLGoMVCfF9TuqTYfpfJnb4sJFe1ahM8yPL5HwuKL6py5AZJFi8SWx9fvaVB699dCPo1GT3JoEBLPCZ9o2jQtnwzLkzTYJnKv2axqhKWFE2sz6TBA5J39eZcjMFUYgyxz6Q5S4MWqYQCb8UET2NAEZoKcfy7j8N25WXL6Gj4j3hBZjpHQQNaGaNEprEqyma3ZuVhpGiCALSsuzVLX3wZVo4icXwe952deMFA4tH3BK1jcSQCgfmcKDJ9nd7bdrnUUs4BoMdF1uDZB5LxE2UH8QiqtYvaUcorF4SJ3gPxM5ykbyPsNK1cSYZF9NMpW2GofyC17eELwnHQTQB2kqphxJZu7BahvkwiDPPeeydiXAkBspJ3nc3PCBujv6WJw22ZHw5j6zAP8ZGnCW44pqtWD5qifF9tTKhySKdANNiWifs3tSCCPQqjfJXu14drNinR6VG8rJxS1qgmRYiRQUa7m1vtoaZFRN5qKUeAfoFKkAVaNnMdwgsNqNH4dqBodTCJFs1LkYwhgRZdZGbwXTn1j7vpR3DSnv4g72i2H556srzK53jdUmdv6yfxt516XDSshqZtHnKZ1tudxKjBXwsqT3imDiZFVka9wKWUAYMCi4XZ79CY6Xpsd9c18U2e9TCngQmgkTATFgrqysfraokNffgqWxvsPMugksbvbPjJs3iCzByvphkC9p7hCf6LwbeF8XnVB91EAgRDA4VLE1f9wkcq5zjy879YWJ4r516h3PQszTz1EaJXNAXdbk5Em7eyuuabGP1Q3nijFTL2yhMDsXpgrjAuEAABNxFMd4J1JRMaic615mHrhwociksrsfQK"; + EXPECT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaSigner, SignDelegateStakeV1) { + const auto privateKeySigner = + PrivateKey(Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746")); + const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); + auto signer = Address(publicKeySigner); + ASSERT_EQ(signer.string(), "zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + + auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); + auto programId = Address("Stake11111111111111111111111111111111111111"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); + + auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); + auto transaction = Transaction(message); + + std::vector signerKeys; + signerKeys.push_back(privateKeySigner); + Signer::sign(signerKeys, transaction); + + std::vector expectedSignatures; + Signature expectedSignature("gDPbnakbktrASmnUwKGpmftvQRbcyAvxyAyVXq3oVLfAdTPDqY8hhLPHTgidEZGWcmiaXnEyKg2GQLkkAh3JYr3"); + expectedSignatures.push_back(expectedSignature); + EXPECT_EQ(transaction.signatures, expectedSignatures); + + auto expectedString = "TKPiN35HzeD3zdwxDFvnkgoqud7CZsda15JkBwM4nDpr623rM7MZsH6QvMMyKpiz7MeRNTrfyHkRLQSBT9Tbg2mgTdfrbhhqeF3Suu5ECphqn8DFYPoMnFzeg5u9gaqevfjhuizzeo2YDJF8aVGy1pez8gMbp5vHz1SuvQUgfcvFctggUMwNiJorSmmp3N6TzQSd38CZrA8ZLhaJjuwDwVMjmj18rGTV1gkX19L7byTFrus2vNvPeUa2AawwUnFpYMPgvCKkHTrpnjvypjoLof9yMUFQ5M1S3Ntv53KJyXwXq6ejJnBDtisnDcdMDNSZp3VeKz6XCr8XVM5xNVh3LX12V4kc3ueqkokYJLP1JmuhA3nNZA1G5KTNno93HUoBkEa1x5h3haoCSgmQC97LoJbJM6B6C2NbaDj2J6iiTaVQdin4He4Jpj575WDhNTqsLjzFUHPUHQF1CRnuss8UpVyMsa4kdVqCDQGeh5DKbkikgcB8GKPBuC91DRxGEqgoygNsu5nnQy4o3YAJnBBK6HsKxpdjbYD8wCUdLw8muhjpEqeBTPShEaogm9zfehidiCcnxbeoX3gmW8oH9gpWoX7GrkJgF6Wn7iWohmrzqzAjoBz8hpeY5nkkhHrf9iswVGMpakdLGy3YxkGJVpsW8KJACwEKXGLq8SVLtXSUHG8EP16zfYHxKjkCSs8PkdFsA5esxsxppPTVZivuEPqJ5og55aNmugdNDrAFYWdcH1Q4rm7BXN6oHECdz2yY4HFVWh9u592oqozt2gQKu3vmhcNFzzQe1xgs6zKSv38kSGTnipd7Hx2VL3qNAR6XBRiwAi226qSTzxi6R82p7cMB7TMy6fk5AZ3sXDSXFNJ9S5SSU1V63ruw75QMtVio"; + EXPECT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaSigner, SignCreateTokenAccount) { + const auto privateKeySigner = + PrivateKey(Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); + const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); + auto signer = Address(publicKeySigner); + EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + + auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + Solana::Hash recentBlockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + + auto message = Message::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); + auto transaction = Transaction(message); + + std::vector signerKeys; + signerKeys.push_back(privateKeySigner); + Signer::sign(signerKeys, transaction); + + std::vector expectedSignatures; + Signature expectedSignature("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); + expectedSignatures.push_back(expectedSignature); + EXPECT_EQ(transaction.signatures, expectedSignatures); + + auto expectedString = + // test data obtained from spl-token create-account + "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVnH2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtCjAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzjB2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2TN3xXwu1"; + EXPECT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaSigner, SignCreateTokenAccountForOther_3E6UFV) { + const auto privateKeySigner = + PrivateKey(parse_hex("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7")); + const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); + auto signer = Address(publicKeySigner); + EXPECT_EQ(signer.string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); + + auto otherMainAddress = Address("3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft"); + auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + auto tokenAddress = Address("67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3"); + Solana::Hash recentBlockhash("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); + + auto message = Message::createTokenCreateAccount(signer, otherMainAddress, token, tokenAddress, recentBlockhash); + auto transaction = Transaction(message); + + std::vector signerKeys; + signerKeys.push_back(privateKeySigner); + Signer::sign(signerKeys, transaction); + + auto expectedString = + // https://explorer.solana.com/tx/3E6UFVamHCm6Bgk8gXdZex7R7tJAVxqJm6t9ephAKu1PjcfZrD7CJqMwKu6RrvWSUESbZFqzdUyLXuxAFaawPHvJ + "4BsrHedHuForcKDhLdnLYDXgtQgQEj3EQVDtEhqa7o6ukFjW3shpTWv6PeKQdMp6af4ASjD4xQeZvXxLK5WUjguVMUf3xdJn7RnFeM7hdDJ56RDBM5PRJbRJVHjz6FJ7SVNTvr9y3gVYQtWx7NfKRxiyEAfq9JG7nqxSWaW6raMr9t35aVcdAVuXE9iXj3rzhVfCS69vVzy5KcFEK3mvDYG6L12V2CfviCydmeCvPw5r3zBUrZSQv7Ti4XFNBrPbk28gcqQwsBknBqasHxHqD9VUyPmBTuUyXq75QN8rhqN55NjxKBUw37tEUS1jKVpWnTeLFq1eRAMdXvjftNuQ5Bmm8Zc12PGWj9vdorBaYyvZXexJST5xNjR4SCkXvXZoRScETck95chv3VBn54jP8DpB4GGUmATFKSxpdtnNV64i1SQXW13KJwswthJvAaDiqevQLKLkvrTEAdb4BxEfPkFjDVti6P58rTZCMg5CTVLqdmWwpTSW5V"; + EXPECT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaSigner, SignTransferToken_3vZ67C) { + const auto privateKeySigner = + PrivateKey(Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5")); + const auto publicKeySigner = privateKeySigner.getPublicKey(TWPublicKeyTypeED25519); + auto signer = Address(publicKeySigner); + EXPECT_EQ(signer.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + + auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + auto senderTokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); + uint64_t amount = 4000; + uint8_t decimals = 6; + Solana::Hash recentBlockhash("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); + + auto message = Message::createTokenTransfer(signer, token, + senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); + auto transaction = Transaction(message); + + std::vector signerKeys; + signerKeys.push_back(privateKeySigner); + Signer::sign(signerKeys, transaction); + + std::vector expectedSignatures; + Signature expectedSignature("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); + expectedSignatures.push_back(expectedSignature); + EXPECT_EQ(transaction.signatures, expectedSignatures); + + auto expectedString = + // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg + // test data obtained from spl-token transfer + "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jRe2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvPiPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMqDV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnotcrH7pUa94xCVvCPPaomF"; + EXPECT_EQ(transaction.serialize(), expectedString); +} + +} // namespace TW::Solana::tests diff --git a/tests/chains/Solana/TWAnySignerTests.cpp b/tests/chains/Solana/TWAnySignerTests.cpp new file mode 100644 index 00000000000..8246f8f48ce --- /dev/null +++ b/tests/chains/Solana/TWAnySignerTests.cpp @@ -0,0 +1,397 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base58.h" +#include "HexCoding.h" +#include "proto/Solana.pb.h" +#include "Solana/Address.h" +#include "Solana/Program.h" +#include "PrivateKey.h" +#include "TestUtilities.h" +#include + +#include + +using namespace TW; +namespace TW::Solana::tests { + +const auto expectedString1 = + "3p2kzZ1DvquqC6LApPuxpTg5CCDVPqJFokGSnGhnBHrta4uq7S2EyehV1XNUVXp51D69GxGzQZU" + "jikfDzbWBG2aFtG3gHT1QfLzyFKHM4HQtMQMNXqay1NAeiiYZjNhx9UvMX4uAQZ4Q6rx6m2AYfQ" + "7aoMUrejq298q1wBFdtS9XVB5QTiStnzC7zs97FUEK2T4XapjF1519EyFBViTfHpGpnf5bfizDz" + "sW9kYUtRDW1UC2LgHr7npgq5W9TBmHf9hSmRgM9XXucjXLqubNWE7HUMhbKjuBqkirRM"; + +TEST(TWAnySignerSolana, SignTransfer) { + auto privateKey = Base58::bitcoin.decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); + auto input = Proto::SigningInput(); + + auto& message = *input.mutable_transfer_transaction(); + message.set_recipient("EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd"); + message.set_value((uint64_t)42L); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + ASSERT_EQ(output.encoded(), expectedString1); +} + +TEST(TWAnySignerSolana, SignTransferToSelf) { + auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Proto::SigningInput(); + + auto& message = *input.mutable_transfer_transaction(); + message.set_recipient("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + message.set_value((uint64_t)42L); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = + "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqe" + "EFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8n" + "UkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignTransferWithMemoAndReference) { + const auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Solana::Proto::SigningInput(); + + auto& message = *input.mutable_transfer_transaction(); + message.set_recipient("71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd"); + message.set_value((uint64_t)10000000L); + message.set_memo("HelloSolanaMemo"); + message.add_references("CuieVDEDtLo7FypA9SbLM9saXFdb1dsshEkyErMqkRQq"); + message.add_references("tFpP7tZUt6zb7YZPpQ11kXNmsc5YzpMXmahGMvCHhqS"); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + const auto expectedString = "NfNH76sST3nJ4FmFGTZJBUpJou7DRuHM3YNprT1HeEau699CQF65xNf21Hoi491bbtVKUXfqCJyeZhfTCEnABuXNC1JrhGBeCv2AbQdaS9gpp9j4xHHomhCYdwYaBWFMcKkdMXrx9xHqL9Vkny4HezkwQfb3wGqcaE9XVRdkkNxsoJnVKddRnrQbjhsZGTcKdfmbTghoUeRECNPTm6nZTA1owWF1Dq6mfr6M3GZRh4ucqEquxKsQC2HQwNRrGZahsfyUvwspPWwMt78q5Jpjd9kHqkFDspZL6Pepv4dAA4uHhYDCHeP2bbDiFMBYxxWCVDDtRKSh3H92xUgh1GCSgNcjGdbVfQUhSDPX3k9xuuszPTsVZ2GnsavAsRp6Vf6fFEikBX6pVV9zjW1cx94EepQ2aGEBSsVu4RzX7rJjCLCq87h8cxxf1XnF8mvYGEK7wzF"; + EXPECT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignDelegateStakeTransaction_noStakeAccount) { + auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Solana::Proto::SigningInput(); + + auto& message = *input.mutable_delegate_stake_transaction(); + message.set_validator_pubkey("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); + message.set_value((uint64_t)42L); + message.set_stake_account(""); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = "j24mVM9Zgu5vDZhPLGGuCRXQnP9djNtxdHh4txN3S7dwJsNNL5fbhzGpPgSUAcLGoMVCfF9TuqTYfpfJnb4sJFe1ahM8yPL5HwuKL6py5AZJFi8SWx9fvaVB699dCPo1GT3JoEBLPCZ9o2jQtnwzLkzTYJnKv2axqhKWFE2sz6TBA5J39eZcjMFUYgyxz6Q5S4MWqYQCb8UET2NAEZoKcfy7j8N25WXL6Gj4j3hBZjpHQQNaGaNEprEqyma3ZuVhpGiCALSsuzVLX3wZVo4icXwe952deMFA4tH3BK1jcSQCgfmcKDJ9nd7bdrnUUs4BoMdF1uDZB5LxE2UH8QiqtYvaUcorF4SJ3gPxM5ykbyPsNK1cSYZF9NMpW2GofyC17eELwnHQTQB2kqphxJZu7BahvkwiDPPeeydiXAkBspJ3nc3PCBujv6WJw22ZHw5j6zAP8ZGnCW44pqtWD5qifF9tTKhySKdANNiWifs3tSCCPQqjfJXu14drNinR6VG8rJxS1qgmRYiRQUa7m1vtoaZFRN5qKUeAfoFKkAVaNnMdwgsNqNH4dqBodTCJFs1LkYwhgRZdZGbwXTn1j7vpR3DSnv4g72i2H556srzK53jdUmdv6yfxt516XDSshqZtHnKZ1tudxKjBXwsqT3imDiZFVka9wKWUAYMCi4XZ79CY6Xpsd9c18U2e9TCngQmgkTATFgrqysfraokNffgqWxvsPMugksbvbPjJs3iCzByvphkC9p7hCf6LwbeF8XnVB91EAgRDA4VLE1f9wkcq5zjy879YWJ4r516h3PQszTz1EaJXNAXdbk5Em7eyuuabGP1Q3nijFTL2yhMDsXpgrjAuEAABNxFMd4J1JRMaic615mHrhwociksrsfQK"; + EXPECT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignDelegateStakeTransaction_withAccount) { + auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Solana::Proto::SigningInput(); + + auto& message = *input.mutable_delegate_stake_transaction(); + message.set_validator_pubkey("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); + message.set_stake_account("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); + message.set_value((uint64_t)42L); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = "TKPiN35HzeD3zdwxDFvnkgoqud7CZsda15JkBwM4nDpr623rM7MZsH6QvMMyKpiz7MeRNTrfyHkRLQSBT9Tbg2mgTdfrbhhqeF3Suu5ECphqn8DFYPoMnFzeg5u9gaqevfjhuizzeo2YDJF8aVGy1pez8gMbp5vHz1SuvQUgfcvFctggUMwNiJorSmmp3N6TzQSd38CZrA8ZLhaJjuwDwVMjmj18rGTV1gkX19L7byTFrus2vNvPeUa2AawwUnFpYMPgvCKkHTrpnjvypjoLof9yMUFQ5M1S3Ntv53KJyXwXq6ejJnBDtisnDcdMDNSZp3VeKz6XCr8XVM5xNVh3LX12V4kc3ueqkokYJLP1JmuhA3nNZA1G5KTNno93HUoBkEa1x5h3haoCSgmQC97LoJbJM6B6C2NbaDj2J6iiTaVQdin4He4Jpj575WDhNTqsLjzFUHPUHQF1CRnuss8UpVyMsa4kdVqCDQGeh5DKbkikgcB8GKPBuC91DRxGEqgoygNsu5nnQy4o3YAJnBBK6HsKxpdjbYD8wCUdLw8muhjpEqeBTPShEaogm9zfehidiCcnxbeoX3gmW8oH9gpWoX7GrkJgF6Wn7iWohmrzqzAjoBz8hpeY5nkkhHrf9iswVGMpakdLGy3YxkGJVpsW8KJACwEKXGLq8SVLtXSUHG8EP16zfYHxKjkCSs8PkdFsA5esxsxppPTVZivuEPqJ5og55aNmugdNDrAFYWdcH1Q4rm7BXN6oHECdz2yY4HFVWh9u592oqozt2gQKu3vmhcNFzzQe1xgs6zKSv38kSGTnipd7Hx2VL3qNAR6XBRiwAi226qSTzxi6R82p7cMB7TMy6fk5AZ3sXDSXFNJ9S5SSU1V63ruw75QMtVio"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignDeactivateStakeTransaction) { + auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Solana::Proto::SigningInput(); + + auto& message = *input.mutable_deactivate_stake_transaction(); + message.set_stake_account("6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = "6x3fSstNz4GpPxmT5jHXwyD62uyJMKaPWeBDNNcwXZA9NJ3E7KavCXPNUd8ZYTX5VpkfHKGszkwzM6AdAp4giLD29jvWdNYjkV1Nvb42xFwGD6ryMPZzXkJijaRTrA7SvPTDSRU2haGVmorqkywAXLQUCw47NmBUfLTb5gDcKoBeaAsahckv1eCE746thJVTg2dQNvUTULKF6xckUg7kwFkcUuRe4HCcRgrKcNAUKLR2rEM3brVQkUyAaAtMMtc3gVDXxxpbtW5Fa9wGaEnh31FdRo4z5YBzAUaz7vcrvzF2j81KCPTVnYyTmeJzCzJafzCVCtw"; + EXPECT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignDeactivateAllStakeTransaction) { + auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Solana::Proto::SigningInput(); + + auto& message = *input.mutable_deactivate_all_stake_transaction(); + *message.add_stake_accounts() = "CJQStmfyoHbosX1GfVn64yWrNJAo214q2aqxwS6FGh4k"; + *message.add_stake_accounts() = "6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"; + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = "U9azMJWRfDhypoDeQLYWyBYFZCwRNZy8sbrVX9awKK84zNGbSQfYTTJ3ZyzjNUVbU5npbw2MsWfmZGHZRvpfN7G7o3sVePyFRXrmLxrGZzGycFv25Zff4zPxDarbsugbCBgzVGpgwu8x7MdkwBAVHVtNsgMcHgArEAjEmk7YEGpZ15rjo39bCRvmuprWLqSv2SK1RyTZPpTPXVevAbA4i9vvcY8eUbwW29SZCoyGaagLU5EBV9vckMjzGa7gq2yMR6rbq8tDdWaXapYs8RavU49WN94yg4wdE4fzYq8DjqXHq3MuUBLxeYDKJnvj84ioeM4eR1EwjBNrGyz5GHTRuhbNg1nc57SpKsSMVSZW5Ra3tUk84YZXYFHxzeQ9Tv4o"; + EXPECT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignWithdrawStakeTransaction) { + auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Solana::Proto::SigningInput(); + + auto& message = *input.mutable_withdraw_transaction(); + message.set_stake_account("6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"); + message.set_value((uint64_t)42L); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = "gxr4o1trVP8DGG8UC21AA964YqAPFA3rBCF9MwmBQpn5fDtcujM9wp1gzT466MxWGR8wMciS6dSL771q29eURrEEuvhJzRaFDGPLgVB3UL4gd4T2amPQkR4Dzq5drKEtPJRBR86KVVc2kjDsbWNpdL8S7pZqW3VUijAbm9TS8ezG8NExSCkhxExKhUjXWWguEL4qXra7s2JZfhtmvuJneWnEY3isUVfC9knWtGNwpNFvRvzbH2sgHzwtSsD7mkYrBJoazLCwT8r9yypxycHL41XcGtH425MA16kVSunvvBfzG9PzBTS65YJBs64tzttasCU9uEphkwgmfrmoEC8iKt8xD47Ra79RyXd95yURsaxvpb1tVAH8kMNtj8iV1Pfm"; + EXPECT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignWithdrawAllStakeTransaction) { + auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Solana::Proto::SigningInput(); + + auto& message = *input.mutable_withdraw_all_transaction(); + message.add_stake_accounts(); + message.add_stake_accounts(); + message.mutable_stake_accounts(0)->set_stake_account("CJQStmfyoHbosX1GfVn64yWrNJAo214q2aqxwS6FGh4k"); + message.mutable_stake_accounts(0)->set_value((uint64_t)42L); + message.mutable_stake_accounts(1)->set_stake_account("6XMLCn47d5kPi3g4YcjqFvDuxWnpVADpN2tXpeRc4XUB"); + message.mutable_stake_accounts(1)->set_value((uint64_t)67L); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = "cvBNusjtHkR74EfWsvFPEe2Mydcr7eoLeY2wJw2ZMZYViotbb63Adai7UD1PW9uLusoVHGLeJC5cPgVBC4F693P9tPAxLs9yiZj1ZJQ4DgnYbeXafqzjdWje1Ly5FgpDUJaaU2RnLCG51CcrmiTJ4KB5fwai6egZaNjbiqo1DEC1wJz4FgKug2aKQWLdeCiH9WhCuvqfhNV6mEE4qRCkU8uS2gfSqBd1AdrczvoDEbKQszosrwmawxqmvTE5EWaFzMb48x9nLqxvpQCvGQu1nX6FxZJjv2swekA7wGLEAA4uSdFLTHNrYSi8pn8hVYGwESEzth9oiPkJCvW7Y2KvGALeERUZn8knHiz2eqaaT72Ajp9UogMdZtiuFHufveLXpBLWUERchhB7eU1magYcPNHcZuEE4uQv5kZJhHAqYCGU6dyUFLVA9Edus7o6fTktYVCjoGb"; + EXPECT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignDeactivateStakeTransaction_1) { + auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Solana::Proto::SigningInput(); + + auto& message = *input.mutable_deactivate_stake_transaction(); + message.set_stake_account("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); + + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = + "AhfB77PTGTKBfbGPGuEz2khbBy8m8Kou1zqZST9dP7PLJNSeEze5NJuCh5qecPLa3S8xAQ6mTULmnAWiW81ib87nhy" + "wFtx5nKiUvmhdXsvKCSX6NNtNXdRz5yZi3UEop4obco85SY2czS6n4SJwmtDedHLtg9urqdZVth7AUM8KAtrRsksyv" + "ZRYXh64Z8QGyNY7ekj31ae11avGiSDNWYZZHqx7VPWRsKeatGyGk5zPmnRdL8ABMQgJ1Te7wAWwVnNn5QcoAxDuPw6" + "uDctP8Q5S4TieRVatCnukQFj5BTJisez3E2ZJPWhVrMh4K3wEFkPHA7dR"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignWithdrawStakeTransaction_1) { + auto privateKey = Base58::bitcoin.decode("AevJ4EWcvQ6dptBDvF2Ri5pU6QSBjkzSGHMfbLFKa746"); + auto input = Solana::Proto::SigningInput(); + + auto& message = *input.mutable_withdraw_transaction(); + message.set_stake_account("6u9vJH9pRj66N5oJFCBADEbpMTrLxQATcL6q5p5MXwYv"); + message.set_value((uint64_t)42L); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_recent_blockhash("11111111111111111111111111111111"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = "NL7WgagucfLd6AkTtcKe1dqd47xxzF356Q7tEhPrz1LRzZiAmokAaUkpwJ7X71Pmz97zZf9gZQU5BNswdcdpqUL8n1jwn4CoZMaPJhX5LF43Sj817cgreSG14TEWfKertpVpTtc5zY7vkDM7t9wjYhkaqgYz76HQtqAqRHnHF2Qr9EEfLj4zYRerWtyfS3EVyVUaasPxJ5vkcaonEfpGc6uWecaFr2A3YbzEBQpWXjMaXLqmMDtNS8rTNZmwvToa71ddFZKDgaHDcc6Lkg8qriZ3aQbUqL1TbeYp2mk9dWTKY62L1YFE2DyZV5P2qz5feywcMZ9JW6X1wBmiHFCseC42QbnbTibr1VdqLbGx7UWn5tHWk5jCN2aatEPfbFDZ"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignCreateTokenAccount1) { + auto privateKeyData = Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); + ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + + auto input = Solana::Proto::SigningInput(); + auto& message = *input.mutable_create_token_account_transaction(); + message.set_main_address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + message.set_token_address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + input.set_private_key(privateKeyData.data(), privateKeyData.size()); + input.set_recent_blockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = + "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVnH2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtCjAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzjB2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2TN3xXwu1"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignCreateTokenAccount2_5KtPn1) { + auto privateKeyData = parse_hex("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"); + ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); + + auto input = Solana::Proto::SigningInput(); + auto& message = *input.mutable_create_token_account_transaction(); + message.set_main_address("Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); + message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + message.set_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); + input.set_private_key(privateKeyData.data(), privateKeyData.size()); + input.set_recent_blockhash("HxaCmxrXgzkzXYvDFTToENtf9rVKk7cbiuSUqnqNheHq"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = + // https://explorer.solana.com/tx/5KtPn1LGuxhFiwjxErkxTb7XxtLVYUBe6Cn33ej7ATNVyorrkk3UAFJWDBUmzP8CZjmkocCxiMAdYnvrKoGpMsJx + "EoJGDRFZdnjmx7rgwYSuDGTMTUdxCBeh8RggrQDzGht9bwzLPpCWkCrN4iQJqg3R6JxP7z2QZuf7dGCZcjMVBmmisYE8waRsohcvygRwmGr6nefbaujR5avm2x3EUvoTGyy8cMZJxX7URx45qQJyCgqFLNFCQzD1Kej3xCEPAJqCdGZgmqkryw2E2nkpGKXgRmbyEg2rFgd5kpvjG6jSLLYzGomxVnaKK2XyMQbcedkTMYJ8Ara71iWPRFUziWfgivZcA1qsQp92Fpao3FSsRprhoQz9u1VyAnh8zEM9jCKiE5s4dwCknqCJYeYsbMLn1be2vNP9bMQfu1jjGSHmbb9WR3E2vakTUEUByASXqSAJZuXYE5scopEzB28rC8nrC31ArLMZng5wWym3QbqEv2Syd6RHoEeoXR6vA5LPqvJKyvtH82p4hc4XbD18128aNrFG3GTD2P"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignCreateTokenAccountForOther_3E6UFV) { + auto privateKeyData = parse_hex("4b9d6f57d28b06cbfa1d4cc710953e62d653caf853415c56ffd9d150acdeb7f7"); + ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); + + auto input = Solana::Proto::SigningInput(); + auto& message = *input.mutable_create_token_account_transaction(); + message.set_main_address("3xJ3MoUVFPNFEHfWdtNFa8ajXUHsJPzXcBSWMKLd76ft"); + message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + message.set_token_address("67BrwFYt7qUnbAcYBVx7sQ4jeD2KWN1ohP6bMikmmQV3"); + input.set_private_key(privateKeyData.data(), privateKeyData.size()); + input.set_recent_blockhash("HmWyvrif3QfZJnDiRyrojmH9iLr7eMxxqiC9RJWFeunr"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = + // https://explorer.solana.com/tx/3E6UFVamHCm6Bgk8gXdZex7R7tJAVxqJm6t9ephAKu1PjcfZrD7CJqMwKu6RrvWSUESbZFqzdUyLXuxAFaawPHvJ + "4BsrHedHuForcKDhLdnLYDXgtQgQEj3EQVDtEhqa7o6ukFjW3shpTWv6PeKQdMp6af4ASjD4xQeZvXxLK5WUjguVMUf3xdJn7RnFeM7hdDJ56RDBM5PRJbRJVHjz6FJ7SVNTvr9y3gVYQtWx7NfKRxiyEAfq9JG7nqxSWaW6raMr9t35aVcdAVuXE9iXj3rzhVfCS69vVzy5KcFEK3mvDYG6L12V2CfviCydmeCvPw5r3zBUrZSQv7Ti4XFNBrPbk28gcqQwsBknBqasHxHqD9VUyPmBTuUyXq75QN8rhqN55NjxKBUw37tEUS1jKVpWnTeLFq1eRAMdXvjftNuQ5Bmm8Zc12PGWj9vdorBaYyvZXexJST5xNjR4SCkXvXZoRScETck95chv3VBn54jP8DpB4GGUmATFKSxpdtnNV64i1SQXW13KJwswthJvAaDiqevQLKLkvrTEAdb4BxEfPkFjDVti6P58rTZCMg5CTVLqdmWwpTSW5V"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignTokenTransfer1_3vZ67C) { + auto privateKeyData = Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); + ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + + auto input = Solana::Proto::SigningInput(); + auto& message = *input.mutable_token_transfer_transaction(); + message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + message.set_sender_token_address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + message.set_recipient_token_address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); + message.set_amount(4000); // 0.004 + message.set_decimals(6); + input.set_private_key(privateKeyData.data(), privateKeyData.size()); + input.set_recent_blockhash("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = + // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg + "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jRe2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvPiPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMqDV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnotcrH7pUa94xCVvCPPaomF"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignTokenTransfer2_2pMvzp) { + auto privateKeyData = Base58::bitcoin.decode("9YtuoD4sH4h88CVM8DSnkfoAaLY7YeGC2TarDJ8eyMS5"); + ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + + auto input = Solana::Proto::SigningInput(); + auto& message = *input.mutable_token_transfer_transaction(); + message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + message.set_sender_token_address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + message.set_recipient_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); + message.set_amount(6100); // 0.0061 + message.set_decimals(6); + input.set_private_key(privateKeyData.data(), privateKeyData.size()); + input.set_recent_blockhash("zMEbroNLJ4vfDTdQyA72rk35c7nPo4K38efHLujbSuz"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = + // https://explorer.solana.com/tx/2pMvzparE16evNgNhiexBfj15eurQgqFJXemYkuGasWV8RfT5tQseadqXA2VXbgGZPM1MpLcGwfkKKqvYvrKTmnR + "LCtawaKHmvh9WEjYPFFMDQXsdKMQbVyK4Q3aRRfLCouqw6GE4p31PRPFoQqtazTziEj3ex3iLgnCspz1MN4SUE9d33g3HiiA6oCS6wGMvB2i3ojtmJzndCiLoDmuZgiuGouVSeS2MAEUoS3CRjdnbNKbRwgKn8YsDe1bZ57ueipfBLJfiE7xr8ji678uAv8FcMgo8Mq88SBGxVCUhjMS2VGQZhRUHHzDmvnzxhbbUzsLDfApzjHExkUm7ws3cQ2i1cSpQNCQWJd6rcDv1sYwDAavPS571Ny3CUq4cZxABh45Gj88LkRpzBMRdoebrh9hPy8ZRnu7PocBVjZytCgdF4CuhzdYNsmdcuU2WN5CEmv5zQ7pBrFdLZ8bBifP"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignCreateAndTransferToken_449VaY) { + auto privateKeyData = Base58::bitcoin.decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); + ASSERT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); + + auto input = Solana::Proto::SigningInput(); + auto& message = *input.mutable_create_and_transfer_token_transaction(); + message.set_recipient_main_address("71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd"); + message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + message.set_recipient_token_address("EF6L8yJT1SoRoDCkAZfSVmaweqMzfhxZiptKi7Tgj5XY"); + message.set_sender_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); + message.set_amount(2900); + message.set_decimals(6); + input.set_private_key(privateKeyData.data(), privateKeyData.size()); + input.set_recent_blockhash("DMmDdJP41M9mw8Z4586VSvxqGCrqPy5uciF6HsKUVDja"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = + // https://explorer.solana.com/tx/449VaYo48LrkMJF6XVKt9sJwVQN6Seqrmh9erDCLtiuj6BgFG3wpF5TwjNkxgJ7qzNa6NTj3TFsU3h9hKszfkA7w + "3Y2MVz2VVi7aEyC9q1awwdk1ModDBPHRSacKmTYnSgkmbbJeZ62Fub1bVPSHaTy4LUcQpzCQYhHAKtTKXUDYijEeLsMAUqPBEMAq1w8zCdqDpdXy6M4PuwNtYVV1WgqeiEsiMWpPp4BGWKfcziwFbmYueUGituacJq4wTnt92fho8mFi49XW64gEG4iNGScDtJkY7Geq8PKiLh1E9JMJoceiHxKbmxzCmmLTxEHdhySYHcDUSXnXWogZskeZNBMtR9dNjEMkCzEjrxRpBtJPtUNshciY45mDPNmw4j3xyLCBTRikyfFLc5g11r3UgyVD4YokoPRvrEXsgt6W3yjBshropBm6mY2eJYvfY2eZz4Yq8kLcUatCHVKtjcb1mP9Ww57KisJ9bRhipC8sodFaMYhZARMEa4a1u9eH4MyNUATRGNXarwQSBY46PWS3nKP6QBK7Dw7Ppp9MmYkdPcXKaLScbyLF3jKu6dHWMkHw3WdXSsM1wwXjXnWF9LxdwaEVcDmySWybj6aKD9QCWTU5kdncqJU56f7SYNRTN289WdUFGNDmSh56tj2v1"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignCreateAndTransferTokenWithMemoReferences) { + const auto privateKeyData = Base58::bitcoin.decode("66ApBuKpo2uSzpjGBraHq7HP8UZMUJzp3um8FdEjkC9c"); + EXPECT_EQ(Address(PrivateKey(privateKeyData).getPublicKey(TWPublicKeyTypeED25519)).string(), "Eg5jqooyG6ySaXKbQUu4Lpvu2SqUPZrNkM4zXs9iUDLJ"); + + auto input = Solana::Proto::SigningInput(); + auto& message = *input.mutable_create_and_transfer_token_transaction(); + message.set_recipient_main_address("71e8mDsh3PR6gN64zL1HjwuxyKpgRXrPDUJT7XXojsVd"); + message.set_token_mint_address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + message.set_recipient_token_address("EF6L8yJT1SoRoDCkAZfSVmaweqMzfhxZiptKi7Tgj5XY"); + message.set_sender_token_address("ANVCrmRw7Ww7rTFfMbrjApSPXEEcZpBa6YEiBdf98pAf"); + message.set_amount(2900); + message.set_decimals(6); + message.set_memo("HelloSolanaMemo370"); + message.add_references("CuieVDEDtLo7FypA9SbLM9saXFdb1dsshEkyErMqkRQq"); + message.add_references("tFpP7tZUt6zb7YZPpQ11kXNmsc5YzpMXmahGMvCHhqS"); + input.set_private_key(privateKeyData.data(), privateKeyData.size()); + input.set_recent_blockhash("DMmDdJP41M9mw8Z4586VSvxqGCrqPy5uciF6HsKUVDja"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeSolana); + + auto expectedString = "FuUw2MoEGPATE38roXAw9mGQhCfdsdpVDdhuf5h8LKc8iWj2HzNS3SteXqyUoZtQ7L1ufLvu7cTMwNzxT8snnVimcknsA52CeN7bgMz1Ad1hRTAr77zE5efzAi8B124kaQ1cBEb6nFMr5Zq4wwDRoJgBaiUaM1U9ZY6GofCKHGMQN7ZNqEFG4fFvPaMXB59dFtiqrtApBGzvDho3nGshyQWZVWfMY44hvVk45FqiGrXuqUwkiJqeRaDhooZdXiFR9ubwJLXo3Ux23ZyijWKXYNsx1Lm5zMFEgRz3kXhzxzb8uzHVSrFYNieXXCQEv1GtErMKeQWuAHcwS3zxC6avTnTWJhTz3kVSXfSTYEg4MF2MBWeGrzKZ7id88ZfbpG4ZwzsDsdUCSMV6YYRNmx9P3B6oC4DL7cbi2g8hwtBdeKojY4G6JMPeg629V9sPyg2KKeYxD3cjhMKAYtrsJEbixep4LZENtdQxmgZFouJVvGy9MVhiTzGEFVwm4G25p5FhWhiS9HxHWVRXpUFHi2K9K2ttoo4Ug39V9f8s9cG1Xb5A4bHhGSuKLeCCBcrBqPWEsuLdVhjxsKJrRBJhyrZ6mpxtDhUWivZa6skmEawTts9rN2aP3dXW3cNch3s3LTXZWXG9QPUARJJPy5QAYsBoR8GunF5FFgHVuEHVpjXAd8ku9f7aoF8RNiMnXAqQHxiM3ug6HZpLHLX8aGoUbJ7vVAnEDLH"; + ASSERT_EQ(output.encoded(), expectedString); +} + +TEST(TWAnySignerSolana, SignJSON) { + auto json = STRING(R"({"recentBlockhash":"11111111111111111111111111111111","transferTransaction":{"recipient":"EN2sCsJ1WDV8UFqsiTXHcUPUxQ4juE71eCknHYYMifkd","value":"42"}})"); + Data keyData = Base58::bitcoin.decode("A7psj2GW7ZMdY4E5hJq14KMeYg7HFjULSsWSrTXZLvYr"); + EXPECT_EQ(hex(keyData), "8778cc93c6596387e751d2dc693bbd93e434bd233bc5b68a826c56131821cb63"); + auto key = WRAPD(TWDataCreateWithBytes(keyData.data(), keyData.size())); + + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeSolana)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeSolana)); + assertStringsEqual(result, expectedString1); +} + +} // namespace TW::Solana::tests diff --git a/tests/chains/Solana/TWCoinTypeTests.cpp b/tests/chains/Solana/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..c6225566d1e --- /dev/null +++ b/tests/chains/Solana/TWCoinTypeTests.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + +TEST(TWSolanaCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeSolana)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("5LmxrEKGchhMuYfw6Qut6CbsvE9pVfb8YvwZKvWssSesDVjHioBCmWKSJQh1WhvcM6CpemhpHNmEMA2a36rzwTa8")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeSolana, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("Bxp8yhH9zNwxyE4UqxP7a7hgJ5xTZfxNNft7YJJ2VRjT")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeSolana, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeSolana)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeSolana)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeSolana), 9); + ASSERT_EQ(TWBlockchainSolana, TWCoinTypeBlockchain(TWCoinTypeSolana)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeSolana)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeSolana)); + assertStringsEqual(symbol, "SOL"); + assertStringsEqual(txUrl, "https://solscan.io/tx/5LmxrEKGchhMuYfw6Qut6CbsvE9pVfb8YvwZKvWssSesDVjHioBCmWKSJQh1WhvcM6CpemhpHNmEMA2a36rzwTa8"); + assertStringsEqual(accUrl, "https://solscan.io/account/Bxp8yhH9zNwxyE4UqxP7a7hgJ5xTZfxNNft7YJJ2VRjT"); + assertStringsEqual(id, "solana"); + assertStringsEqual(name, "Solana"); +} diff --git a/tests/chains/Solana/TWSolanaAddressTests.cpp b/tests/chains/Solana/TWSolanaAddressTests.cpp new file mode 100644 index 00000000000..1cdd42676f2 --- /dev/null +++ b/tests/chains/Solana/TWSolanaAddressTests.cpp @@ -0,0 +1,40 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include +#include + +#include + +TEST(TWSolanaAddress, HDWallet) { + auto mnemonic = + "shoot island position soft burden budget tooth cruel issue economy destroy above"; + auto passphrase = ""; + + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(STRING(mnemonic).get(), STRING(passphrase).get())); + + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKey(wallet.get(), TWCoinTypeSolana, WRAPS(TWCoinTypeDerivationPath(TWCoinTypeSolana)).get())); + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519(privateKey.get())); + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeSolana)); + auto addressStr = WRAPS(TWAnyAddressDescription(address.get())); + + assertStringsEqual(addressStr, "2bUBiBNZyD29gP1oV6de7nxowMLoDBtopMMTGgMvjG5m"); +} + +TEST(TWSolanaProgram, defaultTokenAddress) { + const auto solAddress = STRING("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + const auto serumToken = STRING("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + + auto solanaAddress = WRAP(TWSolanaAddress, TWSolanaAddressCreateWithString(solAddress.get())); + auto description = WRAPS(TWSolanaAddressDescription(solanaAddress.get())); + auto tokenAddress = WRAPS(TWSolanaAddressDefaultTokenAddress(solanaAddress.get(), serumToken.get())); + + assertStringsEqual(tokenAddress, "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + assertStringsEqual(description, "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); +} diff --git a/tests/chains/Solana/TransactionTests.cpp b/tests/chains/Solana/TransactionTests.cpp new file mode 100644 index 00000000000..c6ff726f613 --- /dev/null +++ b/tests/chains/Solana/TransactionTests.cpp @@ -0,0 +1,183 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Solana/Address.h" +#include "Solana/Transaction.h" +#include "Solana/Program.h" +#include "HexCoding.h" + +#include "BinaryCoding.h" + +#include + +namespace TW::Solana { + +TEST(SolanaTransaction, TransferMessageData) { + auto from = Address("6eoo7i1khGhVm8tLBMAdq4ax2FxkKP4G7mCcfHyr3STN"); + auto to = Address("56B334QvCDMSirsmtEJGfanZm8GqeQarrSjdAb2MbeNM"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto transaction = Transaction(from, to, 42, recentBlockhash); + + auto expectedHex = + "0100010353f9d600fe925083bb399907ea648d23a6a081fc7e9059202fd725f7edd281dd3cc1ff9ba3c7a876c8" + "082df2f8a36ea9342ce3819dd4b6fa72d4a18e04a5363a00000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000010202" + "00010c020000002a00000000000000"; + ASSERT_EQ(hex(transaction.messageData()), expectedHex); +} + +TEST(SolanaTransaction, TransferSerializeTransaction) { + auto from = Address("41a5jYky56M6EWDsFfLaZRxoRtgAJSRWxJnxaJNJELn5"); + auto to = Address("4iSnyfDKaejniaPc2pBBckwQqV3mDS93go15NdxWJq2y"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto transaction = Transaction(from, to, 42, recentBlockhash); + Signature signature( + "46SRiQGvtPb1iivDfnuC3dW1GzXkfQPTjdUyvFqF2sdPvFrsfx94fys2xpNKR6UiAj7RgKWdJG6mEfe85up6i1JT"); + transaction.signatures.clear(); + transaction.signatures.push_back(signature); + + auto expectedString = + "5SiHeYyuDgjHxWHbYXSSPfmYc8s7EYZ8bdZ7j15z9Bj1yyZA3Bia9uWkRdXVkuqifXiiQj6fVKy" + "7UkCL5kvv6iKrfjWTZ3szMVssTFxgJ7p8UJ7Mgg2uhHejVJvbzbiHHLbNVuJFs6kBxddnJ2yjWU" + "Cp2dYJgjmphfA8hRHHdPH4Rv6znxEhD8q9XY4nByRPL7oMCo32oxeJn5rGbUZdCkapRUXG7zU9w" + "hv6KjBktcUQZRCahhowGJT4UM5yCNCsUcqY9yan7UxqPyJgaFPuq4duqWJtQ39bTQ36X"; + ASSERT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaTransaction, TransferTransactionPayToSelf) { + auto from = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto transaction = Transaction(from, to, 42, recentBlockhash); + Signature signature( + "3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); + transaction.signatures.clear(); + transaction.signatures.push_back(signature); + + auto expectedString = + "EKUmihvvUPKVN4GSCFwZRtz8WiyAuPvthW69Smo19SCjcPLQ6T7EVZd1HU71WAoe1bfgmPNS5JhU7ZLA9XKG3qbZqe" + "EFJ1xmRwW9ZKw8SKMAL6VRWxp87oLu7PSmf5b8R34vCaww3XLKtZkoP49a7TUK31DqPN5xJCceMB3BZJyaojQaKU8n" + "UkzSGf89LY6abZXp9krKAebvc6bSMzTP8SHSvbmZbf3VtejmpQeN9X6e7WVDn6oDa2bGT"; + ASSERT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaTransaction, TransferWithMemoAndReferenceTransaction) { + const auto from = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + const auto to = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + const Solana::Hash recentBlockhash("11111111111111111111111111111111"); + const auto memo = "HelloSolana73"; + std::vector
references = {Address("GaeTAQZyhVEocTC7iY8GztSyY5cBAJTkAUUA1kLFLMV")}; + auto transaction = Transaction(from, to, 42, recentBlockhash, memo, references); + const Signature signature("3CFWDEK51noPJP4v2t8JZ3qj7kC7kLKyws9akfHMyuJnQ35EtzBptHqvaHfeswiLsvUSxzMVNoj4CuRxWtDD9zB1"); + transaction.signatures.clear(); + transaction.signatures.push_back(signature); + + auto expectedString = "3pzQEdU38uMQgegTyRsRLi23NK4YokgZeSVLXYzFB7HShqZZH8FdBLqj6CeA2d2L8oR9KF2UaJPWbE8YBFmSdaafegoSXJtyj7ciwTjk5ieSXnPXtqH1TEcnMntZATg7gKpeFg6iehqdSUtZuQD1PGmHA1TrzzqLpRSRrc1sqPz8EpSJcQr1Y41B1XCEAfSJDfcuNKrfFrnQaVtRz6tseQfd9uXNYNuR1NQSepWdav5wQiohLUMDiZtxuwb7FQkQ68WE1FDsHmd4JpbWKmDEjz7HFyQY37vf6NBJyX5qWJpFMSg5qGKWvhNCDM32yM4A7HhPeoTWEywE5CXcNmQqdbRt4BzF1A11uqv4etWj"; + EXPECT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaTransaction, StakeSerializeTransactionV2) { + auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); + auto programId = Address("Stake11111111111111111111111111111111111111"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto stakeAddress = StakeProgram::addressFromRecentBlockhash(signer, recentBlockhash, programId); + auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); + auto transaction = Transaction(message); + Signature signature( + "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); + transaction.signatures.clear(); + transaction.signatures.push_back(signature); + + auto expectedString = "W1EAswaWK7mF4r9eZ2hHBZnfPnqLuNPiYkEMzFbwQsgSQu6XbSTL9AN92iyMbAMxPoRpt9ipUyztrmszAnm688N3k7uhiKn2osm9nxi6YkGLfu31jHTSu7mn3RtmenV3qopfPDAM92cXrzUhKGWtQ6cATeQh8i8ZfHpmjyuik7Eg3SQ4sa2543CmcozzjmTTWThThuLdvFCZJzBeRBFWLujqjbs5mA66XVtiDwsEqYByznoo4BN45XUHxnZebmPfo4hi5sf27UkhzPHik371BGxbVDexQp4y5nCEHy8ybfNCvMPLr2SEBiWSifwPkmwYN3hGCkBpqLoHCCiRcyJuRHW8hSDFR4JPQ3Xe3FGfpgbayaawZigUnFuPGSpoGrURZRoLCzc6V4ApqcJbmzFhg5zJz2yTX5GvQSYWLFnTKbPYcgBNpdyMLJTivonrKtgkfdymZVKjDwnHUApC7WD4L9mqzTf1dzR61Fxhu3Rdh8ECiVEDgB1wkWZWkTKEdANmtaYLKCMUs3n4VhuZbSFLEiTg7yRWM2pjBgiBB4qywbF7SE75UtzSFCaDnn27mKkxRBqZEGEgfpEoK2AxjsiCZEZxfLeyZFbwWe7xasmNiXr6CnAQhwsmxJk79h7SYmaje76JLxHVX5gbQmLfn5bc1xthS3YhteSovQ8xYq1jiHCfsXRwbxKrNA4kVMiSa6spoU9AhFL8cDAZjAqoU4YRwBihZVhXSFCRnYAK8FabzEv1M44EeHX1sfMG8T1U7y3DEjom7jv6rqZfLumWpbXDTqanB7zTbTjGyDcBBf21edjpZzBZ7osS5fTVYJ5mZBSvjjhuGkUgZZWgYozAKvdyyrJH6UdcPvNm2XgMRYJxqyCin1zhCeQ25vK1H8Jj"; + EXPECT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaTransaction, StakeSerializeTransactionV1) { + auto signer = Address("zVSpQnbBZ7dyUWzXhrUQRsTYYNzoAdJWHsHSqhPj3Xu"); + auto voteAddress = Address("4jpwTqt1qZoR7u6u639z2AngYFGN3nakvKhowcnRZDEC"); + auto programId = Address("Stake11111111111111111111111111111111111111"); + Solana::Hash recentBlockhash("11111111111111111111111111111111"); + auto stakeAddress = StakeProgram::addressFromValidatorSeed(signer, voteAddress, programId); + auto message = Message::createStake(signer, stakeAddress, voteAddress, 42, recentBlockhash); + auto transaction = Transaction(message); + Signature signature( + "2GXRrZMMWTaY8ycwFTLFojAVZ1EepFqnVGW7b5bBuuKPiVrpaPXMAwyYsSmYc2okCa1MuJjNguu1emSJRtZxVdwt"); + transaction.signatures.clear(); + transaction.signatures.push_back(signature); + + auto expectedString = "W1EAswaWK7mF4r9eZ2hHBZnfPnqLuNPiYkEMzFbwQsgSQu6XbSTL9AN92iyMbAMxPoRpt9ipUyztrmszAnm688N3k7uhiKn2osm9nxi6YkGLfu31jHTSu7mn3RtmenV3qopfPDAM7jtGoYQFb7eFVbujUb6tbeQ9UqLJq1sJ7uMZ4wqecmQPouDmJnpmJk4CHMzLnPNTwyGmGio6sYAS3xKZ7DFXvjwGPuD8PyYHSfdPro1p3jy9igPZNAbQ6fgK7LL3sERKCUdvPy7k14xgHbtsVy2mu54LY5c8F9sFst2uzQiTsXRTdjPFAyCVwB5pccNVotCrJ6Q2aKSC2D2knVH7LgWzSBMSreJG75xyATneu922wSzz7QJDieqhDtdePtSbPtoCdtPNmDfdaeDbHxVAxMios9F7RSRmH2dq86NfWDvF8TuEbYY7gPnygz6jGvwfqSSoSnY8TnUhhceC7wJSMc8Hcf1kyfi8dqKm7rF57YjnrQoMmL5bWqJLKoJtdfFu24ceQN21k38U2tUMWJaBASWukgTJUbNSCemNPZt4P3cNbeB3L1wBj4GEYXVTbTFYKME5JscU5RsnkMJZZ1PgxSi63HT4hwQLok4c18UdJgzMFu1njpZj3Sw76mwV3ea7ruHnP4yyM3YhUGbNjpx5fAcnvdLcXChdsgeUpJhutME6V86Rk2EEskoJeD3qNWi3hvfQx172hZRHyKyr29Ts1uLQxcMJq7oeQUxvTfXxSe6cBuPJUDFkAET3qpS7rWM7rvQQ8rDLQF5QvcJnrYTq12pVgw28WXdgi45811a7DWHGuwHRj5FJdLQAHkKe4EXVeTCdbYHREVwuyTJgAvb8SXjRE5a9n3qpRDr7iEd5UDZKB5HgvMsMYWh5"; + ASSERT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaTransaction, CreateTokenAccountTransaction) { + auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + auto tokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + Solana::Hash recentBlockhash("9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + auto message = Message::createTokenCreateAccount(signer, signer, token, tokenAddress, recentBlockhash); + EXPECT_EQ(message.header.numRequiredSignatures, 1); + EXPECT_EQ(message.header.numCreditOnlySignedAccounts, 0); + EXPECT_EQ(message.header.numCreditOnlyUnsignedAccounts, 5); + ASSERT_EQ(message.accountKeys.size(), 7ul); + EXPECT_EQ(message.accountKeys[0].string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + EXPECT_EQ(message.accountKeys[1].string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + EXPECT_EQ(message.accountKeys[2].string(), "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + EXPECT_EQ(message.accountKeys[3].string(), "11111111111111111111111111111111"); + EXPECT_EQ(message.accountKeys[4].string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); + EXPECT_EQ(message.accountKeys[5].string(), "SysvarRent111111111111111111111111111111111"); + EXPECT_EQ(message.accountKeys[6].string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); + EXPECT_EQ(Base58::bitcoin.encode(message.recentBlockhash.bytes), "9ipJh5xfyoyDaiq8trtrdqQeAhQbQkWy2eANizKvx75K"); + ASSERT_EQ(message.instructions.size(), 1ul); + EXPECT_EQ(message.instructions[0].programId.string(), "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"); + ASSERT_EQ(message.instructions[0].accounts.size(), 7ul); + EXPECT_EQ(message.instructions[0].accounts[0].account.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + EXPECT_EQ(message.instructions[0].accounts[1].account.string(), "EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + EXPECT_EQ(message.instructions[0].accounts[2].account.string(), "B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + EXPECT_EQ(message.instructions[0].accounts[3].account.string(), "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + EXPECT_EQ(message.instructions[0].accounts[4].account.string(), "11111111111111111111111111111111"); + EXPECT_EQ(message.instructions[0].accounts[5].account.string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); + EXPECT_EQ(message.instructions[0].accounts[6].account.string(), "SysvarRent111111111111111111111111111111111"); + auto transaction = Transaction(message); + transaction.signatures.clear(); + Signature signature("3doYbPs5rES3TeDSrntqUvMgXCDE2ViJX2SFhLtiptVNkqPuixXs1SwU5LUZ3KwHnCzDUth6BRr3vU3gqnuUgRvQ"); + transaction.signatures.push_back(signature); + + auto expectedString = + // test data obtained from spl-token create-account + "CKzRLx3AQeVeLQ7T4hss2rdbUpuAHdbwXDazxtRnSKBuncCk3WnYgy7XTrEiya19MJviYHYdTxi9gmWJY8qnR2vHVnH2DbPiKA8g72rD3VvMnjosGUBBvCwbBLge6FeQdgczMyRo9n5PcHvg9yJBTJaEEvuewyBVHwCGyGQci7eYd26xtZtCjAjwcTq4gGr3NZbeRW6jZp6j6APuew7jys4MKYRV4xPodua1TZFCkyWZr1XKzmPh7KTavtN5VzPDA8rbsvoEjHnKzjB2Bszs6pDjcBFSHyQqGsHoF8XPD35BLfjDghNtBmf9cFqo5axa6oSjANAuYg6cMSP4Hy28waSj8isr6gQjE315hWi3W1swwwPcn322gYZx6aMAcmjczaxX9aktpHYgZxixF7cYWEHxJs5QUK9mJePu9Xc6yW75UB4Ynx6dUgaSTEUzoQthF2TN3xXwu1"; + EXPECT_EQ(transaction.serialize(), expectedString); +} + +TEST(SolanaTransaction, TransferTokenTransaction_3vZ67C) { + auto signer = Address("B1iGmDJdvmxyUiYM8UEo2Uw2D58EmUrw4KyLYMmrhf8V"); + auto token = Address("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); + auto senderTokenAddress = Address("EDNd1ycsydWYwVmrYZvqYazFqwk1QjBgAUKFjBoz1jKP"); + auto recipientTokenAddress = Address("3WUX9wASxyScbA7brDipioKfXS1XEYkQ4vo3Kej9bKei"); + uint64_t amount = 4000; + uint8_t decimals = 6; + Solana::Hash recentBlockhash("CNaHfvqePgGYMvtYi9RuUdVxDYttr1zs4TWrTXYabxZi"); + auto message = Message::createTokenTransfer(signer, token, senderTokenAddress, recipientTokenAddress, amount, decimals, recentBlockhash); + EXPECT_EQ(message.header.numRequiredSignatures, 1); + EXPECT_EQ(message.header.numCreditOnlySignedAccounts, 0); + EXPECT_EQ(message.header.numCreditOnlyUnsignedAccounts, 2); + ASSERT_EQ(message.accountKeys.size(), 5ul); + ASSERT_EQ(message.instructions.size(), 1ul); + EXPECT_EQ(message.instructions[0].programId.string(), "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); + ASSERT_EQ(message.instructions[0].accounts.size(), 4ul); + auto transaction = Transaction(message); + transaction.signatures.clear(); + Signature signature("3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg"); + transaction.signatures.push_back(signature); + + auto expectedString = + // https://explorer.solana.com/tx/3vZ67CGoRYkuT76TtpP2VrtTPBfnvG2xj6mUTvvux46qbnpThgQDgm27nC3yQVUZrABFjT9Qo7vA74tCjtV5P9Xg + // test data obtained from spl-token transfer + "PGfKqEaH2zZXDMZLcU6LUKdBSzU1GJWJ1CJXtRYCxaCH7k8uok38WSadZfrZw3TGejiau7nSpan2GvbK26hQim24jRe2AupmcYJFrgsdaCt1Aqs5kpGjPqzgj9krgxTZwwob3xgC1NdHK5BcNwhxwRtrCphGEH7zUFpGFrFrHzgpf2KY8FvPiPELQyxzTBuyNtjLjMMreehSKShEjD9Xzp1QeC1pEF8JL6vUKzxMXuveoEYem8q8JiWszYzmTMfDk13JPgv7pXFGMqDV3yNGCLsWccBeSFKN4UKECre6x2QbUEiKGkHkMc4zQwwyD8tGmEMBAGm339qdANssEMNpDeJp2LxLDStSoWShHnotcrH7pUa94xCVvCPPaomF"; + EXPECT_EQ(transaction.serialize(), expectedString); +} + +} // namespace TW::Solana diff --git a/tests/chains/Stellar/AddressTests.cpp b/tests/chains/Stellar/AddressTests.cpp new file mode 100644 index 00000000000..9f55cc55058 --- /dev/null +++ b/tests/chains/Stellar/AddressTests.cpp @@ -0,0 +1,40 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Bitcoin/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "Stellar/Address.h" + +#include + +using namespace std; +using namespace TW; + +namespace TW::Stellar::tests { + +TEST(StellarAddress, FromPublicKey) { + const auto publicKey = PublicKey(parse_hex("0103E20EC6B4A39A629815AE02C0A1393B9225E3B890CAE45B59F42FA29BE9668D"), TWPublicKeyTypeED25519); + const auto address = Address(publicKey); + auto str = hex(address.bytes); + ASSERT_EQ(string("GAB6EDWGWSRZUYUYCWXAFQFBHE5ZEJPDXCIMVZC3LH2C7IU35FTI2NOQ"), address.string()); +} + +TEST(StellarAddress, FromString) { + string stellarAddress = "GAB6EDWGWSRZUYUYCWXAFQFBHE5ZEJPDXCIMVZC3LH2C7IU35FTI2NOQ"; + const auto address = Address(stellarAddress); + ASSERT_EQ(address.string(), stellarAddress); +} + +TEST(StellarAddress, isValid) { + string stellarAddress = "GABQHYQOY22KHGTCTAK24AWAUE4TXERF4O4JBSXELNM7IL5CTPUWM3SC"; + string bitcoinAddress = "1Ma2DrB78K7jmAwaomqZNRMCvgQrNjE2QC"; + + ASSERT_TRUE(Address::isValid(stellarAddress)); + ASSERT_FALSE(Address::isValid(bitcoinAddress)); +} + +} // namespace TW::Stellar::tests diff --git a/tests/chains/Stellar/TWAnySignerTests.cpp b/tests/chains/Stellar/TWAnySignerTests.cpp new file mode 100644 index 00000000000..7ad662a073a --- /dev/null +++ b/tests/chains/Stellar/TWAnySignerTests.cpp @@ -0,0 +1,192 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "Stellar/Address.h" +#include "proto/Stellar.pb.h" +#include +#include +#include + +using namespace TW; + +namespace TW::Stellar::tests { + +TEST(TWAnySingerStellar, Sign_Payment) { + auto key = parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722"); + Proto::SigningInput input; + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); + input.set_fee(1000); + input.set_sequence(2); + input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); + input.mutable_op_payment()->set_amount(10000000); + input.set_private_key(key.data(), key.size()); + auto& memoText = *input.mutable_memo_text(); + memoText.set_text("Hello, world!"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeStellar); + + EXPECT_EQ(output.signature(), "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAEAAAANSGVsbG8sIHdvcmxkIQAAAAAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAEBQQldEkYJ6rMvOHilkwFCYyroGGUvrNeWVqr/sn3iFFqgz91XxgUT0ou7bMSPRgPROfBYDfQCFfFxbcDPrrCwB"); +} + +TEST(TWAnySingerStellar, Sign_Payment_66b5) { + auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); + PrivateKey privKey = PrivateKey(key); + PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); + Address addr = Address(pubKey); + EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + + Proto::SigningInput input; + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + input.set_fee(1000); + input.set_sequence(144098454883270657); + input.mutable_op_payment()->set_destination("GA3ISGYIE2ZTH3UAKEKBVHBPKUSL3LT4UQ6C5CUGP2IM5F467O267KI7"); + input.mutable_op_payment()->set_amount(1000000); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeStellar); + + // curl "https://horizon.stellar.org/transactions/66b5bca4b4293bdd85a6a559b08918482774b76bcc170b4533411f1d6422ce24" + EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAD6AH/8MgAAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAANokbCCazM+6AURQanC9VJL2ufKQ8LoqGfpDOl577te8AAAAAAAAAAAAPQkAAAAAAAAAAAXfTkXUAAABAM9Nhzr8iWKzqnHknrxSVoa4b2qzbTzgyE2+WWxg6XHH50xiFfmvtRKVhzp0Jg8PfhatOb6KNheKRWEw4OvqEDw=="); +} + +TEST(TWAnySingerStellar, Sign_Payment_Asset_ea50) { + auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); + PrivateKey privKey = PrivateKey(key); + PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); + Address addr = Address(pubKey); + EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + + Proto::SigningInput input; + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + input.set_fee(1000); + input.set_sequence(144098454883270661); + input.mutable_op_payment()->set_destination("GA3ISGYIE2ZTH3UAKEKBVHBPKUSL3LT4UQ6C5CUGP2IM5F467O267KI7"); + input.mutable_op_payment()->mutable_asset()->set_issuer("GA6HCMBLTZS5VYYBCATRBRZ3BZJMAFUDKYYF6AH6MVCMGWMRDNSWJPIH"); + input.mutable_op_payment()->mutable_asset()->set_alphanum4("MOBI"); + input.mutable_op_payment()->set_amount(12000000); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeStellar); + + // curl "https://horizon.stellar.org/transactions/ea50884cd1288d2d5420065995d13d750d812258e0e79280c4033a434e625c99 + EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAD6AH/8MgAAAAFAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAANokbCCazM+6AURQanC9VJL2ufKQ8LoqGfpDOl577te8AAAABTU9CSQAAAAA8cTArnmXa4wEQJxDHOw5SwBaDVjBfAP5lRMNZkRtlZAAAAAAAtxsAAAAAAAAAAAF305F1AAAAQEuWZZvKZuF6SMuSGIyfLqx5sn5O55+Kd489uP4g9jZH4UE7zZ4ME0+74I0BU8YDsYOmmxcfp/vdwTd+n3oGCQw="); +} + +TEST(TWAnySingerStellar, Sign_Change_Trust_ad9c) { + auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); + PrivateKey privKey = PrivateKey(key); + PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); + Address addr = Address(pubKey); + EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + + Proto::SigningInput input; + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + input.set_fee(10000); + input.set_sequence(144098454883270659); + input.mutable_op_change_trust()->mutable_asset()->set_issuer("GA6HCMBLTZS5VYYBCATRBRZ3BZJMAFUDKYYF6AH6MVCMGWMRDNSWJPIH"); + input.mutable_op_change_trust()->mutable_asset()->set_alphanum4("MOBI"); + input.mutable_op_change_trust()->set_valid_before(1613336576); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeStellar); + + // curl "https://horizon.stellar.org/transactions/ad9cd0f3d636096b6502ccae07adbcf2cd3c0da5393fc2b07813dbe90ecc0d7b" + EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAnEAH/8MgAAAADAAAAAQAAAAAAAAAAAAAAAGApkAAAAAAAAAAAAQAAAAAAAAAGAAAAAU1PQkkAAAAAPHEwK55l2uMBECcQxzsOUsAWg1YwXwD+ZUTDWZEbZWR//////////wAAAAAAAAABd9ORdQAAAEAnfyXyaNQX5Bq3AEQVBIaYd+cLib+y2sNY7DF/NYVSE51dZ6swGGElz094ObsPefmVmeRrkGsSc/fF5pmth+wJ"); +} + +TEST(TWAnySingerStellar, Sign_Change_Trust_2) { + auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); + PrivateKey privKey = PrivateKey(key); + PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); + Address addr = Address(pubKey); + EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + + Proto::SigningInput input; + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + input.set_fee(10000); + input.set_sequence(144098454883270659); + input.mutable_op_change_trust()->mutable_asset()->set_issuer("GDUKMGUGDZQK6YHYA5Z6AY2G4XDSZPSZ3SW5UN3ARVMO6QSRDWP5YLEX"); + input.mutable_op_change_trust()->mutable_asset()->set_alphanum4("USD"); + input.mutable_op_change_trust()->set_valid_before(1613336576); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeStellar); + + EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAnEAH/8MgAAAADAAAAAQAAAAAAAAAAAAAAAGApkAAAAAAAAAAAAQAAAAAAAAAGAAAAAVVTRAAAAAAA6KYahh5gr2D4B3PgY0blxyy+Wdyt2jdgjVjvQlEdn9x//////////wAAAAAAAAABd9ORdQAAAEDMZtN05ZsZB4OKOZSFkQvuRqDIvMME3PYMTAGJPQlO6Ee0nOtaRn2q0uf0IhETSSfqcsK5asAZzNj07tG0SPwM"); +} + +TEST(TWAnySingerStellar, Sign_Create_Claimable_Balance_1f1f84) { + auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); + PrivateKey privKey = PrivateKey(key); + PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); + Address addr = Address(pubKey); + EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + + Proto::SigningInput input; + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + input.set_fee(10000); + input.set_sequence(144098454883270687); + input.mutable_op_create_claimable_balance()->set_amount(90000000); + input.mutable_op_create_claimable_balance()->add_claimants(); + input.mutable_op_create_claimable_balance()->mutable_claimants(0)->set_account("GC6CJDAY54D3O4RHEH33LUTBKDZGVOTR6NHBOTL4PIWI2CDKVRSZZJGJ"); + input.mutable_op_create_claimable_balance()->mutable_claimants(0)->set_predicate(Proto::Predicate_unconditional); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeStellar); + + // https://stellar.expert/explorer/public/tx/1f1f849ff2560901c91226f2fc866ef4ed1c67d672262c1f5829abe2348ac638 + // curl -X POST -F "tx=AAAAAMpF..Bg==" "https://horizon.stellar.org/transactions" + EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAnEAH/8MgAAAAfAAAAAAAAAAAAAAABAAAAAAAAAA4AAAAAAAAAAAVdSoAAAAABAAAAAAAAAAC8JIwY7we3cich97XSYVDyarpx804XTXx6LI0IaqxlnAAAAAAAAAAAAAAAAXfTkXUAAABAgms/HPhEP/EYtVr5aWwhKJsn3pIVEZGFnTD2Xd/VPVsn8qogI7RYyjyBxSFPiLAljgGsPaUMfU3WFvyJCWNwBg=="); +} + +TEST(TWAnySingerStellar, Sign_Claim_Claimable_Balance_c1fb3c) { + auto key = parse_hex("3c0635f8638605aed6e461cf3fa2d508dd895df1a1655ff92c79bfbeaf88d4b9"); + PrivateKey privKey = PrivateKey(key); + PublicKey pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519); + Address addr = Address(pubKey); + EXPECT_EQ(addr.string(), "GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + + Proto::SigningInput input; + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GDFEKJIFKUZP26SESUHZONAUJZMBSODVN2XBYN4KAGNHB7LX2OIXLPUL"); + input.set_fee(10000); + input.set_sequence(144098454883270689); + const Data balanceIdHash = parse_hex("9c7b794b7b150f3e4c6dcfa260672bbe0c248b360129112e927e0f7ee2f9faf8"); + input.mutable_op_claim_claimable_balance()->set_balance_id(balanceIdHash.data(), balanceIdHash.size()); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeStellar); + + // https://stellar.expert/explorer/public/tx/c1fb3cf348aeb72bb2e1030c1d7f7f9c6c6d1bbab071b3e7c7c1cadafa795e8e + // curl -X POST -F "tx=AAAAAMpF..DQ==" "https://horizon.stellar.org/transactions" + EXPECT_EQ(output.signature(), "AAAAAMpFJQVVMv16RJUPlzQUTlgZOHVurhw3igGacP1305F1AAAnEAH/8MgAAAAhAAAAAAAAAAAAAAABAAAAAAAAAA8AAAAAnHt5S3sVDz5Mbc+iYGcrvgwkizYBKREukn4PfuL5+vgAAAAAAAAAAXfTkXUAAABAWL7dKkR1JuPZGFbDTRDgGBHW/vLPMWNRkAew+wPfGiCnZhpJJDcyX197EDDZMsJ7ungPUyhczRaeQOwZKx4DDQ=="); + + { // negative test: hash wrong size + const Data invalidBalanceIdHash = parse_hex("010203"); + input.mutable_op_claim_claimable_balance()->set_balance_id(invalidBalanceIdHash.data(), invalidBalanceIdHash.size()); + ANY_SIGN(input, TWCoinTypeStellar); + EXPECT_EQ(output.signature(), "AAAAAXfTkXUAAABAFCywEfLs3q5Tv9eZCIcjhkJR0s8J4Us9G5YjVKUSaMoUz/AadC8dM2oQSLhpC5wjrNBi7hevg7jlkPx5/4AJCQ=="); + } +} + +} // namespace TW::Stellar::tests diff --git a/tests/chains/Stellar/TWCoinTypeTests.cpp b/tests/chains/Stellar/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..2fc1c59fb4b --- /dev/null +++ b/tests/chains/Stellar/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWStellarCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeStellar)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("d9aeabfa9d24df8c5755125f8af243b74cd3ff878656cfa72c566a8824bf6e84")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeStellar, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("GCILJZQ3CKBKBUJWW4TAM6Q37LJA5MQX6GMSFSQN75BPLWIZ33OPRG52")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeStellar, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeStellar)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeStellar)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeStellar), 7); + ASSERT_EQ(TWBlockchainStellar, TWCoinTypeBlockchain(TWCoinTypeStellar)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeStellar)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeStellar)); + assertStringsEqual(symbol, "XLM"); + assertStringsEqual(txUrl, "https://blockchair.com/stellar/transaction/d9aeabfa9d24df8c5755125f8af243b74cd3ff878656cfa72c566a8824bf6e84"); + assertStringsEqual(accUrl, "https://blockchair.com/stellar/account/GCILJZQ3CKBKBUJWW4TAM6Q37LJA5MQX6GMSFSQN75BPLWIZ33OPRG52"); + assertStringsEqual(id, "stellar"); + assertStringsEqual(name, "Stellar"); +} diff --git a/tests/Stellar/TWStellarAddressTests.cpp b/tests/chains/Stellar/TWStellarAddressTests.cpp similarity index 96% rename from tests/Stellar/TWStellarAddressTests.cpp rename to tests/chains/Stellar/TWStellarAddressTests.cpp index 47d1f759c76..97f9fc32f12 100644 --- a/tests/Stellar/TWStellarAddressTests.cpp +++ b/tests/chains/Stellar/TWStellarAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/Stellar/TransactionTests.cpp b/tests/chains/Stellar/TransactionTests.cpp new file mode 100644 index 00000000000..8a7890eac2e --- /dev/null +++ b/tests/chains/Stellar/TransactionTests.cpp @@ -0,0 +1,145 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include "Stellar/Signer.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include +#include +#include "BinaryCoding.h" + +#include + +namespace TW::Stellar::tests { + +using namespace std; + +TEST(StellarTransaction, sign) { + auto words = STRING("indicate rival expand cave giant same grocery burden ugly rose tuna blood"); + auto passphrase = STRING(""); + + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeStellar)); + auto input = TW::Stellar::Proto::SigningInput(); + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); + input.set_fee(1000); + input.set_sequence(2); + input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); + input.mutable_op_payment()->set_amount(10000000); + input.set_private_key(privateKey.get()->impl.bytes.data(), privateKey.get()->impl.bytes.size()); + + const auto signer = TW::Stellar::Signer(input); + + const auto signature = signer.sign(); + ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAxYC2MXoOs5v3/NT6PBn9q0uJu6u/YQle5FBa9uzteq4AAAAAAAAAAACYloAAAAAAAAAAARnfXKIAAABAocQZwTnVvGMQlpdGacWvgenxN5ku8YB8yhEGrDfEV48yDqcj6QaePAitDj/N2gxfYD9Q2pJ+ZpkQMsZZG4ACAg=="); +} + +TEST(StellarTransaction, signWithMemoText) { + auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); + auto input = Proto::SigningInput(); + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); + input.set_fee(1000); + input.set_sequence(2); + auto memoText = Proto::MemoText(); + memoText.set_text("Hello, world!"); + *input.mutable_memo_text() = memoText; + input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); + input.mutable_op_payment()->set_amount(10000000); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto signer = Signer(input); + + const auto signature = signer.sign(); + ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAEAAAANSGVsbG8sIHdvcmxkIQAAAAAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAEBQQldEkYJ6rMvOHilkwFCYyroGGUvrNeWVqr/sn3iFFqgz91XxgUT0ou7bMSPRgPROfBYDfQCFfFxbcDPrrCwB"); +} + +TEST(StellarTransaction, signWithMemoHash) { + auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); + auto input = Proto::SigningInput(); + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); + input.set_fee(1000); + input.set_sequence(2); + auto memoHash = Proto::MemoHash(); + auto fromHex = parse_hex("315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"); + memoHash.set_hash(fromHex.data(), fromHex.size()); + *input.mutable_memo_hash() = memoHash; + input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); + input.mutable_op_payment()->set_amount(10000000); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto signer = Signer(input); + + const auto signature = signer.sign(); + ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAMxX1vbdtB4xDuKwAZOSgFkYSsfznfIaTRb/JTHWJTt0wAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAECIyh1BG+hER5W+dgHDKe49X6VEYRWIjajM4Ufq3DUG/yw7Xv1MMF4eax3U0TRi7Qwj2fio/DRD3+/Ljtvip2MD"); +} + +TEST(StellarTransaction, signWithMemoReturn) { + auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); + auto input = Proto::SigningInput(); + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); + input.set_fee(1000); + input.set_sequence(2); + auto memoHash = Proto::MemoHash(); + auto fromHex = parse_hex("315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"); + memoHash.set_hash(fromHex.data(), fromHex.size()); + *input.mutable_memo_return_hash() = memoHash; + input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); + input.mutable_op_payment()->set_amount(10000000); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto signer = Signer(input); + + const auto signature = signer.sign(); + ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAQxX1vbdtB4xDuKwAZOSgFkYSsfznfIaTRb/JTHWJTt0wAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAEBd77iui04quoaoWMfeJO06nRfn3Z9bptbAj7Ol44j3ApU8c9dJwVhJbQ7La4mKgIkYviEhGx3AIulFYCkokb8M"); +} + +TEST(StellarTransaction, signWithMemoID) { + auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); + auto input = Proto::SigningInput(); + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); + input.set_fee(1000); + input.set_sequence(2); + auto memoId = Proto::MemoId(); + memoId.set_id(1234567890); + *input.mutable_memo_id() = memoId; + input.mutable_op_payment()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); + input.mutable_op_payment()->set_amount(10000000); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto signer = Signer(input); + + const auto signature = signer.sign(); + ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAIAAAAASZYC0gAAAAEAAAAAAAAAAQAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAAAAAAJiWgAAAAAAAAAABGd9cogAAAEAOJ8wwCizQPf6JmkCsCNZolQeqet2qN7fgLUUQlwx3TNzM0+/GJ6Qc2faTybjKy111rE60IlnfaPeMl/nyxKIB"); +} + +TEST(StellarTransaction, signAcreateAccount) { + auto privateKey = PrivateKey(parse_hex("59a313f46ef1c23a9e4f71cea10fc0c56a2a6bb8a4b9ea3d5348823e5a478722")); + auto input = Proto::SigningInput(); + input.set_passphrase(TWStellarPassphrase_Stellar); + input.set_account("GAE2SZV4VLGBAPRYRFV2VY7YYLYGYIP5I7OU7BSP6DJT7GAZ35OKFDYI"); + input.set_fee(1000); + input.set_sequence(2); + auto memoId = Proto::MemoId(); + memoId.set_id(1234567890); + *input.mutable_memo_id() = memoId; + input.mutable_op_create_account()->set_destination("GDCYBNRRPIHLHG7X7TKPUPAZ7WVUXCN3VO7WCCK64RIFV5XM5V5K4A52"); + input.mutable_op_create_account()->set_amount(10000000); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + const auto signer = Signer(input); + + const auto signature = signer.sign(); + ASSERT_EQ(signature, "AAAAAAmpZryqzBA+OIlrquP4wvBsIf1H3U+GT/DTP5gZ31yiAAAD6AAAAAAAAAACAAAAAAAAAAIAAAAASZYC0gAAAAEAAAAAAAAAAAAAAADFgLYxeg6zm/f81Po8Gf2rS4m7q79hCV7kUFr27O16rgAAAAAAmJaAAAAAAAAAAAEZ31yiAAAAQNgqNDqbe0X60gyH+1xf2Tv2RndFiJmyfbrvVjsTfjZAVRrS2zE9hHlqPQKpZkGKEFka7+1ElOS+/m/1JDnauQg="); +} + +} // namespace TW::Stellar::tests diff --git a/tests/chains/THORChain/SignerTests.cpp b/tests/chains/THORChain/SignerTests.cpp new file mode 100644 index 00000000000..85b6bbf68fc --- /dev/null +++ b/tests/chains/THORChain/SignerTests.cpp @@ -0,0 +1,204 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "proto/Cosmos.pb.h" +#include "THORChain/Signer.h" +#include "HexCoding.h" +#include "Bech32Address.h" +#include "TestUtilities.h" + +#include +#include + +using namespace TW; + + +TEST(THORChainSigner, SignTx_Protobuf_7E480F) { + auto input = Cosmos::Proto::SigningInput(); + input.set_signing_mode(Cosmos::Proto::Protobuf); + input.set_chain_id("thorchain-mainnet-v1"); + input.set_account_number(593); + input.set_sequence(21); + input.set_memo(""); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_thorchain_send_message(); + Bech32Address fromAddress("thor"); + EXPECT_TRUE(Bech32Address::decode("thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", fromAddress, "thor")); + Bech32Address toAddress("thor"); + EXPECT_TRUE(Bech32Address::decode("thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn", toAddress, "thor")); + message.set_from_address(std::string(fromAddress.getKeyHash().begin(), fromAddress.getKeyHash().end())); + message.set_to_address(std::string(toAddress.getKeyHash().begin(), toAddress.getKeyHash().end())); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("rune"); + amountOfTx->set_amount("38000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(2500000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("rune"); + amountOfFee->set_amount("200"); + + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + + assertJSONEqual(json, R"( + { + "accountNumber": "593", + "chainId": "thorchain-mainnet-v1", + "fee": { + "amounts": [ + { + "amount": "200", + "denom": "rune" + } + ], + "gas": "2500000" + }, + "messages": [ + { + "thorchainSendMessage": { + "amounts": [ + { + "amount": "38000000", + "denom": "rune" + } + ], + "fromAddress": "FSLnZ9tusZcIsAOAKb+9YHvJvQ4=", + "toAddress": "yoZFn7AFUcffQlQMXnhpGSyDOts=" + } + } + ], + "sequence": "21", + "signingMode": "Protobuf" + } + )"); + + auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = THORChain::Signer::sign(input); + + // https://viewblock.io/thorchain/tx/7E480FA163F6C6AFA17593F214C7BBC218F69AE3BC72366E39042AF381BFE105 + // curl -H 'Content-Type: application/json' --data-binary '{"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"ClIKUAoO..89g="}' https:///cosmos/tx/v1beta1/txs + assertJSONEqual(output.serialized(), R"( + { + "mode": "BROADCAST_MODE_BLOCK", + "tx_bytes": "ClIKUAoOL3R5cGVzLk1zZ1NlbmQSPgoUFSLnZ9tusZcIsAOAKb+9YHvJvQ4SFMqGRZ+wBVHH30JUDF54aRksgzrbGhAKBHJ1bmUSCDM4MDAwMDAwEmYKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQPtmX45bPQpL1/OWkK7pBWZzNXZbjExVKfJ6nBJ3jF8dxIECgIIARgVEhIKCwoEcnVuZRIDMjAwEKDLmAEaQKZtS3ATa26OOGvqdKm14ZbHeNfkPtIajXi5MkZ5XaX2SWOeX+YnCPZ9TxF9Jj5cVIo71m55xq4hVL3yDbRe89g=" + } + )"); + EXPECT_EQ(hex(output.signature()), "a66d4b70136b6e8e386bea74a9b5e196c778d7e43ed21a8d78b93246795da5f649639e5fe62708f67d4f117d263e5c548a3bd66e79c6ae2154bdf20db45ef3d8"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(THORChainSigner, SignTx_Json_Deprecated) { + auto input = Cosmos::Proto::SigningInput(); + input.set_memo("memo1234"); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address("thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r"); + message.set_to_address("thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("rune"); + amountOfTx->set_amount("50000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(2000000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("rune"); + amountOfFee->set_amount("200"); + + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + + assertJSONEqual(json, R"( + { + "fee": { + "amounts": [ + { + "denom": "rune", + "amount": "200" + } + ], + "gas": "2000000" + }, + "memo": "memo1234", + "messages": [ + { + "sendCoinsMessage": { + "fromAddress": "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", + "toAddress": "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn", + "amounts": [ + { + "denom": "rune", + "amount": "50000000" + } + ] + } + } + ] + } + )"); + + auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = THORChain::Signer::sign(input); + + assertJSONEqual(output.json(), R"( + { + "mode": "block", + "tx": { + "fee": { + "amount": [ + { + "amount": "200", + "denom": "rune" + } + ], + "gas": "2000000" + }, + "memo": "memo1234", + "msg": [ + { + "type": "thorchain/MsgSend", + "value": { + "amount": [ + { + "amount": "50000000", + "denom": "rune" + } + ], + "from_address": "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r", + "to_address": "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn" + } + } + ], + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3" + }, + "signature": "12AaNC0v51Rhz8rBf7V7rpI6oksREWrjzba3RK1v1NNlqZq62sG0aXWvStp9zZXe07Pp2FviFBAx+uqWsO30NQ==" + } + ] + } + } + )"); + EXPECT_EQ(hex(output.signature()), "d7601a342d2fe75461cfcac17fb57bae923aa24b11116ae3cdb6b744ad6fd4d365a99abadac1b46975af4ada7dcd95ded3b3e9d85be2141031faea96b0edf435"); +} + +TEST(THORChainSigner, SignJson) { + auto inputJson = R"({"fee":{"amounts":[{"denom":"rune","amount":"200"}],"gas":"2000000"},"memo":"memo1234","messages":[{"sendCoinsMessage":{"fromAddress":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","toAddress":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn","amounts":[{"denom":"rune","amount":"50000000"}]}}]})"; + auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); + + auto outputJson = THORChain::Signer::signJSON(inputJson, privateKey); + + EXPECT_EQ(R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"200","denom":"rune"}],"gas":"2000000"},"memo":"memo1234","msg":[{"type":"thorchain/MsgSend","value":{"amount":[{"amount":"50000000","denom":"rune"}],"from_address":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","to_address":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"12AaNC0v51Rhz8rBf7V7rpI6oksREWrjzba3RK1v1NNlqZq62sG0aXWvStp9zZXe07Pp2FviFBAx+uqWsO30NQ=="}]}})", outputJson); +} diff --git a/tests/chains/THORChain/SwapTests.cpp b/tests/chains/THORChain/SwapTests.cpp new file mode 100644 index 00000000000..1f0738595e4 --- /dev/null +++ b/tests/chains/THORChain/SwapTests.cpp @@ -0,0 +1,423 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "THORChain/Swap.h" +#include "Bitcoin/Script.h" +#include "Bitcoin/SegwitAddress.h" +#include "Ethereum/Address.h" +#include "Ethereum/ABI/Function.h" +#include "Ethereum/ABI/ParamBase.h" +#include "Ethereum/ABI/ParamAddress.h" +#include "Binance/Address.h" +#include "proto/THORChainSwap.pb.h" +#include "proto/Bitcoin.pb.h" +#include "proto/Ethereum.pb.h" +#include "proto/Binance.pb.h" + +#include "HexCoding.h" +#include "Coin.h" +#include +#include +#include "uint256.h" +#include "TestUtilities.h" + +#include + +namespace TW::THORChainSwap { + +// Addresses for wallet 'isolate dismiss fury ... note' +const auto Address1Btc = "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8"; +const auto Address1Eth = "0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7"; +const auto Address1Bnb = "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx"; +const auto Address1Thor = "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r"; +const Data TestKey1Btc = parse_hex("13fcaabaf9e71ffaf915e242ec58a743d55f102cf836968e5bd4881135e0c52c"); +const Data TestKey1Eth = parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904"); +const Data TestKey1Bnb = parse_hex("bcf8b072560dda05122c99390def2c385ec400e1a93df0657a85cf6b57a715da"); +const auto VaultBtc = "bc1q6m9u2qsu8mh8y7v8rr2ywavtj8g5arzlyhcej7"; +const auto VaultEth = "0x1091c4De6a3cF09CdA00AbDAeD42c7c3B69C83EC"; +const auto VaultBnb = "bnb1n9esxuw8ca7ts8l6w66kdh800s09msvul6vlse"; +const auto RouterEth = "0x42A5Ed456650a09Dc10EBc6361A7480fDd61f27B"; + + +TEST(THORChainSwap, SwapBtcEth) { + auto res = Swap::build(Chain::BTC, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultBtc, "", "1000000", "140000000000000000"); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "080110c0843d1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a473d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a313430303030303030303030303030303030"); + + auto tx = Bitcoin::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.amount(), 1000000); + EXPECT_EQ(tx.to_address(), VaultBtc); + EXPECT_EQ(tx.change_address(), Address1Btc); + EXPECT_EQ(tx.output_op_return(), "=:ETH.ETH:0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7:140000000000000000"); + EXPECT_EQ(tx.coin_type(), 0ul); + EXPECT_EQ(tx.private_key_size(), 0); + EXPECT_FALSE(tx.has_plan()); + + // set few fields before signing + tx.set_byte_fee(20); + EXPECT_EQ(Bitcoin::SegwitAddress(PrivateKey(TestKey1Btc).getPublicKey(TWPublicKeyTypeSECP256k1), "bc").string(), Address1Btc); + tx.add_private_key(TestKey1Btc.data(), TestKey1Btc.size()); + auto& utxo = *tx.add_utxo(); + Data utxoHash = parse_hex("1234000000000000000000000000000000000000000000000000000000005678"); + utxo.mutable_out_point()->set_hash(utxoHash.data(), utxoHash.size()); + utxo.mutable_out_point()->set_index(0); + utxo.mutable_out_point()->set_sequence(UINT32_MAX); + auto utxoScript = Bitcoin::Script::lockScriptForAddress(Address1Btc, TWCoinTypeBitcoin); + utxo.set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo.set_amount(50000000); + tx.set_use_max_amount(false); + + // sign and encode resulting input + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), 0); + EXPECT_EQ(hex(output.encoded()), // printed using prettyPrintTransaction + "01000000" // version + "0001" // marker & flag + "01" // inputs + "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" + "03" // outputs + "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" + "609deb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "42" "6a403d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030" + // witness + "02" + "47" "304402205de19c68b5ea683b9d701d45b09f96658088db76e59ad27bd7b8383ee5d484ec0220245459a4d6d679d8b457564fccc7ecc5831c7ebed49e0366c65ac031e8a5b49201" + "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" + "00000000" // nLockTime + ); +} + +TEST(THORChainSwap, SwapBtcBnb) { + auto res = Swap::build(Chain::BTC, Chain::BNB, Address1Btc, "BNB", "", Address1Bnb, VaultBtc, "", "200000", "140000000"); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "080110c09a0c1801222a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a372a2a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070386a41535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a313430303030303030"); + + auto tx = Bitcoin::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.amount(), 200000); + EXPECT_EQ(tx.to_address(), VaultBtc); + EXPECT_EQ(tx.change_address(), Address1Btc); + EXPECT_EQ(tx.output_op_return(), "SWAP:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:140000000"); + EXPECT_EQ(tx.coin_type(), 0ul); + EXPECT_EQ(tx.private_key_size(), 0); + EXPECT_FALSE(tx.has_plan()); + + // set few fields before signing + tx.set_byte_fee(80); + EXPECT_EQ(Bitcoin::SegwitAddress(PrivateKey(TestKey1Btc).getPublicKey(TWPublicKeyTypeSECP256k1), "bc").string(), Address1Btc); + tx.add_private_key(TestKey1Btc.data(), TestKey1Btc.size()); + auto& utxo = *tx.add_utxo(); + Data utxoHash = parse_hex("8eae5c3a4c75058d4e3facd5d72f18a40672bcd3d1f35ebf3094bd6c78da48eb"); + std::reverse(utxoHash.begin(), utxoHash.end()); + utxo.mutable_out_point()->set_hash(utxoHash.data(), utxoHash.size()); + utxo.mutable_out_point()->set_index(0); + utxo.mutable_out_point()->set_sequence(UINT32_MAX - 3); + auto utxoScript = Bitcoin::Script::lockScriptForAddress(Address1Btc, TWCoinTypeBitcoin); + utxo.set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo.set_amount(450000); + tx.set_use_max_amount(false); + + // sign and encode resulting input + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), 0); + EXPECT_EQ(hex(output.encoded()), // printed using prettyPrintTransaction + "01000000" // version + "0001" // marker & flag + "01" // inputs + "eb48da786cbd9430bf5ef3d1d3bc7206a4182fd7d5ac3f4e8d05754c3a5cae8e" "00000000" "00" "" "fcffffff" + "03" // outputs + "400d030000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" + "108d030000000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "42" "6a40535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3134303030303030" + // witness + "02" + "48" "30450221008427ac07af830abbf9f2e1b182096d9faefc9e5b4324786ec68386579b05d02102204fd062817a59255d62aba24b1b0c66bc070d0ddbb70bf130a6159cc057e7f6c801210" + "21" "e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" + "00000000" // nLockTime + ); + + // similar real transaction: + // https://blockchair.com/bitcoin/transaction/1cd9056b212b85d9d7d34d0795a746dd8691b8cd34ef56df0aa9622fbdec5f88 + // https://viewblock.io/thorchain/tx/1CD9056B212B85D9D7D34D0795A746DD8691B8CD34EF56DF0AA9622FBDEC5F88 + // https://explorer.binance.org/tx/8D78469069118E9B9546696214CCD46E63D3FA0D7E854C094D63C8F6061278B7 +} + +Data SwapTest_ethAddressStringToData(const std::string& asString) { + if (asString.empty()) { + return Data(); + } + auto address = Ethereum::Address(asString); + Data asData; + asData.resize(20); + std::copy(address.bytes.begin(), address.bytes.end(), asData.data()); + return asData; +} + +TEST(THORChainSwap, SwapEthBnb) { + auto res = Swap::build(Chain::ETH, Chain::BNB, Address1Eth, "BNB", "", Address1Bnb, VaultEth, RouterEth, "50000000000000000", "600003"); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "0a01001201002201002a0100422a30783432413545643435363635306130394463313045426336333631413734383066446436316632374252f30132f0010a07b1a2bc2ec5000012e4011fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3630303030330000"); + + auto tx = Ethereum::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.to_address(), RouterEth); + ASSERT_TRUE(tx.transaction().has_contract_generic()); + + Data vaultAddressBin = SwapTest_ethAddressStringToData(VaultEth); + EXPECT_EQ(hex(vaultAddressBin), "1091c4de6a3cf09cda00abdaed42c7c3b69c83ec"); + auto func = Ethereum::ABI::Function("deposit", std::vector>{ + std::make_shared(vaultAddressBin), + std::make_shared(parse_hex("0000000000000000000000000000000000000000")), + std::make_shared(uint256_t(50000000000000000)), + std::make_shared("SWAP:BNB.BNB:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:600003") + }); + Data payload; + func.encode(payload); + EXPECT_EQ(hex(payload), "1fece7b4" + "0000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec" + "0000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000b1a2bc2ec50000" + "0000000000000000000000000000000000000000000000000000000000000080" + "000000000000000000000000000000000000000000000000000000000000003e" + "535741503a424e422e424e423a626e6231757334377764686678303863683937" + "7a6475656833783375356d757266727833306a656372783a3630303030330000"); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().amount())), "b1a2bc2ec50000"); + EXPECT_EQ(hex(TW::data(tx.transaction().contract_generic().data())), hex(payload)); + + EXPECT_EQ(hex(TW::data(tx.private_key())), ""); + + // set few fields before signing + auto chainId = store(uint256_t(1)); + tx.set_chain_id(chainId.data(), chainId.size()); + auto nonce = store(uint256_t(3)); + tx.set_nonce(nonce.data(), nonce.size()); + auto gasPrice = store(uint256_t(30000000000)); + tx.set_gas_price(gasPrice.data(), gasPrice.size()); + auto gasLimit = store(uint256_t(80000)); + tx.set_gas_limit(gasLimit.data(), gasLimit.size()); + tx.set_private_key(""); + tx.set_private_key(TestKey1Eth.data(), TestKey1Eth.size()); + + // sign and encode resulting input + Ethereum::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeEthereum); + EXPECT_EQ(hex(output.encoded()), "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000025a06ae104be3201baca38315352f81fac70ca4dd47339981914e64e91149813e780a066a3f0b2c44ddf5a96a38481274f623f552a593d723237d6742185f4885c0064"); +} + +TEST(THORChainSwap, SwapBnbBtc) { + auto res = Swap::build(Chain::BNB, Chain::BTC, Address1Bnb, "BTC", "", Address1Btc, VaultBnb, "", "10000000", "10000000"); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "2a40535741503a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a313030303030303052480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204"); + + auto tx = Binance::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.memo(), "SWAP:BTC.BTC:bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8:10000000"); + ASSERT_TRUE(tx.has_send_order()); + ASSERT_EQ(tx.send_order().inputs_size(), 1); + ASSERT_EQ(tx.send_order().outputs_size(), 1); + EXPECT_EQ(hex(tx.send_order().inputs(0).address()), "e42be736e933cf8b97c26f33789a3ca6f8348cd1"); + EXPECT_EQ(hex(tx.send_order().outputs(0).address()), "99730371c7c77cb81ffa76b566dcef7c1e5dc19c"); + EXPECT_EQ(hex(TW::data(tx.private_key())), ""); + + // set few fields before signing + tx.set_chain_id("Binance-Chain-Tigris"); + tx.set_private_key(TestKey1Bnb.data(), TestKey1Bnb.size()); + + // sign and encode resulting input + Binance::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBinance); + EXPECT_EQ(hex(output.encoded()), "8002f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204126a0a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e191240af2117ebd42e31a9562738e9f8933b3b54b59e6305b5675956525e4edb6a6ac65abea614e90959ae388664e2b36bf720024879b6047e174e3cff95f8f364a4e71a40535741503a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a3130303030303030"); +} + +TEST(THORChainSwap, SwapBnbEth) { + auto res = Swap::build(Chain::BNB, Chain::ETH, Address1Bnb, "ETH", "", Address1Eth, VaultBnb, "", "27000000", "123456"); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "2a3b3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a31323334353652480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e4210c0f9ef0c12220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e4210c0f9ef0c"); + + auto tx = Binance::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.memo(), "=:ETH.ETH:0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7:123456"); + ASSERT_TRUE(tx.has_send_order()); + ASSERT_EQ(tx.send_order().inputs_size(), 1); + ASSERT_EQ(tx.send_order().outputs_size(), 1); + EXPECT_EQ(hex(tx.send_order().inputs(0).address()), "e42be736e933cf8b97c26f33789a3ca6f8348cd1"); + EXPECT_EQ(hex(tx.send_order().outputs(0).address()), "99730371c7c77cb81ffa76b566dcef7c1e5dc19c"); + EXPECT_EQ(hex(TW::data(tx.private_key())), ""); + + // set private key and few other fields + EXPECT_EQ(TW::deriveAddress(TWCoinTypeBinance, PrivateKey(TestKey1Bnb)), Address1Bnb); + tx.set_private_key(TestKey1Bnb.data(), TestKey1Bnb.size()); + tx.set_chain_id("Binance-Chain-Tigris"); + tx.set_account_number(1902570); + tx.set_sequence(12); + // sign and encode resulting input + Binance::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBinance); + EXPECT_EQ(hex(output.encoded()), "8102f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e4210c0f9ef0c12220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e4210c0f9ef0c12700a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e1912409ad3d44f3cc8d5dd2701b0bf3758ef674683533fb63e3e94d39728688c0279f8410395d631075dac62dee74b972c320f5a58e88ab81be6f1bb6a9564468ae1b618ea8f74200c1a3b3d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a313233343536"); + + // real transaction: + // https://explorer.binance.org/tx/F0CFDB0D9467E83B5BBF6DF92E4E2D04FE9EFF9B0A1C71D88DCEF566233DCAA2 + // https://viewblock.io/thorchain/tx/F0CFDB0D9467E83B5BBF6DF92E4E2D04FE9EFF9B0A1C71D88DCEF566233DCAA2 + // https://etherscan.io/tx/0x8e5bb7d87e17af86e649e402bc5c182ea8c32ddaca153804679de1184e0d9747 +} + +TEST(THORChainSwap, SwapBnbRune) { + auto res = Swap::build(Chain::BNB, Chain::THOR, Address1Bnb, "RUNE", "", Address1Thor, VaultBnb, "", "4000000", "121065076"); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "2a44535741503a54484f522e52554e453a74686f72317a3533777765376d64366365777a39737177717a6e306161767061756e3067773065786e32723a31323130363530373652480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e42108092f40112220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e42108092f401"); + + auto tx = Binance::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.memo(), "SWAP:THOR.RUNE:thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r:121065076"); + ASSERT_TRUE(tx.has_send_order()); + ASSERT_EQ(tx.send_order().inputs_size(), 1); + ASSERT_EQ(tx.send_order().outputs_size(), 1); + EXPECT_EQ(hex(tx.send_order().inputs(0).address()), "e42be736e933cf8b97c26f33789a3ca6f8348cd1"); + EXPECT_EQ(hex(tx.send_order().outputs(0).address()), "99730371c7c77cb81ffa76b566dcef7c1e5dc19c"); + EXPECT_EQ(hex(TW::data(tx.private_key())), ""); + + // set private key and few other fields + EXPECT_EQ(TW::deriveAddress(TWCoinTypeBinance, PrivateKey(TestKey1Bnb)), Address1Bnb); + tx.set_private_key(TestKey1Bnb.data(), TestKey1Bnb.size()); + tx.set_chain_id("Binance-Chain-Tigris"); + tx.set_account_number(1902570); + tx.set_sequence(4); + // sign and encode resulting input + Binance::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBinance); + EXPECT_EQ(hex(output.encoded()), "8a02f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e42108092f40112220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e42108092f40112700a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e191240d91b6655ea4ade62a90cc9b28e43ccd2887dcf1c563e42bbd0d6ae4e825c2c6a1ba7784866810f36b6e098b0c877d1daa48016d0558f7b796b3f0b410107ba2f18ea8f7420041a44535741503a54484f522e52554e453a74686f72317a3533777765376d64366365777a39737177717a6e306161767061756e3067773065786e32723a313231303635303736"); + + // real transaction: + // https://explorer.binance.org/tx/84EE429B35945F0568097527A084532A9DE7BBAB0E6A5562E511CEEFB188DE69 + // https://viewblock.io/thorchain/tx/D582E1473FE229F02F162055833C64F49FB4FF515989A4785ED7898560A448FC +} + +TEST(THORChainSwap, SwapBnbBnbToken) { + auto res = Swap::build( + Chain::BNB, + Chain::BNB, + "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx", + "BNB", + "TWT-8C2", + "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx", + "bnb1qefsjm654cdw94ejj8g4s49w7z8te75veslusz", + "", + "10000000", // 0.1 bnb + "5400000000" // 54.0 twt + ); + ASSERT_EQ(std::get<1>(res), 0); + ASSERT_EQ(std::get<2>(res), ""); + EXPECT_EQ(hex(std::get<0>(res)), "2a46535741503a424e422e5457542d3843323a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a3534303030303030303052480a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a140653096f54ae1ae2d73291d15854aef08ebcfa8c120a0a03424e421080ade204"); + + auto tx = Binance::Proto::SigningInput(); + ASSERT_TRUE(tx.ParseFromArray(std::get<0>(res).data(), (int)std::get<0>(res).size())); + + // check fields + EXPECT_EQ(tx.memo(), "SWAP:BNB.TWT-8C2:bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx:5400000000"); + ASSERT_TRUE(tx.has_send_order()); + ASSERT_EQ(tx.send_order().inputs_size(), 1); + ASSERT_EQ(tx.send_order().outputs_size(), 1); + EXPECT_EQ(hex(tx.send_order().inputs(0).address()), "e42be736e933cf8b97c26f33789a3ca6f8348cd1"); + EXPECT_EQ(hex(tx.send_order().outputs(0).address()), "0653096f54ae1ae2d73291d15854aef08ebcfa8c"); + EXPECT_EQ(hex(TW::data(tx.private_key())), ""); + + // set private key and few other fields + const Data privateKey = parse_hex("bcf8b072560dda05122c99390def2c385ec400e1a93df0657a85cf6b57a715da"); + EXPECT_EQ(Binance::Address(PrivateKey(privateKey).getPublicKey(TWPublicKeyTypeSECP256k1)).string(), "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx"); + tx.set_private_key(privateKey.data(), privateKey.size()); + tx.set_chain_id("Binance-Chain-Tigris"); + tx.set_account_number(1902570); + tx.set_sequence(18); + + // sign and encode resulting input + Binance::Proto::SigningOutput output; + ANY_SIGN(tx, TWCoinTypeBinance); + EXPECT_EQ(hex(output.encoded()), "8c02f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a140653096f54ae1ae2d73291d15854aef08ebcfa8c120a0a03424e421080ade20412700a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e1912405fd64a0ed5777f5ea4556624bd096f8b20b6d2b510655e4c928db1ec967e6c7025453882ce7e10138ac92f5d6a949acc5382a5539f81347856c67c4bb678d3c418ea8f7420121a46535741503a424e422e5457542d3843323a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a35343030303030303030"); + + // real transaction: + // curl -X GET "http://dataseed1.binance.org/broadcast_tx_sync?tx=0x8c02...3030" + // https://viewblock.io/thorchain/tx/6D1EDC9BD9BFAFEF0F88F95A164191262EA02A0413BF3D9773110AD5676E1523 + // https://explorer.binance.org/tx/6D1EDC9BD9BFAFEF0F88F95A164191262EA02A0413BF3D9773110AD5676E1523 + // https://explorer.binance.org/tx/60C54C9F253B89C36A2788AB66951045E8AC5F5729597CB6C64A13013A7A54CC +} + +TEST(THORChainSwap, Memo) { + EXPECT_EQ(Swap::buildMemo(Chain::BTC, "BTC", "", "btc123", 1234), "SWAP:BTC.BTC:btc123:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::BNB, "BNB", "", "bnb123", 1234), "SWAP:BNB.BNB:bnb123:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "", "0xaabbccdd", 1234), "=:ETH.ETH:0xaabbccdd:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "", "0xaabbccdd", 1234), "=:ETH.ETH:0xaabbccdd:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "0x0000000000000000000000000000000000000000", "0xaabbccdd", 1234), "=:ETH.ETH:0xaabbccdd:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::ETH, "ETH", "0x4B0F1812e5Df2A09796481Ff14017e6005508003", "0xaabbccdd", 1234), "=:ETH.0x4B0F1812e5Df2A09796481Ff14017e6005508003:0xaabbccdd:1234"); + EXPECT_EQ(Swap::buildMemo(Chain::BNB, "BNB", "TWT-8C2", "bnb123", 1234), "SWAP:BNB.TWT-8C2:bnb123:1234"); +} + +TEST(THORChainSwap, WrongFromAddress) { + { + auto res = Swap::build(Chain::BNB, Chain::ETH, "DummyAddress", "ETH", "", Address1Eth, VaultEth, "", "100000", "100000"); + EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_from_address); + EXPECT_EQ(std::get<2>(res), "Invalid from address"); + } + { + auto res = Swap::build(Chain::BNB, Chain::ETH, Address1Btc, "ETH", "", Address1Eth, VaultEth, "", "100000", "100000"); + EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_from_address); + EXPECT_EQ(std::get<2>(res), "Invalid from address"); + } +} + +TEST(THORChainSwap, WrongToAddress) { + { + auto res = Swap::build(Chain::BNB, Chain::ETH, Address1Bnb, "ETH", "", "DummyAddress", VaultEth, "", "100000", "100000"); + EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_to_address); + EXPECT_EQ(std::get<2>(res), "Invalid to address"); + } + { + auto res = Swap::build(Chain::BNB, Chain::ETH, Address1Bnb, "ETH", "", Address1Btc, VaultEth, "", "100000", "100000"); + EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_to_address); + EXPECT_EQ(std::get<2>(res), "Invalid to address"); + } +} + +TEST(THORChainSwap, FromRuneNotSupported) { + auto res = Swap::build(Chain::THOR, Chain::BNB, Address1Thor, "BNB", "", Address1Bnb, "", "", "1000", "1000"); + EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Unsupported_from_chain); + EXPECT_EQ(std::get<2>(res), "Unsupported from chain: 3"); +} + +TEST(THORChainSwap, EthInvalidVault) { + { + auto res = Swap::build(Chain::ETH, Chain::BNB, Address1Eth, "BNB", "", Address1Bnb, "_INVALID_ADDRESS_", RouterEth, "50000000000000000", "600003"); + EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_vault_address); + EXPECT_EQ(std::get<2>(res), "Invalid vault address: _INVALID_ADDRESS_"); + } + { + auto res = Swap::build(Chain::ETH, Chain::BNB, Address1Eth, "BNB", "", Address1Bnb, VaultEth, "_INVALID_ADDRESS_", "50000000000000000", "600003"); + EXPECT_EQ(std::get<1>(res), Proto::ErrorCode::Error_Invalid_router_address); + EXPECT_EQ(std::get<2>(res), "Invalid router address: _INVALID_ADDRESS_"); + } +} + +} // namespace diff --git a/tests/chains/THORChain/TWAnyAddressTests.cpp b/tests/chains/THORChain/TWAnyAddressTests.cpp new file mode 100644 index 00000000000..52d9bc560cb --- /dev/null +++ b/tests/chains/THORChain/TWAnyAddressTests.cpp @@ -0,0 +1,28 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(THORChainAnyAddress, IsValid) { + EXPECT_TRUE(TWAnyAddressIsValid(STRING("thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r").get(), TWCoinTypeTHORChain)); + EXPECT_TRUE(TWAnyAddressIsValid(STRING("thor1c8jd7ad9pcw4k3wkuqlkz4auv95mldr2kyhc65").get(), TWCoinTypeTHORChain)); + EXPECT_FALSE(TWAnyAddressIsValid(STRING("cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02").get(), TWCoinTypeTHORChain)); +} + +TEST(THORChainAnyAddress, Create) { + auto string = STRING("thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeTHORChain)); + auto string2 = WRAPS(TWAnyAddressDescription(addr.get())); + EXPECT_TRUE(TWStringEqual(string.get(), string2.get())); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "1522e767db6eb19708b0038029bfbd607bc9bd0e"); +} diff --git a/tests/chains/THORChain/TWAnySignerTests.cpp b/tests/chains/THORChain/TWAnySignerTests.cpp new file mode 100644 index 00000000000..8e9e45380d7 --- /dev/null +++ b/tests/chains/THORChain/TWAnySignerTests.cpp @@ -0,0 +1,49 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include "Cosmos/Address.h" +#include "proto/Cosmos.pb.h" +#include "HexCoding.h" + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(THORChainTWAnySigner, SignTx) { + auto privateKey = parse_hex("7105512f0c020a1dd759e14b865ec0125f59ac31e34d7a2807a228ed50cb343e"); + Cosmos::Proto::SigningInput input; + input.set_account_number(593); + input.set_chain_id("thorchain"); + input.set_sequence(3); + input.set_private_key(privateKey.data(), privateKey.size()); + input.set_memo(""); + + auto fromAddress = "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r"; + auto toAddress = "thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"; + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress); + message.set_to_address(toAddress); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("rune"); + amountOfTx->set_amount("10000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("rune"); + amountOfFee->set_amount("2000000"); + + Cosmos::Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeTHORChain); + + // https://viewblock.io/thorchain/tx/FD0445AFFC4ED9ACCB7B5D3ADE361DAE4596EA096340F1360F1020381EA454AF + ASSERT_EQ(output.json(), R"({"mode":"block","tx":{"fee":{"amount":[{"amount":"2000000","denom":"rune"}],"gas":"200000"},"memo":"","msg":[{"type":"thorchain/MsgSend","value":{"amount":[{"amount":"10000000","denom":"rune"}],"from_address":"thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2r","to_address":"thor1e2ryt8asq4gu0h6z2sx9u7rfrykgxwkmr9upxn"}}],"signatures":[{"pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A+2Zfjls9CkvX85aQrukFZnM1dluMTFUp8nqcEneMXx3"},"signature":"qgpMX3WNq4DsNBnYtdmBD4ejiailK4uI/m3/YVqCSNF8AtkUOTmP48ztqCbpkWTFvw1/9S8/ivsFxOcK6AI0jA=="}]}})"); +} diff --git a/tests/chains/THORChain/TWCoinTypeTests.cpp b/tests/chains/THORChain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..de7f561af47 --- /dev/null +++ b/tests/chains/THORChain/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWTHORChainCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTHORChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("ADF0899E58C177E2391F22D04E9C5E1C35BB0F75B42B363A0761687907FD9476")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTHORChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("thor196yf4pq80hjrmz7nnh0ar0ypqg02r0w4dq4mzu")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTHORChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTHORChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTHORChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTHORChain), 8); + ASSERT_EQ(TWBlockchainThorchain, TWCoinTypeBlockchain(TWCoinTypeTHORChain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTHORChain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTHORChain)); + assertStringsEqual(symbol, "RUNE"); + assertStringsEqual(txUrl, "https://viewblock.io/thorchain/tx/ADF0899E58C177E2391F22D04E9C5E1C35BB0F75B42B363A0761687907FD9476"); + assertStringsEqual(accUrl, "https://viewblock.io/thorchain/address/thor196yf4pq80hjrmz7nnh0ar0ypqg02r0w4dq4mzu"); + assertStringsEqual(id, "thorchain"); + assertStringsEqual(name, "THORChain"); +} diff --git a/tests/chains/THORChain/TWSwapTests.cpp b/tests/chains/THORChain/TWSwapTests.cpp new file mode 100644 index 00000000000..ea266cb6584 --- /dev/null +++ b/tests/chains/THORChain/TWSwapTests.cpp @@ -0,0 +1,230 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Bitcoin/Script.h" +#include "Bitcoin/SegwitAddress.h" +#include "PrivateKey.h" +#include "proto/Binance.pb.h" +#include "proto/Ethereum.pb.h" +#include "proto/THORChainSwap.pb.h" +#include +#include +#include + +#include "HexCoding.h" +#include "uint256.h" +#include "TestUtilities.h" + +#include + +using namespace TW; + +namespace TW::THORChainSwap::tests { + +// clang-format off +const auto Address1Bnb = "bnb1us47wdhfx08ch97zdueh3x3u5murfrx30jecrx"; +const auto Address1Btc = "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8"; +const auto Address1Eth = "0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7"; +const auto VaultBnb = "bnb1n9esxuw8ca7ts8l6w66kdh800s09msvul6vlse"; +const auto VaultBtc = "bc1q6m9u2qsu8mh8y7v8rr2ywavtj8g5arzlyhcej7"; +const auto VaultEth = "0x1091c4De6a3cF09CdA00AbDAeD42c7c3B69C83EC"; +const auto RouterEth = "0x42A5Ed456650a09Dc10EBc6361A7480fDd61f27B"; +const Data TestKey1Bnb = parse_hex("bcf8b072560dda05122c99390def2c385ec400e1a93df0657a85cf6b57a715da"); +const Data TestKey1Btc = parse_hex("13fcaabaf9e71ffaf915e242ec58a743d55f102cf836968e5bd4881135e0c52c"); +const Data TestKey1Eth = parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904"); + +TEST(TWTHORChainSwap, SwapBtcToEth) { + // prepare swap input + Proto::SwapInput input; + input.set_from_chain(Proto::BTC); + input.set_from_address(Address1Btc); + Proto::Asset toAsset; + toAsset.set_chain(Proto::ETH); + toAsset.set_symbol("ETH"); + toAsset.set_token_id(""); + *input.mutable_to_asset() = toAsset; + input.set_to_address(Address1Eth); + input.set_vault_address(VaultBtc); + input.set_router_address(""); + input.set_from_amount("1000000"); + input.set_to_amount_limit("140000000000000000"); + + // serialize input + const auto inputData_ = input.SerializeAsString(); + EXPECT_EQ(hex(inputData_), "0801122a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070381a0708021203455448222a3078623966353737316332373636346266323238326439386530396437663530636563376362303161372a2a62633171366d397532717375386d68387937763872723279776176746a38673561727a6c796863656a373a07313030303030304212313430303030303030303030303030303030"); + const auto inputTWData_ = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData_.data(), inputData_.size())); + + // invoke swap + const auto outputTWData_ = WRAPD(TWTHORChainSwapBuildSwap(inputTWData_.get())); + const auto outputData = data(TWDataBytes(outputTWData_.get()), TWDataSize(outputTWData_.get())); + EXPECT_EQ(outputData.size(), 178ul); + // parse result in proto + Proto::SwapOutput outputProto; + EXPECT_TRUE(outputProto.ParseFromArray(outputData.data(), static_cast(outputData.size()))); + EXPECT_EQ(outputProto.from_chain(), Proto::BTC); + EXPECT_EQ(outputProto.to_chain(), Proto::ETH); + EXPECT_EQ(outputProto.error().code(), 0); + EXPECT_EQ(outputProto.error().message(), ""); + EXPECT_TRUE(outputProto.has_bitcoin()); + Bitcoin::Proto::SigningInput txInput = outputProto.bitcoin(); + + // tx input: check some fields + EXPECT_EQ(txInput.amount(), 1000000); + EXPECT_EQ(txInput.to_address(), "bc1q6m9u2qsu8mh8y7v8rr2ywavtj8g5arzlyhcej7"); + EXPECT_EQ(txInput.change_address(), "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8"); + EXPECT_EQ(txInput.output_op_return(), "=:ETH.ETH:0xb9f5771c27664bf2282d98e09d7f50cec7cb01a7:140000000000000000"); + EXPECT_EQ(txInput.coin_type(), 0ul); + + // sign tx input for signed full tx + // set few fields before signing + txInput.set_byte_fee(20); + EXPECT_EQ(Bitcoin::SegwitAddress(PrivateKey(TestKey1Btc).getPublicKey(TWPublicKeyTypeSECP256k1), "bc").string(), Address1Btc); + txInput.add_private_key(TestKey1Btc.data(), TestKey1Btc.size()); + auto& utxo = *txInput.add_utxo(); + Data utxoHash = parse_hex("1234000000000000000000000000000000000000000000000000000000005678"); + utxo.mutable_out_point()->set_hash(utxoHash.data(), utxoHash.size()); + utxo.mutable_out_point()->set_index(0); + utxo.mutable_out_point()->set_sequence(UINT32_MAX); + auto utxoScript = Bitcoin::Script::lockScriptForAddress(Address1Btc, TWCoinTypeBitcoin); + utxo.set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo.set_amount(50000000); + txInput.set_use_max_amount(false); + + // sign and encode resulting input + { + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(txInput, TWCoinTypeBitcoin); + EXPECT_EQ(output.error(), 0); + EXPECT_EQ(hex(output.encoded()), // printed using prettyPrintTransaction + "01000000" // version + "0001" // marker & flag + "01" // inputs + "1234000000000000000000000000000000000000000000000000000000005678" "00000000" "00" "" "ffffffff" + "03" // outputs + "40420f0000000000" "16" "0014d6cbc5021c3eee72798718d447758b91d14e8c5f" + "609deb0200000000" "16" "00140cb9f5c6b62c03249367bc20a90dd2425e6926af" + "0000000000000000" "42" "6a403d3a4554482e4554483a3078623966353737316332373636346266323238326439386530396437663530636563376362303161373a3134303030303030303030" + // witness + "02" + "47" "304402205de19c68b5ea683b9d701d45b09f96658088db76e59ad27bd7b8383ee5d484ec0220245459a4d6d679d8b457564fccc7ecc5831c7ebed49e0366c65ac031e8a5b49201" + "21" "021e582a887bd94d648a9267143eb600449a8d59a0db0653740b1378067a6d0cee" + "00000000" // nLockTime + ); + } +} + +TEST(TWTHORChainSwap, SwapEthBnb) { + // prepare swap input + Proto::SwapInput input; + input.set_from_chain(Proto::ETH); + input.set_from_address(Address1Eth); + Proto::Asset toAsset; + toAsset.set_chain(Proto::BNB); + toAsset.set_symbol("BNB"); + toAsset.set_token_id(""); + *input.mutable_to_asset() = toAsset; + input.set_to_address(Address1Bnb); + input.set_vault_address(VaultEth); + input.set_router_address(RouterEth); + input.set_from_amount("50000000000000000"); + input.set_to_amount_limit("600003"); + + // serialize input + const auto inputData_ = input.SerializeAsString(); + EXPECT_EQ(hex(inputData_), "0802122a3078623966353737316332373636346266323238326439386530396437663530636563376362303161371a0708031203424e42222a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372782a2a307831303931633444653661336346303943644130304162444165443432633763334236394338334543322a3078343241354564343536363530613039446331304542633633363141373438306644643631663237423a1135303030303030303030303030303030304206363030303033"); + const auto inputTWData_ = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData_.data(), inputData_.size())); + + // invoke swap + const auto outputTWData_ = WRAPD(TWTHORChainSwapBuildSwap(inputTWData_.get())); + const auto outputData = data(TWDataBytes(outputTWData_.get()), TWDataSize(outputTWData_.get())); + EXPECT_EQ(outputData.size(), 311ul); + // parse result in proto + Proto::SwapOutput outputProto; + EXPECT_TRUE(outputProto.ParseFromArray(outputData.data(), static_cast(outputData.size()))); + EXPECT_EQ(outputProto.from_chain(), Proto::ETH); + EXPECT_EQ(outputProto.to_chain(), Proto::BNB); + EXPECT_EQ(outputProto.error().code(), 0); + EXPECT_EQ(outputProto.error().message(), ""); + EXPECT_TRUE(outputProto.has_ethereum()); + Ethereum::Proto::SigningInput txInput = outputProto.ethereum(); + + // sign tx input for signed full tx + // set few fields before signing + auto chainId = store(uint256_t(1)); + txInput.set_chain_id(chainId.data(), chainId.size()); + auto nonce = store(uint256_t(3)); + txInput.set_nonce(nonce.data(), nonce.size()); + auto gasPrice = store(uint256_t(30000000000)); + txInput.set_gas_price(gasPrice.data(), gasPrice.size()); + auto gasLimit = store(uint256_t(80000)); + txInput.set_gas_limit(gasLimit.data(), gasLimit.size()); + txInput.set_private_key(""); + txInput.set_private_key(TestKey1Eth.data(), TestKey1Eth.size()); + + // sign and encode resulting input + Ethereum::Proto::SigningOutput output; + ANY_SIGN(txInput, TWCoinTypeEthereum); + EXPECT_EQ(hex(output.encoded()), "f90151038506fc23ac00830138809442a5ed456650a09dc10ebc6361a7480fdd61f27b87b1a2bc2ec50000b8e41fece7b40000000000000000000000001091c4de6a3cf09cda00abdaed42c7c3b69c83ec000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b1a2bc2ec500000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000003e535741503a424e422e424e423a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372783a363030303033000025a06ae104be3201baca38315352f81fac70ca4dd47339981914e64e91149813e780a066a3f0b2c44ddf5a96a38481274f623f552a593d723237d6742185f4885c0064"); +} + +TEST(TWTHORChainSwap, SwapBnbBtc) { + // prepare swap input + Proto::SwapInput input; + input.set_from_chain(Proto::BNB); + input.set_from_address(Address1Bnb); + Proto::Asset toAsset; + toAsset.set_chain(Proto::BTC); + toAsset.set_symbol("BTC"); + toAsset.set_token_id(""); + *input.mutable_to_asset() = toAsset; + input.set_to_address(Address1Btc); + input.set_vault_address(VaultBnb); + input.set_router_address(""); + input.set_from_amount("10000000"); + input.set_to_amount_limit("10000000"); + + // serialize input + const auto inputData_ = input.SerializeAsString(); + EXPECT_EQ(hex(inputData_), "0803122a626e62317573343777646866783038636839377a6475656833783375356d757266727833306a656372781a0708011203425443222a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070382a2a626e62316e396573787577386361377473386c367736366b64683830307330396d7376756c36766c73653a08313030303030303042083130303030303030"); + const auto inputTWData_ = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData_.data(), inputData_.size())); + + // invoke swap + const auto outputTWData_ = WRAPD(TWTHORChainSwapBuildSwap(inputTWData_.get())); + const auto outputData = data(TWDataBytes(outputTWData_.get()), TWDataSize(outputTWData_.get())); + EXPECT_EQ(outputData.size(), 149ul); + // parse result in proto + Proto::SwapOutput outputProto; + EXPECT_TRUE(outputProto.ParseFromArray(outputData.data(), static_cast(outputData.size()))); + EXPECT_EQ(outputProto.from_chain(), Proto::BNB); + EXPECT_EQ(outputProto.to_chain(), Proto::BTC); + EXPECT_EQ(outputProto.error().code(), 0); + EXPECT_EQ(outputProto.error().message(), ""); + EXPECT_TRUE(outputProto.has_binance()); + Binance::Proto::SigningInput txInput = outputProto.binance(); + + // set few fields before signing + txInput.set_chain_id("Binance-Chain-Tigris"); + txInput.set_private_key(TestKey1Bnb.data(), TestKey1Bnb.size()); + + // sign and encode resulting input + Ethereum::Proto::SigningOutput output; + ANY_SIGN(txInput, TWCoinTypeBinance); + EXPECT_EQ(hex(output.encoded()), "8002f0625dee0a4c2a2c87fa0a220a14e42be736e933cf8b97c26f33789a3ca6f8348cd1120a0a03424e421080ade20412220a1499730371c7c77cb81ffa76b566dcef7c1e5dc19c120a0a03424e421080ade204126a0a26eb5ae9872103ea4b4bc12dc6f36a28d2c9775e01eef44def32cc70fb54f0e4177b659dbc0e191240af2117ebd42e31a9562738e9f8933b3b54b59e6305b5675956525e4edb6a6ac65abea614e90959ae388664e2b36bf720024879b6047e174e3cff95f8f364a4e71a40535741503a4254432e4254433a62633171706a756c7433346b3973706a66796d38687373326a72776a676630786a6634307a65307070383a3130303030303030"); +} + +TEST(TWTHORChainSwap, NegativeInvalidInput) { + const auto inputData = parse_hex("00112233"); + const auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData.data(), inputData.size())); + + const auto outputTWData = WRAPD(TWTHORChainSwapBuildSwap(inputTWData.get())); + const auto outputData = data(TWDataBytes(outputTWData.get()), TWDataSize(outputTWData.get())); + EXPECT_EQ(outputData.size(), 39ul); + EXPECT_EQ(hex(outputData), "1a2508021221436f756c64206e6f7420646573657269616c697a6520696e7075742070726f746f"); + EXPECT_EQ(hex(data(std::string("Could not deserialize input proto"))), "436f756c64206e6f7420646573657269616c697a6520696e7075742070726f746f"); +} +// clang-format on + +} // namespace TW::ThorChainSwap::tests diff --git a/tests/chains/Terra/SignerTests.cpp b/tests/chains/Terra/SignerTests.cpp new file mode 100644 index 00000000000..34c01feb560 --- /dev/null +++ b/tests/chains/Terra/SignerTests.cpp @@ -0,0 +1,454 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Base64.h" +#include "proto/Cosmos.pb.h" +#include "Cosmos/Address.h" +#include "Cosmos/Signer.h" +#include "Cosmos/ProtobufSerialization.h" +#include "uint256.h" +#include "TestUtilities.h" + +#include +#include + +namespace TW::Cosmos::tests { + +TEST(TerraClassicSigner, SignSendTx) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::JSON); + input.set_account_number(1037); + input.set_chain_id("columbus-5"); + input.set_memo(""); + input.set_sequence(2); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra1hsk6jryyqjfhp5dhc55tc9jtckygx0ep37hdd2", fromAddress)); + Address toAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", toAddress)); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("luna"); + amountOfTx->set_amount("1000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("luna"); + amountOfFee->set_amount("200"); + + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + EXPECT_EQ(json, R"({"accountNumber":"1037","chainId":"columbus-5","fee":{"amounts":[{"denom":"luna","amount":"200"}],"gas":"200000"},"sequence":"2","messages":[{"sendCoinsMessage":{"fromAddress":"terra1hsk6jryyqjfhp5dhc55tc9jtckygx0ep37hdd2","toAddress":"terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf","amounts":[{"denom":"luna","amount":"1000000"}]}}]})"); + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerra); + + assertJSONEqual(output.json(), R"( + { + "mode": "block", + "tx": { + "fee": { + "amount": [ + { + "amount": "200", + "denom": "luna" + } + ], + "gas": "200000" + }, + "memo": "", + "msg": [ + { + "type": "cosmos-sdk/MsgSend", + "value": { + "amount": [ + { + "amount": "1000000", + "denom": "luna" + } + ], + "from_address": "terra1hsk6jryyqjfhp5dhc55tc9jtckygx0ep37hdd2", + "to_address": "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf" + } + } + ], + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "AlcobsPzfTNVe7uqAAsndErJAjqplnyudaGB0f+R+p3F" + }, + "signature": "ofdIsLJzkODcQwLG89eE2g4HOaUmfKPh/08t07ehKPUqRMl4rVonzo73mkOvqtrHWjdtB+6t6R8DGudPpb6bRg==" + } + ] + } + } + )"); + EXPECT_EQ(hex(output.signature()), "a1f748b0b27390e0dc4302c6f3d784da0e0739a5267ca3e1ff4f2dd3b7a128f52a44c978ad5a27ce8ef79a43afaadac75a376d07eeade91f031ae74fa5be9b46"); + EXPECT_EQ(output.serialized(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(TerraClassicSigner, SignWasmTransferTxProtobuf_9FF3F0) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(3407705); + input.set_chain_id("columbus-5"); + input.set_memo(""); + input.set_sequence(3); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", fromAddress)); + Address toAddress; + ASSERT_TRUE(Address::decode("terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", toAddress)); + const auto tokenContractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76"; // ANC + + auto msg = input.add_messages(); + auto& message = *msg->mutable_wasm_terra_execute_contract_transfer_message(); + message.set_sender_address(fromAddress.string()); + message.set_contract_address(tokenContractAddress); + const auto amount = store(uint256_t(250000), 0); + message.set_amount(amount.data(), amount.size()); + message.set_recipient_address(toAddress.string()); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("3000"); + + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + assertJSONEqual(json, R"( + { + "signingMode": "Protobuf", + "accountNumber": "3407705", + "chainId": "columbus-5", + "fee": { + "amounts": [ + { + "denom": "uluna", + "amount": "3000" + } + ], + "gas": "200000" + }, + "sequence": "3", + "messages": [ + { + "wasmTerraExecuteContractTransferMessage": { + "senderAddress": "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", + "contractAddress": "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76", + "amount": "A9CQ", + "recipientAddress": "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp" + } + } + ] + } + )"); + + auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerra); + + // https://finder.terra.money/mainnet/tx/9FF3F0A16879254C22EB90D8B4D6195467FE5014381FD36BD3C23CA6698FE94B + // curl -H 'Content-Type: application/json' --data-binary '{"mode": "BROADCAST_MODE_BLOCK","tx_bytes": "CogCCo..wld8"})' https:///cosmos/tx/v1beta1/txs + assertJSONEqual(output.serialized(), R"( + { + "tx_bytes": "CucBCuQBCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBK5AQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2Glt7InRyYW5zZmVyIjp7ImFtb3VudCI6IjI1MDAwMCIsInJlY2lwaWVudCI6InRlcnJhMWpsZ2FxeTludm4yaGY1dDJzcmE5eWN6OHM3N3duZjlsMGttZ2NwIn19EmcKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQNwZjrHsPmJKW/rXOWfukpQ1+lOHOJW3/IlFFnKLNmsABIECgIIARgDEhMKDQoFdWx1bmESBDMwMDAQwJoMGkAaprIEMLPH2HmFdwFGoaipb2GIyhXt6ombz+WMnG2mORBI6gFt0M+IymYgzZz6w1SW52R922yafDnn7yXfutRw", + "mode": "BROADCAST_MODE_BLOCK" + } + )"); + EXPECT_EQ(hex(output.signature()), "1aa6b20430b3c7d87985770146a1a8a96f6188ca15edea899bcfe58c9c6da6391048ea016dd0cf88ca6620cd9cfac35496e7647ddb6c9a7c39e7ef25dfbad470"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(TerraClassicSigner, SignWasmTransferTxJson_078E90) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::JSON); + input.set_account_number(3407705); + input.set_chain_id("columbus-5"); + input.set_memo(""); + input.set_sequence(2); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", fromAddress)); + Address toAddress; + ASSERT_TRUE(Address::decode("terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", toAddress)); + const auto tokenContractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76"; // ANC + + auto msg = input.add_messages(); + auto& message = *msg->mutable_wasm_terra_execute_contract_transfer_message(); + message.set_sender_address(fromAddress.string()); + message.set_contract_address(tokenContractAddress); + const auto amount = store(250000); + message.set_amount(amount.data(), amount.size()); + message.set_recipient_address(toAddress.string()); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("3000"); + + auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerra); + + // https://finder.terra.money/mainnet/tx/078E90458061611F6FD8B708882B55FF5C1FFB3FCE61322107A0A0DE39FC0F3E + // curl -H 'Content-Type: application/json' --data-binary '{"mode": "block","tx":{...}}' https:///txs + assertJSONEqual(output.json(), R"( + { + "mode": "block", + "tx": + { + "fee": {"amount":[{"amount": "3000","denom": "uluna"}],"gas": "200000"}, + "memo": "", + "msg": + [ + { + "type": "wasm/MsgExecuteContract", + "value": + { + "sender": "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", + "contract": "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76", + "execute_msg": + { + "transfer": + { + "amount": "250000", + "recipient": "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp" + } + }, + "coins": [] + } + } + ], + "signatures": + [ + { + "pub_key": + { + "type": "tendermint/PubKeySecp256k1", + "value": "A3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awA" + }, + "signature": "BjETdtbA97Wv1zvcsCV1tM+bdYKC8O3uGTk4mMRv6pBJB2y/Ds7qoS7s/zrkhYak1YChklQetHsI30XRXzGIkg==" + } + ] + } + })"); + EXPECT_EQ(hex(output.signature()), "06311376d6c0f7b5afd73bdcb02575b4cf9b758282f0edee19393898c46fea9049076cbf0eceeaa12eecff3ae48586a4d580a192541eb47b08df45d15f318892"); + EXPECT_EQ(output.serialized(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(TerraClassicSigner, SignWasmGeneric_EC4F85) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(3407705); + input.set_chain_id("columbus-5"); + input.set_memo(""); + input.set_sequence(7); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", fromAddress)); + Address toAddress; + ASSERT_TRUE(Address::decode("terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", toAddress)); + const auto tokenContractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76"; // ANC + const auto txMessage = R"({"transfer": { "amount": "250000", "recipient": "terra1d7048csap4wzcv5zm7z6tdqem2agyp9647vdyj" } })"; + + auto msg = input.add_messages(); + auto& message = *msg->mutable_wasm_terra_execute_contract_generic(); + message.set_sender_address(fromAddress.string()); + message.set_contract_address(tokenContractAddress); + message.set_execute_msg(txMessage); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("3000"); + + auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerra); + + // https://finder.terra.money/mainnet/tx/EC4F8532847E4D6AF016E6F6D3F027AE7FB6FF0B533C5132B01382D83B214A6F + // curl -H 'Content-Type: application/json' --data-binary '{"mode": "BROADCAST_MODE_BLOCK","tx_bytes": "Cu4BC...iVt"})' https:///cosmos/tx/v1beta1/txs + assertJSONEqual(output.serialized(), R"( + { + "tx_bytes": "Cu4BCusBCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBLAAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2GmJ7InRyYW5zZmVyIjogeyAiYW1vdW50IjogIjI1MDAwMCIsICJyZWNpcGllbnQiOiAidGVycmExZDcwNDhjc2FwNHd6Y3Y1em03ejZ0ZHFlbTJhZ3lwOTY0N3ZkeWoiIH0gfRJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYBxITCg0KBXVsdW5hEgQzMDAwEMCaDBpAkPsS7xlSng2LMc9KiD1soN5NLaDcUh8I9okPmsdJN3le1B7yxRGNB4aQfhaRl/8Z0r5vitRT0AWuxDasd8wcFw==", + "mode": "BROADCAST_MODE_BLOCK" + } + )"); + + EXPECT_EQ(hex(output.signature()), "90fb12ef19529e0d8b31cf4a883d6ca0de4d2da0dc521f08f6890f9ac74937795ed41ef2c5118d0786907e169197ff19d2be6f8ad453d005aec436ac77cc1c17"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(TerraClassicSigner, SignWasmGenericWithCoins_6651FC) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(3407705); + input.set_chain_id("columbus-5"); + input.set_memo(""); + input.set_sequence(9); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", fromAddress)); + Address toAddress; + ASSERT_TRUE(Address::decode("terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", toAddress)); + const auto tokenContractAddress = "terra1sepfj7s0aeg5967uxnfk4thzlerrsktkpelm5s"; // ANC Market + const auto txMessage = R"({ "deposit_stable": {} })"; + + auto msg = input.add_messages(); + auto& message = *msg->mutable_wasm_terra_execute_contract_generic(); + message.set_sender_address(fromAddress.string()); + message.set_contract_address(tokenContractAddress); + message.set_execute_msg(txMessage); + + auto amount = message.add_coins(); + amount->set_denom("uusd"); + amount->set_amount("1000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(600000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("7000"); + + auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerra); + + // https://finder.terra.money/mainnet/tx/6651FCE0EE5C6D6ACB655CC49A6FD5E939FB082862854616EA0642475BCDD0C9 + // curl -H 'Content-Type: application/json' --data-binary '{"mode": "BROADCAST_MODE_BLOCK","tx_bytes": "CrIBCq8B.....0NWg=="})' https:///cosmos/tx/v1beta1/txs + assertJSONEqual(output.serialized(), R"( + { + "tx_bytes": "CrIBCq8BCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBKEAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMXNlcGZqN3MwYWVnNTk2N3V4bmZrNHRoemxlcnJza3RrcGVsbTVzGhh7ICJkZXBvc2l0X3N0YWJsZSI6IHt9IH0qDAoEdXVzZBIEMTAwMBJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYCRITCg0KBXVsdW5hEgQ3MDAwEMDPJBpAGyi7f1ioY8XV6pjFq1s86Om4++CIUnd3rLHif2iopCcYvX0mLkTlQ6NUERg8nWTYgXcj6fOTO/ptgPuAtv0NWg==", + "mode": "BROADCAST_MODE_BLOCK" + } + )"); + + EXPECT_EQ(hex(output.signature()), "1b28bb7f58a863c5d5ea98c5ab5b3ce8e9b8fbe088527777acb1e27f68a8a42718bd7d262e44e543a35411183c9d64d8817723e9f3933bfa6d80fb80b6fd0d5a"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(TerraClassicSigner, SignWasmSendTxProtobuf) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(3407705); + input.set_chain_id("columbus-5"); + input.set_memo(""); + input.set_sequence(4); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", fromAddress)); + Address toAddress; + ASSERT_TRUE(Address::decode("terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", toAddress)); + const auto tokenContractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76"; // ANC + + auto msg = input.add_messages(); + auto& message = *msg->mutable_wasm_terra_execute_contract_send_message(); + message.set_sender_address(fromAddress.string()); + message.set_contract_address(tokenContractAddress); + const auto amount = store(uint256_t(250000), 0); + message.set_amount(amount.data(), amount.size()); + message.set_recipient_contract_address(toAddress.string()); + const auto msgMsg = Base64::encode(data(std::string(R"({"some_message":{}})"))); + EXPECT_EQ(msgMsg, "eyJzb21lX21lc3NhZ2UiOnt9fQ=="); + message.set_msg(msgMsg); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("3000"); + + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + assertJSONEqual(json, R"( + { + "signingMode": "Protobuf", + "accountNumber": "3407705", + "chainId": "columbus-5", + "fee": { + "amounts": [ + { + "denom": "uluna", + "amount": "3000" + } + ], + "gas": "200000" + }, + "sequence": "4", + "messages": [ + { + "wasmTerraExecuteContractSendMessage": { + "senderAddress": "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", + "contractAddress": "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76", + "amount": "A9CQ", + "recipientContractAddress": "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", + "msg": "eyJzb21lX21lc3NhZ2UiOnt9fQ==" + } + } + ] + } + )"); + + auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerra); + + // https://finder.terra.money/mainnet/tx/9FF3F0A16879254C22EB90D8B4D6195467FE5014381FD36BD3C23CA6698FE94B + // curl -H 'Content-Type: application/json' --data-binary '{"mode": "BROADCAST_MODE_BLOCK","tx_bytes": "CogCCo..wld8"})' https:///cosmos/tx/v1beta1/txs + assertJSONEqual(output.serialized(), R"( + { + "tx_bytes": "CocCCoQCCiYvdGVycmEud2FzbS52MWJldGExLk1zZ0V4ZWN1dGVDb250cmFjdBLZAQosdGVycmExOHd1a3A4NGRxMjI3d3U0bWdoMGptNm45bmxuajZyczgycHA5d2YSLHRlcnJhMTR6NTZsMGZwMmxzZjg2enkzaHR5Mno0N2V6a2hudGh0cjl5cTc2Gnt7InNlbmQiOnsiYW1vdW50IjoiMjUwMDAwIiwiY29udHJhY3QiOiJ0ZXJyYTFqbGdhcXk5bnZuMmhmNXQyc3JhOXljejhzNzd3bmY5bDBrbWdjcCIsIm1zZyI6ImV5SnpiMjFsWDIxbGMzTmhaMlVpT250OWZRPT0ifX0SZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awAEgQKAggBGAQSEwoNCgV1bHVuYRIEMzAwMBDAmgwaQL6NByKeRZsyq5g6CTMdmPqiM77nOe9uLO8FjpetFgkBFiG3Le7ieZZ+4vCMhD1bcFgMwSHibFI/uPil847U/+g=", + "mode": "BROADCAST_MODE_BLOCK" + } + )"); + EXPECT_EQ(hex(output.signature()), "be8d07229e459b32ab983a09331d98faa233bee739ef6e2cef058e97ad1609011621b72deee279967ee2f08c843d5b70580cc121e26c523fb8f8a5f38ed4ffe8"); + EXPECT_EQ(output.json(), ""); + EXPECT_EQ(output.error(), ""); +} + +TEST(TerraClassicSigner, SignWasmTerraTransferPayload) { + auto proto = Proto::Message_WasmTerraExecuteContractTransfer(); + proto.set_recipient_address("recipient=address"); + const auto amount = store(uint256_t(250000), 0); + proto.set_amount(amount.data(), amount.size()); + + const auto payload = Protobuf::wasmTerraExecuteTransferPayload(proto); + + assertJSONEqual(payload.dump(), R"( + { + "transfer": + { + "amount": "250000", + "recipient": "recipient=address" + } + } + )"); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/Terra/TWCoinTypeTests.cpp b/tests/chains/Terra/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..464be13fa31 --- /dev/null +++ b/tests/chains/Terra/TWCoinTypeTests.cpp @@ -0,0 +1,35 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include + +namespace TW::Cosmos::tests { + +TEST(TWTerraCoinType, TWCoinTypeClassic) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTerra)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("D28D8AFC7CE89F2A22FA2DBF78D2C0A36E549BB830C4D9FA7459E3F723CA7182")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTerra, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("terra16t3gx5rqvz6ru37yzn3shuu20erv4ngmfr59zf")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTerra, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTerra)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTerra)); + const auto chainId = WRAPS(TWCoinTypeChainId(TWCoinTypeTerra)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTerra), 6); + ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeTerra)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTerra)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTerra)); + assertStringsEqual(chainId, "columbus-5"); + assertStringsEqual(symbol, "LUNC"); + assertStringsEqual(txUrl, "https://finder.terra.money/classic/tx/D28D8AFC7CE89F2A22FA2DBF78D2C0A36E549BB830C4D9FA7459E3F723CA7182"); + assertStringsEqual(accUrl, "https://finder.terra.money/classic/address/terra16t3gx5rqvz6ru37yzn3shuu20erv4ngmfr59zf"); + assertStringsEqual(id, "terra"); + assertStringsEqual(name, "Terra Classic"); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/TerraV2/SignerTests.cpp b/tests/chains/TerraV2/SignerTests.cpp new file mode 100644 index 00000000000..b1cc0c152b2 --- /dev/null +++ b/tests/chains/TerraV2/SignerTests.cpp @@ -0,0 +1,362 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Base64.h" +#include "proto/Cosmos.pb.h" +#include "Cosmos/Address.h" +#include "Cosmos/Signer.h" +#include "Cosmos/ProtobufSerialization.h" +#include "uint256.h" +#include "TestUtilities.h" + +#include +#include + +namespace TW::Cosmos::tests { + +TEST(TerraSigner, SignSendTx) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(1037); + input.set_chain_id("phoenix-1"); + input.set_memo(""); + input.set_sequence(1); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra1hsk6jryyqjfhp5dhc55tc9jtckygx0ep37hdd2", fromAddress)); + Address toAddress; + ASSERT_TRUE(Address::decode("terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", toAddress)); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address(fromAddress.string()); + message.set_to_address(toAddress.string()); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("uluna"); + amountOfTx->set_amount("1000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("30000"); + + { + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + assertJSONEqual(json, R"( + { + "signingMode": "Protobuf", + "accountNumber": "1037", + "chainId": "phoenix-1", + "fee": { + "amounts": [ + { + "denom": "uluna", + "amount": "30000" + } + ], + "gas": "200000" + }, + "sequence": "1", + "messages": [ + { + "sendCoinsMessage": { + "fromAddress": "terra1hsk6jryyqjfhp5dhc55tc9jtckygx0ep37hdd2", + "toAddress": "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", + "amounts": [ + { + "denom": "uluna", + "amount": "1000000" + } + ] + } + } + ] + } + )"); + } + + auto privateKey = parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerraV2); + + // similar tx: https://finder.terra.money/mainnet/tx/fbbe73ad2f0db3a13911dc424f8a34370dc4b7e8b66687f536797e68ee200ece + assertJSONEqual(output.serialized(), R"( + { + "tx_bytes": "CpEBCo4BChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEm4KLHRlcnJhMWhzazZqcnl5cWpmaHA1ZGhjNTV0YzlqdGNreWd4MGVwMzdoZGQyEix0ZXJyYTFqbGdhcXk5bnZuMmhmNXQyc3JhOXljejhzNzd3bmY5bDBrbWdjcBoQCgV1bHVuYRIHMTAwMDAwMBJoClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECVyhuw/N9M1V7u6oACyd0SskCOqmWfK51oYHR/5H6ncUSBAoCCAEYARIUCg4KBXVsdW5hEgUzMDAwMBDAmgwaQPh0C3rjzdixIUiyPx3FlWAxzbKILNAcSRVeQnaTl1vsI5DEfYa2oYlUBLqyilcMCcU/iaJLhex30No2ak0Zn1Q=", + "mode": "BROADCAST_MODE_BLOCK" + } + )"); + EXPECT_EQ(hex(output.signature()), "f8740b7ae3cdd8b12148b23f1dc5956031cdb2882cd01c49155e427693975bec2390c47d86b6a1895404bab28a570c09c53f89a24b85ec77d0da366a4d199f54"); + EXPECT_EQ(output.error(), ""); + EXPECT_EQ(output.json(), ""); +} + +TEST(TerraSigner, SignWasmTransferTx) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(3407705); + input.set_chain_id("phoenix-1"); + input.set_memo(""); + input.set_sequence(3); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", fromAddress)); + Address toAddress; + ASSERT_TRUE(Address::decode("terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", toAddress)); + const auto tokenContractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76"; + + auto msg = input.add_messages(); + auto& message = *msg->mutable_wasm_execute_contract_transfer_message(); + message.set_sender_address(fromAddress.string()); + message.set_contract_address(tokenContractAddress); + const auto amount = store(uint256_t(250000), 0); + message.set_amount(amount.data(), amount.size()); + message.set_recipient_address(toAddress.string()); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("3000"); + + { + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + assertJSONEqual(json, R"( + { + "signingMode": "Protobuf", + "accountNumber": "3407705", + "chainId": "phoenix-1", + "fee": { + "amounts": [ + { + "denom": "uluna", + "amount": "3000" + } + ], + "gas": "200000" + }, + "sequence": "3", + "messages": [ + { + "wasmExecuteContractTransferMessage": { + "senderAddress": "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", + "contractAddress": "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76", + "amount": "A9CQ", + "recipientAddress": "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp" + } + } + ] + } + )"); + } + + auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerraV2); + + assertJSONEqual(output.serialized(), R"( + { + "tx_bytes": "CuUBCuIBCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QSuQEKLHRlcnJhMTh3dWtwODRkcTIyN3d1NG1naDBqbTZuOW5sbmo2cnM4MnBwOXdmEix0ZXJyYTE0ejU2bDBmcDJsc2Y4Nnp5M2h0eTJ6NDdlemtobnRodHI5eXE3NhpbeyJ0cmFuc2ZlciI6eyJhbW91bnQiOiIyNTAwMDAiLCJyZWNpcGllbnQiOiJ0ZXJyYTFqbGdhcXk5bnZuMmhmNXQyc3JhOXljejhzNzd3bmY5bDBrbWdjcCJ9fRJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiEDcGY6x7D5iSlv61zln7pKUNfpThziVt/yJRRZyizZrAASBAoCCAEYAxITCg0KBXVsdW5hEgQzMDAwEMCaDBpAiBGbQaj+jsXE6/FssD3fC77QOxpli9GqsPea+KoNyMIEgVj89Hii+oU1bAEQS4qV0SaE2V6RNy24uCcFTIRbcQ==", + "mode": "BROADCAST_MODE_BLOCK" + } + )"); + EXPECT_EQ(hex(output.signature()), "88119b41a8fe8ec5c4ebf16cb03ddf0bbed03b1a658bd1aab0f79af8aa0dc8c2048158fcf478a2fa85356c01104b8a95d12684d95e91372db8b827054c845b71"); + EXPECT_EQ(output.error(), ""); + EXPECT_EQ(output.json(), ""); +} + +TEST(TerraSigner, SignWasmGeneric) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(3407705); + input.set_chain_id("phoenix-1"); + input.set_memo(""); + input.set_sequence(7); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", fromAddress)); + const auto tokenContractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76"; + const auto txMessage = R"({"transfer": { "amount": "250000", "recipient": "terra1d7048csap4wzcv5zm7z6tdqem2agyp9647vdyj" } })"; + + auto msg = input.add_messages(); + auto& message = *msg->mutable_wasm_execute_contract_generic(); + message.set_sender_address(fromAddress.string()); + message.set_contract_address(tokenContractAddress); + message.set_execute_msg(txMessage); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("3000"); + + auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerraV2); + + assertJSONEqual(output.serialized(), R"( + { + "tx_bytes": "CuwBCukBCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QSwAEKLHRlcnJhMTh3dWtwODRkcTIyN3d1NG1naDBqbTZuOW5sbmo2cnM4MnBwOXdmEix0ZXJyYTE0ejU2bDBmcDJsc2Y4Nnp5M2h0eTJ6NDdlemtobnRodHI5eXE3NhpieyJ0cmFuc2ZlciI6IHsgImFtb3VudCI6ICIyNTAwMDAiLCAicmVjaXBpZW50IjogInRlcnJhMWQ3MDQ4Y3NhcDR3emN2NXptN3o2dGRxZW0yYWd5cDk2NDd2ZHlqIiB9IH0SZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awAEgQKAggBGAcSEwoNCgV1bHVuYRIEMzAwMBDAmgwaQGlYzOoAu/PfyCTSTisGJVW9KWwifxMbCmzy2xwqNg+ZHQkDjVRyUBl7gmbXXLzdOMqtwF1CMauJhlGwmEdzhK4=", + "mode": "BROADCAST_MODE_BLOCK" + } + )"); + + EXPECT_EQ(hex(output.signature()), "6958ccea00bbf3dfc824d24e2b062555bd296c227f131b0a6cf2db1c2a360f991d09038d547250197b8266d75cbcdd38caadc05d4231ab898651b098477384ae"); + EXPECT_EQ(output.error(), ""); + EXPECT_EQ(output.json(), ""); +} + +TEST(TerraSigner, SignWasmGenericWithCoins) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(3407705); + input.set_chain_id("phoenix-1"); + input.set_memo(""); + input.set_sequence(9); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", fromAddress)); + const auto tokenContractAddress = "terra1sepfj7s0aeg5967uxnfk4thzlerrsktkpelm5s"; + const auto txMessage = R"({ "deposit_stable": {} })"; + + auto msg = input.add_messages(); + auto& message = *msg->mutable_wasm_execute_contract_generic(); + message.set_sender_address(fromAddress.string()); + message.set_contract_address(tokenContractAddress); + message.set_execute_msg(txMessage); + + auto amount = message.add_coins(); + amount->set_denom("uusd"); + amount->set_amount("1000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(600000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("7000"); + + auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerraV2); + + assertJSONEqual(output.serialized(), R"( + { + "tx_bytes": "CrABCq0BCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QShAEKLHRlcnJhMTh3dWtwODRkcTIyN3d1NG1naDBqbTZuOW5sbmo2cnM4MnBwOXdmEix0ZXJyYTFzZXBmajdzMGFlZzU5Njd1eG5mazR0aHpsZXJyc2t0a3BlbG01cxoYeyAiZGVwb3NpdF9zdGFibGUiOiB7fSB9KgwKBHV1c2QSBDEwMDASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohA3BmOsew+Ykpb+tc5Z+6SlDX6U4c4lbf8iUUWcos2awAEgQKAggBGAkSEwoNCgV1bHVuYRIENzAwMBDAzyQaQEDA2foXegF+rslj6o8bX2HPJfn+q/6Ezbq2iAd0SFOTQqS8aAyywQkdZJRToXcaby1HOYL1WvmsMPgrFzChiY4=", + "mode": "BROADCAST_MODE_BLOCK" + } + )"); + + EXPECT_EQ(hex(output.signature()), "40c0d9fa177a017eaec963ea8f1b5f61cf25f9feabfe84cdbab688077448539342a4bc680cb2c1091d649453a1771a6f2d473982f55af9ac30f82b1730a1898e"); + EXPECT_EQ(output.error(), ""); + EXPECT_EQ(output.json(), ""); +} + +TEST(TerraSigner, SignWasmSendTx) { + auto input = Proto::SigningInput(); + input.set_signing_mode(Proto::Protobuf); + input.set_account_number(3407705); + input.set_chain_id("phoenix-1"); + input.set_memo(""); + input.set_sequence(4); + + Address fromAddress; + ASSERT_TRUE(Address::decode("terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", fromAddress)); + Address toAddress; + ASSERT_TRUE(Address::decode("terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", toAddress)); + const auto tokenContractAddress = "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76"; + + auto msg = input.add_messages(); + auto& message = *msg->mutable_wasm_execute_contract_send_message(); + message.set_sender_address(fromAddress.string()); + message.set_contract_address(tokenContractAddress); + const auto amount = store(uint256_t(250000), 0); + message.set_amount(amount.data(), amount.size()); + message.set_recipient_contract_address(toAddress.string()); + const auto msgMsg = Base64::encode(data(std::string(R"({"some_message":{}})"))); + EXPECT_EQ(msgMsg, "eyJzb21lX21lc3NhZ2UiOnt9fQ=="); + message.set_msg(msgMsg); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("uluna"); + amountOfFee->set_amount("3000"); + + std::string json; + google::protobuf::util::MessageToJsonString(input, &json); + assertJSONEqual(json, R"( + { + "signingMode": "Protobuf", + "accountNumber": "3407705", + "chainId": "phoenix-1", + "fee": { + "amounts": [ + { + "denom": "uluna", + "amount": "3000" + } + ], + "gas": "200000" + }, + "sequence": "4", + "messages": [ + { + "wasmExecuteContractSendMessage": { + "senderAddress": "terra18wukp84dq227wu4mgh0jm6n9nlnj6rs82pp9wf", + "contractAddress": "terra14z56l0fp2lsf86zy3hty2z47ezkhnthtr9yq76", + "amount": "A9CQ", + "recipientContractAddress": "terra1jlgaqy9nvn2hf5t2sra9ycz8s77wnf9l0kmgcp", + "msg": "eyJzb21lX21lc3NhZ2UiOnt9fQ==" + } + } + ] + } + )"); + + auto privateKey = parse_hex("cf08ee8493e6f6a53f9721b9045576e80f371c0e36d08fdaf78b27a7afd8e616"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input, TWCoinTypeTerraV2); + + assertJSONEqual(output.serialized(), R"( + { + "tx_bytes": "CoUCCoICCiQvY29zbXdhc20ud2FzbS52MS5Nc2dFeGVjdXRlQ29udHJhY3QS2QEKLHRlcnJhMTh3dWtwODRkcTIyN3d1NG1naDBqbTZuOW5sbmo2cnM4MnBwOXdmEix0ZXJyYTE0ejU2bDBmcDJsc2Y4Nnp5M2h0eTJ6NDdlemtobnRodHI5eXE3Nhp7eyJzZW5kIjp7ImFtb3VudCI6IjI1MDAwMCIsImNvbnRyYWN0IjoidGVycmExamxnYXF5OW52bjJoZjV0MnNyYTl5Y3o4czc3d25mOWwwa21nY3AiLCJtc2ciOiJleUp6YjIxbFgyMWxjM05oWjJVaU9udDlmUT09In19EmcKUApGCh8vY29zbW9zLmNyeXB0by5zZWNwMjU2azEuUHViS2V5EiMKIQNwZjrHsPmJKW/rXOWfukpQ1+lOHOJW3/IlFFnKLNmsABIECgIIARgEEhMKDQoFdWx1bmESBDMwMDAQwJoMGkBKJbW1GDrv9j2FIckm7MtpDZzP2RjgDjU84oYmOHNHsxEBPLjtt3YAjsKWBCAsjbnbVoJ3s2XFG08nxQXS9xBK", + "mode": "BROADCAST_MODE_BLOCK" + } + )"); + EXPECT_EQ(hex(output.signature()), "4a25b5b5183aeff63d8521c926eccb690d9ccfd918e00e353ce28626387347b311013cb8edb776008ec29604202c8db9db568277b365c51b4f27c505d2f7104a"); + EXPECT_EQ(output.error(), ""); + EXPECT_EQ(output.json(), ""); +} + +TEST(TerraSigner, SignWasmTransferPayload) { + auto proto = Proto::Message_WasmExecuteContractTransfer(); + proto.set_recipient_address("recipient=address"); + const auto amount = store(uint256_t(250000), 0); + proto.set_amount(amount.data(), amount.size()); + + const auto payload = Protobuf::wasmExecuteTransferPayload(proto); + + assertJSONEqual(payload.dump(), R"( + { + "transfer": + { + "amount": "250000", + "recipient": "recipient=address" + } + } + )"); +} + +} // namespace TW::Cosmos::tests diff --git a/tests/chains/TerraV2/TWCoinTypeTests.cpp b/tests/chains/TerraV2/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..ee436963959 --- /dev/null +++ b/tests/chains/TerraV2/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include + +namespace TW::Cosmos::tests { + +TEST(TWTerraCoinType, TWCoinType20) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTerraV2)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("CFF732C6EBEE06FFA08ABE54EE1657FD53E90FAA81604619E2062C46572A6986")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTerraV2, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("terra16t3gx5rqvz6ru37yzn3shuu20erv4ngmfr59zf")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTerraV2, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTerraV2)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTerraV2)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTerraV2), 6); + ASSERT_EQ(TWBlockchainCosmos, TWCoinTypeBlockchain(TWCoinTypeTerraV2)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTerraV2)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTerraV2)); + assertStringsEqual(symbol, "LUNA"); + assertStringsEqual(txUrl, "https://finder.terra.money/mainnet/tx/CFF732C6EBEE06FFA08ABE54EE1657FD53E90FAA81604619E2062C46572A6986"); + assertStringsEqual(accUrl, "https://finder.terra.money/mainnet/address/terra16t3gx5rqvz6ru37yzn3shuu20erv4ngmfr59zf"); + assertStringsEqual(id, "terrav2"); + assertStringsEqual(name, "Terra"); +} + +} // namespace TW::Cosmos::tests + diff --git a/tests/chains/Tezos/AddressTests.cpp b/tests/chains/Tezos/AddressTests.cpp new file mode 100644 index 00000000000..38e121ce7df --- /dev/null +++ b/tests/chains/Tezos/AddressTests.cpp @@ -0,0 +1,90 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HDWallet.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "Tezos/Address.h" +#include "Tezos/Forging.h" + +#include + +#include +#include +#include + +using namespace TW; + +namespace TW::Tezos::tests { + +TEST(TezosAddress, forge_tz1) { + auto input = Address("tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3Don"); + auto expected = "00cfa4aae60f5d9389752d41e320da224d43287fe2"; + + ASSERT_EQ(input.forge(), parse_hex(expected)); +} + +TEST(TezosAddress, forge_tz2) { + auto input = Address("tz2Rh3NYeLxrqTuvaZJmaMiVMqCajeXMWtYo"); + auto expected = "01be99dd914e38388ec80432818b517759e3524f16"; + + ASSERT_EQ(input.forge(), parse_hex(expected)); +} + +TEST(TezosAddress, forge_tz3) { + auto input = Address("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9"); + auto expected = "02358cbffa97149631cfb999fa47f0035fb1ea8636"; + + ASSERT_EQ(input.forge(), parse_hex(expected)); +} + +TEST(TezosAddress, isInvalid) { + std::array invalidAddresses{ + "NmH7tmeJUmHcncBDvpr7aJNEBk7rp5zYsB1qt", // Invalid prefix, valid checksum + "tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3AAAA", // Valid prefix, invalid checksum + "1tzeZwq8b5cvE2bPKokatLkVMzkxz24zAAAAA" // Invalid prefix, invalid checksum + }; + + for (auto& address : invalidAddresses) { + ASSERT_FALSE(Address::isValid(address)); + } +} + +TEST(TezosAddress, isValid) { + std::array validAddresses{ + "tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt", + "tz2PdGc7U5tiyqPgTSgqCDct94qd6ovQwP6u", + "tz3VEZ4k6a4Wx42iyev6i2aVAptTRLEAivNN"}; + + for (auto& address : validAddresses) { + ASSERT_TRUE(Address::isValid(address)); + } +} + +TEST(TezosAddress, string) { + auto addressString = "tz1d1qQL3mYVuiH4JPFvuikEpFwaDm85oabM"; + auto address = Address(addressString); + ASSERT_EQ(address.string(), addressString); +} + +TEST(TezosAddress, deriveOriginatedAddress) { + auto operationHash = "oo7VeTEPjEusPKnsHtKcGYbYa7i4RWpcEhUVo3Suugbbs6K62Ro"; + auto operationIndex = 0; + auto expected = "KT1WrtjtAYQSrUVvSNJPTZTebiUWoopQL5hw"; + + ASSERT_EQ(Address::deriveOriginatedAddress(operationHash, operationIndex), expected); +} + +TEST(TezosAddress, PublicKeyInit) { + Data bytes = parse_hex("01fe157cc8011727936c592f856c9071d39cf4acdadfa6d76435e4619c9dc56f63"); + const auto publicKey = PublicKey(bytes, TWPublicKeyTypeED25519); + auto address = Address(publicKey); + + auto expected = "tz1cG2jx3W4bZFeVGBjsTxUAG8tdpTXtE8PT"; + ASSERT_EQ(address.string(), expected); +} + +} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/ForgingTests.cpp b/tests/chains/Tezos/ForgingTests.cpp new file mode 100644 index 00000000000..66187be27d2 --- /dev/null +++ b/tests/chains/Tezos/ForgingTests.cpp @@ -0,0 +1,266 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HDWallet.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "Tezos/Address.h" +#include "Tezos/BinaryCoding.h" +#include "Tezos/Forging.h" +#include "proto/Tezos.pb.h" +#include +#include +#include +#include + +namespace TW::Tezos::tests { + +TEST(Forging, ForgeBoolTrue) { + auto expected = "ff"; + + auto output = forgeBool(true); + + ASSERT_EQ(output, parse_hex(expected)); +} + +TEST(Forging, ForgeBoolFalse) { + auto expected = "00"; + + auto output = forgeBool(false); + + ASSERT_EQ(output, parse_hex(expected)); +} + +TEST(Forging, ForgeZarithZero) { + auto expected = "00"; + + auto output = forgeZarith(0); + + ASSERT_EQ(hex(output), hex(parse_hex(expected))); +} + +TEST(Forging, ForgeZarithTen) { + auto expected = "0a"; + + auto output = forgeZarith(10); + + ASSERT_EQ(output, parse_hex(expected)); +} + +TEST(Forging, ForgeZarithTwenty) { + auto expected = "14"; + + auto output = forgeZarith(20); + + ASSERT_EQ(output, parse_hex(expected)); +} + +TEST(Forging, ForgeZarithOneHundredFifty) { + auto expected = "9601"; + + auto output = forgeZarith(150); + + ASSERT_EQ(output, parse_hex(expected)); +} + +TEST(Forging, ForgeZarithLarge) { + auto expected = "bbd08001"; + + auto output = forgeZarith(2107451); + + ASSERT_EQ(hex(output), expected); +} + +TEST(Forging, forge_tz1) { + auto expected = "00cfa4aae60f5d9389752d41e320da224d43287fe2"; + + auto output = forgePublicKeyHash("tz1eZwq8b5cvE2bPKokatLkVMzkxz24z3Don"); + + ASSERT_EQ(output, parse_hex(expected)); +} + +TEST(Forging, forge_tz2) { + auto expected = "01be99dd914e38388ec80432818b517759e3524f16"; + + auto output = forgePublicKeyHash("tz2Rh3NYeLxrqTuvaZJmaMiVMqCajeXMWtYo"); + + ASSERT_EQ(output, parse_hex(expected)); +} + +TEST(Forging, forge_tz3) { + auto expected = "02358cbffa97149631cfb999fa47f0035fb1ea8636"; + + auto output = forgePublicKeyHash("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9"); + + ASSERT_EQ(output, parse_hex(expected)); +} + +TEST(Forging, ForgePublicKey) { + auto expected = "00311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95"; + + auto privateKey = PrivateKey(parse_hex("c6377a4cc490dc913fc3f0d9cf67d293a32df4547c46cb7e9e33c3b7b97c64d8")); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + auto output = forgePublicKey(publicKey); + + ASSERT_EQ(hex(output), expected); +} + +TEST(Forging, ForgeInt32) { + auto expected = "01"; + ASSERT_EQ(hex(forgeInt32(1, 1)), expected); +} + +TEST(Forging, ForgeString) { + auto expected = "087472616e73666572"; + ASSERT_EQ(hex(forgeString("transfer", 1)), expected); +} + +TEST(Forging, ForgeEntrypoint) { + auto expected = "ff087472616e73666572"; + ASSERT_EQ(hex(forgeEntrypoint("transfer")), expected); +} + +TEST(Forging, ForgeMichelsonFA12) { + Tezos::Proto::FA12Parameters data; + data.set_entrypoint("transfer"); + data.set_from("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + data.set_to("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + data.set_value("123"); + auto v = FA12ParameterToMichelson(data); + ASSERT_EQ(hex(forgeMichelson(v)), "07070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb01"); +} + +TEST(TezosTransaction, forgeTransaction) { + auto transactionOperationData = new TW::Tezos::Proto::TransactionOperationData(); + transactionOperationData->set_amount(1); + transactionOperationData->set_destination("tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt"); + + auto transactionOperation = TW::Tezos::Proto::Operation(); + transactionOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); + transactionOperation.set_fee(1272); + transactionOperation.set_counter(30738); + transactionOperation.set_gas_limit(10100); + transactionOperation.set_storage_limit(257); + transactionOperation.set_kind(TW::Tezos::Proto::Operation::TRANSACTION); + transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); + + auto expected = "6c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e81020100008fb5cea62d147c696afd9a93dbce962f4c8a9c9100"; + auto serialized = forgeOperation(transactionOperation); + + ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); +} + +TEST(TezosTransaction, forgeTransactionFA12) { + auto transactionOperationData = new TW::Tezos::Proto::TransactionOperationData(); + transactionOperationData->set_amount(0); + transactionOperationData->set_destination("KT1EwXFWoG9bYebmF4pYw72aGjwEnBWefgW5"); + transactionOperationData->mutable_parameters()->mutable_fa12_parameters()->set_from("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + transactionOperationData->mutable_parameters()->mutable_fa12_parameters()->set_to("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + transactionOperationData->mutable_parameters()->mutable_fa12_parameters()->set_entrypoint("transfer"); + transactionOperationData->mutable_parameters()->mutable_fa12_parameters()->set_value("123"); + + auto transactionOperation = TW::Tezos::Proto::Operation(); + transactionOperation.set_source("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + transactionOperation.set_fee(100000); + transactionOperation.set_counter(2993172); + transactionOperation.set_gas_limit(100000); + transactionOperation.set_storage_limit(0); + transactionOperation.set_kind(TW::Tezos::Proto::Operation::TRANSACTION); + transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); + + auto expected = "6c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0694d8b601a08d0600000145bd8a65cc48159d8ea60a55df735b7c5ad45f0e00ffff087472616e736665720000005907070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb01"; + auto serialized = forgeOperation(transactionOperation); + + ASSERT_EQ(hex(serialized), expected); +} + +TEST(TezosTransaction, forgeTransactionFA2) { + auto transactionOperationData = new TW::Tezos::Proto::TransactionOperationData(); + transactionOperationData->set_amount(0); + transactionOperationData->set_destination("KT1DYk1XDzHredJq1EyNkDindiWDqZyekXGj"); + auto& fa2 = *transactionOperationData->mutable_parameters()->mutable_fa2_parameters(); + fa2.set_entrypoint("transfer"); + auto& txObject = *fa2.add_txs_object(); + txObject.set_from("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + auto& tx = *txObject.add_txs(); + tx.set_amount("10"); + tx.set_token_id("0"); + tx.set_to("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + + auto transactionOperation = TW::Tezos::Proto::Operation(); + transactionOperation.set_source("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + transactionOperation.set_fee(100000); + transactionOperation.set_counter(2993173); + transactionOperation.set_gas_limit(100000); + transactionOperation.set_storage_limit(0); + transactionOperation.set_kind(TW::Tezos::Proto::Operation::TRANSACTION); + transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); + auto serialized = forgeOperation(transactionOperation); + auto expected = "6c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0695d8b601a08d0600000136767f88850bae28bfb9f46b73c5e87ede4de12700ffff087472616e7366657200000066020000006107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b5550020000003107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070000000a"; + ASSERT_EQ(hex(serialized), expected); +} + +TEST(TezosTransaction, forgeReveal) { + PublicKey publicKey = parsePublicKey("edpku9ZF6UUAEo1AL3NWy1oxHLL6AfQcGYwA5hFKrEKVHMT3Xx889A"); + + auto revealOperationData = new TW::Tezos::Proto::RevealOperationData(); + revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); + + auto revealOperation = TW::Tezos::Proto::Operation(); + revealOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); + revealOperation.set_fee(1272); + revealOperation.set_counter(30738); + revealOperation.set_gas_limit(10100); + revealOperation.set_storage_limit(257); + revealOperation.set_kind(TW::Tezos::Proto::Operation::REVEAL); + revealOperation.set_allocated_reveal_operation_data(revealOperationData); + + auto expected = "6b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e"; + auto serialized = forgeOperation(revealOperation); + + ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); +} + +TEST(TezosTransaction, forgeDelegate) { + auto delegateOperationData = new TW::Tezos::Proto::DelegationOperationData(); + delegateOperationData->set_delegate("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); + + auto delegateOperation = TW::Tezos::Proto::Operation(); + delegateOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); + delegateOperation.set_fee(1272); + delegateOperation.set_counter(30738); + delegateOperation.set_gas_limit(10100); + delegateOperation.set_storage_limit(257); + delegateOperation.set_kind(TW::Tezos::Proto::Operation::DELEGATION); + delegateOperation.set_allocated_delegation_operation_data(delegateOperationData); + + auto expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e8102ff003e47f837f0467b4acde406ed5842f35e2414b1a8"; + auto serialized = forgeOperation(delegateOperation); + + ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); +} + +TEST(TezosTransaction, forgeUndelegate) { + auto delegateOperationData = new TW::Tezos::Proto::DelegationOperationData(); + delegateOperationData->set_delegate(""); + + auto delegateOperation = TW::Tezos::Proto::Operation(); + delegateOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); + delegateOperation.set_fee(1272); + delegateOperation.set_counter(30738); + delegateOperation.set_gas_limit(10100); + delegateOperation.set_storage_limit(257); + delegateOperation.set_kind(TW::Tezos::Proto::Operation::DELEGATION); + delegateOperation.set_allocated_delegation_operation_data(delegateOperationData); + + auto expected = "6e0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200"; + auto serialized = forgeOperation(delegateOperation); + + ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); +} + +} // namespace TW::Tezos::tests diff --git a/tests/Tezos/OperationListTests.cpp b/tests/chains/Tezos/OperationListTests.cpp similarity index 75% rename from tests/Tezos/OperationListTests.cpp rename to tests/chains/Tezos/OperationListTests.cpp index db57ea1da0e..9fb89d9e8a6 100644 --- a/tests/Tezos/OperationListTests.cpp +++ b/tests/chains/Tezos/OperationListTests.cpp @@ -12,8 +12,7 @@ #include -using namespace TW::Tezos; -using namespace TW::Tezos::Proto; +namespace TW::Tezos::tests { TEST(TezosOperationList, ForgeBranch) { auto input = TW::Tezos::OperationList("BMNY6Jkas7BzKb7wDLCFoQ4YxfYoieU7Xmo1ED3Y9Lo3ZvVGdgW"); @@ -27,17 +26,17 @@ TEST(TezosOperationList, ForgeOperationList_TransactionOnly) { auto op_list = TW::Tezos::OperationList(branch); auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - auto transactionOperationData = new TW::Tezos::Proto::TransactionOperationData(); - transactionOperationData -> set_amount(1); - transactionOperationData -> set_destination("tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt"); + auto transactionOperationData = new Proto::TransactionOperationData(); + transactionOperationData->set_amount(1); + transactionOperationData->set_destination("tz1Yju7jmmsaUiG9qQLoYv35v5pHgnWoLWbt"); - auto transactionOperation = TW::Tezos::Proto::Operation(); + auto transactionOperation = Proto::Operation(); transactionOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); transactionOperation.set_fee(1272); transactionOperation.set_counter(30738); transactionOperation.set_gas_limit(10100); transactionOperation.set_storage_limit(257); - transactionOperation.set_kind(TW::Tezos::Proto::Operation::TRANSACTION); + transactionOperation.set_kind(Proto::Operation::TRANSACTION); transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); op_list.addOperation(transactionOperation); @@ -53,10 +52,10 @@ TEST(TezosOperationList, ForgeOperationList_RevealOnly) { auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); PublicKey publicKey = parsePublicKey("edpku9ZF6UUAEo1AL3NWy1oxHLL6AfQcGYwA5hFKrEKVHMT3Xx889A"); - auto revealOperationData = new TW::Tezos::Proto::RevealOperationData(); - revealOperationData -> set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); + auto revealOperationData = new Proto::RevealOperationData(); + revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); - auto revealOperation = TW::Tezos::Proto::Operation(); + auto revealOperation = Proto::Operation(); revealOperation.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); revealOperation.set_fee(1272); revealOperation.set_counter(30738); @@ -74,18 +73,18 @@ TEST(TezosOperationList, ForgeOperationList_RevealOnly) { TEST(TezosOperationList, ForgeOperationList_Delegation_ClearDelegate) { auto branch = "BLGJfQDFEYZBRLj5GSHskj8NPaRYhk7Kx5WAfdcDucD3q98WdeW"; - auto op_list = TW::Tezos::OperationList(branch); + auto op_list = OperationList(branch); auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - auto delegationOperationData = new TW::Tezos::Proto::DelegationOperationData(); + auto delegationOperationData = new Proto::DelegationOperationData(); delegationOperationData->set_delegate("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); - auto delegationOperation = TW::Tezos::Proto::Operation(); + auto delegationOperation = Proto::Operation(); delegationOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); delegationOperation.set_fee(1257); delegationOperation.set_counter(67); delegationOperation.set_gas_limit(10000); delegationOperation.set_storage_limit(0); - delegationOperation.set_kind(TW::Tezos::Proto::Operation::DELEGATION); + delegationOperation.set_kind(Proto::Operation::DELEGATION); delegationOperation.set_allocated_delegation_operation_data(delegationOperationData); op_list.addOperation(delegationOperation); @@ -96,20 +95,20 @@ TEST(TezosOperationList, ForgeOperationList_Delegation_ClearDelegate) { TEST(TezosOperationList, ForgeOperationList_Delegation_AddDelegate) { auto branch = "BLa4GrVQTxUgQWbHv6cF7RXWSGzHGPbgecpQ795R3cLzw4cGfpD"; - auto op_list = TW::Tezos::OperationList(branch); + auto op_list = OperationList(branch); auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - auto delegationOperationData = new TW::Tezos::Proto::DelegationOperationData(); - delegationOperationData -> set_delegate("tz1dYUCcrorfCoaQCtZaxi1ynGrP3prTZcxS"); + auto delegationOperationData = new Proto::DelegationOperationData(); + delegationOperationData->set_delegate("tz1dYUCcrorfCoaQCtZaxi1ynGrP3prTZcxS"); - auto delegationOperation = TW::Tezos::Proto::Operation(); + auto delegationOperation = Proto::Operation(); delegationOperation.set_source("KT1D5jmrBD7bDa3jCpgzo32FMYmRDdK2ihka"); delegationOperation.set_fee(1257); delegationOperation.set_counter(68); delegationOperation.set_gas_limit(10000); delegationOperation.set_storage_limit(0); - delegationOperation.set_kind(TW::Tezos::Proto::Operation::DELEGATION); + delegationOperation.set_kind(Proto::Operation::DELEGATION); delegationOperation.set_allocated_delegation_operation_data(delegationOperationData); - + op_list.addOperation(delegationOperation); auto expected = "7105102c032807994dd9b5edf219261896a559876ca16cbf9d31dbe3612b89f26e00315b1206ec00b1b1e64cc3b8b93059f58fa2fc39e90944904e00ff00c4650fd609f88c67356e5fe01e37cd3ff654b18c"; auto forged = op_list.forge(key); @@ -118,33 +117,33 @@ TEST(TezosOperationList, ForgeOperationList_Delegation_AddDelegate) { TEST(TezosOperationList, ForgeOperationList_TransactionAndReveal) { auto branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; - auto op_list = TW::Tezos::OperationList(branch); + auto op_list = OperationList(branch); auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); auto publicKey = parsePublicKey("edpkuNb9N2UHtGeSc2BZCBHN8ETx7E4DwkSfz5Hw3m3tF3dLZTU8qp"); - auto revealOperationData = new TW::Tezos::Proto::RevealOperationData(); - revealOperationData -> set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); + auto revealOperationData = new Proto::RevealOperationData(); + revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); - auto revealOperation = TW::Tezos::Proto::Operation(); + auto revealOperation = Proto::Operation(); revealOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); revealOperation.set_fee(1272); revealOperation.set_counter(30738); revealOperation.set_gas_limit(10100); revealOperation.set_storage_limit(257); - revealOperation.set_kind(TW::Tezos::Proto::Operation::REVEAL); + revealOperation.set_kind(Proto::Operation::REVEAL); revealOperation.set_allocated_reveal_operation_data(revealOperationData); - - auto transactionOperationData = new TW::Tezos::Proto::TransactionOperationData(); - transactionOperationData -> set_amount(1); - transactionOperationData -> set_destination("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); - auto transactionOperation = TW::Tezos::Proto::Operation(); + auto transactionOperationData = new Proto::TransactionOperationData(); + transactionOperationData->set_amount(1); + transactionOperationData->set_destination("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); + + auto transactionOperation = Proto::Operation(); transactionOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); transactionOperation.set_fee(1272); transactionOperation.set_counter(30739); transactionOperation.set_gas_limit(10100); transactionOperation.set_storage_limit(257); - transactionOperation.set_kind(TW::Tezos::Proto::Operation::TRANSACTION); + transactionOperation.set_kind(Proto::Operation::TRANSACTION); transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); op_list.addOperation(revealOperation); @@ -158,18 +157,18 @@ TEST(TezosOperationList, ForgeOperationList_TransactionAndReveal) { TEST(TezosOperationList, ForgeOperationList_RevealWithoutPublicKey) { auto branch = "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"; - auto op_list = TW::Tezos::OperationList(branch); + auto op_list = OperationList(branch); auto key = parsePrivateKey("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); - auto revealOperationData = new TW::Tezos::Proto::RevealOperationData(); + auto revealOperationData = new Proto::RevealOperationData(); - auto revealOperation = TW::Tezos::Proto::Operation(); + auto revealOperation = Proto::Operation(); revealOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); revealOperation.set_fee(1272); revealOperation.set_counter(30738); revealOperation.set_gas_limit(10100); revealOperation.set_storage_limit(257); - revealOperation.set_kind(TW::Tezos::Proto::Operation::REVEAL); + revealOperation.set_kind(Proto::Operation::REVEAL); revealOperation.set_allocated_reveal_operation_data(revealOperationData); op_list.addOperation(revealOperation); @@ -179,3 +178,5 @@ TEST(TezosOperationList, ForgeOperationList_RevealWithoutPublicKey) { ASSERT_EQ(hex(forged.begin(), forged.end()), expected); } + +} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/PublicKeyTests.cpp b/tests/chains/Tezos/PublicKeyTests.cpp new file mode 100644 index 00000000000..66c7087dc7e --- /dev/null +++ b/tests/chains/Tezos/PublicKeyTests.cpp @@ -0,0 +1,32 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Tezos/BinaryCoding.h" +#include "Tezos/Forging.h" +#include "PublicKey.h" +#include "Data.h" +#include "HexCoding.h" + +#include + +namespace TW::Tezos::tests { + +TEST(TezosPublicKey, forge) { + auto input = parsePublicKey("edpkuAfEJCEatRgFpRGg3gn3FdWniLXBoubARreRwuVZPWufkgDBvR"); + auto expected = "00451bde832454ba73e6e0de313fcf5d1565ec51080edc73bb19287b8e0ab2122b"; + auto serialized = forgePublicKey(input); + ASSERT_EQ(hex(serialized.begin(), serialized.end()), expected); +} + +TEST(TezosPublicKey, parse) { + auto input = "edpkuAfEJCEatRgFpRGg3gn3FdWniLXBoubARreRwuVZPWufkgDBvR"; + auto bytes = Data({1, 69, 27, 222, 131, 36, 84, 186, 115, 230, 224, 222, 49, 63, 207, 93, 21, 101, 236, 81, 8, 14, 220, 115, 187, 25, 40, 123, 142, 10, 178, 18, 43}); + auto output = parsePublicKey(input); + auto expected = PublicKey(bytes, TWPublicKeyTypeED25519); + ASSERT_EQ(output, expected); +} + +} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/SignerTests.cpp b/tests/chains/Tezos/SignerTests.cpp new file mode 100644 index 00000000000..92681344646 --- /dev/null +++ b/tests/chains/Tezos/SignerTests.cpp @@ -0,0 +1,94 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base58.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "Tezos/BinaryCoding.h" +#include "Tezos/OperationList.h" +#include "Tezos/Signer.h" + +#include + +using namespace TW; + +namespace TW::Tezos::tests { + +TEST(TezosSigner, SignString) { + Data bytesToSign = parse_hex("ffaa"); + Data expectedSignature = parse_hex("eaab7f4066217b072b79609a9f76cdfadd93f8dde41763887e131c02324f18c8e41b1009e334baf87f9d2e917bf4c0e73165622e5522409a0c5817234a48cc02"); + Data expected = Data(); + append(expected, bytesToSign); + append(expected, expectedSignature); + + auto key = PrivateKey(parse_hex("0x2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f")); + auto signedBytes = Signer().signData(key, bytesToSign); + + ASSERT_EQ(signedBytes, expected); +} + +TEST(TezosSigner, SignOperationList) { + auto branch = "BLDnkhhVgwdBAtmDNQc5HtEMsrxq8L3t7NQbjUbbdTdw5Ug1Mpe"; + auto op_list = Tezos::OperationList(branch); + + auto transactionOperationData = new Proto::TransactionOperationData(); + transactionOperationData->set_amount(11100000); + transactionOperationData->set_destination("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); + + auto transactionOperation = TW::Tezos::Proto::Operation(); + transactionOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); + transactionOperation.set_fee(1283); + transactionOperation.set_counter(1878); + transactionOperation.set_gas_limit(10307); + transactionOperation.set_storage_limit(0); + transactionOperation.set_kind(Proto::Operation::TRANSACTION); + transactionOperation.set_allocated_transaction_operation_data(transactionOperationData); + + op_list.addOperation(transactionOperation); + + PublicKey publicKey = parsePublicKey("edpkuNb9N2UHtGeSc2BZCBHN8ETx7E4DwkSfz5Hw3m3tF3dLZTU8qp"); + auto revealOperationData = new TW::Tezos::Proto::RevealOperationData(); + revealOperationData->set_public_key(publicKey.bytes.data(), publicKey.bytes.size()); + + auto revealOperation = Proto::Operation(); + revealOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); + revealOperation.set_fee(1268); + revealOperation.set_counter(1876); + revealOperation.set_gas_limit(10100); + revealOperation.set_storage_limit(0); + revealOperation.set_kind(Proto::Operation::REVEAL); + revealOperation.set_allocated_reveal_operation_data(revealOperationData); + + op_list.addOperation(revealOperation); + + auto delegateOperationData = new Tezos::Proto::DelegationOperationData(); + delegateOperationData->set_delegate("tz1gSM6yiwr85jEASZ1q3UekgHEoxYt7wg2M"); + + auto delegateOperation = Proto::Operation(); + delegateOperation.set_source("tz1RKLoYm4vtLzo7TAgGifMDAkiWhjfyXwP4"); + delegateOperation.set_fee(1257); + delegateOperation.set_counter(1879); + delegateOperation.set_gas_limit(10100); + delegateOperation.set_storage_limit(0); + delegateOperation.set_kind(Proto::Operation::DELEGATION); + delegateOperation.set_allocated_delegation_operation_data(delegateOperationData); + + op_list.addOperation(delegateOperation); + + auto decodedPrivateKey = Base58::bitcoin.decodeCheck("edsk4bMQMM6HYtMazF3m7mYhQ6KQ1WCEcBuRwh6DTtdnoqAvC3nPCc"); + auto key = PrivateKey(Data(decodedPrivateKey.begin() + 4, decodedPrivateKey.end())); + + std::string expectedForgedBytesToSign = hex(op_list.forge(key)); + std::string expectedSignature = "871693145f2dc72861ff6816e7ac3ce93c57611ac09a4c657a5a35270fa57153334c14cd8cae94ee228b6ef52f0e3f10948721e666318bc54b6c455404b11e03"; + std::string expectedSignedBytes = expectedForgedBytesToSign + expectedSignature; + + auto signedBytes = Signer().signOperationList(key, op_list); + auto signedBytesHex = hex(signedBytes.begin(), signedBytes.end()); + + ASSERT_EQ(hex(signedBytes.begin(), signedBytes.end()), expectedSignedBytes); +} + +} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/TWAnySignerTests.cpp b/tests/chains/Tezos/TWAnySignerTests.cpp new file mode 100644 index 00000000000..89fac461677 --- /dev/null +++ b/tests/chains/Tezos/TWAnySignerTests.cpp @@ -0,0 +1,127 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "Tezos/BinaryCoding.h" +#include "proto/Tezos.pb.h" +#include "TestUtilities.h" +#include + +#include + +using namespace TW; +namespace TW::Tezos::tests { + +TEST(TWAnySignerTezos, SignFA12) { + // https://ghostnet.tzkt.io/ooTBu7DLbeC7DmVfXEsp896A6WTwimedbsM9QRqUVtqA8Vxt6D3/2993172 + auto key = parse_hex("363265a0b3f06661001cab8b4f3ca8fd97ae70608184979cf7300836f57ec2d6"); + + Proto::SigningInput input; + input.set_private_key(key.data(), key.size()); + auto& operations = *input.mutable_operation_list(); + operations.set_branch("BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"); + + auto& transaction = *operations.add_operations(); + auto& txData = *transaction.mutable_transaction_operation_data(); + txData.set_amount(0); + txData.set_destination("KT1EwXFWoG9bYebmF4pYw72aGjwEnBWefgW5"); + txData.mutable_parameters()->mutable_fa12_parameters()->set_entrypoint("transfer"); + txData.mutable_parameters()->mutable_fa12_parameters()->set_from("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + txData.mutable_parameters()->mutable_fa12_parameters()->set_to("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + txData.mutable_parameters()->mutable_fa12_parameters()->set_value("123"); + transaction.set_source("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + transaction.set_fee(100000); + transaction.set_counter(2993172); + transaction.set_gas_limit(100000); + transaction.set_storage_limit(0); + transaction.set_kind(Proto::Operation::TRANSACTION); + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeTezos); + ASSERT_EQ(hex(output.encoded()), "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0694d8b601a08d0600000145bd8a65cc48159d8ea60a55df735b7c5ad45f0e00ffff087472616e736665720000005907070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555000bb012914d768155fba2df319a81136e8e3e573b9cadb1676834490c90212615d271da029b6b0531e290e9063bcdb40bea43627af048b18e036f02be2b6b22fc8b307"); +} + +TEST(TWAnySignerTezos, SignFA2) { + // https://ghostnet.tzkt.io/onxLBoPaf23M3A8kHTwncSFG2GVXPfnGXUhkC8BhKj8QDdCEbng + auto key = parse_hex("363265a0b3f06661001cab8b4f3ca8fd97ae70608184979cf7300836f57ec2d6"); + + Proto::SigningInput input; + input.set_private_key(key.data(), key.size()); + auto& operations = *input.mutable_operation_list(); + operations.set_branch("BKvEAX9HXfJZWYfTQbR1C7B3ADoKY6a1aKVRF7qQqvc9hS8Rr3m"); + + auto& transaction = *operations.add_operations(); + + auto* transactionOperationData = transaction.mutable_transaction_operation_data(); + transactionOperationData->set_amount(0); + transactionOperationData->set_destination("KT1DYk1XDzHredJq1EyNkDindiWDqZyekXGj"); + + auto& fa2 = *transactionOperationData->mutable_parameters()->mutable_fa2_parameters(); + fa2.set_entrypoint("transfer"); + auto& txObject = *fa2.add_txs_object(); + txObject.set_from("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + auto& tx = *txObject.add_txs(); + tx.set_amount("10"); + tx.set_token_id("0"); + tx.set_to("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + + transaction.set_source("tz1ioz62kDw6Gm5HApeQtc1PGmN2wPBtJKUP"); + transaction.set_fee(100000); + transaction.set_counter(2993173); + transaction.set_gas_limit(100000); + transaction.set_storage_limit(0); + transaction.set_kind(Proto::Operation::TRANSACTION); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeTezos); + ASSERT_EQ(hex(output.encoded()), "1b1f9345dc9f77bd24b09034d1d2f9a28f02ac837f49db54b8d68341f53dc4b76c00fe2ce0cccc0214af521ad60c140c5589b4039247a08d0695d8b601a08d0600000136767f88850bae28bfb9f46b73c5e87ede4de12700ffff087472616e7366657200000066020000006107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b5550020000003107070100000024747a31696f7a36326b447736476d35484170655174633150476d4e32775042744a4b555007070000000a552d24710d6c59383286700c6c2917b25a6c1fa8b587e593c289dd47704278796792f1e522c1623845ec991e292b0935445e6994850bd03f035a006c5ed93806"); +} + +TEST(TWAnySignerTezos, Sign) { + auto key = parse_hex("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f"); + auto revealKey = parse_hex("311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff95"); + + Proto::SigningInput input; + input.set_private_key(key.data(), key.size()); + auto& operations = *input.mutable_operation_list(); + operations.set_branch("BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp"); + + auto& reveal = *operations.add_operations(); + auto& revealData = *reveal.mutable_reveal_operation_data(); + revealData.set_public_key(revealKey.data(), revealKey.size()); + reveal.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); + reveal.set_fee(1272); + reveal.set_counter(30738); + reveal.set_gas_limit(10100); + reveal.set_storage_limit(257); + reveal.set_kind(Proto::Operation::REVEAL); + + auto& transaction = *operations.add_operations(); + auto& txData = *transaction.mutable_transaction_operation_data(); + txData.set_amount(1); + txData.set_destination("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); + transaction.set_source("tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW"); + transaction.set_fee(1272); + transaction.set_counter(30739); + transaction.set_gas_limit(10100); + transaction.set_storage_limit(257); + transaction.set_kind(Proto::Operation::TRANSACTION); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeTezos); + + EXPECT_EQ(hex(output.encoded()), "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200311f002e899cdd9a52d96cb8be18ea2bbab867c505da2b44ce10906f511cff956c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80993f001f44e810201000081faa75f741ef614b0e35fcc8c90dfa3b0b95721000217034271b815e5f0c0a881342838ce49d7b48cdf507c72b1568c69a10db70c98774cdad1a74df760763e25f760ff13afcbbf3a1f2c833a0beeb9576a579c05"); +} + +TEST(TWAnySignerTezos, SignJSON) { + auto json = STRING(R"({"operationList": {"branch": "BL8euoCWqNCny9AR3AKjnpi38haYMxjei1ZqNHuXMn19JSQnoWp","operations": [{"source": "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW","fee": 1272,"counter": 30738,"gasLimit": 10100,"storageLimit": 257,"kind": 107,"revealOperationData": {"publicKey": "QpqYbIBypAofOj4qtaWBm7Gy+2mZPFAEg3gVudxVkj4="}},{"source": "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW","fee": 1272,"counter": 30739,"gasLimit": 10100,"storageLimit": 257,"kind": 108,"transactionOperationData": {"destination": "tz1XVJ8bZUXs7r5NV8dHvuiBhzECvLRLR3jW","amount": 1}}]}})"); + auto key = DATA("2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6f"); + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeTezos)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeTezos)); + assertStringsEqual(result, "3756ef37b1be849e3114643f0aa5847cabf9a896d3bfe4dd51448de68e91da016b0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80992f001f44e810200429a986c8072a40a1f3a3e2ab5a5819bb1b2fb69993c5004837815b9dc55923e6c0081faa75f741ef614b0e35fcc8c90dfa3b0b95721f80993f001f44e810201000081faa75f741ef614b0e35fcc8c90dfa3b0b957210001b86398d5b9be737dca8e4106ea18d70e69b75e92f892fb283546a99152b8d7794b919c0fbf1c31de386069a60014491c0e7505adef5781cead1cfe6608030b"); +} + +} // namespace TW::Tezos::tests diff --git a/tests/chains/Tezos/TWCoinTypeTests.cpp b/tests/chains/Tezos/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..2c33d888f68 --- /dev/null +++ b/tests/chains/Tezos/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWTezosCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTezos)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("onk3Z6V4StyfiXTPSHwZFvTKVAaws37cHmZacmULPr3VbVHpKrg")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTezos, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("tz1SiPXX4MYGNJNDsRc7n8hkvUqFzg8xqF9m")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTezos, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTezos)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTezos)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTezos), 6); + ASSERT_EQ(TWBlockchainTezos, TWCoinTypeBlockchain(TWCoinTypeTezos)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTezos)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTezos)); + assertStringsEqual(symbol, "XTZ"); + assertStringsEqual(txUrl, "https://tzstats.com/onk3Z6V4StyfiXTPSHwZFvTKVAaws37cHmZacmULPr3VbVHpKrg"); + assertStringsEqual(accUrl, "https://tzstats.com/tz1SiPXX4MYGNJNDsRc7n8hkvUqFzg8xqF9m"); + assertStringsEqual(id, "tezos"); + assertStringsEqual(name, "Tezos"); +} diff --git a/tests/Theta/SignerTests.cpp b/tests/chains/Theta/SignerTests.cpp similarity index 100% rename from tests/Theta/SignerTests.cpp rename to tests/chains/Theta/SignerTests.cpp diff --git a/tests/chains/Theta/TWAnySignerTests.cpp b/tests/chains/Theta/TWAnySignerTests.cpp new file mode 100644 index 00000000000..1c18296d549 --- /dev/null +++ b/tests/chains/Theta/TWAnySignerTests.cpp @@ -0,0 +1,40 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Theta.pb.h" +#include "uint256.h" +#include + +#include "TestUtilities.h" +#include + +using namespace TW; + +namespace TW::Theta::tests { + +TEST(TWAnySignerTheta, Sign) { + auto privateKey = parse_hex("93a90ea508331dfdf27fb79757d4250b4e84954927ba0073cd67454ac432c737"); + + Proto::SigningInput input; + input.set_chain_id("privatenet"); + input.set_to_address("0x9F1233798E905E173560071255140b4A8aBd3Ec6"); + auto amount = store(uint256_t(10)); + input.set_theta_amount(amount.data(), amount.size()); + auto tfuelAmount = store(uint256_t(20)); + input.set_tfuel_amount(tfuelAmount.data(), tfuelAmount.size()); + auto fee = store(uint256_t(1000000000000)); + input.set_fee(fee.data(), fee.size()); + input.set_sequence(1); + input.set_private_key(privateKey.data(), privateKey.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeTheta); + + ASSERT_EQ(hex(output.encoded()), "02f887c78085e8d4a51000f863f861942e833968e5bb786ae419c4d13189fb081cc43babc70a85e8d4a5101401b8415190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8fe9134ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501d9d8949f1233798e905e173560071255140b4a8abd3ec6c20a14"); +} + +} // namespace TW::Thetha::tests diff --git a/tests/chains/Theta/TWCoinTypeTests.cpp b/tests/chains/Theta/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..94107b0898c --- /dev/null +++ b/tests/chains/Theta/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWThetaCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTheta)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTheta, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTheta, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTheta)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTheta)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTheta), 18); + ASSERT_EQ(TWBlockchainTheta, TWCoinTypeBlockchain(TWCoinTypeTheta)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTheta)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTheta)); + assertStringsEqual(symbol, "THETA"); + assertStringsEqual(txUrl, "https://explorer.thetatoken.org/txs/t123"); + assertStringsEqual(accUrl, "https://explorer.thetatoken.org/account/a12"); + assertStringsEqual(id, "theta"); + assertStringsEqual(name, "Theta"); +} diff --git a/tests/chains/Theta/TransactionTests.cpp b/tests/chains/Theta/TransactionTests.cpp new file mode 100644 index 00000000000..3ff9a4b3926 --- /dev/null +++ b/tests/chains/Theta/TransactionTests.cpp @@ -0,0 +1,38 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Theta/Transaction.h" + +#include "HexCoding.h" + +#include + +namespace TW::Theta::tests { + +TEST(ThetaTransaction, Encode) { + const auto from = Ethereum::Address("0x2E833968E5bB786Ae419c4d13189fB081Cc43bab"); + const auto to = Ethereum::Address("0x9F1233798E905E173560071255140b4A8aBd3Ec6"); + auto transaction = Transaction(from, to, 10, 20, 1); + ASSERT_EQ(hex(transaction.encode()), + "02f843c78085e8d4a51000e0df942e833968e5bb786ae419c4d13189fb081cc43babc70a85e8d4a51014" + "0180d9d8949f1233798e905e173560071255140b4a8abd3ec6c20a14"); +} + +TEST(ThetaTransaction, EncodeWithSignature) { + const auto from = Ethereum::Address("0x2E833968E5bB786Ae419c4d13189fB081Cc43bab"); + const auto to = Ethereum::Address("0x9F1233798E905E173560071255140b4A8aBd3Ec6"); + auto transaction = Transaction(from, to, 10, 20, 1); + transaction.setSignature( + from, parse_hex("5190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8fe9134" + "ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501")); + ASSERT_EQ(hex(transaction.encode()), + "02f887c78085e8d4a51000f863f861942e833968e5bb786ae419c4d13189fb081cc43babc70a85e8d4a5" + "101401b8415190868498d587d074d57298f41853d0109d997f15ddf617f471eb8cbb7fff267cb8fe9134" + "ccdef053ec7cabd18070325c9c436efe1abbacd14eb7561d3fc10501d9d8949f1233798e905e17356007" + "1255140b4a8abd3ec6c20a14"); +} + +} // namespace TW::Theta::tests diff --git a/tests/chains/ThunderToken/TWCoinTypeTests.cpp b/tests/chains/ThunderToken/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..c001ea07218 --- /dev/null +++ b/tests/chains/ThunderToken/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWThunderTokenCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeThunderToken)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeThunderToken, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeThunderToken, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeThunderToken)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeThunderToken)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeThunderToken), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeThunderToken)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeThunderToken)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeThunderToken)); + assertStringsEqual(symbol, "TT"); + assertStringsEqual(txUrl, "https://scan.thundercore.com/transactions/t123"); + assertStringsEqual(accUrl, "https://scan.thundercore.com/address/a12"); + assertStringsEqual(id, "thundertoken"); + assertStringsEqual(name, "Thunder Token"); +} diff --git a/tests/chains/TomoChain/TWCoinTypeTests.cpp b/tests/chains/TomoChain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..4b6ec3810b4 --- /dev/null +++ b/tests/chains/TomoChain/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWTomoChainCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTomoChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x35a8d3ab06c94d5b7d27221b7c9a24ba3f1710dd0fcfd75c5d59b3a885fd709b")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTomoChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x86cCbD9bfb371c355202086882bC644A7D0b024B")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTomoChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTomoChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTomoChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTomoChain), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeTomoChain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTomoChain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTomoChain)); + assertStringsEqual(symbol, "TOMO"); + assertStringsEqual(txUrl, "https://tomoscan.io/tx/0x35a8d3ab06c94d5b7d27221b7c9a24ba3f1710dd0fcfd75c5d59b3a885fd709b"); + assertStringsEqual(accUrl, "https://tomoscan.io/address/0x86cCbD9bfb371c355202086882bC644A7D0b024B"); + assertStringsEqual(id, "tomochain"); + assertStringsEqual(name, "TomoChain"); +} diff --git a/tests/Tron/AddressTests.cpp b/tests/chains/Tron/AddressTests.cpp similarity index 100% rename from tests/Tron/AddressTests.cpp rename to tests/chains/Tron/AddressTests.cpp diff --git a/tests/Tron/SerializationTests.cpp b/tests/chains/Tron/SerializationTests.cpp similarity index 100% rename from tests/Tron/SerializationTests.cpp rename to tests/chains/Tron/SerializationTests.cpp diff --git a/tests/Tron/SignerTests.cpp b/tests/chains/Tron/SignerTests.cpp similarity index 100% rename from tests/Tron/SignerTests.cpp rename to tests/chains/Tron/SignerTests.cpp diff --git a/tests/chains/Tron/TWAnySignerTests.cpp b/tests/chains/Tron/TWAnySignerTests.cpp new file mode 100644 index 00000000000..ba7f2964e7b --- /dev/null +++ b/tests/chains/Tron/TWAnySignerTests.cpp @@ -0,0 +1,49 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Tron.pb.h" +#include + +#include "TestUtilities.h" +#include + +namespace TW::Tron { + +TEST(TWAnySignerTron, SignTransferAsset) { + auto input = Proto::SigningInput(); + auto& transaction = *input.mutable_transaction(); + + auto& transfer = *transaction.mutable_transfer_asset(); + transfer.set_owner_address("TJRyWwFs9wTFGZg3JbrVriFbNfCug5tDeC"); + transfer.set_to_address("THTR75o8xXAgCTQqpiot2AFRAjvW1tSbVV"); + transfer.set_amount(4); + transfer.set_asset_name("1000959"); + + transaction.set_timestamp(1539295479000); + transaction.set_expiration(1541890116000 + 10 * 60 * 60 * 1000); + + auto& blockHeader = *transaction.mutable_block_header(); + blockHeader.set_timestamp(1541890116000); + const auto txTrieRoot = parse_hex("845ab51bf63c2c21ee71a4dc0ac3781619f07a7cd05e1e0bd8ba828979332ffa"); + blockHeader.set_tx_trie_root(txTrieRoot.data(), txTrieRoot.size()); + const auto parentHash = parse_hex("00000000003cb800a7e69e9144e3d16f0cf33f33a95c7ce274097822c67243c1"); + blockHeader.set_parent_hash(parentHash.data(), parentHash.size()); + blockHeader.set_number(3979265); + const auto witnessAddress = parse_hex("41b487cdc02de90f15ac89a68c82f44cbfe3d915ea"); + blockHeader.set_witness_address(witnessAddress.data(), witnessAddress.size()); + blockHeader.set_version(3); + + const auto privateKey = parse_hex("2d8f68944bdbfbc0769542fba8fc2d2a3de67393334471624364c7006da2aa54"); + input.set_private_key(privateKey.data(), privateKey.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeTron); + + ASSERT_EQ(hex(output.signature()), "77f5eabde31e739d34a66914540f1756981dc7d782c9656f5e14e53b59a15371603a183aa12124adeee7991bf55acc8e488a6ca04fb393b1a8ac16610eeafdfc00"); +} + +} diff --git a/tests/chains/Tron/TWCoinTypeTests.cpp b/tests/chains/Tron/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..b549e7bae86 --- /dev/null +++ b/tests/chains/Tron/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWTronCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeTron)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeTron, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeTron, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeTron)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeTron)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeTron), 6); + ASSERT_EQ(TWBlockchainTron, TWCoinTypeBlockchain(TWCoinTypeTron)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeTron)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeTron)); + assertStringsEqual(symbol, "TRX"); + assertStringsEqual(txUrl, "https://tronscan.org/#/transaction/t123"); + assertStringsEqual(accUrl, "https://tronscan.org/#/address/a12"); + assertStringsEqual(id, "tron"); + assertStringsEqual(name, "Tron"); +} diff --git a/tests/VeChain/SignerTests.cpp b/tests/chains/VeChain/SignerTests.cpp similarity index 100% rename from tests/VeChain/SignerTests.cpp rename to tests/chains/VeChain/SignerTests.cpp diff --git a/tests/chains/VeChain/TWAnySignerTests.cpp b/tests/chains/VeChain/TWAnySignerTests.cpp new file mode 100644 index 00000000000..992e65dbc51 --- /dev/null +++ b/tests/chains/VeChain/TWAnySignerTests.cpp @@ -0,0 +1,39 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/VeChain.pb.h" +#include "TestUtilities.h" +#include +#include + +namespace TW::VeChain::tests { + +TEST(TWAnySignerVeChain, Sign) { + auto input = Proto::SigningInput(); + + input.set_chain_tag(1); + input.set_block_ref(1); + input.set_expiration(1); + input.set_gas_price_coef(0); + input.set_gas(21000); + input.set_nonce(1); + + auto key = parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646"); + input.set_private_key(key.data(), key.size()); + + auto& clause = *input.add_clauses(); + auto amount = parse_hex("31303030"); // 1000 + clause.set_to("0x3535353535353535353535353535353535353535"); + clause.set_value(amount.data(), amount.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeVeChain); + + ASSERT_EQ(hex(output.encoded()), "f86a010101dcdb943535353535353535353535353535353535353535843130303080808252088001c0b841bf8edf9600e645b5abd677cb52f585e7f655d1361075d511b37f707a9f31da6702d28739933b264527a1d05b046f5b74044b88c30c3f5a09d616bd7a4af4901601"); +} + +} // namespace TW::VeChain::tests diff --git a/tests/chains/VeChain/TWCoinTypeTests.cpp b/tests/chains/VeChain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..60d7e2b709c --- /dev/null +++ b/tests/chains/VeChain/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWVeChainCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeVeChain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xa424053be0063555aee73a595ca69968c2e4d90d36f280753e503b92b11a655d")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeVeChain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x8a0a035a33173601bfbec8b6ae7c4a6557a55103")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeVeChain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeVeChain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeVeChain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeVeChain), 18); + ASSERT_EQ(TWBlockchainVechain, TWCoinTypeBlockchain(TWCoinTypeVeChain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeVeChain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeVeChain)); + assertStringsEqual(symbol, "VET"); + assertStringsEqual(txUrl, "https://explore.vechain.org/transactions/0xa424053be0063555aee73a595ca69968c2e4d90d36f280753e503b92b11a655d"); + assertStringsEqual(accUrl, "https://explore.vechain.org/accounts/0x8a0a035a33173601bfbec8b6ae7c4a6557a55103"); + assertStringsEqual(id, "vechain"); + assertStringsEqual(name, "VeChain"); +} diff --git a/tests/chains/Viacoin/TWCoinTypeTests.cpp b/tests/chains/Viacoin/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..3fcc86d7e37 --- /dev/null +++ b/tests/chains/Viacoin/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWViacoinCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeViacoin)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeViacoin, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeViacoin, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeViacoin)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeViacoin)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeViacoin), 8); + ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeViacoin)); + ASSERT_EQ(0x21, TWCoinTypeP2shPrefix(TWCoinTypeViacoin)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeViacoin)); + assertStringsEqual(symbol, "VIA"); + assertStringsEqual(txUrl, "https://explorer.viacoin.org/tx/t123"); + assertStringsEqual(accUrl, "https://explorer.viacoin.org/address/a12"); + assertStringsEqual(id, "viacoin"); + assertStringsEqual(name, "Viacoin"); +} diff --git a/tests/Viacoin/TWViacoinAddressTests.cpp b/tests/chains/Viacoin/TWViacoinAddressTests.cpp similarity index 99% rename from tests/Viacoin/TWViacoinAddressTests.cpp rename to tests/chains/Viacoin/TWViacoinAddressTests.cpp index 55ef8fe74a5..d4ea5b115ea 100644 --- a/tests/Viacoin/TWViacoinAddressTests.cpp +++ b/tests/chains/Viacoin/TWViacoinAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/Wanchain/TWCoinTypeTests.cpp b/tests/chains/Wanchain/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..cc49f5ae5ae --- /dev/null +++ b/tests/chains/Wanchain/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWWanchainCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeWanchain)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x180ea96a3218b82b9b35d796823266d8a425c182507adfe5eeffc96e6a14d856")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeWanchain, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x69B492D57bb777e97aa7044D0575228434e2E8B1")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeWanchain, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeWanchain)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeWanchain)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeWanchain), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeWanchain)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeWanchain)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeWanchain)); + assertStringsEqual(symbol, "WAN"); + assertStringsEqual(txUrl, "https://www.wanscan.org/tx/0x180ea96a3218b82b9b35d796823266d8a425c182507adfe5eeffc96e6a14d856"); + assertStringsEqual(accUrl, "https://www.wanscan.org/address/0x69B492D57bb777e97aa7044D0575228434e2E8B1"); + assertStringsEqual(id, "wanchain"); + assertStringsEqual(name, "Wanchain"); +} diff --git a/tests/chains/Waves/AddressTests.cpp b/tests/chains/Waves/AddressTests.cpp new file mode 100644 index 00000000000..1e99f576d1e --- /dev/null +++ b/tests/chains/Waves/AddressTests.cpp @@ -0,0 +1,87 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "Waves/Address.h" + +#include +#include +#include + +using namespace std; +using namespace TW; + +namespace TW::Waves::tests { + +TEST(WavesAddress, SecureHash) { + const auto secureHash = + hex(Address::secureHash(parse_hex("0157c7fefc0c6acc54e9e4354a81ac1f038e01745731"))); + + ASSERT_EQ(secureHash, "a7978a753c6496866dc75ba3abcaaec796f2380037a1fa7c46cbf9762ee380df"); +} + +TEST(WavesAddress, FromPrivateKey) { + const auto privateKey = + PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); + const auto publicKeyEd25519 = privateKey.getPublicKey(TWPublicKeyTypeED25519); + ASSERT_EQ(hex(Data(publicKeyEd25519.bytes.begin(), publicKeyEd25519.bytes.end())), + "ff84c4bfc095df25b01e48807715856d95af93d88c5b57f30cb0ce567ca4ced6"); + const auto publicKeyCurve25519 = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); + ASSERT_EQ(hex(Data(publicKeyCurve25519.bytes.begin(), publicKeyCurve25519.bytes.end())), + "559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"); + const auto address = Address(publicKeyCurve25519); + + ASSERT_EQ(address.string(), "3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds"); +} + +TEST(WavesAddress, FromPublicKey) { + const auto publicKey = + PublicKey(parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"), + TWPublicKeyTypeCURVE25519); + const auto address = Address(publicKey); + + ASSERT_EQ(address.string(), "3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds"); +} + +TEST(WavesAddress, Invalid) { + ASSERT_FALSE(Address::isValid(std::string("abc"))); + ASSERT_FALSE(Address::isValid(std::string("0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"))); + ASSERT_FALSE(Address::isValid(std::string("3PLANf4MgtNN5v5k4NNnyx2m4zKJiw1tF9v"))); + ASSERT_FALSE(Address::isValid(std::string("3PLANf4MgtNN5v6k4NNnyx2m4zKJiw1tF8v"))); +} + +TEST(WavesAddress, Valid) { + ASSERT_TRUE(Address::isValid(std::string("3PLANf4MgtNN5v6k4NNnyx2m4zKJiw1tF9v"))); + ASSERT_TRUE(Address::isValid(std::string("3PDjjLFDR5aWkKgufika7KSLnGmAe8ueDpC"))); + ASSERT_TRUE(Address::isValid(std::string("3PLjucTjqEfmgBF7fs2CER3fHQapCtknPeW"))); + ASSERT_TRUE(Address::isValid(std::string("3PB9ffP1YKQer3e7t283gPCLyjEfK8xrGp7"))); +} + +TEST(WavesAddress, InitWithString) { + const auto address = Address("3PQupTC1yRiHneotFt79LF2pkN6GrGMwEy3"); + ASSERT_EQ(address.string(), "3PQupTC1yRiHneotFt79LF2pkN6GrGMwEy3"); +} + +TEST(WavesAddress, InitWithInvalidString) { + EXPECT_THROW(Address("3PQupTC1yRiHneotFt79LF2pkN6GrGMwEy2"), invalid_argument); +} + +TEST(WavesAddress, Derive) { + const auto mnemonic = + "water process satisfy repeat flag avoid town badge sketch surge split between cabin sugar " + "ill special axis adjust pull useful craft peace flee physical"; + const auto wallet = HDWallet(mnemonic, ""); + const auto address1 = TW::deriveAddress( + TWCoinTypeWaves, wallet.getKey(TWCoinTypeWaves, DerivationPath("m/44'/5741564'/0'/0'/0'"))); + const auto address2 = TW::deriveAddress( + TWCoinTypeWaves, wallet.getKey(TWCoinTypeWaves, DerivationPath("m/44'/5741564'/0'/0'/1'"))); + + ASSERT_EQ(address1, "3PQupTC1yRiHneotFt79LF2pkN6GrGMwEy3"); + ASSERT_EQ(address2, "3PEXw52bkS9XuLhttWoKyykZjXqEY8zeLxf"); +} + +} // namespace TW::Waves::tests diff --git a/tests/chains/Waves/LeaseTests.cpp b/tests/chains/Waves/LeaseTests.cpp new file mode 100644 index 00000000000..53f6e192cbd --- /dev/null +++ b/tests/chains/Waves/LeaseTests.cpp @@ -0,0 +1,142 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "Waves/Address.h" +#include "Waves/Transaction.h" +#include "proto/Waves.pb.h" + +#include +#include + +using json = nlohmann::json; + +using namespace std; +using namespace TW; + +namespace TW::Waves::tests { + +TEST(WavesLease, serialize) { + const auto privateKey = + PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); + auto input = Proto::SigningInput(); + input.set_timestamp(int64_t(1526646497465)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto& message = *input.mutable_lease_message(); + message.set_amount(int64_t(100000000)); + message.set_fee(int64_t(100000)); + message.set_to("3P9DEDP5VbyXQyKtXDUt2crRPn5B7gs6ujc"); + auto tx1 = Transaction( + input, + /* pub_key */ + parse_hex("425f57a8cb5439e4e912e66376f7041565d029ae4437dae1a3ebe15649d43461")); + auto serialized1 = tx1.serializeToSign(); + ASSERT_EQ(hex(serialized1), "080200425f57a8cb5439e4e912e66376f7041565d029ae4437dae1a3ebe15649d4346101574" + "fdfcd1bfb19114bd2ac369e32013c70c6d03a4627879cbf0000000005f5e100000000000001" + "86a0000001637338e0b9"); +} + +TEST(WavesLease, CancelSerialize) { + const auto privateKey = + PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); + auto input = Proto::SigningInput(); + input.set_timestamp(int64_t(1568831000826)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto& message = *input.mutable_cancel_lease_message(); + message.set_fee(int64_t(100000)); + message.set_lease_id("44re3UEDw1QwPFP8dKzfuGHVMNBejUW9NbhxG6b4KJ1T"); + auto tx1 = Transaction( + input, + /* pub_key */ + parse_hex("425f57a8cb5439e4e912e66376f7041565d029ae4437dae1a3ebe15649d43461")); + auto serialized1 = tx1.serializeToSign(); + ASSERT_EQ(hex(serialized1), "090257425f57a8cb5439e4e912e66376f7041565d029ae4437dae1a3ebe15649d" + "4346100000000000186a00000016d459d50fa2d8fee08efc97f79bcd97a4d977c" + "76183580d723909af2b50e72b02f1e36707e"); +} + +TEST(WavesLease, jsonSerialize) { + const auto privateKey = PrivateKey(parse_hex( + "9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); + const auto publicKeyCurve25519 = + privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); + auto input = Proto::SigningInput(); + input.set_timestamp(int64_t(1568973547102)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto& message = *input.mutable_lease_message(); + message.set_amount(int64_t(100000)); + message.set_fee(int64_t(100000)); + message.set_to("3P9DEDP5VbyXQyKtXDUt2crRPn5B7gs6ujc"); + auto tx1 = Transaction(input, + /* pub_key */ + parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d")); + + auto signature = Signer::sign(privateKey, tx1); + auto json = tx1.buildJson(signature); + + ASSERT_EQ(json["type"], TransactionType::lease); + ASSERT_EQ(json["version"], TransactionVersion::V2); + ASSERT_EQ(json["fee"], int64_t(100000)); + ASSERT_EQ(json["senderPublicKey"], + "6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU"); + ASSERT_EQ(json["timestamp"], int64_t(1568973547102)); + ASSERT_EQ(json["proofs"].dump(), + "[\"4opce9e99827upK3m3D3NicnvBqbMLtAJ4Jc8ksTLiScqBgjdqzr9JyXG" + "C1NAGZUbkqJvix9bNrBokrxtGruwmu3\"]"); + ASSERT_EQ(json["recipient"], "3P9DEDP5VbyXQyKtXDUt2crRPn5B7gs6ujc"); + ASSERT_EQ(json["amount"], int64_t(100000)); + ASSERT_EQ(json.dump(), + "{\"amount\":100000,\"fee\":100000,\"proofs\":[" + "\"4opce9e99827upK3m3D3NicnvBqbMLtAJ4Jc8ksTLiScqBgjdqzr9JyXGC1NAGZUbkqJ" + "vix9bNrBokrxtGruwmu3\"],\"recipient\":" + "\"3P9DEDP5VbyXQyKtXDUt2crRPn5B7gs6ujc\",\"senderPublicKey\":" + "\"6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU\",\"timestamp\":" + "1568973547102,\"type\":8,\"version\":2}"); +} + +TEST(WavesLease, jsonCancelSerialize) { + const auto privateKey = PrivateKey(parse_hex( + "9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); + const auto publicKeyCurve25519 = + privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); + auto input = Proto::SigningInput(); + input.set_timestamp(int64_t(1568973547102)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto& message = *input.mutable_cancel_lease_message(); + message.set_lease_id("DKhmXrCsBwf6WVhGh8bYVBnjtAXGpk2K4Yd3CW4u1huG"); + message.set_fee(int64_t(100000)); + auto tx1 = Transaction(input, + /* pub_key */ + parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d")); + auto signature = Signer::sign(privateKey, tx1); + auto json = tx1.buildJson(signature); + + ASSERT_EQ(json["type"], TransactionType::cancelLease); + ASSERT_EQ(json["version"], TransactionVersion::V2); + ASSERT_EQ(json["fee"], int64_t(100000)); + ASSERT_EQ(json["senderPublicKey"], + "6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU"); + ASSERT_EQ(json["leaseId"], "DKhmXrCsBwf6WVhGh8bYVBnjtAXGpk2K4Yd3CW4u1huG"); + ASSERT_EQ(json["chainId"], 87); + ASSERT_EQ(json["timestamp"], int64_t(1568973547102)); + ASSERT_EQ(json["proofs"].dump(), + "[\"Mwhh7kdbhPv9vtnPh6pjEcHTFJ5h5JtAziwFpqH8Ykw1yWYie4Nquh" + "eYtAWPbRowgpDVBxvG1rTrv82LnFdByQY\"]"); + ASSERT_EQ(json.dump(), + "{\"chainId\":87,\"fee\":100000,\"leaseId\":\"DKhmXrCsBwf6WVhGh8bYVBnjtAXGpk2K4Yd3CW4u1huG\"," + "\"proofs\":[\"Mwhh7kdbhPv9vtnPh6pjEcHTFJ5h5JtAziwFpqH8Ykw1yWYie4NquheYtAWP" + "bRowgpDVBxvG1rTrv82LnFdByQY\"],\"senderPublicKey\":" + "\"6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU\",\"timestamp\":" + "1568973547102,\"type\":9,\"version\":2}"); +} + +} // namespace TW::Waves::tests diff --git a/tests/chains/Waves/SignerTests.cpp b/tests/chains/Waves/SignerTests.cpp new file mode 100644 index 00000000000..95938c16d39 --- /dev/null +++ b/tests/chains/Waves/SignerTests.cpp @@ -0,0 +1,64 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PublicKey.h" +#include "Waves/Signer.h" +#include "Waves/Transaction.h" + +#include +#include + +using namespace TW; + +namespace TW::Waves::tests { + +TEST(WavesSigner, SignTransaction) { + const auto privateKey = + PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); + const auto publicKeyCurve25519 = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); + ASSERT_EQ(hex(Data(publicKeyCurve25519.bytes.begin(), publicKeyCurve25519.bytes.end())), + "559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"); + // 3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds + const auto address = Address(publicKeyCurve25519); + auto input = Proto::SigningInput(); + input.set_timestamp(int64_t(1526641218066)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto& message = *input.mutable_transfer_message(); + message.set_amount(int64_t(100000000)); + message.set_asset(Transaction::WAVES); + message.set_fee(int64_t(100000000)); + message.set_fee_asset(Transaction::WAVES); + message.set_to(address.string()); + message.set_attachment("falafel"); + auto tx1 = Transaction( + input, + /* pub_key */ + parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d")); + + auto signature = Signer::sign(privateKey, tx1); + + EXPECT_EQ(hex(tx1.serializeToSign()), + "0402559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d00000000016372e8" + "52120000000005f5e1000000000005f5e10001570acc4110b78a6d38b34d879b5bba38806202ecf1732f" + "8542000766616c6166656c"); + EXPECT_EQ(hex(signature), "af7989256f496e103ce95096b3f52196dd9132e044905fe486da3b829b5e403bcba9" + "5ab7e650a4a33948c2d05cfca2dce4d4df747e26402974490fb4c49fbe8f"); + + ASSERT_TRUE(publicKeyCurve25519.verify(signature, tx1.serializeToSign())); +} + +TEST(WavesSigner, curve25519_pk_to_ed25519) { + const auto publicKeyCurve25519 = + parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"); + auto r = Data(); + r.resize(32); + curve25519_pk_to_ed25519(r.data(), publicKeyCurve25519.data()); + EXPECT_EQ(hex(r), "ff84c4bfc095df25b01e48807715856d95af93d88c5b57f30cb0ce567ca4ce56"); +} + +} // namespace TW::Waves::tests diff --git a/tests/chains/Waves/TWAnySignerTests.cpp b/tests/chains/Waves/TWAnySignerTests.cpp new file mode 100644 index 00000000000..c814761a4f9 --- /dev/null +++ b/tests/chains/Waves/TWAnySignerTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Base58.h" +#include "HexCoding.h" +#include "proto/Waves.pb.h" +#include "TestUtilities.h" +#include +#include + +namespace TW::Waves::tests { + +TEST(TWAnySignerWaves, Sign) { + auto input = Proto::SigningInput(); + const auto privateKey = Base58::bitcoin.decode("83mqJpmgB5Mko1567sVAdqZxVKsT6jccXt3eFSi4G1zE"); + + input.set_timestamp(int64_t(1559146613)); + input.set_private_key(privateKey.data(), privateKey.size()); + auto& message = *input.mutable_transfer_message(); + message.set_amount(int64_t(100000000)); + message.set_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); + message.set_fee(int64_t(100000)); + message.set_fee_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); + message.set_to("3PPCZQkvdMJpmx7Zrz1cnYsPe9Bt1XT2Ckx"); + message.set_attachment("hello"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeWaves); + + ASSERT_EQ(hex(output.signature()), "5d6a77b1fd9b53d9735cd2543ba94215664f2b07d6c7befb081221fcd49f5b6ad6b9ac108582e8d3e74943bdf35fd80d985edf4b4de1fb1c5c427e84d0879f8f"); +} + +} // namespace TW::Waves::tests diff --git a/tests/chains/Waves/TWCoinTypeTests.cpp b/tests/chains/Waves/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..6460c3cbff0 --- /dev/null +++ b/tests/chains/Waves/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWWavesCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeWaves)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeWaves, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeWaves, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeWaves)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeWaves)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeWaves), 8); + ASSERT_EQ(TWBlockchainWaves, TWCoinTypeBlockchain(TWCoinTypeWaves)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeWaves)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeWaves)); + assertStringsEqual(symbol, "WAVES"); + assertStringsEqual(txUrl, "https://wavesexplorer.com/tx/t123"); + assertStringsEqual(accUrl, "https://wavesexplorer.com/address/a12"); + assertStringsEqual(id, "waves"); + assertStringsEqual(name, "Waves"); +} diff --git a/tests/chains/Waves/TransactionTests.cpp b/tests/chains/Waves/TransactionTests.cpp new file mode 100644 index 00000000000..1e2f1354d8b --- /dev/null +++ b/tests/chains/Waves/TransactionTests.cpp @@ -0,0 +1,137 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "Waves/Address.h" +#include "proto/Waves.pb.h" +#include "Waves/Transaction.h" + +#include +#include + +namespace TW::Waves::tests { + +using json = nlohmann::json; +using namespace std; + +TEST(WavesTransaction, serialize) { + const auto privateKey = + PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); + auto input = Proto::SigningInput(); + input.set_timestamp(int64_t(1526641218066)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto& message = *input.mutable_transfer_message(); + message.set_amount(int64_t(100000000)); + message.set_asset(""); + message.set_fee(int64_t(100000000)); + message.set_fee_asset(Transaction::WAVES); + message.set_to("3PLgzJXQiN77G7KgnR1WVa8jBYhF2dmWndx"); + message.set_attachment("falafel"); + auto tx1 = Transaction( + input, + /* pub_key */ + parse_hex("d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef22")); + auto serialized1 = tx1.serializeToSign(); + ASSERT_EQ(hex(serialized1), "0402d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef" + "2200000000016372e852120000000005f5e1000000000005f5e1000157cdc9381c" + "071beb5abd27738d5cd36cf75f3cbfdd69e8e6bb000766616c6166656c"); + + auto input2 = Proto::SigningInput(); + input2.set_timestamp(int64_t(1)); + input2.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto& message2 = *input2.mutable_transfer_message(); + message2.set_amount(int64_t(1)); + message2.set_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); + message2.set_fee(int64_t(1)); + message2.set_fee_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); + message2.set_to("3PLgzJXQiN77G7KgnR1WVa8jBYhF2dmWndx"); + message2.set_attachment(""); + + auto tx2 = Transaction( + input2, + /* pub_key */ + parse_hex("d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef22")); + auto serialized2 = tx2.serializeToSign(); + ASSERT_EQ(hex(serialized2), + "0402d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef2201bae8ddc9955fa6" + "f69f8e7b155efcdb97bc3bb3a95db4c4604408cec245cd187201bae8ddc9955fa6f69f8e7b155efcdb97" + "bc3bb3a95db4c4604408cec245cd18720000000000000001000000000000000100000000000000010157" + "cdc9381c071beb5abd27738d5cd36cf75f3cbfdd69e8e6bb0000"); +} + +TEST(WavesTransaction, failedSerialize) { + // 141 bytes attachment + const auto privateKey = + PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); + auto input = Proto::SigningInput(); + input.set_timestamp(int64_t(1526641218066)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto& message = *input.mutable_transfer_message(); + message.set_amount(int64_t(100000000)); + message.set_asset(""); + message.set_fee(int64_t(100000000)); + message.set_fee_asset(""); + message.set_to("3PLgzJXQiN77G7KgnR1WVa8jBYhF2dmWndx"); + message.set_attachment("falafelfalafelfalafelfalafelfalafelfalafelfalafel" + "falafelfalafelfalafelfalafelfalafelfalafelfalafel" + "falafelfalafelfalafelfalafelfalafelfalafel"); + auto tx1 = Transaction( + input, + /* pub_key */ + parse_hex("d528aabec35ca100d87c7b7a128632faf19cd44531819457445113a32a21ef22")); + EXPECT_THROW(tx1.serializeToSign(), invalid_argument); +} + +TEST(WavesTransaction, jsonSerialize) { + + const auto privateKey = + PrivateKey(parse_hex("9864a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a")); + const auto publicKeyCurve25519 = privateKey.getPublicKey(TWPublicKeyTypeCURVE25519); + ASSERT_EQ(hex(Data(publicKeyCurve25519.bytes.begin(), publicKeyCurve25519.bytes.end())), + "559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d"); + const auto address = Address(publicKeyCurve25519); + + auto input = Proto::SigningInput(); + input.set_timestamp(int64_t(1526641218066)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto& message = *input.mutable_transfer_message(); + message.set_amount(int64_t(10000000)); + message.set_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); + message.set_fee(int64_t(100000000)); + message.set_fee_asset("DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD82zq"); + message.set_to(address.string()); + message.set_attachment("falafel"); + auto tx1 = Transaction( + input, + /* pub_key */ + parse_hex("559a50cb45a9a8e8d4f83295c354725990164d10bb505275d1a3086c08fb935d")); + + auto signature = Signer::sign(privateKey, tx1); + + auto json = tx1.buildJson(signature); + + ASSERT_EQ(json["type"], TransactionType::transfer); + ASSERT_EQ(json["version"], TransactionVersion::V2); + ASSERT_EQ(json["fee"], int64_t(100000000)); + ASSERT_EQ(json["senderPublicKey"], "6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU"); + ASSERT_EQ(json["timestamp"], int64_t(1526641218066)); + ASSERT_EQ(json["proofs"].dump(), "[\"5ynN2NUiFHkQzw9bK8R7dZcNfTWMAtcWRJsrMvFFM6dUT3fSnPCCX7CTajNU8bJCB" + "H69vU1mnwfx4zpDtF1SkzKg\"]"); + ASSERT_EQ(json["recipient"], "3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds"); + ASSERT_EQ(json["assetId"], "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq"); + ASSERT_EQ(json["feeAssetId"], "DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD82zq"); + ASSERT_EQ(json["amount"], int64_t(10000000)); + ASSERT_EQ(json["attachment"], "4t2Xazb2SX"); + ASSERT_EQ(json.dump(), "{\"amount\":10000000,\"assetId\":\"DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD81zq\",\"attachment\":\"4t2Xazb2SX\",\"fee\":100000000,\"feeAssetId\":\"DacnEpaUVFRCYk8Fcd1F3cqUZuT4XG7qW9mRyoZD82zq\",\"proofs\":[\"5ynN2NUiFHkQzw9bK8R7dZcNfTWMAtcWRJsrMvFFM6dUT3fSnPCCX7CTajNU8bJCBH69vU1mnwfx4zpDtF1SkzKg\"],\"recipient\":\"3P2uzAzX9XTu1t32GkWw68YFFLwtapWvDds\",\"senderPublicKey\":\"6mA8eQjie53kd4jbZrwL3ZhMBqCX6nzit1k55tR2X7zU\",\"timestamp\":1526641218066,\"type\":4,\"version\":2}"); +} + +} // namespace TW::Waves::tests diff --git a/tests/chains/XRP/AddressTests.cpp b/tests/chains/XRP/AddressTests.cpp new file mode 100644 index 00000000000..591b2842123 --- /dev/null +++ b/tests/chains/XRP/AddressTests.cpp @@ -0,0 +1,58 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "XRP/Address.h" +#include "XRP/XAddress.h" +#include "HexCoding.h" + +#include + +namespace TW::Ripple { + +TEST(RippleAddress, FromPublicKey) { + const auto publicKey = PublicKey(parse_hex("0303E20EC6B4A39A629815AE02C0A1393B9225E3B890CAE45B59F42FA29BE9668D"), TWPublicKeyTypeSECP256k1); + const auto address = Address(publicKey); + ASSERT_EQ(std::string("rnBFvgZphmN39GWzUJeUitaP22Fr9be75H"), address.string()); +} + +TEST(RippleAddress, FromString) { + std::string classic = "rnBFvgZphmN39GWzUJeUitaP22Fr9be75H"; + const auto address = Address(classic); + + ASSERT_EQ(address.string(), classic); +} + +TEST(RippleXAddress, FromPublicKey) { + const auto publicKey = PublicKey(parse_hex("0303E20EC6B4A39A629815AE02C0A1393B9225E3B890CAE45B59F42FA29BE9668D"), TWPublicKeyTypeSECP256k1); + const auto address = XAddress(publicKey, 12345); + ASSERT_EQ(std::string("X76UnYEMbQfEs3mUqgtjp4zFy9exgThRj7XVZ6UxsdrBptF"), address.string()); +} + +TEST(RippleXAddress, FromString) { + std::string xAddress = "X76UnYEMbQfEs3mUqgtjp4zFy9exgThRj7XVZ6UxsdrBptF"; + std::string xAddress2 = "X76UnYEMbQfEs3mUqgtjp4zFy9exgTsM93nriVZAPufrpE3"; + const auto address = XAddress(xAddress); + const auto address2 = XAddress(xAddress2); + + ASSERT_EQ(address.tag, 12345ul); + ASSERT_EQ(address.string(), xAddress); + + ASSERT_EQ(address2.tag, 0ul); + ASSERT_EQ(address2.string(), xAddress2); +} + +TEST(RippleAddress, isValid) { + std::string classicAddress = "r36yxStAh7qgTQNHTzjZvXybCTzUFhrfav"; + std::string bitcoinAddress = "1Ma2DrB78K7jmAwaomqZNRMCvgQrNjE2QC"; + std::string xAddress = "XVfvixWZQKkcenFRYApCjpTUyJ4BePTe3jJv7beatUZvQYh"; + + ASSERT_TRUE(Address::isValid(classicAddress)); + ASSERT_TRUE(XAddress::isValid(xAddress)); + ASSERT_FALSE(Address::isValid(bitcoinAddress)); + ASSERT_FALSE(XAddress::isValid(bitcoinAddress)); +} + +} // namespace TW::Ripple diff --git a/tests/chains/XRP/TWAnySignerTests.cpp b/tests/chains/XRP/TWAnySignerTests.cpp new file mode 100644 index 00000000000..1b9e05728e6 --- /dev/null +++ b/tests/chains/XRP/TWAnySignerTests.cpp @@ -0,0 +1,44 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "proto/Common.pb.h" +#include "proto/Ripple.pb.h" +#include + +#include "TestUtilities.h" +#include + +using namespace TW; + +namespace TW::Ripple::tests { + +TEST(TWAnySignerRipple, Sign) { + auto key = parse_hex("ba005cd605d8a02e3d5dfd04234cef3a3ee4f76bfbad2722d1fb5af8e12e6764"); + Proto::SigningInput input; + + input.set_amount(29000000); + input.set_fee(200000); + input.set_sequence(1); + input.set_account("rDpysuumkweqeC7XdNgYNtzL5GxbdsmrtF"); + input.set_destination("rU893viamSnsfP3zjzM2KPxjqZjXSXK6VF"); + input.set_private_key(key.data(), key.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(hex(output.encoded()), "12000022800000002400000001614000000001ba8140684000000000030d407321026cc34b92cefb3a4537b3edb0b6044c04af27c01583c577823ecc69a9a21119b6744630440220067f20b3eebfc7107dd0bcc72337a236ac3be042c0469f2341d76694a17d4bb9022048393d7ee7dcb729783b33f5038939ddce1bb8337e66d752974626854556bbb681148400b6b6d08d5d495653d73eda6804c249a5148883148132e4e20aecf29090ac428a9c43f230a829220d"); + + // invalid tag + input.set_destination_tag(641505641505); + + ANY_SIGN(input, TWCoinTypeXRP); + + EXPECT_EQ(output.error(), Common::Proto::SigningError::Error_invalid_memo); + EXPECT_EQ(output.encoded(), ""); +} + +} // namespace TW::Ripple::tests diff --git a/tests/chains/XRP/TWCoinTypeTests.cpp b/tests/chains/XRP/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..1f1a967a712 --- /dev/null +++ b/tests/chains/XRP/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWXRPCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeXRP)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("E26AB8F3372D2FC02DEC1FD5674ADAB762D684BFFDBBDF5D674E9D7CF4A47054")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeXRP, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("rfkH7EuS1XcSkB9pocy1R6T8F4CsNYixYU")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeXRP, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeXRP)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeXRP)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeXRP), 6); + ASSERT_EQ(TWBlockchainRipple, TWCoinTypeBlockchain(TWCoinTypeXRP)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeXRP)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeXRP)); + assertStringsEqual(symbol, "XRP"); + assertStringsEqual(txUrl, "https://bithomp.com/explorer/E26AB8F3372D2FC02DEC1FD5674ADAB762D684BFFDBBDF5D674E9D7CF4A47054"); + assertStringsEqual(accUrl, "https://bithomp.com/explorer/rfkH7EuS1XcSkB9pocy1R6T8F4CsNYixYU"); + assertStringsEqual(id, "ripple"); + assertStringsEqual(name, "XRP"); +} diff --git a/tests/chains/XRP/TWRippleAddressTests.cpp b/tests/chains/XRP/TWRippleAddressTests.cpp new file mode 100644 index 00000000000..6c21a4a4bc5 --- /dev/null +++ b/tests/chains/XRP/TWRippleAddressTests.cpp @@ -0,0 +1,33 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include + +#include + +TEST(TWRipple, ExtendedKeys) { + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic( + STRING("ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal").get(), + STRING("TREZOR").get())); + + auto xpub = WRAPS(TWHDWalletGetExtendedPublicKey(wallet.get(), TWPurposeBIP44, TWCoinTypeXRP, TWHDVersionXPUB)); + auto xprv = WRAPS(TWHDWalletGetExtendedPrivateKey(wallet.get(), TWPurposeBIP44, TWCoinTypeXRP, TWHDVersionXPRV)); + + assertStringsEqual(xpub, "xpub6D9oDY4gqFBtsFEonh5GTDiUm6nmij373YWzmYdshcnM4AFzdhUf55iZD33vNU2ZqfQJU5wiCJUgisMt2RHKDzhi1PbZfh5Y2NiiYJAQqUn"); + assertStringsEqual(xprv, "xprv9zASp2XnzsdbemALgfYG65mkD4xHKGKFgKbPyAEG9HFNBMvr6AAQXHQ5MmqM66EnbJfe9TvYMy1bucz7hSQjG43NVizRZwJJYfLmeKo4nVB"); +} + +TEST(TWRipple, XAddress) { + const auto string = STRING("XVfvixWZQKkcenFRYApCjpTUyJ4BePTe3jJv7beatUZvQYh"); + const auto xAddress = WRAP(TWRippleXAddress, TWRippleXAddressCreateWithString(string.get())); + + EXPECT_TRUE(TWRippleXAddressIsValidString(string.get())); + EXPECT_EQ(TWRippleXAddressTag(xAddress.get()), 12345ul); + assertStringsEqual(WRAPS(TWRippleXAddressDescription(xAddress.get())), "XVfvixWZQKkcenFRYApCjpTUyJ4BePTe3jJv7beatUZvQYh"); +} diff --git a/tests/chains/XRP/TransactionTests.cpp b/tests/chains/XRP/TransactionTests.cpp new file mode 100644 index 00000000000..f9015b684da --- /dev/null +++ b/tests/chains/XRP/TransactionTests.cpp @@ -0,0 +1,128 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "XRP/Address.h" +#include "XRP/Transaction.h" +#include "XRP/BinaryCoding.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" + +#include + +using namespace std; + +namespace TW::Ripple::tests { + +TEST(RippleTransaction, serializeAmount) { + /// From https://github.com/trezor/trezor-core/blob/master/tests/test_apps.ripple.serializer.py + auto data0 = Transaction::serializeAmount(0); + auto data1 = Transaction::serializeAmount(1); + auto data2 = Transaction::serializeAmount(93493429243); + auto data3 = Transaction::serializeAmount(25000000); + auto data4 = Transaction::serializeAmount(100000000000); + /// more than max supply + auto data5 = Transaction::serializeAmount(200000000000000000); + /// negative value + auto data6 = Transaction::serializeAmount(-1); + + ASSERT_EQ(hex(data0), "4000000000000000"); + ASSERT_EQ(hex(data1), "4000000000000001"); + ASSERT_EQ(hex(data2), "40000015c4a483fb"); + ASSERT_EQ(hex(data3), "40000000017d7840"); + ASSERT_EQ(hex(data4), "400000174876e800"); + ASSERT_EQ(hex(data5), "42c68af0bb140000"); + ASSERT_EQ(hex(data6), ""); +} + +TEST(RippleTransaction, serialize) { + /// From https://github.com/trezor/trezor-core/blob/master/tests/test_apps.ripple.serializer.py + auto account = Address("r9TeThyi5xiuUUrFjtPKZiHcDxs7K9H6Rb"); + auto destination = "r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C"; + auto tx1 = Transaction( + /* amount */25000000, + /* fee */10, + /* flags */0, + /* sequence */2, + /* last_ledger_sequence */0, + /* account */account, + /* destination */destination, + /* destination_tag*/0 + ); + auto serialized1 = tx1.serialize(); + ASSERT_EQ(hex(serialized1), "120000220000000024000000026140000000017d784068400000000000000a81145ccb151f6e9d603f394ae778acf10d3bece874f68314e851bbbe79e328e43d68f43445368133df5fba5a"); + + auto tx2 = Transaction( + /* amount */200000, + /* fee */15, + /* flags */0, + /* sequence */144, + /* last_ledger_sequence */0, + /* account */Address("rGWTUVmm1fB5QUjMYn8KfnyrFNgDiD9H9e"), + /* destination */"rw71Qs1UYQrSQ9hSgRohqNNQcyjCCfffkQ", + /* destination_tag*/0 + ); + auto serialized2 = tx2.serialize(); + ASSERT_EQ(hex(serialized2), "12000022000000002400000090614000000000030d4068400000000000000f8114aa1bd19d9e87be8069fdbf6843653c43837c03c6831467fe6ec28e0464dd24fb2d62a492aac697cfad02"); + + auto tx3 = Transaction( + /* amount */25000000, + /* fee */12, + /* flags */0, + /* sequence */1, + /* last_ledger_sequence */0, + /* account */Address("r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C"), + /* destination */"rBqSFEFg2B6GBMobtxnU1eLA1zbNC9NDGM", + /* destination_tag*/4146942154 + ); + auto serialized3 = tx3.serialize(); + ASSERT_EQ(hex(serialized3), "120000220000000024000000012ef72d50ca6140000000017d784068400000000000000c8114e851bbbe79e328e43d68f43445368133df5fba5a831476dac5e814cd4aa74142c3ab45e69a900e637aa2"); + + auto tx4 = Transaction( + /* amount */25000000, + /* fee */12, + /* flags */0, + /* sequence */1, + /* last_ledger_sequence */0, + /* account */Address("r4BPgS7DHebQiU31xWELvZawwSG2fSPJ7C"), + /* destination */"XVhidoXkozM5DTZFdDnJ5nYC8FPrTuJiyGh1VxSGS6RNJJ5", + /* ignore destination_tag*/12345 + ); + auto serialized4 = tx4.serialize(); + ASSERT_EQ(hex(serialized4), hex(serialized3)); +} + +TEST(RippleTransaction, preImage) { + auto account = Address("r9LqNeG6qHxjeUocjvVki2XR35weJ9mZgQ"); + auto destination = "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"; + auto tx1 = Transaction( + /* amount */1000, + /* fee */10, + /* flags */2147483648, + /* sequence */1, + /* last_ledger_sequence */0, + /* account */account, + /* destination */destination, + /* destination_tag*/0 + ); + tx1.pub_key = parse_hex("ed5f5ac8b98974a3ca843326d9b88cebd0560177b973ee0b149f782cfaa06dc66a"); + auto unsignedTx = tx1.getPreImage(); + + ASSERT_EQ(hex(unsignedTx), + /* prefix */ "53545800" + /* tx type */ "120000" + /* flags */ "2280000000" + /* sequence */ "2400000001" + /* amount */ "6140000000000003e8" + /* fee */ "68400000000000000a" + /* pub key */ "7321ed5f5ac8b98974a3ca843326d9b88cebd0560177b973ee0b149f782cfaa06dc66a" + /* account */ "81145b812c9d57731e27a2da8b1830195f88ef32a3b6" + /* destination */ "8314b5f762798a53d543a014caf8b297cff8f2f937e8" + ); + ASSERT_EQ(unsignedTx.size(), 114ul); +} + +} // namespace TW::Ripple::tests diff --git a/tests/Zcash/AddressTests.cpp b/tests/chains/Zcash/AddressTests.cpp similarity index 100% rename from tests/Zcash/AddressTests.cpp rename to tests/chains/Zcash/AddressTests.cpp diff --git a/tests/chains/Zcash/TWCoinTypeTests.cpp b/tests/chains/Zcash/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..46deb231511 --- /dev/null +++ b/tests/chains/Zcash/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWZcashCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeZcash)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("f2438a93039faf08d39bd3df1f7b5f19a2c29ffe8753127e2956ab4461adab35")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeZcash, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("t1Yfrf1dssDLmaMBsq2LFKWPbS5vH3nGpa2")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeZcash, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeZcash)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeZcash)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeZcash), 8); + ASSERT_EQ(TWBlockchainZcash, TWCoinTypeBlockchain(TWCoinTypeZcash)); + ASSERT_EQ(0xbd, TWCoinTypeP2shPrefix(TWCoinTypeZcash)); + ASSERT_EQ(0x1c, TWCoinTypeStaticPrefix(TWCoinTypeZcash)); + assertStringsEqual(symbol, "ZEC"); + assertStringsEqual(txUrl, "https://blockchair.com/zcash/transaction/f2438a93039faf08d39bd3df1f7b5f19a2c29ffe8753127e2956ab4461adab35"); + assertStringsEqual(accUrl, "https://blockchair.com/zcash/address/t1Yfrf1dssDLmaMBsq2LFKWPbS5vH3nGpa2"); + assertStringsEqual(id, "zcash"); + assertStringsEqual(name, "Zcash"); +} diff --git a/tests/Zcash/TWZcashAddressTests.cpp b/tests/chains/Zcash/TWZcashAddressTests.cpp similarity index 99% rename from tests/Zcash/TWZcashAddressTests.cpp rename to tests/chains/Zcash/TWZcashAddressTests.cpp index bbaa4ad932e..d3820059c5a 100644 --- a/tests/Zcash/TWZcashAddressTests.cpp +++ b/tests/chains/Zcash/TWZcashAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Zcash/TAddress.h" diff --git a/tests/Zcash/TWZcashTransactionTests.cpp b/tests/chains/Zcash/TWZcashTransactionTests.cpp similarity index 99% rename from tests/Zcash/TWZcashTransactionTests.cpp rename to tests/chains/Zcash/TWZcashTransactionTests.cpp index 96861a4a507..b0acaf0f132 100644 --- a/tests/Zcash/TWZcashTransactionTests.cpp +++ b/tests/chains/Zcash/TWZcashTransactionTests.cpp @@ -5,7 +5,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/OutPoint.h" #include "Bitcoin/Script.h" diff --git a/tests/chains/Zelcash/TWCoinTypeTests.cpp b/tests/chains/Zelcash/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..29ff10fe24e --- /dev/null +++ b/tests/chains/Zelcash/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWZelcashCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeZelcash)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeZelcash, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeZelcash, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeZelcash)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeZelcash)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeZelcash), 8); + ASSERT_EQ(TWBlockchainZcash, TWCoinTypeBlockchain(TWCoinTypeZelcash)); + ASSERT_EQ(0xbd, TWCoinTypeP2shPrefix(TWCoinTypeZelcash)); + ASSERT_EQ(0x1c, TWCoinTypeStaticPrefix(TWCoinTypeZelcash)); + assertStringsEqual(symbol, "FLUX"); + assertStringsEqual(txUrl, "https://explorer.runonflux.io/tx/t123"); + assertStringsEqual(accUrl, "https://explorer.runonflux.io/address/a12"); + assertStringsEqual(id, "zelcash"); + assertStringsEqual(name, "Flux"); +} diff --git a/tests/Zelcash/TWZelcashAddressTests.cpp b/tests/chains/Zelcash/TWZelcashAddressTests.cpp similarity index 99% rename from tests/Zelcash/TWZelcashAddressTests.cpp rename to tests/chains/Zelcash/TWZelcashAddressTests.cpp index 6d2b5807778..f78524d8898 100644 --- a/tests/Zelcash/TWZelcashAddressTests.cpp +++ b/tests/chains/Zelcash/TWZelcashAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/Zelcash/TWZelcashTransactionTests.cpp b/tests/chains/Zelcash/TWZelcashTransactionTests.cpp similarity index 99% rename from tests/Zelcash/TWZelcashTransactionTests.cpp rename to tests/chains/Zelcash/TWZelcashTransactionTests.cpp index 90d2b09cb41..74f3413670e 100644 --- a/tests/Zelcash/TWZelcashTransactionTests.cpp +++ b/tests/chains/Zelcash/TWZelcashTransactionTests.cpp @@ -5,7 +5,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include "Bitcoin/OutPoint.h" #include "HexCoding.h" diff --git a/tests/chains/Zilliqa/AddressTests.cpp b/tests/chains/Zilliqa/AddressTests.cpp new file mode 100644 index 00000000000..440df29db64 --- /dev/null +++ b/tests/chains/Zilliqa/AddressTests.cpp @@ -0,0 +1,56 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "Zilliqa/Address.h" +#include "Zilliqa/AddressChecksum.h" + +#include + +#include + +namespace TW::Zilliqa::tests { + +TEST(ZilliqaAddress, FromPrivateKey) { + const auto privateKey = + PrivateKey(parse_hex("3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6")); + const auto publicKey = PublicKey(privateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + const auto address = Address(publicKey); + auto expectedAddress = "zil1j8xae6lggm8y63m3y2r7aefu797ze7mhzulnqg"; + + ASSERT_EQ(address.getHrp(), stringForHRP(TWHRPZilliqa)); + ASSERT_EQ(address.string(), expectedAddress); +} + +TEST(ZilliqaAddress, Validation) { + ASSERT_FALSE(Zilliqa::Address::isValid("0x91cddcebe846ce4d47712287eee53cf17c2cfb7")); + ASSERT_FALSE(Zilliqa::Address::isValid("")); + ASSERT_FALSE(Zilliqa::Address::isValid("0x")); + ASSERT_FALSE(Zilliqa::Address::isValid("91cddcebe846ce4d47712287eee53cf17c2cfb7")); + + ASSERT_TRUE(Zilliqa::Address::isValid("zil1fwh4ltdguhde9s7nysnp33d5wye6uqpugufkz7")); +} + +TEST(ZilliqaAddress, Checksum) { + ASSERT_EQ( + checksum(parse_hex("4BAF5FADA8E5DB92C3D3242618C5B47133AE003C")), + "4BAF5faDA8e5Db92C3d3242618c5B47133AE003C"); + ASSERT_EQ( + checksum(parse_hex("448261915A80CDE9BDE7C7A791685200D3A0BF4E")), + "448261915a80cdE9BDE7C7a791685200D3A0bf4E"); + ASSERT_EQ( + checksum(parse_hex("0xDED02FD979FC2E55C0243BD2F52DF022C40ADA1E")), + "Ded02fD979fC2e55c0243bd2F52df022c40ADa1E"); + ASSERT_EQ( + checksum(parse_hex("0x13F06E60297BEA6A3C402F6F64C416A6B31E586E")), + "13F06E60297bea6A3c402F6f64c416A6b31e586e"); + ASSERT_EQ( + checksum(parse_hex("0x1A90C25307C3CC71958A83FA213A2362D859CF33")), + "1a90C25307C3Cc71958A83fa213A2362D859CF33"); +} + +} // namespace TW::Zilliqa::tests diff --git a/tests/chains/Zilliqa/SignatureTests.cpp b/tests/chains/Zilliqa/SignatureTests.cpp new file mode 100644 index 00000000000..37684d1b11b --- /dev/null +++ b/tests/chains/Zilliqa/SignatureTests.cpp @@ -0,0 +1,29 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include "HexCoding.h" +#include "Data.h" +#include +#include + +#include + +using namespace TW; + +TEST(ZilliqaSignature, Signing) { + auto keyData = WRAPD(TWDataCreateWithHexString(STRING("0xafeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5").get())); + auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(keyData.get())); + auto pubKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), true)); + + auto message = "hello schnorr"; + auto messageData = WRAPD(TWDataCreateWithBytes((uint8_t *)message, strnlen(message, 13))); + auto signatureData = WRAPD(TWPrivateKeySignZilliqaSchnorr(privateKey.get(), messageData.get())); + auto signature = data(TWDataBytes(signatureData.get()), TWDataSize(signatureData.get())); + + ASSERT_TRUE(TWPublicKeyVerifyZilliqaSchnorr(pubKey.get(), signatureData.get(), messageData.get())); + EXPECT_EQ(hex(signature), "d166b1ae7892c5ef541461dc12a50214d0681b63d8037cda29a3fe6af8bb973e4ea94624d85bc0010bdc1b38d05198328fae21254adc2bf5feaf2804d54dba55"); +} diff --git a/tests/chains/Zilliqa/SignerTests.cpp b/tests/chains/Zilliqa/SignerTests.cpp new file mode 100644 index 00000000000..18c192e0572 --- /dev/null +++ b/tests/chains/Zilliqa/SignerTests.cpp @@ -0,0 +1,113 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "Zilliqa/Address.h" +#include "Zilliqa/Signer.h" +#include "proto/Zilliqa.pb.h" +#include "uint256.h" + +#include + +namespace TW::Zilliqa::tests { + +TEST(ZilliqaSigner, PreImage) { + auto privateKey = PrivateKey(parse_hex("0E891B9DFF485000C7D1DC22ECF3A583CC50328684321D61947A86E57CF6C638")); + auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + ASSERT_EQ(hex(pubKey.bytes), "034ae47910d58b9bde819c3cffa8de4441955508db00aa2540db8e6bf6e99abc1b"); + + auto amount = uint256_t(15000000000000); + auto gasPrice = uint256_t(1000000000); + auto amountData = store(amount); + auto gasData = store(gasPrice); + auto toAddress = Address(parse_hex("0x9Ca91EB535Fb92Fda5094110FDaEB752eDb9B039")); + + auto input = Proto::SigningInput(); + auto& tx = *input.mutable_transaction(); + auto& transfer = *tx.mutable_transfer(); + transfer.set_amount(amountData.data(), amountData.size()); + + input.set_version(65537); + input.set_nonce(4); + input.set_to(toAddress.string()); + input.set_gas_price(gasData.data(), gasData.size()); + input.set_gas_limit(uint64_t(1)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + Address address; + auto preImage = Signer::getPreImage(input, address); + auto signature = Signer::sign(input).signature(); + + ASSERT_EQ(hex(preImage.begin(), preImage.end()), "0881800410041a149ca91eb535fb92fda5094110fdaeb752edb9b03922230a21034ae47910d58b9bde819c3cffa8de4441955508db00aa2540db8e6bf6e99abc1b2a120a10000000000000000000000da475abf00032120a100000000000000000000000003b9aca003801"); + + ASSERT_TRUE(pubKey.verifyZilliqa(Data(signature.begin(), signature.end()), preImage)); +} + +TEST(ZilliqaSigner, Signing) { + auto privateKey = PrivateKey(parse_hex("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748")); + auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + + // 1 ZIL + auto amount = uint256_t(1000000000000); + auto gasPrice = uint256_t(1000000000); + auto amountData = store(amount); + auto gasData = store(gasPrice); + auto toAddress = Address(parse_hex("0x7FCcaCf066a5F26Ee3AFfc2ED1FA9810Deaa632C")); + + auto input = Proto::SigningInput(); + auto& tx = *input.mutable_transaction(); + auto& transfer = *tx.mutable_transfer(); + transfer.set_amount(amountData.data(), amountData.size()); + + input.set_version(65537); + input.set_nonce(2); + input.set_to(toAddress.string()); + input.set_gas_price(gasData.data(), gasData.size()); + input.set_gas_limit(uint64_t(1)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.signature().begin(), output.signature().end()), "001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268"); + ASSERT_EQ(output.json(), R"({"amount":"1000000000000","code":"","data":"","gasLimit":"1","gasPrice":"1000000000","nonce":2,"pubKey":"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c","signature":"001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268","toAddr":"7FCcaCf066a5F26Ee3AFfc2ED1FA9810Deaa632C","version":65537})"); +} + +TEST(ZilliqaSigner, SigningData) { + // https://viewblock.io/zilliqa/tx/0x6228b3d7e69fc3481b84fd00e892cec359a41654f58948ff7b1b932396b00ad9 + auto privateKey = PrivateKey(parse_hex("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748")); + auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + + // 10 ZIL + auto amount = uint256_t(10000000000000); + auto gasPrice = uint256_t(2000000000); + auto amountData = store(amount); + auto gasData = store(gasPrice); + + std::string json = "{\"_tag\":\"DelegateStake\",\"params\":[{\"type\":\"ByStr20\",\"value\":\"0x122219cCeAb410901e96c3A0e55E46231480341b\",\"vname\":\"ssnaddr\"}]}"; + auto jsonData = Data(json.begin(), json.end()); + + auto input = Proto::SigningInput(); + auto& tx = *input.mutable_transaction(); + auto& raw = *tx.mutable_raw_transaction(); + raw.set_amount(amountData.data(), amountData.size()); + raw.set_data(jsonData.data(), jsonData.size()); + + input.set_version(65537); + input.set_nonce(56); + input.set_to("zil1g029nmzsf36r99vupp4s43lhs40fsscx3jjpuy"); + input.set_gas_price(gasData.data(), gasData.size()); + input.set_gas_limit(uint64_t(5000)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto output = Signer::sign(input); + //ASSERT_EQ(output.json(), R"({"amount":"10000000000000","code":"","data":"{\"_tag\":\"DelegateStake\",\"params\":[{\"type\":\"ByStr20\",\"value\":\"0x122219cCeAb410901e96c3A0e55E46231480341b\",\"vname\":\"ssnaddr\"}]}","gasLimit":"5000","gasPrice":"2000000000","nonce":56,"pubKey":"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c","signature":"437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d","toAddr":"43D459eC504C7432959c086B0ac7F7855E984306","version":65537})"); + ASSERT_EQ(output.json(), "{\"amount\":\"10000000000000\",\"code\":\"\",\"data\":\"{\\\"_tag\\\":\\\"DelegateStake\\\",\\\"params\\\":[{\\\"type\\\":\\\"ByStr20\\\",\\\"value\\\":\\\"0x122219cCeAb410901e96c3A0e55E46231480341b\\\",\\\"vname\\\":\\\"ssnaddr\\\"}]}\",\"gasLimit\":\"5000\",\"gasPrice\":\"2000000000\",\"nonce\":56,\"pubKey\":\"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c\",\"signature\":\"437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d\",\"toAddr\":\"43D459eC504C7432959c086B0ac7F7855E984306\",\"version\":65537}");//win + + ASSERT_EQ(hex(output.signature().begin(), output.signature().end()), "437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d"); +} + +} // namespace TW::Zilliqa::tests \ No newline at end of file diff --git a/tests/chains/Zilliqa/TWAnySignerTests.cpp b/tests/chains/Zilliqa/TWAnySignerTests.cpp new file mode 100644 index 00000000000..acf610c7a29 --- /dev/null +++ b/tests/chains/Zilliqa/TWAnySignerTests.cpp @@ -0,0 +1,48 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "uint256.h" +#include "proto/Zilliqa.pb.h" +#include "TestUtilities.h" +#include +#include + +namespace TW::Zilliqa::tests { + +TEST(TWAnySignerZilliqa, Sign) { + auto input = Proto::SigningInput(); + auto& tx = *input.mutable_transaction(); + auto& transfer = *tx.mutable_transfer(); + auto key = parse_hex("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748"); + auto amount = store(uint256_t(1000000000000)); + auto gasPrice = store(uint256_t(1000000000)); + + input.set_version(65537); + input.set_nonce(2); + input.set_to("zil10lx2eurx5hexaca0lshdr75czr025cevqu83uz"); + input.set_gas_price(gasPrice.data(), gasPrice.size()); + input.set_gas_limit(1); + input.set_private_key(key.data(), key.size()); + transfer.set_amount(amount.data(), amount.size()); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeZilliqa); + + EXPECT_EQ(hex(output.signature()), "001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268"); + EXPECT_EQ(hex(output.json()), "7b22616d6f756e74223a2231303030303030303030303030222c22636f6465223a22222c2264617461223a22222c226761734c696d6974223a2231222c226761735072696365223a2231303030303030303030222c226e6f6e6365223a322c227075624b6579223a22303366623330623139366365336539373635393365636332646132323064636139636465613863383464323337333737303034326139333062383932616330663563222c227369676e6174757265223a223030316661346466303863313161346137396539366536393339396565343865656563633738323331613738623033353561386361373833633737633133393433366533373933346665636332323532656438646163303065323335653232643138343130343631666238393636383563343237303634323733386564323638222c22746f41646472223a2237464363614366303636613546323645653341466663324544314641393831304465616136333243222c2276657273696f6e223a36353533377d"); +} + +TEST(TWAnySignerZilliqa, SignJSON) { + auto json = STRING(R"({"version":65537,"nonce":"2","to":"zil10lx2eurx5hexaca0lshdr75czr025cevqu83uz","gasPrice":"O5rKAA==","gasLimit":"1","transaction":{"transfer":{"amount":"6NSlEAA="}}})"); + auto key = DATA("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748"); + auto result = WRAPS(TWAnySignerSignJSON(json.get(), key.get(), TWCoinTypeZilliqa)); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeZilliqa)); + assertStringsEqual(result, "7b22616d6f756e74223a2231303030303030303030303030222c22636f6465223a22222c2264617461223a22222c226761734c696d6974223a2231222c226761735072696365223a2231303030303030303030222c226e6f6e6365223a322c227075624b6579223a22303366623330623139366365336539373635393365636332646132323064636139636465613863383464323337333737303034326139333062383932616330663563222c227369676e6174757265223a223030316661346466303863313161346137396539366536393339396565343865656563633738323331613738623033353561386361373833633737633133393433366533373933346665636332323532656438646163303065323335653232643138343130343631666238393636383563343237303634323733386564323638222c22746f41646472223a2237464363614366303636613546323645653341466663324544314641393831304465616136333243222c2276657273696f6e223a36353533377d"); +} + +} // namespace TW::Zilliqa::tests \ No newline at end of file diff --git a/tests/chains/Zilliqa/TWCoinTypeTests.cpp b/tests/chains/Zilliqa/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..d0d51a694b2 --- /dev/null +++ b/tests/chains/Zilliqa/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWZilliqaCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeZilliqa)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("t123")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeZilliqa, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("a12")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeZilliqa, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeZilliqa)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeZilliqa)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeZilliqa), 12); + ASSERT_EQ(TWBlockchainZilliqa, TWCoinTypeBlockchain(TWCoinTypeZilliqa)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeZilliqa)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeZilliqa)); + assertStringsEqual(symbol, "ZIL"); + assertStringsEqual(txUrl, "https://viewblock.io/zilliqa/tx/t123"); + assertStringsEqual(accUrl, "https://viewblock.io/zilliqa/address/a12"); + assertStringsEqual(id, "zilliqa"); + assertStringsEqual(name, "Zilliqa"); +} diff --git a/tests/Zilliqa/TWZilliqaAddressTests.cpp b/tests/chains/Zilliqa/TWZilliqaAddressTests.cpp similarity index 97% rename from tests/Zilliqa/TWZilliqaAddressTests.cpp rename to tests/chains/Zilliqa/TWZilliqaAddressTests.cpp index e2e19630cbe..2677af67c45 100644 --- a/tests/Zilliqa/TWZilliqaAddressTests.cpp +++ b/tests/chains/Zilliqa/TWZilliqaAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "../interface/TWTestUtilities.h" +#include "TestUtilities.h" #include #include diff --git a/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp b/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..95b4854e53e --- /dev/null +++ b/tests/chains/ZkSyncV2/TWCoinTypeTests.cpp @@ -0,0 +1,36 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include +#include + +namespace TW::TWZksync::tests { + +TEST(TWZksyncCoinType, TWCoinType) { + const auto coin = TWCoinTypeZksync; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto chainId = WRAPS(TWCoinTypeChainId(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0xb526861291c0335435e3c976e672a464b70762e54d7167409fb4f66e374ed708")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x970978989a51790ee591b2a54f92c7cd9cdc2f88")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "zksync"); + assertStringsEqual(name, "zkSync v2"); + assertStringsEqual(symbol, "ETH"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0x0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0x0); + assertStringsEqual(chainId, "280"); + assertStringsEqual(txUrl, "https://zksync2-testnet.zkscan.io/tx/0xb526861291c0335435e3c976e672a464b70762e54d7167409fb4f66e374ed708"); + assertStringsEqual(accUrl, "https://zksync2-testnet.zkscan.io/address/0x970978989a51790ee591b2a54f92c7cd9cdc2f88"); +} + +} // namespace TW::TWZksync::tests diff --git a/tests/chains/xDai/TWCoinTypeTests.cpp b/tests/chains/xDai/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..c49ec7395f1 --- /dev/null +++ b/tests/chains/xDai/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "TestUtilities.h" +#include +#include + + +TEST(TWxDaiCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeXDai)); + auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x936798a1ef607c9e856d7861b15999c770c06f0887c4fc1f6acbf3bef09899c1")); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeXDai, txId.get())); + auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x12d61a95CF55e18D267C2F1AA67d8e42ae1368f8")); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeXDai, accId.get())); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeXDai)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeXDai)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeXDai), 18); + ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeXDai)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeXDai)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeXDai)); + assertStringsEqual(symbol, "xDAI"); + assertStringsEqual(txUrl, "https://blockscout.com/xdai/mainnet/tx/0x936798a1ef607c9e856d7861b15999c770c06f0887c4fc1f6acbf3bef09899c1"); + assertStringsEqual(accUrl, "https://blockscout.com/xdai/mainnet/address/0x12d61a95CF55e18D267C2F1AA67d8e42ae1368f8"); + assertStringsEqual(id, "xdai"); + assertStringsEqual(name, "Gnosis Chain"); +} diff --git a/tests/common/AnyAddressTests.cpp b/tests/common/AnyAddressTests.cpp new file mode 100644 index 00000000000..bc7dc5af2b2 --- /dev/null +++ b/tests/common/AnyAddressTests.cpp @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "AnyAddress.h" +#include "HexCoding.h" + +#include + +namespace TW::tests { + +constexpr auto ANY_ADDRESS_TEST_ADDRESS = "bc1qcj2vfjec3c3luf9fx9vddnglhh9gawmncmgxhz"; +constexpr auto ANY_ADDRESS_TEST_PUBKEY = "02753f5c275e1847ba4d2fd3df36ad00af2e165650b35fe3991e9c9c46f68b12bc"; + +TEST(AnyAddress, createFromString) { + std::unique_ptr addr(AnyAddress::createAddress(ANY_ADDRESS_TEST_ADDRESS, TWCoinTypeBitcoin)); + EXPECT_EQ(ANY_ADDRESS_TEST_ADDRESS, addr->address); +} + +TEST(AnyAddress, createFromPubKey) { + const Data key = parse_hex(ANY_ADDRESS_TEST_PUBKEY); + PublicKey publicKey(key, TWPublicKeyTypeSECP256k1); + std::unique_ptr addr(AnyAddress::createAddress(publicKey, TWCoinTypeBitcoin)); + EXPECT_EQ(ANY_ADDRESS_TEST_ADDRESS, addr->address); +} + +TEST(AnyAddress, createFromWrongString) { + std::unique_ptr addr(AnyAddress::createAddress("1aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax", TWCoinTypeBitcoin)); + EXPECT_EQ(nullptr, addr); +} + +} // namespace TW::tests diff --git a/tests/common/BCSTests.cpp b/tests/common/BCSTests.cpp new file mode 100644 index 00000000000..fee7c0805df --- /dev/null +++ b/tests/common/BCSTests.cpp @@ -0,0 +1,165 @@ +// Copyright © 2017-2022 Trust Wallet. +// Created by Clément Doumergue +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "BCS.h" +#include "HexCoding.h" + +#include + +namespace TW::BCS::tests { + +TEST(BCS, Integral) { + Serializer os; + os << uint32_t(0xAABBCCDD); + ASSERT_EQ(os.bytes, parse_hex("0xDDCCBBAA")); + + os.clear(); + os << int32_t(-305419896); + ASSERT_EQ(os.bytes, parse_hex("0x88A9CBED")); +} + +TEST(BCS, ULEB128) { + Serializer os; + os << uleb128{0x00000001}; + ASSERT_EQ(os.bytes, parse_hex("0x01")); + + os.clear(); + os << uleb128{0x00000080}; + ASSERT_EQ(os.bytes, parse_hex("0x8001")); + + os.clear(); + os << uleb128{0x00004000}; + ASSERT_EQ(os.bytes, parse_hex("0x808001")); + + os.clear(); + os << uleb128{0x00200000}; + ASSERT_EQ(os.bytes, parse_hex("0x80808001")); + + os.clear(); + os << uleb128{0x10000000}; + ASSERT_EQ(os.bytes, parse_hex("0x8080808001")); + + os.clear(); + os << uleb128{0x0000250F}; + ASSERT_EQ(os.bytes, parse_hex("0x8F4A")); +} + +TEST(BCS, String) { + Serializer os; + os << std::string_view("abcd"); + ASSERT_EQ(os.bytes, parse_hex("0x0461626364")); + + os.clear(); + os << std::string_view(""); + ASSERT_EQ(os.bytes, parse_hex("0x00")); +} + +TEST(BCS, Optional) { + Serializer os; + os << std::optional{0xBBCCDD}; + ASSERT_EQ(os.bytes, parse_hex("0x01DDCCBB00")); + + os.clear(); + os << std::optional{}; + ASSERT_EQ(os.bytes, parse_hex("0x00")); + + os.clear(); + os << std::nullopt; + ASSERT_EQ(os.bytes, parse_hex("0x00")); +} + +TEST(BCS, Tuple) { + Serializer os; + os << std::tuple{uint16_t(1), 'a'}; + ASSERT_EQ(os.bytes, parse_hex("0x010061")); + + os.clear(); + os << std::tuple{std::optional{123}, std::string_view("abcd"), uint8_t(0x0E)}; + ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); + + os.clear(); + os << std::tuple{}; + ASSERT_EQ(os.bytes, (Data{})); +} + +TEST(BCS, Pair) { + Serializer os; + os << std::pair{uint16_t(1), 'a'}; + ASSERT_EQ(os.bytes, parse_hex("0x010061")); + + os.clear(); + os << std::pair{std::optional{123}, std::string_view("abcd")}; + ASSERT_EQ(os.bytes, parse_hex("0x017b0000000461626364")); +} +/* +struct my_struct { + std::optional first; + std::string_view second; + uint8_t third; +}; + +TEST(BCS, Struct) { + Serializer os; + os << my_struct{{123}, "abcd", 0x0E}; + ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); +} +*/ +TEST(BCS, Variant) { + using V = std::variant; + + Serializer os; + os << V{uint32_t(1)}; + ASSERT_EQ(os.bytes, parse_hex("0x0001000000")); + + os.clear(); + os << V{char('a')}; + ASSERT_EQ(os.bytes, parse_hex("0x0161")); + + os.clear(); + os << V{true}; + ASSERT_EQ(os.bytes, parse_hex("0x0201")); +} + +TEST(BCS, Map) { + Serializer os; + os << std::map{{'a', 0}, {'b', 1}, {'c', 2}}; + ASSERT_EQ(os.bytes, parse_hex("0x03610062016302")); +} + +class my_number { +private: + int value; + +public: + explicit my_number(int value) noexcept + : value(value) { + } + + [[nodiscard]] auto get_value() const { + return value; + } +}; + +Serializer& operator<<(Serializer& stream, my_number n) noexcept { + return stream << n.get_value(); +} + +static_assert(CustomSerializable, "my_number does not model the CustomSerializable concept"); + +TEST(BCS, Custom) { + Serializer os; + os << my_number{0xBBCCDD}; + ASSERT_EQ(os.bytes, parse_hex("0xDDCCBB00")); +} + +TEST(BCS, Vector) { + Serializer os; + os << std::vector{1}; + ASSERT_EQ(os.bytes, parse_hex("0101")); +} + +} diff --git a/tests/Base64Tests.cpp b/tests/common/Base64Tests.cpp similarity index 95% rename from tests/Base64Tests.cpp rename to tests/common/Base64Tests.cpp index 9a918d7fd46..64787ffaaf8 100644 --- a/tests/Base64Tests.cpp +++ b/tests/common/Base64Tests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -10,8 +10,7 @@ #include -using namespace TW; -using namespace TW::Base64; +namespace TW::Base64::tests { TEST(Base64, encode) { auto encoded = encode(data("Hello, world!")); @@ -49,7 +48,7 @@ TEST(Base64, decode) { TEST(Base64, UrlFormat) { const std::string const1 = "11003faa8556289975ec991ac9994dfb613abec4ea000d5094e6379080f594e559b330b8"; - + // Encoded string has both special characters auto encoded = encode(parse_hex(const1)); EXPECT_EQ("EQA/qoVWKJl17JkayZlN+2E6vsTqAA1QlOY3kID1lOVZszC4", encoded); @@ -61,3 +60,5 @@ TEST(Base64, UrlFormat) { decoded = decodeBase64Url("EQA_qoVWKJl17JkayZlN-2E6vsTqAA1QlOY3kID1lOVZszC4"); EXPECT_EQ(const1, hex(decoded)); } + +} // namespace TW::Base64::tests diff --git a/tests/BaseEncoding.cpp b/tests/common/BaseEncoding.cpp similarity index 86% rename from tests/BaseEncoding.cpp rename to tests/common/BaseEncoding.cpp index 65763eb404f..48003ec5fad 100644 --- a/tests/BaseEncoding.cpp +++ b/tests/common/BaseEncoding.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -9,18 +9,15 @@ #include -using namespace TW; -using namespace TW::Base32; +namespace TW::Base32::tests { -void TestBase32Encode(const char* decoded_hex, const char* expected_encoded_in, const char* alphabet_in = nullptr) -{ +void TestBase32Encode(const char* decoded_hex, const char* expected_encoded_in, const char* alphabet_in = nullptr) { auto decoded = parse_hex(std::string(decoded_hex)); auto encoded = encode(decoded, alphabet_in); ASSERT_EQ(std::string(expected_encoded_in), encoded); } -void TestBase32Decode(const char* encoded_in, const char* expected_decoded_hex, const char* alphabet_in = nullptr) -{ +void TestBase32Decode(const char* encoded_in, const char* expected_decoded_hex, const char* alphabet_in = nullptr) { Data decoded; bool res = decode(std::string(encoded_in), decoded, alphabet_in); ASSERT_TRUE(res); @@ -33,7 +30,7 @@ TEST(Base32, Encode) { TestBase32Encode("010203", "AEBAG"); TestBase32Encode("", ""); TestBase32Encode( - "48450c2745890def7da06fc2551f912a14f9fc581c12db6e4d6f73f2fd0b2ad50df3d396", + "48450c2745890def7da06fc2551f912a14f9fc581c12db6e4d6f73f2fd0b2ad50df3d396", "JBCQYJ2FREG667NAN7BFKH4RFIKPT7CYDQJNW3SNN5Z7F7ILFLKQ346TSY"); TestBase32Encode( "3dd160d60673bd9b13adc25dad5d988d0d9f4ccdbe95a2122f9ef28b3ce4e89693074620", @@ -49,7 +46,7 @@ TEST(Base32, Decode) { TestBase32Decode("", ""); TestBase32Decode( "JBCQYJ2FREG667NAN7BFKH4RFIKPT7CYDQJNW3SNN5Z7F7ILFLKQ346TSY", - "48450c2745890def7da06fc2551f912a14f9fc581c12db6e4d6f73f2fd0b2ad50df3d396"); + "48450c2745890def7da06fc2551f912a14f9fc581c12db6e4d6f73f2fd0b2ad50df3d396"); TestBase32Decode( "HXIWBVQGOO6ZWE5NYJO22XMYRUGZ6TGNX2K2EERPT3ZIWPHE5CLJGB2GEA", "3dd160d60673bd9b13adc25dad5d988d0d9f4ccdbe95a2122f9ef28b3ce4e89693074620"); @@ -66,7 +63,9 @@ TEST(Base32, EncodeNimiq) { TEST(Base32, DecodeInvalid) { Data decoded; - ASSERT_FALSE(decode("+-", decoded)); // invalid characters - ASSERT_FALSE(decode("A", decoded)); // invalid odd length + ASSERT_FALSE(decode("+-", decoded)); // invalid characters + ASSERT_FALSE(decode("A", decoded)); // invalid odd length ASSERT_FALSE(decode("ABC", decoded)); // invalid odd length } + +} // namespace TW::Base32::tests diff --git a/tests/Bech32AddressTests.cpp b/tests/common/Bech32AddressTests.cpp similarity index 90% rename from tests/Bech32AddressTests.cpp rename to tests/common/Bech32AddressTests.cpp index ec29247288d..7152fd9ce78 100644 --- a/tests/Bech32AddressTests.cpp +++ b/tests/common/Bech32AddressTests.cpp @@ -104,33 +104,33 @@ TEST(Bech32Address, FromPublicKey) { auto privateKey = PrivateKey(parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832")); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); ASSERT_EQ(hex(publicKey.bytes.begin(), publicKey.bytes.end()), "026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502"); - auto address = Bech32Address("bnb", HASHER_SHA2_RIPEMD, publicKey); + auto address = Bech32Address("bnb", Hash::HasherSha256ripemd, publicKey); ASSERT_EQ("bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", address.string()); } { auto privateKey = PrivateKey(parse_hex("80e81ea269e66a0a05b11236df7919fb7fbeedba87452d667489d7403a02f005")); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); ASSERT_EQ(hex(publicKey.bytes.begin(), publicKey.bytes.end()), "0257286ec3f37d33557bbbaa000b27744ac9023aa9967cae75a181d1ff91fa9dc5"); - auto address = Bech32Address("cosmos", HASHER_SHA2_RIPEMD, publicKey); + auto address = Bech32Address("cosmos", Hash::HasherSha256ripemd, publicKey); ASSERT_EQ(address.string(), "cosmos1hsk6jryyqjfhp5dhc55tc9jtckygx0eph6dd02"); } { auto privateKey = PrivateKey(parse_hex("e2f88b4974ae763ca1c2db49218802c2e441293a09eaa9ab681779e05d1b7b94")); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); - auto address = Bech32Address("one", HASHER_SHA3K, publicKey); + auto address = Bech32Address("one", Hash::HasherKeccak256, publicKey); ASSERT_EQ(address.string(), "one1a50tun737ulcvwy0yvve0pvu5skq0kjargvhwe"); } { auto privateKey = PrivateKey(parse_hex("0806c458b262edd333a191e92f561aff338211ee3e18ab315a074a2d82aa343f")); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); - auto address = Bech32Address("io", HASHER_SHA3K, publicKey); + auto address = Bech32Address("io", Hash::HasherKeccak256, publicKey); ASSERT_EQ(address.string(), "io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j"); } { const auto privateKey = PrivateKey(parse_hex("3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6")); auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); ASSERT_EQ(hex(publicKey.bytes.begin(), publicKey.bytes.end()), "02b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d"); - const auto address = Bech32Address("zil", HASHER_SHA2, publicKey); + const auto address = Bech32Address("zil", Hash::HasherSha256, publicKey); ASSERT_EQ("zil", address.getHrp()); ASSERT_EQ("zil1j8xae6lggm8y63m3y2r7aefu797ze7mhzulnqg", address.string()); } @@ -143,10 +143,10 @@ TEST(Bech32Address, Hashes) { auto publicKey1 = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); ASSERT_EQ("02b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d", hex(publicKey1.bytes.begin(), publicKey1.bytes.end())); - const auto address1 = Bech32Address("hrp", HASHER_SHA2_RIPEMD, publicKey1); + const auto address1 = Bech32Address("hrp", Hash::HasherSha256ripemd, publicKey1); ASSERT_EQ("hrp186zwn9h0z9fyvwfqs4jl92cw3kexusm4xw6ptp", address1.string()); - const auto address2 = Bech32Address("hrp", HASHER_SHA2, publicKey1); + const auto address2 = Bech32Address("hrp", Hash::HasherSha256, publicKey1); ASSERT_EQ("hrp1j8xae6lggm8y63m3y2r7aefu797ze7mhgfetvu", address2.string()); auto publicKey2 = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); @@ -154,7 +154,7 @@ TEST(Bech32Address, Hashes) { "04b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d83c307736082c09f1f22328e0fbeab40ddd198cf0f70fcdaa1e5969ca400c098", hex(publicKey2.bytes.begin(), publicKey2.bytes.end())); - const auto address3 = Bech32Address("hrp", HASHER_SHA3K, publicKey2); + const auto address3 = Bech32Address("hrp", Hash::HasherKeccak256, publicKey2); ASSERT_EQ("hrp17hff3s97m5uxpjcdq3nzqxxatt8cmumnsf03su", address3.string()); } @@ -165,10 +165,10 @@ TEST(Bech32Address, Prefixes) { auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); ASSERT_EQ("02b65744e8bd0ba7666468abaff2aeb862c88a25ed605e0153100aa8f2661c1c3d", hex(publicKey.bytes.begin(), publicKey.bytes.end())); - const auto address1 = Bech32Address("hrpone", HASHER_SHA2_RIPEMD, publicKey); + const auto address1 = Bech32Address("hrpone", Hash::HasherSha256ripemd, publicKey); ASSERT_EQ("hrpone186zwn9h0z9fyvwfqs4jl92cw3kexusm47das6p", address1.string()); - const auto address2 = Bech32Address("hrptwo", HASHER_SHA2_RIPEMD, publicKey); + const auto address2 = Bech32Address("hrptwo", Hash::HasherSha256ripemd, publicKey); ASSERT_EQ("hrptwo186zwn9h0z9fyvwfqs4jl92cw3kexusm4qzr8p7", address2.string()); - const auto address3 = Bech32Address("hrpthree", HASHER_SHA2_RIPEMD, publicKey); + const auto address3 = Bech32Address("hrpthree", Hash::HasherSha256ripemd, publicKey); ASSERT_EQ("hrpthree186zwn9h0z9fyvwfqs4jl92cw3kexusm4wuqkvd", address3.string()); } diff --git a/tests/Bech32Tests.cpp b/tests/common/Bech32Tests.cpp similarity index 99% rename from tests/Bech32Tests.cpp rename to tests/common/Bech32Tests.cpp index adb569ec333..9c8b0b32baa 100644 --- a/tests/Bech32Tests.cpp +++ b/tests/common/Bech32Tests.cpp @@ -82,7 +82,7 @@ TEST(Bech32, decode) { auto res = Bech32::decode(td.encoded); if (!td.isValid && !td.isValidM) { EXPECT_EQ(std::get<0>(res), ""); - EXPECT_EQ(std::get<1>(res).size(), 0); + EXPECT_EQ(std::get<1>(res).size(), 0ul); } else { if (td.isValid) { EXPECT_EQ(std::get<2>(res), Bech32::ChecksumVariant::Bech32); diff --git a/tests/BinaryCodingTests.cpp b/tests/common/BinaryCodingTests.cpp similarity index 98% rename from tests/BinaryCodingTests.cpp rename to tests/common/BinaryCodingTests.cpp index 138cbb7bea8..1627a90951c 100644 --- a/tests/BinaryCodingTests.cpp +++ b/tests/common/BinaryCodingTests.cpp @@ -47,10 +47,6 @@ TEST(BinaryCodingTests, varIntSize) { } } -void testEncodeVarInt(uint64_t input, const std::string& expectedEncoded) { - -} - TEST(BinaryCodingTests, encodeAndDecodeVarInt) { vector> tests = { {0, "00"}, diff --git a/tests/common/CborTests.cpp b/tests/common/CborTests.cpp new file mode 100644 index 00000000000..592b0ec9322 --- /dev/null +++ b/tests/common/CborTests.cpp @@ -0,0 +1,523 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cbor.h" + +#include "HexCoding.h" + +#include + +namespace TW::Cbor::tests { + +using namespace std; + +// clang-format off + +TEST(Cbor, EncSample1) { + EXPECT_EQ( + "8205a26178186461793831", + hex(Encode::array({ + Encode::uint(5), + Encode::map({ + make_pair(Encode::string("x"), Encode::uint(100)), + make_pair(Encode::string("y"), Encode::negInt(50)), + }), + }) + .encoded()) + ); +} + +TEST(Cbor, EncUInt) { + EXPECT_EQ("00", hex(Encode::uint(0).encoded())); + EXPECT_EQ("01", hex(Encode::uint(1).encoded())); + EXPECT_EQ("0a", hex(Encode::uint(10).encoded())); + EXPECT_EQ("17", hex(Encode::uint(23).encoded())); + EXPECT_EQ("1818", hex(Encode::uint(24).encoded())); + EXPECT_EQ("1819", hex(Encode::uint(25).encoded())); + EXPECT_EQ("181a", hex(Encode::uint(26).encoded())); + EXPECT_EQ("181b", hex(Encode::uint(27).encoded())); + EXPECT_EQ("181c", hex(Encode::uint(28).encoded())); + EXPECT_EQ("181d", hex(Encode::uint(29).encoded())); + EXPECT_EQ("181e", hex(Encode::uint(30).encoded())); + EXPECT_EQ("181f", hex(Encode::uint(31).encoded())); + EXPECT_EQ("1820", hex(Encode::uint(32).encoded())); + EXPECT_EQ("183f", hex(Encode::uint(0x3f).encoded())); + EXPECT_EQ("1840", hex(Encode::uint(0x40).encoded())); + EXPECT_EQ("1864", hex(Encode::uint(100).encoded())); + EXPECT_EQ("187f", hex(Encode::uint(0x7f).encoded())); + EXPECT_EQ("1880", hex(Encode::uint(0x80).encoded())); + EXPECT_EQ("18ff", hex(Encode::uint(0xff).encoded())); + EXPECT_EQ("190100", hex(Encode::uint(0x0100).encoded())); + EXPECT_EQ("1903e8", hex(Encode::uint(1000).encoded())); + EXPECT_EQ("198765", hex(Encode::uint(0x8765).encoded())); + EXPECT_EQ("19ffff", hex(Encode::uint(0xffff).encoded())); + EXPECT_EQ("1a00010000", hex(Encode::uint(0x00010000).encoded())); + EXPECT_EQ("1a000f4240", hex(Encode::uint(1000000).encoded())); + EXPECT_EQ("1a00800000", hex(Encode::uint(0x00800000).encoded())); + EXPECT_EQ("1a87654321", hex(Encode::uint(0x87654321).encoded())); + EXPECT_EQ("1affffffff", hex(Encode::uint(0xffffffff).encoded())); + EXPECT_EQ("1b0000000100000000", hex(Encode::uint(0x0000000100000000).encoded())); + EXPECT_EQ("1b000000e8d4a51000", hex(Encode::uint(1000000000000).encoded())); + EXPECT_EQ("1b876543210fedcba9", hex(Encode::uint(0x876543210fedcba9).encoded())); + EXPECT_EQ("1bffffffffffffffff", hex(Encode::uint(0xffffffffffffffff).encoded())); +} + +TEST(Cbor, EncNegInt) { + EXPECT_EQ("20", hex(Encode::negInt(1).encoded())); // -1 + EXPECT_EQ("00", hex(Encode::negInt(0).encoded())); // 0 + EXPECT_EQ("21", hex(Encode::negInt(2).encoded())); + EXPECT_EQ("28", hex(Encode::negInt(9).encoded())); + EXPECT_EQ("37", hex(Encode::negInt(24).encoded())); + EXPECT_EQ("3818", hex(Encode::negInt(25).encoded())); + EXPECT_EQ("38ff", hex(Encode::negInt(0x0100).encoded())); + EXPECT_EQ("390100", hex(Encode::negInt(0x0101).encoded())); + EXPECT_EQ("39ffff", hex(Encode::negInt(0x10000).encoded())); + EXPECT_EQ("3a00010000", hex(Encode::negInt(0x00010001).encoded())); + EXPECT_EQ("3a00800000", hex(Encode::negInt(0x00800001).encoded())); + EXPECT_EQ("3a87654321", hex(Encode::negInt(0x87654322).encoded())); + EXPECT_EQ("3affffffff", hex(Encode::negInt(0x100000000).encoded())); + EXPECT_EQ("3b0000000100000000", hex(Encode::negInt(0x0000000100000001).encoded())); + EXPECT_EQ("3b876543210fedcba9", hex(Encode::negInt(0x876543210fedcbaa).encoded())); + EXPECT_EQ("3bfffffffffffffffe", hex(Encode::negInt(0xffffffffffffffff).encoded())); + + EXPECT_EQ("-9", Decode(Encode::negInt(9).encoded()).dumpToString()); +} + +TEST(Cbor, EncString) { + EXPECT_EQ("60", hex(Encode::string("").encoded())); + EXPECT_EQ("6141", hex(Encode::string("A").encoded())); + EXPECT_EQ("656162636465", hex(Encode::string("abcde").encoded())); + Data long258(258); + EXPECT_EQ( + "590102000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + hex(Encode::bytes(long258).encoded())); + + EXPECT_EQ("\"abcde\"", Decode(Encode::string("abcde").encoded()).dumpToString()); + EXPECT_EQ("h\"6162636465\"", Decode(Encode::bytes(parse_hex("6162636465")).encoded()).dumpToString()); +} + +TEST(Cbor, EncTag) { + { + Data cbor = Encode::tag(5, Encode::uint(6)).encoded(); + EXPECT_EQ("c506", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("tag 5 6", Decode(cbor).dumpToString()); + } + EXPECT_EQ("d94321191234", hex(Encode::tag(0x4321, Encode::uint(0x1234)).encoded())); +} + +TEST(Cbor, EncNull) { + { + Data cbor = Encode::null().encoded(); + EXPECT_EQ("f6", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("null", Decode(cbor).dumpToString()); + } +} + +TEST(Cbor, EncInvalid) { + Data invalid = parse_hex("5b99999999999999991234"); // invalid very looong string + EXPECT_FALSE(Decode(invalid).isValid()); + + try { + Encode::fromRaw(invalid); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, DecInt) { + EXPECT_EQ(0ul, Decode(parse_hex("00")).getValue()); + EXPECT_EQ(1ul, Decode(parse_hex("01")).getValue()); + EXPECT_EQ(10ul, Decode(parse_hex("0a")).getValue()); + EXPECT_EQ(23ul, Decode(parse_hex("17")).getValue()); + EXPECT_EQ(24ul, Decode(parse_hex("1818")).getValue()); + EXPECT_EQ(25ul, Decode(parse_hex("1819")).getValue()); + EXPECT_EQ(26ul, Decode(parse_hex("181a")).getValue()); + EXPECT_EQ(27ul, Decode(parse_hex("181b")).getValue()); + EXPECT_EQ(28ul, Decode(parse_hex("181c")).getValue()); + EXPECT_EQ(29ul, Decode(parse_hex("181d")).getValue()); + EXPECT_EQ(30ul, Decode(parse_hex("181e")).getValue()); + EXPECT_EQ(31ul, Decode(parse_hex("181f")).getValue()); + EXPECT_EQ(32ul, Decode(parse_hex("1820")).getValue()); + EXPECT_EQ(0x3ful, Decode(parse_hex("183f")).getValue()); + EXPECT_EQ(0x40ul, Decode(parse_hex("1840")).getValue()); + EXPECT_EQ(100ul, Decode(parse_hex("1864")).getValue()); + EXPECT_EQ(0x7ful, Decode(parse_hex("187f")).getValue()); + EXPECT_EQ(0x80ul, Decode(parse_hex("1880")).getValue()); + EXPECT_EQ(0xfful, Decode(parse_hex("18ff")).getValue()); + EXPECT_EQ(0x100ul, Decode(parse_hex("190100")).getValue()); + EXPECT_EQ(1000ul, Decode(parse_hex("1903e8")).getValue()); + EXPECT_EQ(0x8765ul, Decode(parse_hex("198765")).getValue()); + EXPECT_EQ(0xfffful, Decode(parse_hex("19ffff")).getValue()); + EXPECT_EQ(0x00010000ul, Decode(parse_hex("1a00010000")).getValue()); + EXPECT_EQ(1000000ul, Decode(parse_hex("1a000f4240")).getValue()); + EXPECT_EQ(0x00800000ul, Decode(parse_hex("1a00800000")).getValue()); + EXPECT_EQ(0x87654321, Decode(parse_hex("1a87654321")).getValue()); + EXPECT_EQ(0xffffffff, Decode(parse_hex("1affffffff")).getValue()); + EXPECT_EQ(0x0000000100000000ul, Decode(parse_hex("1b0000000100000000")).getValue()); + EXPECT_EQ(1000000000000ul, Decode(parse_hex("1b000000e8d4a51000")).getValue()); + EXPECT_EQ(0x876543210fedcba9, Decode(parse_hex("1b876543210fedcba9")).getValue()); + EXPECT_EQ(0xffffffffffffffff, Decode(parse_hex("1bffffffffffffffff")).getValue()); +} + +TEST(Cbor, DecMinortypeInvalid) { + EXPECT_FALSE(Decode(parse_hex("1c")).isValid()); // 28 unused + EXPECT_FALSE(Decode(parse_hex("1d")).isValid()); // 29 unused + EXPECT_FALSE(Decode(parse_hex("1e")).isValid()); // 30 unused + EXPECT_TRUE(Decode(parse_hex("1b0000000000000000")).isValid()); +} + +TEST(Cbor, DecArray3) { + Decode cbor = Decode(parse_hex("83010203")); + EXPECT_EQ(3ul, cbor.getArrayElements().size()); +} + +TEST(Cbor, DecArrayNested) { + Data d1 = parse_hex("8301820203820405"); + Decode cbor = Decode(d1); + EXPECT_EQ(3ul, cbor.getArrayElements().size()); + + EXPECT_EQ(1ul, cbor.getArrayElements()[0].getValue()); + EXPECT_EQ(2ul, cbor.getArrayElements()[1].getArrayElements().size()); + EXPECT_EQ(2ul, cbor.getArrayElements()[1].getArrayElements()[0].getValue()); + EXPECT_EQ(3ul, cbor.getArrayElements()[1].getArrayElements()[1].getValue()); + EXPECT_EQ(2ul, cbor.getArrayElements()[2].getArrayElements().size()); + EXPECT_EQ(4ul, cbor.getArrayElements()[2].getArrayElements()[0].getValue()); + EXPECT_EQ(5ul, cbor.getArrayElements()[2].getArrayElements()[1].getValue()); +} + +TEST(Cbor, DecEncoded) { + // sometimes getting the encoded version is useful during decoding too + Decode cbor = Decode(parse_hex("8301820203820405")); + EXPECT_EQ(3ul, cbor.getArrayElements().size()); + EXPECT_EQ("820203", hex(cbor.getArrayElements()[1].encoded())); + EXPECT_EQ("820405", hex(cbor.getArrayElements()[2].encoded())); +} + +TEST(Cbor, DecMemoryref) { + // make sure reference to data is valid even if parent object has been destroyed + Decode* cbor = new Decode(parse_hex("828301020383010203")); + auto elems = cbor->getArrayElements(); + // delete parent + delete cbor; + // also do some new allocation + Decode* dummy = new Decode(parse_hex("5555555555555555")); + // work with the child references + EXPECT_EQ(2ul, elems.size()); + EXPECT_EQ(3ul, elems[0].getArrayElements().size()); + EXPECT_EQ(3ul, elems[1].getArrayElements().size()); + delete dummy; +} + +TEST(Cbor, GetValue) { + EXPECT_EQ(5ul, Decode(parse_hex("05")).getValue()); +} + +TEST(Cbor, GetValueInvalid) { + try { + Decode(parse_hex("83010203")).getValue(); // array + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, GetString) { + // bytes/string and getString/getBytes work in all combinations + EXPECT_EQ("abcde", Decode(parse_hex("656162636465")).getString()); + EXPECT_EQ("abcde", Decode(parse_hex("456162636465")).getString()); + EXPECT_EQ("6162636465", hex(Decode(parse_hex("656162636465")).getBytes())); + EXPECT_EQ("6162636465", hex(Decode(parse_hex("456162636465")).getBytes())); +} + +TEST(Cbor, GetStringInvalidType) { + try { + Decode cbor = Decode(Encode::uint(5).encoded()); + cbor.getBytes(); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, GetStringInvalidTooShort) { + try { + Decode cbor = Decode(parse_hex("65616263")); // too short + cbor.getBytes(); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, ArrayEmpty) { + Data cbor = Encode::array({}).encoded(); + + EXPECT_EQ("80", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("[]", Decode(cbor).dumpToString()); + + Decode decode(cbor); + EXPECT_EQ(0ul, decode.getArrayElements().size()); +} + +TEST(Cbor, Array3) { + Data cbor = Encode::array({ + Encode::uint(1), + Encode::uint(2), + Encode::uint(3), + }).encoded(); + + EXPECT_EQ("83010203", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("[1, 2, 3]", Decode(cbor).dumpToString()); + + Decode decode(cbor); + EXPECT_EQ(3ul, decode.getArrayElements().size()); + EXPECT_EQ(1ul, decode.getArrayElements()[0].getValue()); + EXPECT_EQ(2ul, decode.getArrayElements()[1].getValue()); + EXPECT_EQ(3ul, decode.getArrayElements()[2].getValue()); +} + +TEST(Cbor, ArrayNested) { + Data cbor = Encode::array({ + Encode::uint(1), + Encode::array({ + Encode::uint(2), + Encode::uint(3), + }), + Encode::array({ + Encode::uint(4), + Encode::uint(5), + }), + }).encoded(); + + EXPECT_EQ("8301820203820405", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("[1, [2, 3], [4, 5]]", Decode(cbor).dumpToString()); + + Decode decode(cbor); + EXPECT_EQ(3ul, decode.getArrayElements().size()); + EXPECT_EQ(1ul, decode.getArrayElements()[0].getValue()); + EXPECT_EQ(2ul, decode.getArrayElements()[1].getArrayElements().size()); + EXPECT_EQ(2ul, decode.getArrayElements()[1].getArrayElements()[0].getValue()); + EXPECT_EQ(3ul, decode.getArrayElements()[1].getArrayElements()[1].getValue()); + EXPECT_EQ(2ul, decode.getArrayElements()[2].getArrayElements().size()); + EXPECT_EQ(4ul, decode.getArrayElements()[2].getArrayElements()[0].getValue()); + EXPECT_EQ(5ul, decode.getArrayElements()[2].getArrayElements()[1].getValue()); +} + +TEST(Cbor, Array25) { + auto elem = vector(); + for (int i = 1; i <= 25; ++i) { + elem.push_back(Encode::uint(i)); + } + Data cbor = Encode::array(elem).encoded(); + + EXPECT_EQ("98190102030405060708090a0b0c0d0e0f101112131415161718181819", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]", + Decode(cbor).dumpToString()); + + Decode decode(cbor); + EXPECT_EQ(25ul, decode.getArrayElements().size()); + for (auto i = 1ul; i <= 25; ++i) { + EXPECT_EQ(i, decode.getArrayElements()[i - 1].getValue()); + } +} + +TEST(Cbor, MapEmpty) { + Data cbor = Encode::map({}).encoded(); + + EXPECT_EQ("a0", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("{}", Decode(cbor).dumpToString()); + + Decode decode(cbor); + EXPECT_EQ(0ul, decode.getMapElements().size()); +} + +TEST(Cbor, Map2Num) { + Data cbor = Encode::map({ + make_pair(Encode::uint(1), Encode::uint(2)), + make_pair(Encode::uint(3), Encode::uint(4)), + }).encoded(); + + EXPECT_EQ("a201020304", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("{1: 2, 3: 4}", Decode(cbor).dumpToString()); + + Decode decode(cbor); + EXPECT_EQ(2ul, decode.getMapElements().size()); + EXPECT_EQ(1ul, decode.getMapElements()[0].first.getValue()); + EXPECT_EQ(2ul, decode.getMapElements()[0].second.getValue()); +} + +TEST(Cbor, Map2WithArr) { + Data cbor = Encode::map({ + make_pair(Encode::string("a"), Encode::uint(1)), + make_pair(Encode::string("b"), Encode::array({ + Encode::uint(2), + Encode::uint(3), + })), + }).encoded(); + + EXPECT_EQ("a26161016162820203", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("{\"a\": 1, \"b\": [2, 3]}", + Decode(cbor).dumpToString()); + + Decode decode(cbor); + EXPECT_EQ(2ul, decode.getMapElements().size()); + EXPECT_EQ("a", decode.getMapElements()[0].first.getString()); + EXPECT_EQ(1ul, decode.getMapElements()[0].second.getValue()); + EXPECT_EQ("b", decode.getMapElements()[1].first.getString()); + EXPECT_EQ(2ul, decode.getMapElements()[1].second.getArrayElements().size()); + EXPECT_EQ(2ul, decode.getMapElements()[1].second.getArrayElements()[0].getValue()); + EXPECT_EQ(3ul, decode.getMapElements()[1].second.getArrayElements()[1].getValue()); +} + +TEST(Cbor, MapNested) { + Data cbor = Encode::map({ + make_pair(Encode::string("a"), Encode::map({ + make_pair(Encode::string("b"), Encode::string("c")), + })), + }).encoded(); + + EXPECT_EQ("a16161a161626163", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("{\"a\": {\"b\": \"c\"}}", + Decode(cbor).dumpToString()); + + Decode decode(cbor); + EXPECT_EQ(1ul, decode.getMapElements().size()); + EXPECT_EQ("a", decode.getMapElements()[0].first.getString()); + EXPECT_EQ(1ul, decode.getMapElements()[0].second.getMapElements().size()); + EXPECT_EQ("b", decode.getMapElements()[0].second.getMapElements()[0].first.getString()); + EXPECT_EQ("c", decode.getMapElements()[0].second.getMapElements()[0].second.getString()); +} + +TEST(Cbor, MapIndef) { + Decode cbor = Decode(parse_hex("bf01020304ff")); + EXPECT_EQ("{_ 1: 2, 3: 4}", cbor.dumpToString()); + EXPECT_EQ(2ul, cbor.getMapElements().size()); + EXPECT_EQ(1ul, cbor.getMapElements()[0].first.getValue()); + EXPECT_EQ(2ul, cbor.getMapElements()[0].second.getValue()); +} + +TEST(Cbor, MapIsValidInvalidTooShort) { + { + Decode cbor = Decode(parse_hex("a301020304")); // too short + EXPECT_FALSE(cbor.isValid()); + } + { + Decode cbor = Decode(parse_hex("a3010203")); // too short, partial element + EXPECT_FALSE(cbor.isValid()); + } +} + +TEST(Cbor, MapGetInvalidTooShort1) { + try { + Decode cbor = Decode(parse_hex("a301020304")); // too short + auto elems = cbor.getMapElements(); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, MapGetInvalidTooShort2) { + try { + Decode cbor = Decode(parse_hex("a3010203")); // too short, partial element + auto elems = cbor.getMapElements(); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, ArrayIndef) { + Data cbor = Encode::indefArray() + .addIndefArrayElem(Encode::uint(1)) + .addIndefArrayElem(Encode::uint(2)) + .closeIndefArray() + .encoded(); + + EXPECT_EQ("9f0102ff", hex(cbor)); + EXPECT_TRUE(Decode(cbor).isValid()); + EXPECT_EQ("[_ 1, 2]", + Decode(cbor).dumpToString()); + + Decode decode(cbor); + EXPECT_EQ(2ul, decode.getArrayElements().size()); + EXPECT_EQ(1ul, decode.getArrayElements()[0].getValue()); + EXPECT_EQ(2ul, decode.getArrayElements()[1].getValue()); + + EXPECT_EQ("[_ 1, 2]", Decode(parse_hex("9f0102ff")).dumpToString()); + EXPECT_EQ("", Decode(parse_hex("ff")).dumpToString()); + EXPECT_EQ("spec 1", Decode(parse_hex("e1")).dumpToString()); +} + +TEST(Cbor, ArrayInfefErrorAddNostart) { + try { + Data cbor = Encode::uint(0).addIndefArrayElem(Encode::uint(1)).encoded(); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, ArrayInfefErrorCloseNostart) { + try { + Data cbor = Encode::uint(0).closeIndefArray().encoded(); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, ArrayInfefErrorResultNoclose) { + try { + Data cbor = Encode::indefArray() + .addIndefArrayElem(Encode::uint(1)) + .addIndefArrayElem(Encode::uint(2)) + // close is missing, break command not written + .encoded(); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, ArrayInfefErrorNoBreak) { + EXPECT_TRUE(Decode(parse_hex("9f0102ff")).isValid()); + // without break it's invalid + EXPECT_FALSE(Decode(parse_hex("9f0102")).isValid()); +} + +TEST(Cbor, GetTagValueNotTag) { + try { + Decode cbor = Decode(Encode::string("abc").encoded()); + cbor.getTagValue(); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} + +TEST(Cbor, GetTagElementNotTag) { + try { + Decode cbor = Decode(Encode::string("abc").encoded()); + Decode tagElement = cbor.getTagElement(); + } catch (exception& ex) { + return; + } + FAIL() << "Expected exception"; +} +// clang-format on +} // namespace TW::Cbor::tests diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp new file mode 100644 index 00000000000..5bd9acd5d63 --- /dev/null +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -0,0 +1,292 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Coin.h" +#include "HexCoding.h" + +#include + +#include +#include + +namespace TW { + +TEST(Coin, DeriveAddress) { + auto dummyKeyData = parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646"); + const auto privateKey = PrivateKey(dummyKeyData); + const auto privateKeyExt = PrivateKey(dummyKeyData, dummyKeyData, dummyKeyData, dummyKeyData, dummyKeyData, dummyKeyData); + + const auto coins = TW::getCoinTypes(); + for (auto& c : coins) { + std::string address; + switch (c) { + default: + address = TW::deriveAddress(c, privateKey); + break; + + case TWCoinTypeCardano: + case TWCoinTypeNEO: + address = TW::deriveAddress(c, privateKeyExt); + break; + } + + switch (c) { + // Ethereum and ... + case TWCoinTypeEthereum: + // ... clones: + case TWCoinTypeArbitrum: + case TWCoinTypeAurora: + case TWCoinTypeAvalancheCChain: + case TWCoinTypeBoba: + case TWCoinTypeCallisto: + case TWCoinTypeCelo: + case TWCoinTypeCronosChain: + case TWCoinTypeECOChain: + case TWCoinTypeEthereumClassic: + case TWCoinTypeEvmos: + case TWCoinTypeFantom: + case TWCoinTypeGoChain: + case TWCoinTypeKavaEvm: + case TWCoinTypeKlaytn: + case TWCoinTypeKuCoinCommunityChain: + case TWCoinTypeMeter: + case TWCoinTypeMetis: + case TWCoinTypeMoonbeam: + case TWCoinTypeMoonriver: + case TWCoinTypeOptimism: + case TWCoinTypeZksync: + case TWCoinTypeOKXChain: + case TWCoinTypePOANetwork: + case TWCoinTypePolygon: + case TWCoinTypeSmartBitcoinCash: + case TWCoinTypeSmartChain: + case TWCoinTypeSmartChainLegacy: + case TWCoinTypeTheta: + case TWCoinTypeThunderToken: + case TWCoinTypeTomoChain: + case TWCoinTypeVeChain: + case TWCoinTypeWanchain: + case TWCoinTypeXDai: + EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); + break; + + case TWCoinTypeKin: + case TWCoinTypeStellar: + EXPECT_EQ(address, "GDXJHJHWN6GRNOAZXON6XH74ZX6NYFAS5B7642RSJQVJTIPA4ZYUQLEB"); + break; + + case TWCoinTypeNEO: + case TWCoinTypeOntology: + EXPECT_EQ(address, "AeicEjZyiXKgUeSBbYQHxsU1X3V5Buori5"); + break; + + case TWCoinTypeTerra: + case TWCoinTypeTerraV2: + EXPECT_EQ(address, "terra1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0ll9rwp"); + break; + + case TWCoinTypeZcash: + case TWCoinTypeZelcash: + EXPECT_EQ(address, "t1b9xfAk3kZp5Qk3rinDPq7zzLkJGHTChDS"); + break; + + case TWCoinTypeAeternity: + EXPECT_EQ(address, "ak_2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5FeVfcw"); + break; + case TWCoinTypeAion: + EXPECT_EQ(address, "0xa0010b0ea04ba4d76ca6e5e9900bacf19bc4402eaec7e36ea7ddd8eed48f60f3"); + break; + case TWCoinTypeAlgorand: + EXPECT_EQ(address, "52J2J5TPRULLQGN3TPVZ77GN7TOBIEXIP7XGUMSMFKM2DYHGOFEOGBP2T4"); + break; + case TWCoinTypeBandChain: + EXPECT_EQ(address, "band1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0q5lp5f"); + break; + case TWCoinTypeBinance: + EXPECT_EQ(address, "bnb1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0mlq0d0"); + break; + case TWCoinTypeBitcoin: + EXPECT_EQ(address, "bc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z00ppggv"); + break; + case TWCoinTypeBitcoinCash: + EXPECT_EQ(address, "bitcoincash:qz7eyzytkl5z6cg6nw20hd62pyyp22mcfuardfd2vn"); + break; + case TWCoinTypeBitcoinGold: + EXPECT_EQ(address, "btg1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z0eg8day"); + break; + case TWCoinTypeBluzelle: + EXPECT_EQ(address, "bluzelle1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0vrup2s"); + break; + case TWCoinTypeCardano: + EXPECT_EQ(address, "addr1qxzk4wqhh5qmzas4e26aghcvkz8feju6sa43nghfj5xxsly9d2up00gpk9mptj44630sevywnn9e4pmtrx3wn9gvdp7qjhvjl4"); + break; + case TWCoinTypeCosmos: + EXPECT_EQ(address, "cosmos1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0emlrvp"); + break; + case TWCoinTypeCryptoOrg: + EXPECT_EQ(address, "cro1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0pqh6ss"); + break; + case TWCoinTypeDash: + EXPECT_EQ(address, "XsyCV5yojxF4y3bYeEiVYqarvRgsWFELZL"); + break; + case TWCoinTypeDecred: + EXPECT_EQ(address, "Dsp4u8xxTHSZU2ELWTQLQP77xJhgeWrTsGK"); + break; + case TWCoinTypeDigiByte: + EXPECT_EQ(address, "dgb1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z0c69ssz"); + break; + case TWCoinTypeDogecoin: + EXPECT_EQ(address, "DNRTC6GZ5evmM7BZWwPqF54fyDqUqULMyu"); + break; + case TWCoinTypeECash: + EXPECT_EQ(address, "ecash:qz7eyzytkl5z6cg6nw20hd62pyyp22mcfuywezks2y"); + break; + case TWCoinTypeEOS: + EXPECT_EQ(address, "EOS5TrYnZP1RkDSUMzBY4GanCy6AP68kCMdkAb5EACkAwkdgRLShz"); + break; + case TWCoinTypeElrond: + EXPECT_EQ(address, "erd1a6f6fan035ttsxdmn04ellxdlnwpgyhg0lhx5vjv92v6rc8xw9yq83344f"); + break; + case TWCoinTypeEverscale: + EXPECT_EQ(address, "0:ef64d51f95ef17973b737277cfecbd2a8d551141be2f58f5fb362575fc3eb5b0"); + break; + case TWCoinTypeFIO: + EXPECT_EQ(address, "FIO5TrYnZP1RkDSUMzBY4GanCy6AP68kCMdkAb5EACkAwkdgRLShz"); + break; + case TWCoinTypeFilecoin: + EXPECT_EQ(address, "f1qsx7qwiojh5duxbxhbqgnlyx5hmpcf7mcz5oxsy"); + break; + case TWCoinTypeFiro: + EXPECT_EQ(address, "aHzpPjmY132KseS4nkiQTbDahTEXqesY89"); + break; + case TWCoinTypeGroestlcoin: + EXPECT_EQ(address, "grs1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z0jsaf3d"); + break; + case TWCoinTypeHarmony: + EXPECT_EQ(address, "one1nk9x9ajk4rgkzhqjjn7hr6w0k0jg2kj0nmx3dt"); + break; + case TWCoinTypeICON: + EXPECT_EQ(address, "hx4728fc65c31728f0d3538b8783b5394b31a136b9"); + break; + case TWCoinTypeIoTeX: + EXPECT_EQ(address, "io1nk9x9ajk4rgkzhqjjn7hr6w0k0jg2kj0zgdt6h"); + break; + case TWCoinTypeKava: + EXPECT_EQ(address, "kava1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z09wt76x"); + break; + case TWCoinTypeKusama: + EXPECT_EQ(address, "Hy8mqcexg5FMwMYnQvzrUvD723qMxDjMRU9HdNCnTsMAypY"); + break; + case TWCoinTypeLitecoin: + EXPECT_EQ(address, "ltc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z0tamvsu"); + break; + case TWCoinTypeMonacoin: + EXPECT_EQ(address, "MRBWtGEKHGCHhmyJ1L4CwaWQZJzM5DnVcs"); + break; + case TWCoinTypeNEAR: + EXPECT_EQ(address, "ee93a4f66f8d16b819bb9beb9ffccdfcdc1412e87fee6a324c2a99a1e0e67148"); + break; + case TWCoinTypeNULS: + EXPECT_EQ(address, "NULSd6HgfXT3m5JBGxeCZXHRQbb82FKgZGT8o"); + break; + case TWCoinTypeNano: + EXPECT_EQ(address, "nano_1qepdf4k95dhb5gsmhmq3iddqsxiafwkihunm7irn48jdiwdtnn6pe93k3f6"); + break; + case TWCoinTypeNativeEvmos: + EXPECT_EQ(address, "evmos1nk9x9ajk4rgkzhqjjn7hr6w0k0jg2kj07me7uu"); + break; + case TWCoinTypeNebulas: + EXPECT_EQ(address, "n1XTciu9ZRYt3ni7SxNBmivk9Y6XpP6VrhT"); + break; + case TWCoinTypeNimiq: + EXPECT_EQ(address, "NQ74 D40G N3M0 9EJD ET56 UPLR 02VC X6DU 8G1E"); + break; + case TWCoinTypeOasis: + EXPECT_EQ(address, "oasis1qzw4h3wmyjtrttduqqrs8udggyy2emwdzqmuzwg4"); + break; + case TWCoinTypeOsmosis: + EXPECT_EQ(address, "osmo1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z03qvn6n"); + break; + case TWCoinTypePolkadot: + EXPECT_EQ(address, "16PpFrXrC6Ko3pYcyMAx6gPMp3mFFaxgyYMt4G5brkgNcSz8"); + break; + case TWCoinTypeQtum: + EXPECT_EQ(address, "QdtLm8ccxhuJFF5zCgikpaghbM3thdaGsW"); + break; + case TWCoinTypeRavencoin: + EXPECT_EQ(address, "RSZYjMDCP4q3t7NAFXPPnqEGrMZn971pdB"); + break; + case TWCoinTypeRonin: + EXPECT_EQ(address, "ronin:9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); + break; + case TWCoinTypeSolana: + EXPECT_EQ(address, "H4JcMPicKkHcxxDjkyyrLoQj7Kcibd9t815ak4UvTr9M"); + break; + case TWCoinTypeTHORChain: + EXPECT_EQ(address, "thor1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0luxce7"); + break; + case TWCoinTypeTezos: + EXPECT_EQ(address, "tz1gcEWswVU6dxfNQWbhTgaZrUrNUFwrsT4z"); + break; + case TWCoinTypeTron: + EXPECT_EQ(address, "TQLCsShbQNXMTVCjprY64qZmEA4rBarpQp"); + break; + case TWCoinTypeViacoin: + EXPECT_EQ(address, "via1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z09y9mn2"); + break; + case TWCoinTypeWaves: + EXPECT_EQ(address, "3P2C786D6mBuvyf4WYr6K6Vch5uhi97nBHG"); + break; + case TWCoinTypeXRP: + EXPECT_EQ(address, "rJHMeqKu8Ep7Fazx8MQG6JunaafBXz93YQ"); + break; + case TWCoinTypeZilliqa: + EXPECT_EQ(address, "zil1j2cvtd7j9n7fnxfv2r3neucjw8tp4xz9sp07v4"); + break; + case TWCoinTypeNervos: + EXPECT_EQ(address, "ckb1qzda0cr08m85hc8jlnfp3zer7xulejywt49kt2rr0vthywaa50xwsqtsqfsf77ae0wn5a7795hs2ydv83g6hl4qleywxw"); + break; + case TWCoinTypeAptos: + EXPECT_EQ(address, "0xce2fd04ac9efa74f17595e5785e847a2399d7e637f5e8179244f76191f653276"); + break; + // no default branch here, intentionally, to better notice any missing coins + } + } +} + +int countThreadReady = 0; +std::mutex countThreadReadyMutex; + +void useCoinFromThread() { + const int tryCount = 20; + for (int i = 0; i < tryCount; ++i) { + // perform some operations + TW::validateAddress(TWCoinTypeZilliqa, "zil1j8xae6lggm8y63m3y2r7aefu797ze7mhzulnqg"); + TW::validateAddress(TWCoinTypeEthereum, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); + const auto coinTypes = TW::getCoinTypes(); + } + countThreadReadyMutex.lock(); + ++countThreadReady; + countThreadReadyMutex.unlock(); +} + +TEST(Coin, InitMultithread) { + const int numThread = 20; + countThreadReady = 0; + std::thread thread[numThread]; + // execute in threads + for (int i = 0; i < numThread; ++i) { + thread[i] = std::thread(useCoinFromThread); + } + // wait for completion + for (int i = 0; i < numThread; ++i) { + thread[i].join(); + } + // check that all completed OK + ASSERT_EQ(countThreadReady, numThread); +} + +} // namespace TW diff --git a/tests/CoinAddressValidationTests.cpp b/tests/common/CoinAddressValidationTests.cpp similarity index 93% rename from tests/CoinAddressValidationTests.cpp rename to tests/common/CoinAddressValidationTests.cpp index 18f31f6ffb2..a1816d6e911 100644 --- a/tests/CoinAddressValidationTests.cpp +++ b/tests/common/CoinAddressValidationTests.cpp @@ -203,8 +203,8 @@ TEST(Coin, validateAddressTron) { } TEST(Coin, validateAddressZcoin) { - EXPECT_TRUE(validateAddress(TWCoinTypeZcoin, "aHzpPjmY132KseS4nkiQTbDahTEXqesY89")); - EXPECT_FALSE(validateAddress(TWCoinTypeZcoin, "xHzpPjmY132KseS4nkiQTbDahTEXqesY89")); + EXPECT_TRUE(validateAddress(TWCoinTypeFiro, "aHzpPjmY132KseS4nkiQTbDahTEXqesY89")); + EXPECT_FALSE(validateAddress(TWCoinTypeFiro, "xHzpPjmY132KseS4nkiQTbDahTEXqesY89")); } TEST(Coin, validateAddressLitecoin) { @@ -324,7 +324,7 @@ TEST(Coin, ValidateAddressBluzelle) { TEST(Coin, ValidateAddresCardano) { // valid V3 address - EXPECT_TRUE(validateAddress(TWCoinTypeCardano, "addr1s3hdtrqgs47l7ue5srga8wmk9dzw279x9e7lxadalt6z0fk64nnn2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59j5lempe")); + EXPECT_TRUE(validateAddress(TWCoinTypeCardano, "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23")); // valid V2 address EXPECT_TRUE(validateAddress(TWCoinTypeCardano, "Ae2tdPwUPEZ6RUCnjGHFqi59k5WZLiv3HoCCNGCW8SYc5H9srdTzn1bec4W")); // valid V1 address @@ -392,4 +392,21 @@ TEST(Coin, ValidateAddresTHORChain) { EXPECT_FALSE(validateAddress(TWCoinTypeTHORChain, "thor1z53wwe7md6cewz9sqwqzn0aavpaun0gw0exn2s")); } +TEST(Coin, ValidateAddressECash) { + EXPECT_TRUE(validateAddress(TWCoinTypeECash, "ecash:qruxj7zq6yzpdx8dld0e9hfvt7u47zrw9gswqul42q")); + EXPECT_TRUE(validateAddress(TWCoinTypeECash, "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2")); + EXPECT_TRUE(validateAddress(TWCoinTypeECash, "qq07l6rr5lsdm3m80qxw80ku2ex0tj76vvft48qjaw")); + EXPECT_TRUE(validateAddress(TWCoinTypeECash, "qqslmu0jxk4st3ldjyuazfpf5thd6vlgfu395x2elz")); + + ASSERT_EQ(normalizeAddress(TWCoinTypeECash, "qqslmu0jxk4st3ldjyuazfpf5thd6vlgfu395x2elz"), "ecash:qqslmu0jxk4st3ldjyuazfpf5thd6vlgfu395x2elz"); + ASSERT_EQ(normalizeAddress(TWCoinTypeECash, "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2"), "1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2"); +} + +TEST(Coin, ValidateAddressEverscale) { + EXPECT_TRUE(validateAddress(TWCoinTypeEverscale, "0:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a")); + EXPECT_FALSE(validateAddress(TWCoinTypeEverscale, "83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a")); + + ASSERT_EQ(normalizeAddress(TWCoinTypeEverscale, "0:83A0352908060FA87839195D8A763A8D9AB28F8FA41468832B398A719CC6469A"), "0:83a0352908060fa87839195d8a763a8d9ab28f8fa41468832b398a719cc6469a"); +} + } // namespace TW diff --git a/tests/common/DataTests.cpp b/tests/common/DataTests.cpp new file mode 100644 index 00000000000..dec40018c9c --- /dev/null +++ b/tests/common/DataTests.cpp @@ -0,0 +1,96 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Data.h" +#include "HexCoding.h" + +#include + +using namespace std; +using namespace TW; + +TEST(DataTests, fromVector) { + const Data data = {1, 2, 3}; + EXPECT_EQ(data.size(), 3ul); + EXPECT_EQ(data[1], 2); + EXPECT_EQ(hex(data), "010203"); +} + +TEST(DataTests, fromHex) { + const Data data = parse_hex("01020304"); + EXPECT_EQ(data.size(), 4ul); + EXPECT_EQ(hex(data), "01020304"); +} + +TEST(DataTests, fromString) { + const Data data = TW::data(std::string("ABC")); + EXPECT_EQ(data.size(), 3ul); + EXPECT_EQ(hex(data), "414243"); +} + +TEST(DataTests, fromBytes) { + const std::vector vec = {1, 2, 3}; + const Data data = TW::data(vec.data(), vec.size()); + EXPECT_EQ(data.size(), 3ul); + EXPECT_EQ(hex(data), "010203"); +} + +TEST(DataTests, padLeft) { + Data data = parse_hex("01020304"); + pad_left(data, 10); + EXPECT_EQ(data.size(), 10ul); + EXPECT_EQ(hex(data), "00000000000001020304"); +} + +TEST(DataTests, append) { + Data data1 = parse_hex("01020304"); + const Data data2 = parse_hex("aeaf"); + append(data1, data2); + EXPECT_EQ(data1.size(), 6ul); + EXPECT_EQ(hex(data1), "01020304aeaf"); +} + +TEST(DataTests, appendByte) { + Data data1 = parse_hex("01020304"); + append(data1, 5); + EXPECT_EQ(data1.size(), 5ul); + EXPECT_EQ(hex(data1), "0102030405"); +} + +TEST(DataTests, subData) { + const Data data = parse_hex("0102030405060708090a"); + EXPECT_EQ(data.size(), 10ul); + + EXPECT_EQ(hex(subData(data, 2, 3)), "030405"); + EXPECT_EQ(hex(subData(data, 0, 10)), "0102030405060708090a"); + EXPECT_EQ(hex(subData(data, 3, 1)), "04"); + EXPECT_EQ(hex(subData(data, 3, 0)), ""); + EXPECT_EQ(hex(subData(data, 200, 3)), ""); // index too big + EXPECT_EQ(hex(subData(data, 2, 300)), "030405060708090a"); // length too big + EXPECT_EQ(hex(subData(data, 200, 300)), ""); // index & length too big + + EXPECT_EQ(hex(subData(data, 3)), "0405060708090a"); + EXPECT_EQ(hex(subData(data, 0)), "0102030405060708090a"); + EXPECT_EQ(hex(subData(data, 200)), ""); // index too big +} + +TEST(DataTests, hasPrefix) { + const Data data = parse_hex("0102030405060708090a"); + + const Data prefix11 = parse_hex("010203"); + EXPECT_TRUE(has_prefix(data, prefix11)); + const Data prefix12 = parse_hex("01"); + EXPECT_TRUE(has_prefix(data, prefix12)); + const Data prefix13 = parse_hex("0102030405060708090a"); + EXPECT_TRUE(has_prefix(data, prefix13)); + + const Data prefix21 = parse_hex("020304"); + EXPECT_FALSE(has_prefix(data, prefix21)); + const Data prefix22 = parse_hex("02"); + EXPECT_FALSE(has_prefix(data, prefix22)); + const Data prefix23 = parse_hex("bb"); + EXPECT_FALSE(has_prefix(data, prefix23)); +} diff --git a/tests/EncryptTests.cpp b/tests/common/EncryptTests.cpp similarity index 81% rename from tests/EncryptTests.cpp rename to tests/common/EncryptTests.cpp index 9870f4e2937..74fe4a1f13a 100644 --- a/tests/EncryptTests.cpp +++ b/tests/common/EncryptTests.cpp @@ -12,41 +12,42 @@ #include -using namespace TW::Encrypt; using namespace TW; -const Data key = parse_hex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); +namespace TW::Encrypt::test { + +const Data gKey = parse_hex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); inline void assertHexEqual(const Data& data, const char* expected) { EXPECT_EQ(hex(data), expected); } TEST(Encrypt, paddingSize) { - EXPECT_EQ(paddingSize(0, 16, TWAESPaddingModeZero), 0); - EXPECT_EQ(paddingSize(1, 16, TWAESPaddingModeZero), 15); - EXPECT_EQ(paddingSize(8, 16, TWAESPaddingModeZero), 8); - EXPECT_EQ(paddingSize(15, 16, TWAESPaddingModeZero), 1); - EXPECT_EQ(paddingSize(16, 16, TWAESPaddingModeZero), 0); - EXPECT_EQ(paddingSize(17, 16, TWAESPaddingModeZero), 15); - EXPECT_EQ(paddingSize(24, 16, TWAESPaddingModeZero), 8); - EXPECT_EQ(paddingSize(31, 16, TWAESPaddingModeZero), 1); - EXPECT_EQ(paddingSize(32, 16, TWAESPaddingModeZero), 0); - EXPECT_EQ(paddingSize(0, 16, TWAESPaddingModePKCS7), 16); - EXPECT_EQ(paddingSize(1, 16, TWAESPaddingModePKCS7), 15); - EXPECT_EQ(paddingSize(8, 16, TWAESPaddingModePKCS7), 8); - EXPECT_EQ(paddingSize(15, 16, TWAESPaddingModePKCS7), 1); - EXPECT_EQ(paddingSize(16, 16, TWAESPaddingModePKCS7), 16); - EXPECT_EQ(paddingSize(17, 16, TWAESPaddingModePKCS7), 15); - EXPECT_EQ(paddingSize(24, 16, TWAESPaddingModePKCS7), 8); - EXPECT_EQ(paddingSize(31, 16, TWAESPaddingModePKCS7), 1); - EXPECT_EQ(paddingSize(32, 16, TWAESPaddingModePKCS7), 16); + EXPECT_EQ(paddingSize(0, 16, TWAESPaddingModeZero), 0ul); + EXPECT_EQ(paddingSize(1, 16, TWAESPaddingModeZero), 15ul); + EXPECT_EQ(paddingSize(8, 16, TWAESPaddingModeZero), 8ul); + EXPECT_EQ(paddingSize(15, 16, TWAESPaddingModeZero), 1ul); + EXPECT_EQ(paddingSize(16, 16, TWAESPaddingModeZero), 0ul); + EXPECT_EQ(paddingSize(17, 16, TWAESPaddingModeZero), 15ul); + EXPECT_EQ(paddingSize(24, 16, TWAESPaddingModeZero), 8ul); + EXPECT_EQ(paddingSize(31, 16, TWAESPaddingModeZero), 1ul); + EXPECT_EQ(paddingSize(32, 16, TWAESPaddingModeZero), 0ul); + EXPECT_EQ(paddingSize(0, 16, TWAESPaddingModePKCS7), 16ul); + EXPECT_EQ(paddingSize(1, 16, TWAESPaddingModePKCS7), 15ul); + EXPECT_EQ(paddingSize(8, 16, TWAESPaddingModePKCS7), 8ul); + EXPECT_EQ(paddingSize(15, 16, TWAESPaddingModePKCS7), 1ul); + EXPECT_EQ(paddingSize(16, 16, TWAESPaddingModePKCS7), 16ul); + EXPECT_EQ(paddingSize(17, 16, TWAESPaddingModePKCS7), 15ul); + EXPECT_EQ(paddingSize(24, 16, TWAESPaddingModePKCS7), 8ul); + EXPECT_EQ(paddingSize(31, 16, TWAESPaddingModePKCS7), 1ul); + EXPECT_EQ(paddingSize(32, 16, TWAESPaddingModePKCS7), 16ul); } TEST(Encrypt, AESCBCEncrypt) { auto iv = parse_hex("000102030405060708090A0B0C0D0E0F"); auto data = parse_hex("6bc1bee22e409f96e93d7e117393172a"); - auto encryptResult = AESCBCEncrypt(key, data, iv); + auto encryptResult = AESCBCEncrypt(gKey, data, iv); assertHexEqual(encryptResult, "f58c4c04d6e5f1ba779eabfb5f7bfbd6"); } @@ -70,7 +71,7 @@ TEST(Encrypt, AESCBCDecrypt) { auto iv = parse_hex("000102030405060708090A0B0C0D0E0F"); auto cipher = parse_hex("f58c4c04d6e5f1ba779eabfb5f7bfbd6"); - auto decryptResult = AESCBCDecrypt(key, cipher, iv); + auto decryptResult = AESCBCDecrypt(gKey, cipher, iv); assertHexEqual(decryptResult, "6bc1bee22e409f96e93d7e117393172a"); } @@ -98,7 +99,7 @@ TEST(Encrypt, AESCTREncrypt) { auto iv = parse_hex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"); auto data = parse_hex("6bc1bee22e409f96e93d7e117393172a"); - auto encryptResult = AESCTREncrypt(key, data, iv); + auto encryptResult = AESCTREncrypt(gKey, data, iv); assertHexEqual(encryptResult, "601ec313775789a5b7a7f504bbf3d228"); } @@ -106,7 +107,7 @@ TEST(Encrypt, AESCTRDecrypt) { auto iv = parse_hex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"); auto cipher = parse_hex("601ec313775789a5b7a7f504bbf3d228"); - auto decryptResult = AESCTRDecrypt(key, cipher, iv); + auto decryptResult = AESCTRDecrypt(gKey, cipher, iv); assertHexEqual(decryptResult, "6bc1bee22e409f96e93d7e117393172a"); } @@ -200,3 +201,5 @@ TEST(Encrypt, AESCTRDecryptInvalidKeySize) { } ADD_FAILURE() << "Missed expected exeption"; } + +} // namespace TW::Encrypt::tests diff --git a/tests/common/HDWallet/HDWalletInternalTests.cpp b/tests/common/HDWallet/HDWalletInternalTests.cpp new file mode 100644 index 00000000000..1f27ad22cca --- /dev/null +++ b/tests/common/HDWallet/HDWalletInternalTests.cpp @@ -0,0 +1,156 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HDWallet.h" +#include "Data.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include +#include +#include "TestUtilities.h" + +#include +#include +#include + +namespace TW::HDWalletInternalTests { + +const auto mnemonic1 = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; + +std::string nodeToHexString(const HDNode& node) { + std::string s; + s += std::to_string(node.depth); + s += "-" + std::to_string(node.child_num); + s += "--" + hex(data(node.chain_code, 32)); + s += "--" + hex(data(node.private_key, 32)); + s += "--" + hex(data(node.private_key_extension, 32)); + s += "--" + hex(data(node.public_key, 33)); + return s; +} + +Data publicKeyFromPrivateKey(const Data& privateKey) { + return PrivateKey(privateKey).getPublicKey(TWPublicKeyTypeSECP256k1).bytes; +} + +TEST(HDWalletInternal, SquareDerivationRoutes) { + /* + Test 'square' derivation routes, result should be the same. + Performing private derivation, then taking the public key yields the same as + taking the public key first, and performing public derivation. + This makes XPUB schemes possible. + + priv_node --priv_deriv.--> priv_child_node + + | | + get_pub get_pub + | | + v v + + pub_node ---pub_deriv.---> pub_key + + */ + + HDWallet wallet = HDWallet(mnemonic1, ""); + const auto derivationPath = DerivationPath("m/84'/0'/0'/1"); + const auto dpLastIndex = 2; + const auto ExpectedFinalPublicKey = "02e0b4765e9012bfbbfe8c714f99beb681acc0a92bdb5acbf2f362c0aff986ad49"; + + // getMasterNode + auto masterNode = HDNode(); + hdnode_from_seed(wallet.getSeed().data(), HDWallet::seedSize, SECP256K1_NAME, &masterNode); + + auto node0 = masterNode; + // getNode + for (auto& index : derivationPath.indices) { + hdnode_private_ckd(&node0, index.derivationIndex()); + } + EXPECT_EQ(nodeToHexString(node0), "4-1--8423e869d887b6b0e7ca5f3bbed6e69234fb5d51aa02e9e8069fbffb2dc3c095--55f85b343359ec33aa3519a40673773d1f52677a044c7185529ce8f5fdb70625--0000000000000000000000000000000000000000000000000000000000000000--000000000000000000000000000000000000000000000000000000000000000000"); + + { + // Route 1 step 1: private node derivation + auto node11 = node0; + EXPECT_EQ(hdnode_private_ckd(&node11, dpLastIndex), 1); + EXPECT_EQ(nodeToHexString(node11), "5-2--53aaec7a112524bc3c8c91b278ccb0cf7e322077dac6a832563eb33149bb0051--512503395481ea0c26fe341bc342c29f4a706be003d12179ec6b65aa8a8c352d--0000000000000000000000000000000000000000000000000000000000000000--000000000000000000000000000000000000000000000000000000000000000000"); + + // Route 1 step 2: public key from private + const auto publicKey = publicKeyFromPrivateKey(data(node11.private_key, 32)); + EXPECT_EQ(hex(publicKey), ExpectedFinalPublicKey); + } + + { + // Route 2 step 1: public node from private (extended public key) + auto node21 = node0; + const auto pub21 = publicKeyFromPrivateKey(data(node21.private_key, 32)); + ::memcpy(node21.public_key, pub21.data(), 33); + EXPECT_EQ(nodeToHexString(node21), "4-1--8423e869d887b6b0e7ca5f3bbed6e69234fb5d51aa02e9e8069fbffb2dc3c095--55f85b343359ec33aa3519a40673773d1f52677a044c7185529ce8f5fdb70625--0000000000000000000000000000000000000000000000000000000000000000--026a940b5b683237037ecb230c402c5e351f38d41f00215e4d36006e9ff6b5cfba"); + + // Route 2 step 2: public node derivation + auto node22 = node21; + EXPECT_EQ(hdnode_public_ckd(&node22, dpLastIndex), 1); + EXPECT_EQ(nodeToHexString(node22), "5-2--53aaec7a112524bc3c8c91b278ccb0cf7e322077dac6a832563eb33149bb0051--0000000000000000000000000000000000000000000000000000000000000000--0000000000000000000000000000000000000000000000000000000000000000--02e0b4765e9012bfbbfe8c714f99beb681acc0a92bdb5acbf2f362c0aff986ad49"); + const auto publicKey = PublicKey(data(node22.public_key, 33), TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(publicKey.bytes), ExpectedFinalPublicKey); + } +} + +TEST(HDWalletInternal, PrivateAndPublicCkdDerivation) { + /* + + PrivateKey1 ----> PrivateKey2 + + | | + v v + + PublicKey1 ----> PublicKey2 + + */ + + const auto PrivateKey1 = "55f85b343359ec33aa3519a40673773d1f52677a044c7185529ce8f5fdb70625"; + const auto PrivateKey2 = "512503395481ea0c26fe341bc342c29f4a706be003d12179ec6b65aa8a8c352d"; + const auto PublicKey1 = "026a940b5b683237037ecb230c402c5e351f38d41f00215e4d36006e9ff6b5cfba"; + const auto PublicKey2 = "02e0b4765e9012bfbbfe8c714f99beb681acc0a92bdb5acbf2f362c0aff986ad49"; + const auto ChainCode0 = "8423e869d887b6b0e7ca5f3bbed6e69234fb5d51aa02e9e8069fbffb2dc3c095"; + const auto dpIndex = 2; + const auto curve = get_curve_by_name(SECP256K1_NAME); + + { // PrivateKey1 -> PublicKey1 + EXPECT_EQ(hex(publicKeyFromPrivateKey(parse_hex(PrivateKey1))), PublicKey1); + } + { // PrivateKey2 -> PublicKey2 + EXPECT_EQ(hex(publicKeyFromPrivateKey(parse_hex(PrivateKey2))), PublicKey2); + } + { // PrivateKey1 -> PrivateKey2 + auto node = HDNode(); + node.depth = 4; + node.child_num = 1; + node.curve = curve; + ::memcpy(node.chain_code, parse_hex(ChainCode0).data(), 32); + ::memcpy(node.private_key, parse_hex(PrivateKey1).data(), 32); + EXPECT_EQ(nodeToHexString(node), "4-1--8423e869d887b6b0e7ca5f3bbed6e69234fb5d51aa02e9e8069fbffb2dc3c095--55f85b343359ec33aa3519a40673773d1f52677a044c7185529ce8f5fdb70625--0000000000000000000000000000000000000000000000000000000000000000--000000000000000000000000000000000000000000000000000000000000000000"); + + EXPECT_EQ(hdnode_private_ckd(&node, dpIndex), 1); + + EXPECT_EQ(nodeToHexString(node), "5-2--53aaec7a112524bc3c8c91b278ccb0cf7e322077dac6a832563eb33149bb0051--512503395481ea0c26fe341bc342c29f4a706be003d12179ec6b65aa8a8c352d--0000000000000000000000000000000000000000000000000000000000000000--000000000000000000000000000000000000000000000000000000000000000000"); + EXPECT_EQ(hex(data(node.private_key, 32)), PrivateKey2); + } + { // PublicKey1 -> PublicKey2 + auto node = HDNode(); + node.depth = 4; + node.child_num = 1; + node.curve = curve; + ::memcpy(node.chain_code, parse_hex(ChainCode0).data(), 32); + ::memcpy(node.public_key, parse_hex(PublicKey1).data(), 33); + EXPECT_EQ(nodeToHexString(node), "4-1--8423e869d887b6b0e7ca5f3bbed6e69234fb5d51aa02e9e8069fbffb2dc3c095--0000000000000000000000000000000000000000000000000000000000000000--0000000000000000000000000000000000000000000000000000000000000000--026a940b5b683237037ecb230c402c5e351f38d41f00215e4d36006e9ff6b5cfba"); + + EXPECT_EQ(hdnode_public_ckd(&node, dpIndex), 1); + + EXPECT_EQ(nodeToHexString(node), "5-2--53aaec7a112524bc3c8c91b278ccb0cf7e322077dac6a832563eb33149bb0051--0000000000000000000000000000000000000000000000000000000000000000--0000000000000000000000000000000000000000000000000000000000000000--02e0b4765e9012bfbbfe8c714f99beb681acc0a92bdb5acbf2f362c0aff986ad49"); + EXPECT_EQ(hex(data(node.public_key, 33)), PublicKey2); + } +} + +} // namespace diff --git a/tests/common/HDWallet/HDWalletTests.cpp b/tests/common/HDWallet/HDWalletTests.cpp new file mode 100644 index 00000000000..9bc90832bed --- /dev/null +++ b/tests/common/HDWallet/HDWalletTests.cpp @@ -0,0 +1,437 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HDWallet.h" +#include "Mnemonic.h" +#include "Bitcoin/Address.h" +#include "Bitcoin/CashAddress.h" +#include "Bitcoin/SegwitAddress.h" +#include "Ethereum/Address.h" +#include "HexCoding.h" +#include "PublicKey.h" +#include "Hash.h" +#include "Base58.h" +#include "Coin.h" +#include "TestUtilities.h" + +#include + +extern std::string TESTS_ROOT; + +namespace TW::HDWalletTests { + +const auto mnemonic1 = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; +const auto gPassphrase = "passphrase"; + +TEST(HDWallet, generate) { + { + HDWallet wallet = HDWallet(128, gPassphrase); + EXPECT_TRUE(Mnemonic::isValid(wallet.getMnemonic())); + EXPECT_EQ(wallet.getPassphrase(), gPassphrase); + EXPECT_EQ(wallet.getEntropy().size(), 16ul); + } + { + HDWallet wallet = HDWallet(256, gPassphrase); + EXPECT_TRUE(Mnemonic::isValid(wallet.getMnemonic())); + EXPECT_EQ(wallet.getPassphrase(), gPassphrase); + EXPECT_EQ(wallet.getEntropy().size(), 32ul); + } +} + +TEST(HDWallet, generateInvalid) { + EXPECT_EXCEPTION(HDWallet(64, gPassphrase), "Invalid strength"); + EXPECT_EXCEPTION(HDWallet(129, gPassphrase), "Invalid strength"); + EXPECT_EXCEPTION(HDWallet(512, gPassphrase), "Invalid strength"); +} + +TEST(HDWallet, createFromMnemonic) { + { + HDWallet wallet = HDWallet(mnemonic1, gPassphrase); + EXPECT_EQ(wallet.getMnemonic(), mnemonic1); + EXPECT_EQ(wallet.getPassphrase(), gPassphrase); + EXPECT_EQ(hex(wallet.getEntropy()), "ba5821e8c356c05ba5f025d9532fe0f21f65d594"); + EXPECT_EQ(hex(wallet.getSeed()), "143cd5fc27ae46eb423efebc41610473f5e24a80f2ca2e2fa7bf167e537f58f4c68310ae487fce82e25bad29bab2530cf77fd724a5ebfc05a45872773d7ee2d6"); + } + { // empty passphrase + HDWallet wallet = HDWallet(mnemonic1, ""); + EXPECT_EQ(wallet.getMnemonic(), mnemonic1); + EXPECT_EQ(wallet.getPassphrase(), ""); + EXPECT_EQ(hex(wallet.getEntropy()), "ba5821e8c356c05ba5f025d9532fe0f21f65d594"); + EXPECT_EQ(hex(wallet.getSeed()), "354c22aedb9a37407adc61f657a6f00d10ed125efa360215f36c6919abd94d6dbc193a5f9c495e21ee74118661e327e84a5f5f11fa373ec33b80897d4697557d"); + } +} + +TEST(HDWallet, entropyLength_createFromMnemonic) { + { // 12 words + HDWallet wallet = HDWallet("oil oil oil oil oil oil oil oil oil oil oil oil", ""); + EXPECT_EQ(wallet.getEntropy().size(), 16ul); + EXPECT_EQ(hex(wallet.getEntropy()), "99d33a674ce99d33a674ce99d33a674c"); + } + { // 12 words, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json + HDWallet wallet = HDWallet("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", ""); + EXPECT_EQ(wallet.getEntropy().size(), 16ul); + EXPECT_EQ(hex(wallet.getEntropy()), "00000000000000000000000000000000"); + } + { // 15 words + HDWallet wallet = HDWallet("history step cheap card humble screen raise seek robot slot coral roof spoil wreck caution", ""); + EXPECT_EQ(wallet.getEntropy().size(), 20ul); + EXPECT_EQ(hex(wallet.getEntropy()), "6c3aac9b9146ef832c4e18bb3980c0dddd25fc49"); + } + { // 18 words + HDWallet wallet = HDWallet("caught hockey split gun symbol code payment copy broccoli silly shed secret stove tell citizen staff photo high", ""); + EXPECT_EQ(wallet.getEntropy().size(), 24ul); + EXPECT_EQ(hex(wallet.getEntropy()), "246d8f48b3fdc65a2869801c791715614d6bbd8a56a0a3ad"); + } + { // 21 words + HDWallet wallet = HDWallet("diary shine country alpha bridge coast loan hungry hip media sell crucial swarm share gospel lake visa coin dizzy physical basket", ""); + EXPECT_EQ(wallet.getEntropy().size(), 28ul); + EXPECT_EQ(hex(wallet.getEntropy()), "3d58bcc40381bc59a0c37a6bf14f0d9a3db78a5933e5f4a5ad00d1f1"); + } + { // 24 words + HDWallet wallet = HDWallet("poet spider smile swift roof pilot subject save hand diet ice universe over brown inspire ugly wide economy symbol shove episode patient plug swamp", ""); + EXPECT_EQ(wallet.getEntropy().size(), 32ul); + EXPECT_EQ(hex(wallet.getEntropy()), "a73a3732edebbb49f5fdfe68c7b5c0f6e9de3a1d5760faa8c771e384bf4229b6"); + } + { // 24 words, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json + HDWallet wallet = HDWallet("letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", ""); + EXPECT_EQ(wallet.getEntropy().size(), 32ul); + EXPECT_EQ(hex(wallet.getEntropy()), "8080808080808080808080808080808080808080808080808080808080808080"); + } +} + +TEST(HDWallet, createFromSpanishMnemonic) { + { + EXPECT_EXCEPTION(HDWallet("llanto radical atraer riesgo actuar masa fondo cielo dieta archivo sonrisa mamut", ""), "Invalid mnemonic"); + } + { + HDWallet wallet = HDWallet("llanto radical atraer riesgo actuar masa fondo cielo dieta archivo sonrisa mamut", "", false); + EXPECT_EQ(wallet.getMnemonic(), "llanto radical atraer riesgo actuar masa fondo cielo dieta archivo sonrisa mamut"); + EXPECT_EQ(wallet.getPassphrase(), ""); + EXPECT_EQ(hex(wallet.getEntropy()), ""); + EXPECT_EQ(hex(wallet.getSeed()), "ec8f8703432fc7d32e699ee056e9d84b1435e6a64a6a40ad63dbde11eab189a276ddcec20f3326d3c6ee39cbd018585b104fc3633b801c011063ae4c318fb9b6"); + } +} + +TEST(HDWallet, createFromMnemonicInvalid) { + EXPECT_EXCEPTION(HDWallet("THIS IS AN INVALID MNEMONIC", gPassphrase), "Invalid mnemonic"); + EXPECT_EXCEPTION(HDWallet("", gPassphrase), "Invalid mnemonic"); + + EXPECT_EXCEPTION(HDWallet("", gPassphrase, false), "Invalid mnemonic"); + HDWallet walletUnchecked = HDWallet("THIS IS AN INVALID MNEMONIC", gPassphrase, false); +} + +TEST(HDWallet, createFromEntropy) { + { + HDWallet wallet = HDWallet(parse_hex("ba5821e8c356c05ba5f025d9532fe0f21f65d594"), gPassphrase); + EXPECT_EQ(wallet.getMnemonic(), mnemonic1); + } +} + +TEST(HDWallet, createFromEntropyInvalid) { + EXPECT_EXCEPTION(HDWallet(parse_hex(""), gPassphrase), "Invalid mnemonic data"); + EXPECT_EXCEPTION(HDWallet(parse_hex("123456"), gPassphrase), "Invalid mnemonic data"); +} + +TEST(HDWallet, recreateFromEntropy) { + { + HDWallet wallet1 = HDWallet(mnemonic1, gPassphrase); + EXPECT_EQ(wallet1.getMnemonic(), mnemonic1); + EXPECT_EQ(hex(wallet1.getEntropy()), "ba5821e8c356c05ba5f025d9532fe0f21f65d594"); + HDWallet wallet2 = HDWallet(wallet1.getEntropy(), gPassphrase); + EXPECT_EQ(wallet2.getMnemonic(), wallet1.getMnemonic()); + EXPECT_EQ(wallet2.getEntropy(), wallet1.getEntropy()); + EXPECT_EQ(wallet2.getSeed(), wallet1.getSeed()); + } +} + +TEST(HDWallet, privateKeyFromXPRV) { + const std::string xprv = "xprv9yqEgpMG2KCjvotCxaiMkzmKJpDXz2xZi3yUe4XsURvo9DUbPySW1qRbdeDLiSxZt88hESHUhm2AAe2EqfWM9ucdQzH3xv1HoKoLDqHMK9n"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + ASSERT_TRUE(privateKey); + auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); + auto address = Bitcoin::BitcoinCashAddress(publicKey); + + EXPECT_EQ(hex(publicKey.bytes), "025108168f7e5aad52f7381c18d8f880744dbee21dc02c15abe512da0b1cca7e2f"); + EXPECT_EQ(address.string(), "bitcoincash:qp3y0dyg6ya8nt4n3algazn073egswkytqs00z7rz4"); +} + +TEST(HDWallet, privateKeyFromXPRV_Invalid) { + const std::string xprv = "xprv9y0000"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + ASSERT_FALSE(privateKey); +} + +TEST(HDWallet, privateKeyFromXPRV_InvalidVersion) { + { + // Version bytes (first 4) are invalid, 0x00000000 + const std::string xprv = "11117pE7xwz2GARukXY8Vj2ge4ozfX4HLgy5ztnJXjr5btzJE8EbtPhZwrcPWAodW2aFeYiXkXjSxJYm5QrnhSKFXDgACcFdMqGns9VLqESCq3"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + ASSERT_FALSE(privateKey); + } + { + // Version bytes (first 4) are invalid, 0xdeadbeef + const std::string xprv = "pGoh3VZXR4mTkT4bfqj4paog12KmHkAWkdLY8HNsZagD1ihVccygLr1ioLBhVQsny47uEh5swP3KScFc4JJrazx1Y7xvzmH2y5AseLgVMwomBTg2"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + ASSERT_FALSE(privateKey); + } +} + +TEST(HDWallet, privateKeyFromExtended_InvalidCurve) { + // invalid coin & curve, should fail + const std::string xprv = "xprv9yqEgpMG2KCjvotCxaiMkzmKJpDXz2xZi3yUe4XsURvo9DUbPySW1qRbdeDLiSxZt88hESHUhm2AAe2EqfWM9ucdQzH3xv1HoKoLDqHMK9n"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinType(123456), DerivationPath(TWPurposeBIP44, 123456, 0, 0, 0)); + ASSERT_FALSE(privateKey); +} + +TEST(HDWallet, privateKeyFromXPRV_Invalid45) { + // 45th byte is not 0 + const std::string xprv = "xprv9yqEgpMG2KCjvotCxaiMkzmKJpDXz2xZi3yUe4XsURvo9DUbPySW1qRbhw2dJ8QexahgVSfkjxU4FgmN4GLGN3Ui8oLqC6433CeyPUNVHHh"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(xprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 3)); + ASSERT_FALSE(privateKey); +} + +TEST(HDWallet, privateKeyFromMptv) { + const std::string mptv = "Mtpv7SkyM349Svcf1WiRtB5hC91ZZkVsGuv3kz1V7tThGxBFBzBLFnw6LpaSvwpHHuy8dAfMBqpBvaSAHzbffvhj2TwfojQxM7Ppm3CzW67AFL5"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(mptv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoinCash), 0, 0, 4)); + auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); + + auto witness = Data{0x00, 0x14}; + auto keyHash = Hash::sha256ripemd(publicKey.bytes.data(), 33); + witness.insert(witness.end(), keyHash.begin(), keyHash.end()); + + auto prefix = Data{TW::p2shPrefix(TWCoinTypeLitecoin)}; + auto redeemScript = Hash::sha256ripemd(witness.data(), witness.size()); + prefix.insert(prefix.end(), redeemScript.begin(), redeemScript.end()); + + auto address = Bitcoin::Address(prefix); + + EXPECT_EQ(hex(publicKey.bytes), "02c36f9c3051e9cfbb196ecc35311f3ad705ea6798ffbe6b039e70f6bd047e6f2c"); + EXPECT_EQ(address.string(), "MBzcCaoLk9626cLj2UVvcxs6nsVUi39zEy"); +} + +TEST(HDWallet, privateKeyFromZprv) { + const std::string zprv = "zprvAdzGEQ44z4WPLNCRpDaup2RumWxLGgR8PQ9UVsSmJigXsHVDaHK1b6qGM2u9PmxB2Gx264ctAz4yRoN3Xwf1HZmKcn6vmjqwsawF4WqQjfd"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(zprv, TWCoinTypeBitcoinCash, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeBitcoin), 0, 0, 5)); + auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); + auto address = Bitcoin::SegwitAddress(publicKey, "bc"); + + EXPECT_EQ(hex(publicKey.bytes), "022dc3f5a3fcfd2d1cc76d0cb386eaad0e30247ba729da0d8847a2713e444fdafa"); + EXPECT_EQ(address.string(), "bc1q5yyq60jepll68hds7exa7kpj20gsvdu0aztw5x"); +} + +TEST(HDWallet, privateKeyFromDGRV) { + const std::string dgpv = "dgpv595jAJYGBLanByCJXRzrWBZFVXdNisfuPmKRDquCQcwBbwKbeR21AtkETf4EpjBsfsK3kDZgMqhcuky1B9PrT5nxiEcjghxpUVYviHXuCmc"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(dgpv, TWCoinTypeDogecoin, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeDogecoin), 0, 0, 1)); + auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); + auto address = Bitcoin::Address(publicKey, TW::p2pkhPrefix(TWCoinTypeDogecoin)); + + EXPECT_EQ(hex(publicKey.bytes), "03eb6bf281990ee074a39c71ed8ce78c486066ac433bcf066dd5eb08f87d3a6c34"); + EXPECT_EQ(address.string(), "D5taDndQJ1fDF3AM1yWavmJY2BgSi17CUv"); +} + +TEST(HDWallet, privateKeyFromXPRVForDGB) { + const std::string xprvForDgb = "xprv9ynLofyuR3uCqCMJADwzBaPnXB53EVe5oLujvPfdvCxae3NzgEpYjZMgcUeS8EUeYfYVLG61ZgPXm9TZWiwBnLVCgd551vCwpXC19hX3mFJ"; + auto privateKey = HDWallet::getPrivateKeyFromExtended(xprvForDgb, TWCoinTypeDigiByte, DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeDigiByte), 0, 0, 1)); + auto publicKey = privateKey->getPublicKey(TWPublicKeyTypeSECP256k1); + auto address = Bitcoin::Address(publicKey, TW::p2pkhPrefix(TWCoinTypeDigiByte)); + + EXPECT_EQ(hex(publicKey.bytes), "03238a5c541c2cbbf769dbe0fb2a373c22db4da029370767fbe746d59da4de07f1"); + EXPECT_EQ(address.string(), "D9Gv7jWSVsS9Y5q98C79WyfEj6P2iM5Nzs"); +} + +TEST(HDWallet, DeriveWithLeadingZerosEth) { + // Derivation test case with leading zeroes, see https://blog.polychainlabs.com/bitcoin,/bip32,/bip39,/kdf/2021/05/17/inconsistent-bip32-derivations.html + const auto mnemonic = "name dash bleak force moral disease shine response menu rescue more will"; + const auto derivationPath = "m/44'/60'"; + const auto coin = TWCoinTypeEthereum; + auto wallet = HDWallet(mnemonic, ""); + const auto addr = Ethereum::Address(wallet.getKey(coin, DerivationPath(derivationPath)).getPublicKey(TW::publicKeyType(coin))); + EXPECT_EQ(addr.string(), "0x0ba17e928471c64AaEaf3ABfB3900EF4c27b380D"); +} + +static nlohmann::json getVectors() { + const std::string vectorsJsonPath = std::string(TESTS_ROOT) + "/common/HDWallet/bip39_vectors.json"; + auto vectorsJson = loadJson(vectorsJsonPath)["english"]; + return vectorsJson; +} + +TEST(HDWallet, Bip39Vectors) { + // BIP39 test vectors, from https://github.com/trezor/python-mnemonic/blob/master/vectors.json + const auto passphrase = "TREZOR"; + const auto vectors = getVectors(); + for (const auto& v: vectors) { + const std::string entropy = v[0]; + const std::string mnemonic = v[1]; + const std::string seed = v[2]; + const std::string xprv = v[3]; + { // from mnemonic + HDWallet wallet = HDWallet(mnemonic, passphrase); + EXPECT_EQ(wallet.getMnemonic(), mnemonic); + EXPECT_EQ(wallet.getPassphrase(), passphrase); + EXPECT_EQ(hex(wallet.getEntropy()), entropy); + EXPECT_EQ(hex(wallet.getSeed()), seed); + EXPECT_EQ(wallet.getRootKey(TWCoinTypeBitcoin, TWHDVersionXPRV), xprv); + } + { // from entropy + HDWallet wallet = HDWallet(parse_hex(entropy), passphrase); + EXPECT_EQ(wallet.getMnemonic(), mnemonic); + EXPECT_EQ(wallet.getPassphrase(), passphrase); + EXPECT_EQ(hex(wallet.getEntropy()), entropy); + EXPECT_EQ(hex(wallet.getSeed()), seed); + EXPECT_EQ(wallet.getRootKey(TWCoinTypeBitcoin, TWHDVersionXPRV), xprv); + } + } +} + +TEST(HDWallet, getExtendedPrivateKey) { + const HDWallet wallet = HDWallet(mnemonic1, ""); + const auto purpose = TWPurposeBIP44; + const auto coin = TWCoinTypeBitcoin; + const auto hdVersion = TWHDVersionZPRV; + + // default + const auto extPubKey1 = wallet.getExtendedPrivateKey(purpose, coin, hdVersion); + EXPECT_EQ(extPubKey1, "zprvAcwsTZNaY1f7rfwsy5GseSDStYBrxwtsBZDkb3iyuQUs4NF6n58BuH7Xj54RuaSCWtU5CiQzuYQgFgqr1HokgKcVAeGeXokhJUAJeP3VmvY"); + + // explicitly specify default account=0 + const auto extPubKey2 = wallet.getExtendedPrivateKeyAccount(purpose, coin, TWDerivationDefault, hdVersion, 0); + EXPECT_EQ(extPubKey2, "zprvAcwsTZNaY1f7rfwsy5GseSDStYBrxwtsBZDkb3iyuQUs4NF6n58BuH7Xj54RuaSCWtU5CiQzuYQgFgqr1HokgKcVAeGeXokhJUAJeP3VmvY"); + + // custom account=1 + const auto extPubKey3 = wallet.getExtendedPrivateKeyAccount(purpose, coin, TWDerivationDefault, hdVersion, 1); + EXPECT_EQ(extPubKey3, "zprvAcwsTZNaY1f7sifgNNgdNa4P9mPtyg3zRVgwkx2qF9Sn7F255MzP6Zyumn6bgV5xuoS8ZrDvjzE7APcFSacXdzFYpGvyybb1bnAoh5nHxpn"); +} + +TEST(HDWallet, getExtendedPublicKey) { + const HDWallet wallet = HDWallet(mnemonic1, ""); + const auto purpose = TWPurposeBIP44; + const auto coin = TWCoinTypeBitcoin; + const auto hdVersion = TWHDVersionZPUB; + const auto derivation = TWDerivationDefault; + + // default + const auto extPubKey1 = wallet.getExtendedPublicKey(purpose, coin, hdVersion); + EXPECT_EQ(extPubKey1, "zpub6qwDs4uUNPDR5A2M56ot1aABSa2MNQciYn9MPS8bTk1qwAaFKcSST5S1aLidvPp9twqpaumG7vikR2vHhBXjp5oGgHyMvWK3AtUkfeEgyns"); + + // explicitly specify default account=0 + const auto extPubKey2 = wallet.getExtendedPublicKeyAccount(purpose, coin, derivation, hdVersion, 0); + EXPECT_EQ(extPubKey2, "zpub6qwDs4uUNPDR5A2M56ot1aABSa2MNQciYn9MPS8bTk1qwAaFKcSST5S1aLidvPp9twqpaumG7vikR2vHhBXjp5oGgHyMvWK3AtUkfeEgyns"); + + // custom account=1 + const auto extPubKey3 = wallet.getExtendedPublicKeyAccount(purpose, coin, derivation, hdVersion, 1); + EXPECT_EQ(extPubKey3, "zpub6qwDs4uUNPDR6Ck9UQDdji17hoEPP8mqnicYZLSSoUykz3MDcuJdeNJPd3BozqEafeLZkegWqzAvkgA4JZZ5tTN2rDpGKfk54essyfx1eZP"); +} + +TEST(HDWallet, Derive_XpubPub_vs_PrivPub) { + // Test different routes for deriving address from mnemonic, result should be the same: + // - Direct: mnemonic -> seed -> privateKey -> publicKey -> address + // - Extended Public: mnemonic -> seed -> zpub -> publicKey -> address + // - Extended Private: mnemonic -> seed -> zpriv -> privateKey -> publicKey -> address + + const HDWallet wallet = HDWallet(mnemonic1, ""); + const auto coin = TWCoinTypeBitcoin; + const auto derivPath1 = DerivationPath("m/84'/0'/0'/0/0"); + const auto derivPath2 = DerivationPath("m/84'/0'/0'/0/2"); + const auto expectedPublicKey1 = "02df9ef2a7a5552765178b181e1e1afdefc7849985c7dfe9647706dd4fa40df6ac"; + const auto expectedPublicKey2 = "031e1f64d2f6768dccb6814545b2e2d58e26ad5f91b7cbaffe881ed572c65060db"; + const auto expectedAddress1 = "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"; + const auto expectedAddress2 = "bc1q7zddsunzaftf4zlsg9exhzlkvc5374a6v32jf6"; + + // -> privateKey -> publicKey + { + const auto privateKey1 = wallet.getKey(coin, derivPath1); + const auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(publicKey1.bytes), expectedPublicKey1); + const auto address1 = Bitcoin::SegwitAddress(publicKey1, "bc"); + EXPECT_EQ(address1.string(), expectedAddress1); + } + { + const auto privateKey2 = wallet.getKey(coin, derivPath2); + const auto publicKey2 = privateKey2.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(publicKey2.bytes), expectedPublicKey2); + const auto address2 = Bitcoin::SegwitAddress(publicKey2, "bc"); + EXPECT_EQ(address2.string(), expectedAddress2); + } + + // zpub -> publicKey + const auto zpub = wallet.getExtendedPublicKey(TWPurposeBIP84, coin, TWHDVersionZPUB); + EXPECT_EQ(zpub, "zpub6rNUNtxSa9Gxvm4Bdxf1MPMwrvkzwDx6vP96Hkzw3jiQKdg3fhXBStxjn12YixQB8h88B3RMSRscRstf9AEVaYr3MAqVBEWBDuEJU4PGaT9"); + + { + const auto publicKey1 = wallet.getPublicKeyFromExtended(zpub, coin, derivPath1); + EXPECT_TRUE(publicKey1.has_value()); + EXPECT_EQ(hex(publicKey1->bytes), expectedPublicKey1); + const auto address1 = Bitcoin::SegwitAddress(publicKey1.value(), "bc"); + EXPECT_EQ(address1.string(), expectedAddress1); + } + { + const auto publicKey2 = wallet.getPublicKeyFromExtended(zpub, coin, derivPath2); + EXPECT_TRUE(publicKey2.has_value()); + EXPECT_EQ(hex(publicKey2->bytes), expectedPublicKey2); + const auto address2 = Bitcoin::SegwitAddress(publicKey2.value(), "bc"); + EXPECT_EQ(address2.string(), expectedAddress2); + } + + // zpriv -> privateKey -> publicKey + const auto zpriv = wallet.getExtendedPrivateKey(TWPurposeBIP84, coin, TWHDVersionZPRV); + EXPECT_EQ(zpriv, "zprvAdP7yPRYjmifiGyiXw7zzFRDJtvWXmEFZADVVNbKVQBRSqLu8ACvu6eFvhrnnw4QwdTD8PUVa48MguwiPTiyfn85zWx9iA5MYy4Eufu5bas"); + + { + const auto privateKey1 = wallet.getPrivateKeyFromExtended(zpriv, coin, derivPath1); + EXPECT_TRUE(privateKey1.has_value()); + const auto publicKey1 = privateKey1->getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(publicKey1.bytes), expectedPublicKey1); + const auto address1 = Bitcoin::SegwitAddress(publicKey1, "bc"); + EXPECT_EQ(address1.string(), expectedAddress1); + } + { + const auto privateKey2 = wallet.getPrivateKeyFromExtended(zpriv, coin, derivPath2); + EXPECT_TRUE(privateKey2.has_value()); + const auto publicKey2 = privateKey2->getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(publicKey2.bytes), expectedPublicKey2); + const auto address2 = Bitcoin::SegwitAddress(publicKey2, "bc"); + EXPECT_EQ(address2.string(), expectedAddress2); + } +} + +TEST(HDWallet, getKeyByCurve) { + const auto derivPath = "m/44'/539'/0'/0/0"; + HDWallet wallet = HDWallet(mnemonic1, ""); + { + const auto privateKey = wallet.getKeyByCurve(TWCurveSECP256k1, DerivationPath(derivPath)); + EXPECT_EQ(hex(privateKey.bytes), "4fb8657d6464adcaa086d6758d7f0b6b6fc026c98dc1671fcc6460b5a74abc62"); + } + { + const auto privateKey = wallet.getKeyByCurve(TWCurveNIST256p1, DerivationPath(derivPath)); + EXPECT_EQ(hex(privateKey.bytes), "a13df52d5a5b438bbf921bbf86276e4347fe8e2f2ed74feaaee12b77d6d26f86"); + } +} + +TEST(HDWallet, getKey) { + const auto derivPath = "m/44'/539'/0'/0/0"; + HDWallet wallet = HDWallet(mnemonic1, ""); + { + const auto privateKey = wallet.getKey(TWCoinTypeBitcoin, DerivationPath(derivPath)); + EXPECT_EQ(hex(privateKey.bytes), "4fb8657d6464adcaa086d6758d7f0b6b6fc026c98dc1671fcc6460b5a74abc62"); + } + { + const auto privateKey = wallet.getKey(TWCoinTypeNEO, DerivationPath(derivPath)); + EXPECT_EQ(hex(privateKey.bytes), "a13df52d5a5b438bbf921bbf86276e4347fe8e2f2ed74feaaee12b77d6d26f86"); + } +} + +TEST(HDWallet, AptosKey) { + const auto derivPath = "m/44'/637'/0'/0'/0'"; + HDWallet wallet = HDWallet(mnemonic1, ""); + { + const auto privateKey = wallet.getKey(TWCoinTypeAptos, DerivationPath(derivPath)); + EXPECT_EQ(hex(privateKey.bytes), "7f2634c0e2414a621e96e39c41d09021700cee12ee43328ed094c5580cd0bd6f"); + EXPECT_EQ(hex(privateKey.getPublicKey(TWPublicKeyTypeED25519).bytes), "633e5c7e355bdd484706436ce1f06fdf280bd7c2229a7f9b6489684412c6967c"); + } +} + + +} // namespace diff --git a/tests/HDWallet/bip39_vectors.json b/tests/common/HDWallet/bip39_vectors.json similarity index 100% rename from tests/HDWallet/bip39_vectors.json rename to tests/common/HDWallet/bip39_vectors.json diff --git a/tests/common/HashTests.cpp b/tests/common/HashTests.cpp new file mode 100644 index 00000000000..c60acec946c --- /dev/null +++ b/tests/common/HashTests.cpp @@ -0,0 +1,96 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Hash.h" +#include "HexCoding.h" + +#include + +using namespace std; +using namespace TW; + +const string brownFox = "The quick brown fox jumps over the lazy dog"; +const string brownFoxDot = "The quick brown fox jumps over the lazy dog."; + +TEST(HashTests, Blake2b) { + auto content = string("Hello world"); + auto hashed = Hash::blake2b(content, 64); + auto result = hex(hashed); + + ASSERT_EQ(result, string("6ff843ba685842aa82031d3f53c48b66326df7639a63d128974c5c14f31a0f33343a8c65551134ed1ae0f2b0dd2bb495dc81039e3eeb0aa1bb0388bbeac29183")); +} + +TEST(HashTests, Blake2bPersonal) { + auto personal_string = string("MyApp Files Hash"); + auto personal_data = Data(personal_string.begin(), personal_string.end()); + auto content = string("the same content"); + auto hashed = Hash::blake2b(content, 32, personal_data); + auto result = hex(hashed); + + ASSERT_EQ(result, string("20d9cd024d4fb086aae819a1432dd2466de12947831b75c5a30cf2676095d3b4")); +} + +TEST(HashTests, Sha512_256) { + auto tests = { + make_tuple(string(""), string("c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a")), + make_tuple(brownFox, string("dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d")), + make_tuple(brownFoxDot, string("1546741840f8a492b959d9b8b2344b9b0eb51b004bba35c0aebaac86d45264c3")), + }; + for (auto& test : tests) { + auto hashed = Hash::sha512_256(get<0>(test)); + ASSERT_EQ(hex(hashed), get<1>(test)); + } +} + +TEST(HashTests, Sha1) { + auto tests = { + make_tuple(string(""), string("da39a3ee5e6b4b0d3255bfef95601890afd80709")), + make_tuple(brownFox, string("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")), + make_tuple(brownFoxDot, string("408d94384216f890ff7a0c3528e8bed1e0b01621")), + }; + for (auto& test: tests) { + const auto hash = Hash::sha1(TW::data(get<0>(test))); + EXPECT_EQ(hex(hash), get<1>(test)); + } +} + +TEST(HashTests, hmac256) { + const Data key = parse_hex("531cbfcf12a168faff61af28bf437377397b4bf435ee732cf4ac95761a651f14"); + const Data data = parse_hex("f300888ca4f512cebdc0020ff0f7224c7f896315e90e172bed65d005138f224d"); + const auto expectedHmac = "a7301d5563614e3955750e4480aabf7753f44b4975308aeb8e23c31e114962ab"; + const Data hmac = Hash::hmac256(key, data); + EXPECT_EQ(hex(hmac), expectedHmac); +} + +TEST(HashTests, allHashEnum) { + const auto tests = { + make_tuple(Hash::HasherSha1, "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"), + make_tuple(Hash::HasherSha256, "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"), + make_tuple(Hash::HasherSha512, "07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6"), + make_tuple(Hash::HasherSha512_256, "dd9d67b371519c339ed8dbd25af90e976a1eeefd4ad3d889005e532fc5bef04d"), + make_tuple(Hash::HasherKeccak256, "4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15"), + make_tuple(Hash::HasherKeccak512, "d135bb84d0439dbac432247ee573a23ea7d3c9deb2a968eb31d47c4fb45f1ef4422d6c531b5b9bd6f449ebcc449ea94d0a8f05f62130fda612da53c79659f609"), + make_tuple(Hash::HasherSha3_256, "69070dda01975c8c120c3aada1b282394e7f032fa9cf32f4cb2259a0897dfc04"), + make_tuple(Hash::HasherSha3_512, "01dedd5de4ef14642445ba5f5b97c15e47b9ad931326e4b0727cd94cefc44fff23f07bf543139939b49128caf436dc1bdee54fcb24023a08d9403f9b4bf0d450"), + make_tuple(Hash::HasherRipemd, "37f332f68db77bd9d7edd4969571ad671cf9dd3b"), + make_tuple(Hash::HasherBlake256, "7576698ee9cad30173080678e5965916adbb11cb5245d386bf1ffda1cb26c9d7"), + make_tuple(Hash::HasherGroestl512, "badc1f70ccd69e0cf3760c3f93884289da84ec13c70b3d12a53a7a8a4a513f99715d46288f55e1dbf926e6d084a0538e4eebfc91cf2b21452921ccde9131718d"), + make_tuple(Hash::HasherSha256d, "6d37795021e544d82b41850edf7aabab9a0ebe274e54a519840c4666f35b3937"), + make_tuple(Hash::HasherSha256ripemd, "0e3397b4abc7a382b3ea2365883c3c7ca5f07600"), + make_tuple(Hash::HasherSha3_256ripemd, "e70a0c74dd1b0c0d5af3c7ccbbe4b488d1b474b5"), + make_tuple(Hash::HasherBlake256d, "4511ab8713d8d580cae73061345df903f603b99e7ec699ddae63c56eea200059"), + make_tuple(Hash::HasherBlake256ripemd, "b4b44de1e854f7f3c0520b654204163f75f704e5"), + make_tuple(Hash::HasherGroestl512d, "1209d229cfc9d7d6711369e2d7f369b0efc1459a9d407cbfc7daf4f54209347f2ee7e3e7522ba5d5ac4e7365445739919e23e2917baee10f23557f3d3fbc696d"), + }; + + for (auto& test: tests) { + const auto hashFunc = get<0>(test); + const auto expected = get<1>(test); + EXPECT_EQ(hex(Hash::hash(hashFunc, brownFox)), expected); + } +} + +// More tests in TWHashTests diff --git a/tests/HexCodingTests.cpp b/tests/common/HexCodingTests.cpp similarity index 100% rename from tests/HexCodingTests.cpp rename to tests/common/HexCodingTests.cpp diff --git a/tests/Keystore/Data/empty-accounts.json b/tests/common/Keystore/Data/empty-accounts.json similarity index 100% rename from tests/Keystore/Data/empty-accounts.json rename to tests/common/Keystore/Data/empty-accounts.json diff --git a/tests/Keystore/Data/ethereum-wallet-address-no-0x.json b/tests/common/Keystore/Data/ethereum-wallet-address-no-0x.json similarity index 100% rename from tests/Keystore/Data/ethereum-wallet-address-no-0x.json rename to tests/common/Keystore/Data/ethereum-wallet-address-no-0x.json diff --git a/tests/Keystore/Data/key.json b/tests/common/Keystore/Data/key.json old mode 100755 new mode 100644 similarity index 100% rename from tests/Keystore/Data/key.json rename to tests/common/Keystore/Data/key.json diff --git a/tests/Keystore/Data/key_bitcoin.json b/tests/common/Keystore/Data/key_bitcoin.json old mode 100755 new mode 100644 similarity index 100% rename from tests/Keystore/Data/key_bitcoin.json rename to tests/common/Keystore/Data/key_bitcoin.json diff --git a/tests/Keystore/Data/legacy-mnemonic.json b/tests/common/Keystore/Data/legacy-mnemonic.json similarity index 100% rename from tests/Keystore/Data/legacy-mnemonic.json rename to tests/common/Keystore/Data/legacy-mnemonic.json diff --git a/tests/Keystore/Data/legacy-private-key.json b/tests/common/Keystore/Data/legacy-private-key.json similarity index 100% rename from tests/Keystore/Data/legacy-private-key.json rename to tests/common/Keystore/Data/legacy-private-key.json diff --git a/tests/Keystore/Data/livepeer.json b/tests/common/Keystore/Data/livepeer.json similarity index 100% rename from tests/Keystore/Data/livepeer.json rename to tests/common/Keystore/Data/livepeer.json diff --git a/tests/Keystore/Data/missing-address.json b/tests/common/Keystore/Data/missing-address.json similarity index 100% rename from tests/Keystore/Data/missing-address.json rename to tests/common/Keystore/Data/missing-address.json diff --git a/tests/Keystore/Data/myetherwallet.uu b/tests/common/Keystore/Data/myetherwallet.uu old mode 100755 new mode 100644 similarity index 100% rename from tests/Keystore/Data/myetherwallet.uu rename to tests/common/Keystore/Data/myetherwallet.uu diff --git a/tests/Keystore/Data/pbkdf2.json b/tests/common/Keystore/Data/pbkdf2.json similarity index 100% rename from tests/Keystore/Data/pbkdf2.json rename to tests/common/Keystore/Data/pbkdf2.json diff --git a/tests/Keystore/Data/wallet.json b/tests/common/Keystore/Data/wallet.json old mode 100755 new mode 100644 similarity index 100% rename from tests/Keystore/Data/wallet.json rename to tests/common/Keystore/Data/wallet.json diff --git a/tests/Keystore/Data/watch.json b/tests/common/Keystore/Data/watch.json similarity index 100% rename from tests/Keystore/Data/watch.json rename to tests/common/Keystore/Data/watch.json diff --git a/tests/Keystore/Data/web3j.json b/tests/common/Keystore/Data/web3j.json similarity index 100% rename from tests/Keystore/Data/web3j.json rename to tests/common/Keystore/Data/web3j.json diff --git a/tests/common/Keystore/DerivationPathTests.cpp b/tests/common/Keystore/DerivationPathTests.cpp new file mode 100644 index 00000000000..62d21aab939 --- /dev/null +++ b/tests/common/Keystore/DerivationPathTests.cpp @@ -0,0 +1,97 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Coin.h" +#include "DerivationPath.h" +#include + +#include + +namespace TW { + +TEST(DerivationPath, InitWithIndices) { + const auto path = DerivationPath(TWPurposeBIP44, TWCoinTypeSlip44Id(TWCoinTypeEthereum), 0, 0, 0); + ASSERT_EQ(path.indices[0], DerivationPathIndex(44, /* hardened: */ true)); + ASSERT_EQ(path.indices[1], DerivationPathIndex(60, /* hardened: */ true)); + ASSERT_EQ(path.indices[2], DerivationPathIndex(0, /* hardened: */ true)); + ASSERT_EQ(path.indices[3], DerivationPathIndex(0, /* hardened: */ false)); + ASSERT_EQ(path.indices[4], DerivationPathIndex(0, /* hardened: */ false)); +} + +TEST(DerivationPath, InitWithString) { + ASSERT_NO_THROW(DerivationPath("m/44'/60'/0'/0/0")); + const auto path = DerivationPath("m/44'/60'/0'/0/0"); + + ASSERT_EQ(path.indices[0], DerivationPathIndex(44, /* hardened: */ true)); + ASSERT_EQ(path.indices[1], DerivationPathIndex(60, /* hardened: */ true)); + ASSERT_EQ(path.indices[2], DerivationPathIndex(0, /* hardened: */ true)); + ASSERT_EQ(path.indices[3], DerivationPathIndex(0, /* hardened: */ false)); + ASSERT_EQ(path.indices[4], DerivationPathIndex(0, /* hardened: */ false)); + + ASSERT_EQ(path.purpose(), 44); + ASSERT_EQ(path.coin(), 60ul); + ASSERT_EQ(path.account(), 0ul); + ASSERT_EQ(path.change(), 0ul); + ASSERT_EQ(path.address(), 0ul); +} + +TEST(DerivationPath, InitInvalid) { + ASSERT_THROW(DerivationPath("a/b/c"), std::invalid_argument); + ASSERT_THROW(DerivationPath("m/44'/60''/"), std::invalid_argument); +} + +TEST(DerivationPath, IndexOutOfBounds) { + DerivationPath path; + + EXPECT_EQ(path.indices.size(), 0ul); + + EXPECT_EQ(path.purpose(), TWPurposeBIP44); + EXPECT_EQ(path.coin(), TWCoinTypeBitcoin); + EXPECT_EQ(path.account(), 0ul); + EXPECT_EQ(path.change(), 0ul); + EXPECT_EQ(path.address(), 0ul); + + ASSERT_NO_THROW(path.setPurpose(TWPurposeBIP44)); + ASSERT_NO_THROW(path.setCoin(TWCoinTypeBitcoin)); + ASSERT_NO_THROW(path.setAccount(0)); + ASSERT_NO_THROW(path.setChange(0)); + ASSERT_NO_THROW(path.setAddress(0)); +} + +TEST(DerivationPath, String) { + const auto path = DerivationPath("m/44'/60'/0'/0/0"); + ASSERT_EQ(path.string(), "m/44'/60'/0'/0/0"); +} + +TEST(DerivationPath, Equal) { + const auto path1 = DerivationPath("m/44'/60'/0'/0/0"); + const auto path2 = DerivationPath("44'/60'/0'/0/0"); + ASSERT_EQ(path1, path2); +} + +TEST(Derivation, derivationPath) { + EXPECT_EQ(TW::derivationPath(TWCoinTypeBitcoin).string(), "m/84'/0'/0'/0/0"); + EXPECT_EQ(TW::derivationPath(TWCoinTypeSolana).string(), "m/44'/501'/0'"); +} + +TEST(Derivation, alternativeDerivation) { + EXPECT_EQ(TW::derivationPath(TWCoinTypeBitcoin).string(), "m/84'/0'/0'/0/0"); + EXPECT_EQ(TW::derivationPath(TWCoinTypeBitcoin, TWDerivationDefault).string(), "m/84'/0'/0'/0/0"); + EXPECT_EQ(TW::derivationPath(TWCoinTypeBitcoin, TWDerivationBitcoinSegwit).string(), "m/84'/0'/0'/0/0"); + EXPECT_EQ(std::string(TW::derivationName(TWCoinTypeBitcoin, TWDerivationBitcoinSegwit)), "segwit"); + EXPECT_EQ(TW::derivationPath(TWCoinTypeBitcoin, TWDerivationBitcoinLegacy).string(), "m/44'/0'/0'/0/0"); + EXPECT_EQ(std::string(TW::derivationName(TWCoinTypeBitcoin, TWDerivationBitcoinLegacy)), "legacy"); + + EXPECT_EQ(TW::derivationPath(TWCoinTypeLitecoin, TWDerivationDefault).string(), "m/84'/2'/0'/0/0"); + EXPECT_EQ(TW::derivationPath(TWCoinTypeLitecoin, TWDerivationLitecoinLegacy).string(), "m/44'/2'/0'/0/0"); + + EXPECT_EQ(TW::derivationPath(TWCoinTypeSolana).string(), "m/44'/501'/0'"); + EXPECT_EQ(TW::derivationPath(TWCoinTypeSolana, TWDerivationDefault).string(), "m/44'/501'/0'"); + EXPECT_EQ(TW::derivationPath(TWCoinTypeSolana, TWDerivationSolanaSolana).string(), "m/44'/501'/0'/0'"); + EXPECT_EQ(std::string(TW::derivationName(TWCoinTypeSolana, TWDerivationSolanaSolana)), "solana"); +} + +} // namespace TW diff --git a/tests/common/Keystore/StoredKeyTests.cpp b/tests/common/Keystore/StoredKeyTests.cpp new file mode 100644 index 00000000000..51cc959968d --- /dev/null +++ b/tests/common/Keystore/StoredKeyTests.cpp @@ -0,0 +1,653 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Keystore/StoredKey.h" + +#include "Coin.h" +#include "HexCoding.h" +#include "Data.h" +#include "PrivateKey.h" +#include "Mnemonic.h" +#include "Bitcoin/Address.h" + +#include +#include + +extern std::string TESTS_ROOT; + +namespace TW::Keystore::tests { + +using namespace std; + +const auto passwordString = "password"; +const auto gPassword = TW::data(string(passwordString)); +const auto gMnemonic = "team engine square letter hero song dizzy scrub tornado fabric divert saddle"; +const TWCoinType coinTypeBc = TWCoinTypeBitcoin; +const TWCoinType coinTypeBnb = TWCoinTypeBinance; +const TWCoinType coinTypeBsc = TWCoinTypeSmartChain; +const TWCoinType coinTypeEth = TWCoinTypeEthereum; +const TWCoinType coinTypeBscLegacy = TWCoinTypeSmartChainLegacy; + +const std::string testDataPath(const char* subpath) { + return TESTS_ROOT + "/common/Keystore/Data/" + subpath; +} + +TEST(StoredKey, CreateWithMnemonic) { + auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelDefault); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + const Data& mnemo2Data = key.payload.decrypt(gPassword); + EXPECT_EQ(string(mnemo2Data.begin(), mnemo2Data.end()), string(gMnemonic)); + EXPECT_EQ(key.accounts.size(), 0ul); + EXPECT_EQ(key.wallet(gPassword).getMnemonic(), string(gMnemonic)); + + const auto json = key.json(); + EXPECT_EQ(json["name"], "name"); + EXPECT_EQ(json["type"], "mnemonic"); + EXPECT_EQ(json["version"], 3); +} + +TEST(StoredKey, CreateWithMnemonicInvalid) { + try { + auto key = StoredKey::createWithMnemonic("name", gPassword, "_THIS_IS_NOT_A_VALID_MNEMONIC_", TWStoredKeyEncryptionLevelDefault); + } catch (std::invalid_argument&) { + // expedcted exception OK + return; + } + FAIL() << "Missing excpected excpetion"; +} + +TEST(StoredKey, CreateWithMnemonicRandom) { + const auto key = StoredKey::createWithMnemonicRandom("name", gPassword, TWStoredKeyEncryptionLevelDefault); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + // random mnemonic: check only length and validity + const Data& mnemo2Data = key.payload.decrypt(gPassword); + EXPECT_TRUE(mnemo2Data.size() >= 36); + EXPECT_TRUE(Mnemonic::isValid(string(mnemo2Data.begin(), mnemo2Data.end()))); + EXPECT_EQ(key.accounts.size(), 0ul); +} + +TEST(StoredKey, CreateWithMnemonicAddDefaultAddress) { + auto key = StoredKey::createWithMnemonicAddDefaultAddress("name", gPassword, gMnemonic, coinTypeBc); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + + const Data& mnemo2Data = key.payload.decrypt(gPassword); + + EXPECT_EQ(string(mnemo2Data.begin(), mnemo2Data.end()), string(gMnemonic)); + EXPECT_EQ(key.accounts.size(), 1ul); + EXPECT_EQ(key.accounts[0].coin, coinTypeBc); + EXPECT_EQ(key.accounts[0].address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + EXPECT_EQ(key.accounts[0].publicKey, "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006"); + EXPECT_EQ(key.accounts[0].extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); + EXPECT_EQ(hex(key.privateKey(coinTypeBc, gPassword).bytes), "d2568511baea8dc347f14c4e0479eb8ebe29eb5f664ed796e755896250ffd11f"); +} + +TEST(StoredKey, CreateWithPrivateKeyAddDefaultAddress) { + const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); + auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", gPassword, coinTypeBc, privateKey); + EXPECT_EQ(key.type, StoredKeyType::privateKey); + EXPECT_EQ(key.accounts.size(), 1ul); + EXPECT_EQ(key.accounts[0].coin, coinTypeBc); + EXPECT_EQ(key.accounts[0].address, "bc1q375sq4kl2nv0mlmup3vm8znn4eqwu7mt6hkwhr"); + EXPECT_EQ(hex(key.privateKey(coinTypeBc, gPassword).bytes), hex(privateKey)); + + const auto json = key.json(); + EXPECT_EQ(json["name"], "name"); + EXPECT_EQ(json["type"], "private-key"); + EXPECT_EQ(json["version"], 3); +} + +TEST(StoredKey, CreateWithPrivateKeyAddDefaultAddressInvalid) { + try { + const auto privateKeyInvalid = parse_hex("0001020304"); + auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", gPassword, coinTypeBc, privateKeyInvalid); + } catch (std::invalid_argument&) { + // expected exception ok + return; + } + FAIL() << "Missing expected exception"; +} + +TEST(StoredKey, AccountGetCreate) { + auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelDefault); + EXPECT_EQ(key.accounts.size(), 0ul); + + // not exists + EXPECT_FALSE(key.account(coinTypeBc).has_value()); + EXPECT_EQ(key.accounts.size(), 0ul); + + auto wallet = key.wallet(gPassword); + // not exists, wallet null, not create + EXPECT_FALSE(key.account(coinTypeBc, nullptr).has_value()); + EXPECT_EQ(key.accounts.size(), 0ul); + + // not exists, wallet nonnull, create + std::optional acc3 = key.account(coinTypeBc, &wallet); + EXPECT_TRUE(acc3.has_value()); + EXPECT_EQ(acc3->coin, coinTypeBc); + EXPECT_EQ(key.accounts.size(), 1ul); + + // exists + std::optional acc4 = key.account(coinTypeBc); + EXPECT_TRUE(acc4.has_value()); + EXPECT_EQ(acc4->coin, coinTypeBc); + EXPECT_EQ(key.accounts.size(), 1ul); + + // exists, wallet nonnull, not create + std::optional acc5 = key.account(coinTypeBc, &wallet); + EXPECT_TRUE(acc5.has_value()); + EXPECT_EQ(acc5->coin, coinTypeBc); + EXPECT_EQ(key.accounts.size(), 1ul); + + // exists, wallet null, not create + std::optional acc6 = key.account(coinTypeBc, nullptr); + EXPECT_TRUE(acc6.has_value()); + EXPECT_EQ(acc6->coin, coinTypeBc); + EXPECT_EQ(key.accounts.size(), 1ul); +} + +TEST(StoredKey, AccountGetDoesntChange) { + auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelDefault); + auto wallet = key.wallet(gPassword); + EXPECT_EQ(key.accounts.size(), 0ul); + + vector coins = {coinTypeBc, coinTypeEth, coinTypeBnb}; + // retrieve multiple accounts, which will be created + vector accounts; + for (auto coin: coins) { + std::optional account = key.account(coin, &wallet); + accounts.push_back(*account); + + // check + ASSERT_TRUE(account.has_value()); + EXPECT_EQ(account->coin, coin); + } + + // Check again; make sure returned references don't change + for (auto i = 0ul; i < accounts.size(); ++i) { + // check + EXPECT_EQ(accounts[i].coin, coins[i]); + } +} + +TEST(StoredKey, AddRemoveAccount) { + auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelDefault); + EXPECT_EQ(key.accounts.size(), 0ul); + + { + const auto derivationPath = DerivationPath("m/84'/0'/0'/0/0"); + key.addAccount("bc1qaucw06s3agez8tyyk4zj9kt0q2934e3mcewdpf", coinTypeBc, TWDerivationDefault, derivationPath, "", "zpub6rxtad3SPT1C5GUDjPiKQ5oJN5DBeMbdUR7LrdYt12VbU7TBSpGUkdLvfVYGuj1N5edkDoZ3bu1fdN1HprQYfCBdsSH5CaAAygHGsanwtTe"); + EXPECT_EQ(key.accounts.size(), 1ul); + } + { + const auto derivationPath = DerivationPath("m/714'/0'/0'/0/0"); + key.addAccount("bnb1utrnnjym7ustgw7pgyvtmnxay4qmt3ahh276nu", coinTypeBnb, TWDerivationDefault, derivationPath, "", ""); + key.addAccount("0x23b02dC8f67eD6cF8DCa47935791954286ffe7c9", coinTypeBsc, TWDerivationDefault, derivationPath, "", ""); + EXPECT_EQ(key.accounts.size(), 3ul); + } + { + const auto derivationPath = DerivationPath("m/60'/0'/0'/0/0"); + key.addAccount("0xC0d97f61A84A0708225F15d54978D628Fe2C5E62", coinTypeEth, TWDerivationDefault, derivationPath, "", ""); + key.addAccount("0xC0d97f61A84A0708225F15d54978D628Fe2C5E62", coinTypeBscLegacy, TWDerivationDefault, derivationPath, "", ""); + EXPECT_EQ(key.accounts.size(), 5ul); + } + + key.removeAccount(coinTypeBc); + key.removeAccount(coinTypeBnb); + key.removeAccount(coinTypeBsc); + key.removeAccount(coinTypeEth); + key.removeAccount(coinTypeBscLegacy); + EXPECT_EQ(key.accounts.size(), 0ul); +} + +TEST(StoredKey, AddRemoveAccountDerivation) { + auto key = StoredKey::createWithMnemonic("name", Data(), gMnemonic, TWStoredKeyEncryptionLevelDefault); + EXPECT_EQ(key.accounts.size(), 0ul); + + const auto derivationPath = DerivationPath("m/84'/0'/0'/0/0"); + { + key.addAccount("bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny", coinTypeBc, TWDerivationDefault, derivationPath, "", "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); + EXPECT_EQ(key.accounts.size(), 1ul); + } + { + key.addAccount("1NyRyFewhZcWMa9XCj3bBxSXPXyoSg8dKz", coinTypeBc, TWDerivationBitcoinLegacy, derivationPath, "", "xpub6CR52eaUuVb4kXAVyHC2i5ZuqJ37oWNPZFtjXaazFPXZD45DwWBYEBLdrF7fmCR9pgBuCA9Q57zZfyJjDUBDNtWkhWuGHNYKLgDHpqrHsxV"); + EXPECT_EQ(key.accounts.size(), 2ul); + } + + key.removeAccount(coinTypeBc, TWDerivationDefault); + EXPECT_EQ(key.accounts.size(), 1ul); + key.removeAccount(coinTypeBc, TWDerivationDefault); // try 2nd time + EXPECT_EQ(key.accounts.size(), 1ul); + key.removeAccount(coinTypeBc, TWDerivationBitcoinLegacy); + EXPECT_EQ(key.accounts.size(), 0ul); + key.removeAccount(coinTypeBc, TWDerivationBitcoinLegacy); // try 2nd time + EXPECT_EQ(key.accounts.size(), 0ul); +} + +TEST(StoredKey, AddRemoveAccountDerivationPath) { + auto key = StoredKey::createWithMnemonic("name", Data(), gMnemonic, TWStoredKeyEncryptionLevelDefault); + EXPECT_EQ(key.accounts.size(), 0ul); + + const auto derivationPath0 = DerivationPath("m/84'/0'/0'/0/0"); + { + key.addAccount("bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny", coinTypeBc, TWDerivationDefault, derivationPath0, "", "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); + EXPECT_EQ(key.accounts.size(), 1ul); + } + const auto derivationPath1 = DerivationPath("m/84'/0'/0'/1/0"); + { + key.addAccount("bc1qumuzptwdr6jlsqum8jnzz80rdg8nx6x29m2qpu", coinTypeBc, TWDerivationDefault, derivationPath1, "", "zpub6rxtad3SPT1C5GUDjPiKQ5oJN5DBeMbdUR7LrdYt12VbU7TBSpGUkdLvfVYGuj1N5edkDoZ3bu1fdN1HprQYfCBdsSH5CaAAygHGsanwtTe"); + EXPECT_EQ(key.accounts.size(), 2ul); + } + + key.removeAccount(coinTypeBc, derivationPath0); + EXPECT_EQ(key.accounts.size(), 1ul); + key.removeAccount(coinTypeBc, derivationPath0); // try 2nd time + EXPECT_EQ(key.accounts.size(), 1ul); + key.removeAccount(coinTypeBc, derivationPath1); + EXPECT_EQ(key.accounts.size(), 0ul); + key.removeAccount(coinTypeBc, derivationPath1); // try 2nd time + EXPECT_EQ(key.accounts.size(), 0ul); +} + +TEST(StoredKey, FixAddress) { + { + auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelDefault); + key.fixAddresses(gPassword); + } + { + const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); + auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", gPassword, coinTypeBc, privateKey); + key.fixAddresses(gPassword); + } +} + +TEST(StoredKey, WalletInvalid) { + const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); + auto key = StoredKey::createWithPrivateKeyAddDefaultAddress("name", gPassword, coinTypeBc, privateKey); + try { + auto wallet = key.wallet(gPassword); + } catch (std::invalid_argument&) { + // expected exception ok + return; + } + FAIL() << "Missing expected exception"; +} + +TEST(StoredKey, LoadNonexistent) { + ASSERT_THROW(StoredKey::load(testDataPath("nonexistent.json")), invalid_argument); +} + +TEST(StoredKey, LoadLegacyPrivateKey) { + const auto key = StoredKey::load(testDataPath("legacy-private-key.json")); + EXPECT_EQ(key.type, StoredKeyType::privateKey); + EXPECT_EQ(key.id, "3051ca7d-3d36-4a4a-acc2-09e9083732b0"); + EXPECT_EQ(key.accounts[0].coin, TWCoinTypeEthereum); + EXPECT_EQ(hex(key.payload.decrypt(TW::data("testpassword"))), "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"); +} + +TEST(StoredKey, LoadLivepeerKey) { + const auto key = StoredKey::load(testDataPath("livepeer.json")); + EXPECT_EQ(key.type, StoredKeyType::privateKey); + EXPECT_EQ(key.id, "70ea3601-ee21-4e94-a7e4-66255a987d22"); + EXPECT_EQ(key.accounts[0].coin, TWCoinTypeEthereum); + EXPECT_EQ(hex(key.payload.decrypt(TW::data("Radchenko"))), "09b4379d9a41a71d94ee36357bccb4d77b45e7fd9307e2c0f673dd54c0558c73"); +} + +TEST(StoredKey, LoadPBKDF2Key) { + const auto key = StoredKey::load(testDataPath("pbkdf2.json")); + EXPECT_EQ(key.type, StoredKeyType::privateKey); + EXPECT_EQ(key.id, "3198bc9c-6672-5ab3-d995-4942343ae5b6"); + + const auto& payload = key.payload; + ASSERT_TRUE(std::holds_alternative(payload.params.kdfParams)); + EXPECT_EQ(std::get(payload.params.kdfParams).desiredKeyLength, 32ul); + EXPECT_EQ(std::get(payload.params.kdfParams).iterations, 262144ul); + EXPECT_EQ(hex(std::get(payload.params.kdfParams).salt), "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"); + + EXPECT_EQ(hex(payload.decrypt(TW::data("testpassword"))), "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"); +} + +TEST(StoredKey, LoadLegacyMnemonic) { + const auto key = StoredKey::load(testDataPath("legacy-mnemonic.json")); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + EXPECT_EQ(key.id, "629aad29-0b22-488e-a0e7-b4219d4f311c"); + + const auto data = key.payload.decrypt(gPassword); + const auto mnemonic = string(reinterpret_cast(data.data())); + EXPECT_EQ(mnemonic, "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn back"); + + EXPECT_EQ(key.accounts[0].coin, TWCoinTypeEthereum); + EXPECT_EQ(key.accounts[0].derivationPath.string(), "m/44'/60'/0'/0/0"); + EXPECT_EQ(key.accounts[0].address, ""); + EXPECT_EQ(key.accounts[1].coin, coinTypeBc); + EXPECT_EQ(key.accounts[1].derivationPath.string(), "m/84'/0'/0'/0/0"); + EXPECT_EQ(key.accounts[1].address, ""); + EXPECT_EQ(key.accounts[1].extendedPublicKey, "zpub6r97AegwVxVbJeuDAWP5KQgX5y4Q6KyFUrsFQRn8yzSXrnmpwg1ZKHSWwECR1Kiqgr4h93WN5kdS48KC6hVFniuZHqVFXjULZZkCwurqyPn"); +} + +TEST(StoredKey, LoadFromWeb3j) { + const auto key = StoredKey::load(testDataPath("web3j.json")); + EXPECT_EQ(key.type, StoredKeyType::privateKey); + EXPECT_EQ(key.id, "86066d8c-8dba-4d81-afd4-934e2a2b72a2"); + const auto password = parse_hex("2d6eefbfbd4622efbfbdefbfbd516718efbfbdefbfbdefbfbdefbfbd59efbfbd30efbfbdefbfbd3a4348efbfbd2aefbfbdefbfbd49efbfbd27efbfbd0638efbfbdefbfbdefbfbd4cefbfbd6befbfbdefbfbd6defbfbdefbfbd63efbfbd5aefbfbd61262b70efbfbdefbfbdefbfbdefbfbdefbfbdc7aa373163417cefbfbdefbfbdefbfbd44efbfbdefbfbd1d10efbfbdefbfbdefbfbd61dc9e5b124befbfbd11efbfbdefbfbd2fefbfbdefbfbd3d7c574868efbfbdefbfbdefbfbd37043b7b5c1a436471592f02efbfbd18efbfbdefbfbd2befbfbdefbfbd7218efbfbd6a68efbfbdcb8e5f3328773ec48174efbfbd67efbfbdefbfbdefbfbdefbfbdefbfbd2a31efbfbd7f60efbfbdd884efbfbd57efbfbd25efbfbd590459efbfbd37efbfbd2bdca20fefbfbdefbfbdefbfbdefbfbd39450113efbfbdefbfbdefbfbd454671efbfbdefbfbdd49fefbfbd47efbfbdefbfbdefbfbdefbfbd00efbfbdefbfbdefbfbdefbfbd05203f4c17712defbfbd7bd1bbdc967902efbfbdc98a77efbfbd707a36efbfbd12efbfbdefbfbd57c78cefbfbdefbfbdefbfbd10efbfbdefbfbdefbfbde1a1bb08efbfbdefbfbd26efbfbdefbfbd58efbfbdefbfbdc4b1efbfbd295fefbfbd0eefbfbdefbfbdefbfbd0e6eefbfbd"); + const auto data = key.payload.decrypt(password); + EXPECT_EQ(hex(data), "043c5429c7872502531708ec0d821c711691402caf37ef7ba78a8c506f10653b"); +} + +TEST(StoredKey, ReadWallet) { + const auto key = StoredKey::load(testDataPath("key.json")); + + EXPECT_EQ(key.type, StoredKeyType::privateKey); + EXPECT_EQ(key.id, "e13b209c-3b2f-4327-bab0-3bef2e51630d"); + EXPECT_EQ(key.name, "Test Account"); + + const auto header = key.payload; + + EXPECT_EQ(header.params.cipher, "aes-128-ctr"); + EXPECT_EQ(hex(header.encrypted), "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c"); + EXPECT_EQ(hex(header._mac), "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"); + EXPECT_EQ(hex(header.params.cipherParams.iv), "83dbcc02d8ccb40e466191a123791e0e"); + + ASSERT_TRUE(std::holds_alternative(header.params.kdfParams)); + EXPECT_EQ(std::get(header.params.kdfParams).desiredKeyLength, 32ul); + EXPECT_EQ(std::get(header.params.kdfParams).n, 262144ul); + EXPECT_EQ(std::get(header.params.kdfParams).p, 8ul); + EXPECT_EQ(std::get(header.params.kdfParams).r, 1ul); + EXPECT_EQ(hex(std::get(header.params.kdfParams).salt), "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"); +} + +TEST(StoredKey, ReadMyEtherWallet) { + ASSERT_NO_THROW(StoredKey::load(testDataPath("myetherwallet.uu"))); +} + +TEST(StoredKey, InvalidPassword) { + const auto key = StoredKey::load(testDataPath("key.json")); + + ASSERT_THROW(key.payload.decrypt(gPassword), DecryptionError); +} + +TEST(StoredKey, EmptyAccounts) { + const auto key = StoredKey::load(testDataPath("empty-accounts.json")); + + ASSERT_NO_THROW(key.payload.decrypt(TW::data("testpassword"))); +} + +TEST(StoredKey, Decrypt) { + const auto key = StoredKey::load(testDataPath("key.json")); + const auto privateKey = key.payload.decrypt(TW::data("testpassword")); + + EXPECT_EQ(hex(privateKey), "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d"); +} + +TEST(StoredKey, CreateWallet) { + const auto privateKey = parse_hex("3a1076bf45ab87712ad64ccb3b10217737f7faacbf2872e88fdd9a537d8fe266"); + const auto key = StoredKey::createWithPrivateKey("name", gPassword, privateKey); + const auto decrypted = key.payload.decrypt(gPassword); + + EXPECT_EQ(hex(decrypted), hex(privateKey)); +} + +TEST(StoredKey, CreateAccounts) { + string mnemonicPhrase = "team engine square letter hero song dizzy scrub tornado fabric divert saddle"; + auto key = StoredKey::createWithMnemonic("name", gPassword, mnemonicPhrase, TWStoredKeyEncryptionLevelDefault); + const auto wallet = key.wallet(gPassword); + + EXPECT_EQ(key.account(TWCoinTypeEthereum, &wallet)->address, "0x494f60cb6Ac2c8F5E1393aD9FdBdF4Ad589507F7"); + EXPECT_EQ(key.account(TWCoinTypeEthereum, &wallet)->publicKey, "04cc32a479080d83fdcf69966713f0aad1bc1dc3ecf873b034894e84259841bc1c9b122717803e68905220ff54952d3f5ea2ab2698ca31f843addf94ae73fae9fd"); + EXPECT_EQ(key.account(TWCoinTypeEthereum, &wallet)->extendedPublicKey, ""); + + EXPECT_EQ(key.account(coinTypeBc, &wallet)->address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + EXPECT_EQ(key.account(coinTypeBc, &wallet)->publicKey, "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006"); + EXPECT_EQ(key.account(coinTypeBc, &wallet)->extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); +} + +TEST(StoredKey, DecodingEthereumAddress) { + const auto key = StoredKey::load(testDataPath("key.json")); + + EXPECT_EQ(key.accounts[0].address, "0x008AeEda4D805471dF9b2A5B0f38A0C3bCBA786b"); +} + +TEST(StoredKey, DecodingBitcoinAddress) { + const auto key = StoredKey::load(testDataPath("key_bitcoin.json")); + + EXPECT_EQ(key.accounts[0].address, "3PWazDi9n1Hfyq9gXFxDxzADNL8RNYyK2y"); +} + +TEST(StoredKey, RemoveAccount) { + auto key = StoredKey::load(testDataPath("legacy-mnemonic.json")); + EXPECT_EQ(key.accounts.size(), 2ul); + key.removeAccount(TWCoinTypeEthereum); + EXPECT_EQ(key.accounts.size(), 1ul); + EXPECT_EQ(key.accounts[0].coin, coinTypeBc); +} + +TEST(StoredKey, MissingAddressFix) { + auto key = StoredKey::load(testDataPath("missing-address.json")); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + + const auto wallet = key.wallet(gPassword); + EXPECT_EQ(wallet.getMnemonic(), "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"); + EXPECT_TRUE(Mnemonic::isValid(wallet.getMnemonic())); + + EXPECT_EQ(key.account(TWCoinTypeBitcoin)->address, ""); + EXPECT_EQ(key.account(TWCoinTypeEthereum)->address, ""); + + key.fixAddresses(gPassword); + + EXPECT_EQ(key.account(TWCoinTypeEthereum, nullptr)->address, "0xA3Dcd899C0f3832DFDFed9479a9d828c6A4EB2A7"); + EXPECT_EQ(key.account(TWCoinTypeEthereum, nullptr)->publicKey, "0448a9ffac8022f1c7eb5253746e24d11d9b6b2737c0aecd48335feabb95a179916b1f3a97bed6740a85a2d11c663d38566acfb08af48a47ce0c835c65c9b23d0d"); + EXPECT_EQ(key.account(coinTypeBc, nullptr)->address, "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + EXPECT_EQ(key.account(coinTypeBc, nullptr)->publicKey, "02df9ef2a7a5552765178b181e1e1afdefc7849985c7dfe9647706dd4fa40df6ac"); +} + +TEST(StoredKey, MissingAddressReadd) { + auto key = StoredKey::load(testDataPath("missing-address.json")); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + + const auto wallet = key.wallet(gPassword); + EXPECT_EQ(wallet.getMnemonic(), "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"); + EXPECT_TRUE(Mnemonic::isValid(wallet.getMnemonic())); + + EXPECT_EQ(key.account(TWCoinTypeBitcoin)->address, ""); + EXPECT_EQ(key.account(TWCoinTypeEthereum)->address, ""); + + // get accounts, this will also fill addresses as they are empty + const auto btcAccount = key.account(TWCoinTypeBitcoin, &wallet); + const auto ethAccount = key.account(TWCoinTypeEthereum, &wallet); + + EXPECT_TRUE(btcAccount.has_value()); + EXPECT_EQ(btcAccount->address, "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"); + EXPECT_TRUE(ethAccount.has_value()); + EXPECT_EQ(ethAccount->address, "0xA3Dcd899C0f3832DFDFed9479a9d828c6A4EB2A7"); +} + +TEST(StoredKey, EtherWalletAddressNo0x) { + auto key = StoredKey::load(testDataPath("ethereum-wallet-address-no-0x.json")); + key.fixAddresses(TW::data("15748c4e3dca6ae2110535576ab0c398cb79d985707c68ee6c9f9df9d421dd53")); + const auto account = key.account(TWCoinTypeEthereum, nullptr); + EXPECT_EQ(account->address, "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309"); + EXPECT_EQ(account->publicKey, "0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91"); +} + +TEST(StoredKey, CreateMinimalEncryptionParameters) { + const auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelMinimal); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + const Data& mnemo2Data = key.payload.decrypt(gPassword); + EXPECT_EQ(string(mnemo2Data.begin(), mnemo2Data.end()), string(gMnemonic)); + EXPECT_EQ(key.accounts.size(), 0ul); + EXPECT_EQ(key.wallet(gPassword).getMnemonic(), string(gMnemonic)); + + const auto json = key.json(); + + EXPECT_EQ(json["crypto"]["kdf"], "scrypt"); + EXPECT_EQ(json["crypto"]["kdfparams"]["n"], 4096); + + // load it back + const auto key2 = StoredKey::createWithJson(json); + EXPECT_EQ(key2.wallet(gPassword).getMnemonic(), string(gMnemonic)); +} + +TEST(StoredKey, CreateWeakEncryptionParameters) { + const auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelWeak); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + const Data& mnemo2Data = key.payload.decrypt(gPassword); + EXPECT_EQ(string(mnemo2Data.begin(), mnemo2Data.end()), string(gMnemonic)); + EXPECT_EQ(key.accounts.size(), 0ul); + EXPECT_EQ(key.wallet(gPassword).getMnemonic(), string(gMnemonic)); + + const auto json = key.json(); + + EXPECT_EQ(json["crypto"]["kdf"], "scrypt"); + EXPECT_EQ(json["crypto"]["kdfparams"]["n"], 16384); + + // load it back + const auto key2 = StoredKey::createWithJson(json); + EXPECT_EQ(key2.wallet(gPassword).getMnemonic(), string(gMnemonic)); +} + +TEST(StoredKey, CreateStandardEncryptionParameters) { + const auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelStandard); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + const Data& mnemo2Data = key.payload.decrypt(gPassword); + EXPECT_EQ(string(mnemo2Data.begin(), mnemo2Data.end()), string(gMnemonic)); + EXPECT_EQ(key.accounts.size(), 0ul); + EXPECT_EQ(key.wallet(gPassword).getMnemonic(), string(gMnemonic)); + + const auto json = key.json(); + + EXPECT_EQ(json["crypto"]["kdf"], "scrypt"); + EXPECT_EQ(json["crypto"]["kdfparams"]["n"], 262144); + + // load it back + const auto key2 = StoredKey::createWithJson(json); + EXPECT_EQ(key2.wallet(gPassword).getMnemonic(), string(gMnemonic)); +} + +TEST(StoredKey, CreateMultiAccounts) { // Multiple accounts for the same coin + auto key = StoredKey::createWithMnemonic("name", gPassword, gMnemonic, TWStoredKeyEncryptionLevelDefault); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + const Data& mnemo2Data = key.payload.decrypt(gPassword); + EXPECT_EQ(string(mnemo2Data.begin(), mnemo2Data.end()), string(gMnemonic)); + EXPECT_EQ(key.wallet(gPassword).getMnemonic(), string(gMnemonic)); + EXPECT_EQ(key.accounts.size(), 0ul); + + const auto expectedBtc1 = "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"; + const auto expectedBtc2 = "1NyRyFewhZcWMa9XCj3bBxSXPXyoSg8dKz"; + const auto expectedSol1 = "HiipoCKL8hX2RVmJTz3vaLy34hS2zLhWWMkUWtw85TmZ"; + const auto wallet = key.wallet(gPassword); + auto expectedAccounts = 0ul; + + { // Create default Bitcoin account + const auto coin = TWCoinTypeBitcoin; + + const auto btc1 = key.account(coin, &wallet); + + EXPECT_TRUE(btc1.has_value()); + EXPECT_EQ(btc1->address, expectedBtc1); + EXPECT_EQ(btc1->derivationPath.string(), "m/84'/0'/0'/0/0"); + EXPECT_EQ(btc1->extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); + EXPECT_EQ(key.accounts.size(), ++expectedAccounts); + EXPECT_EQ(key.accounts[expectedAccounts - 1].address, expectedBtc1); + EXPECT_EQ(key.account(coin)->address, expectedBtc1); + EXPECT_EQ(key.getAccounts(coin).size(), 1ul); + EXPECT_EQ(key.getAccounts(coin)[0].address, expectedBtc1); + } + { // Create default Solana account + const auto coin = TWCoinTypeSolana; + + const auto sol1 = key.account(coin, &wallet); + + EXPECT_TRUE(sol1.has_value()); + EXPECT_EQ(sol1->address, expectedSol1); + EXPECT_EQ(sol1->derivationPath.string(), "m/44'/501'/0'"); + EXPECT_EQ(key.accounts.size(), ++expectedAccounts); + EXPECT_EQ(key.accounts[expectedAccounts - 1].address, expectedSol1); + EXPECT_EQ(key.account(coin)->address, expectedSol1); + EXPECT_EQ(key.getAccounts(coin).size(), 1ul); + EXPECT_EQ(key.getAccounts(coin)[0].address, expectedSol1); + } + { // Create alternative P2PK Bitcoin account (different address format) + const auto coin = TWCoinTypeBitcoin; + + const auto btc2 = key.account(coin, TWDerivationBitcoinLegacy, wallet); + + EXPECT_EQ(btc2.address, expectedBtc2); + EXPECT_EQ(btc2.derivationPath.string(), "m/44'/0'/0'/0/0"); + EXPECT_EQ(btc2.extendedPublicKey, "xpub6CR52eaUuVb4kXAVyHC2i5ZuqJ37oWNPZFtjXaazFPXZD45DwWBYEBLdrF7fmCR9pgBuCA9Q57zZfyJjDUBDNtWkhWuGHNYKLgDHpqrHsxV"); + EXPECT_EQ(key.accounts.size(), ++expectedAccounts); + EXPECT_EQ(key.accounts[expectedAccounts - 1].address, expectedBtc2); + EXPECT_EQ(key.account(coin)->address, expectedBtc1); + EXPECT_EQ(key.account(coin, TWDerivationBitcoinLegacy, wallet).address, expectedBtc2); + EXPECT_EQ(key.getAccounts(coin).size(), 2ul); + EXPECT_EQ(key.getAccounts(coin)[0].address, expectedBtc1); + EXPECT_EQ(key.getAccounts(coin)[1].address, expectedBtc2); + } + { // Create alternative Solana account with non-default derivation path (different derivation path and address) + const auto coin = TWCoinTypeSolana; + + const auto sol2 = key.account(coin, TWDerivationSolanaSolana, wallet); + + const auto expectedSol2 = "CgWJeEWkiYqosy1ba7a3wn9HAQuHyK48xs3LM4SSDc1C"; + EXPECT_EQ(sol2.address, expectedSol2); + EXPECT_EQ(sol2.derivationPath.string(), "m/44'/501'/0'/0'"); + EXPECT_EQ(key.accounts.size(), ++expectedAccounts); + EXPECT_EQ(key.accounts[expectedAccounts - 1].address, expectedSol2); + // Now we have 2 Solana addresses, 1st is returned here + EXPECT_EQ(key.account(coin)->address, expectedSol1); + EXPECT_EQ(key.account(coin, TWDerivationSolanaSolana, wallet).address, expectedSol2); + EXPECT_EQ(key.getAccounts(coin).size(), 2ul); + EXPECT_EQ(key.getAccounts(coin)[0].address, expectedSol1); + EXPECT_EQ(key.getAccounts(coin)[1].address, expectedSol2); + } + { // Create CUSTOM account with alternative Bitcoin address. Note: this is not recommended. + const auto coin = TWCoinTypeBitcoin; + const auto customPath = DerivationPath("m/44'/2'/0'/0/0"); + const auto btcPrivateKey = wallet.getKey(coin, customPath); + EXPECT_NE(TW::deriveAddress(coin, btcPrivateKey), expectedBtc1); + const auto btcPublicKey = btcPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + const auto p2pkhBtcAddress = Bitcoin::Address(btcPublicKey, TWCoinTypeP2pkhPrefix(coin)).string(); + const auto expectedBtc3 = "1C43YUWSYTgaoBEsRffAkzF6HruJegEqP5"; + EXPECT_EQ(p2pkhBtcAddress, expectedBtc3); + const auto extendedPublicKey = wallet.getExtendedPublicKey(TW::purpose(coin), coin, TWHDVersionZPUB); + EXPECT_EQ(extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); + + key.addAccount(p2pkhBtcAddress, coin, TWDerivationCustom, customPath, hex(btcPublicKey.bytes), extendedPublicKey); + + EXPECT_EQ(key.accounts.size(), ++expectedAccounts); + EXPECT_EQ(key.accounts[expectedAccounts - 1].address, expectedBtc3); + // Now we have 2 Bitcoin addresses, 1st is returned here + EXPECT_EQ(key.account(coin)->address, expectedBtc1); + EXPECT_EQ(key.getAccounts(coin).size(), 3ul); + EXPECT_EQ(key.getAccounts(coin)[0].address, expectedBtc1); + EXPECT_EQ(key.getAccounts(coin)[1].address, expectedBtc2); + EXPECT_EQ(key.getAccounts(coin)[2].address, expectedBtc3); + EXPECT_EQ(key.getAccounts(coin)[2].derivationPath.string(), "m/44'/2'/0'/0/0"); + } +} + +TEST(StoredKey, CreateWithMnemonicAlternativeDerivation) { + const auto coin = TWCoinTypeSolana; + auto key = StoredKey::createWithMnemonicAddDefaultAddress("name", gPassword, gMnemonic, coin); + EXPECT_EQ(key.type, StoredKeyType::mnemonicPhrase); + + ASSERT_EQ(key.accounts.size(), 1ul); + EXPECT_EQ(key.accounts[0].coin, coin); + EXPECT_EQ(key.accounts[0].address, "HiipoCKL8hX2RVmJTz3vaLy34hS2zLhWWMkUWtw85TmZ"); + EXPECT_EQ(key.accounts[0].publicKey, "f86b18399096c8134dd185f1e72dd7e26528772a2a998abfd81c5f8c547223d0"); + EXPECT_EQ(hex(key.privateKey(coin, gPassword).bytes), "d81b5c525979e487736b69cb84ed8331559de17294f38491b304555c26687e83"); + EXPECT_EQ(hex(key.privateKey(coin, TWDerivationDefault, gPassword).bytes), "d81b5c525979e487736b69cb84ed8331559de17294f38491b304555c26687e83"); + ASSERT_EQ(key.accounts.size(), 1ul); + + // alternative derivation, different keys + EXPECT_EQ(hex(key.privateKey(coin, TWDerivationSolanaSolana, gPassword).bytes), "d49a5fa7f77593534c7afd2ba8dc8e9d8b007bc6ec65fe8df25ffe6fafc57151"); + + ASSERT_EQ(key.accounts.size(), 2ul); + EXPECT_EQ(key.accounts[1].coin, coin); + EXPECT_EQ(key.accounts[1].address, "CgWJeEWkiYqosy1ba7a3wn9HAQuHyK48xs3LM4SSDc1C"); + EXPECT_EQ(key.accounts[1].publicKey, "ad8f57924dce62f9040f93b4f6ce3c3d39afde7e29bcb4013dad59db7913c4c7"); + EXPECT_EQ(hex(key.privateKey(coin, TWDerivationSolanaSolana, gPassword).bytes), "d49a5fa7f77593534c7afd2ba8dc8e9d8b007bc6ec65fe8df25ffe6fafc57151"); +} + +} // namespace TW::Keystore diff --git a/tests/MnemonicTests.cpp b/tests/common/MnemonicTests.cpp similarity index 100% rename from tests/MnemonicTests.cpp rename to tests/common/MnemonicTests.cpp diff --git a/tests/common/NumericLiteralTests.cpp b/tests/common/NumericLiteralTests.cpp new file mode 100644 index 00000000000..07813e2f48e --- /dev/null +++ b/tests/common/NumericLiteralTests.cpp @@ -0,0 +1,14 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "NumericLiteral.h" + +#include + +TEST(UzLitteralOperator, SizetEquality) { + [[maybe_unused]] auto size = 42_uz; + static_assert(std::is_same_v); +} \ No newline at end of file diff --git a/tests/common/PrivateKeyTests.cpp b/tests/common/PrivateKeyTests.cpp new file mode 100644 index 00000000000..f969ce40c8e --- /dev/null +++ b/tests/common/PrivateKeyTests.cpp @@ -0,0 +1,375 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Hash.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" + +#include + +using namespace TW; +using namespace std; + +namespace TW::tests { + +TEST(PrivateKey, CreateValid) { + Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveSECP256k1)); + auto privateKey = PrivateKey(privKeyData); + EXPECT_EQ(hex(privKeyData), hex(privateKey.bytes)); +} + +string TestInvalid(const Data& privKeyData) { + try { + auto privateKey = PrivateKey(privKeyData); + return hex(privateKey.bytes); + } catch (invalid_argument& ex) { + // expected exception + return string("EXCEPTION: ") + string(ex.what()); + } +} + +TEST(PrivateKey, InvalidShort) { + string res = TestInvalid(parse_hex("deadbeef")); + EXPECT_EQ("EXCEPTION: Invalid private key data", res); +} + +TEST(PrivateKey, InvalidAllZeros) { + string res = TestInvalid(Data(32)); + EXPECT_EQ("EXCEPTION: Invalid private key data", res); +} + +TEST(PrivateKey, InvalidSECP256k1) { + { + auto privKeyData = parse_hex("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"); + auto valid = PrivateKey::isValid(privKeyData, TWCurveSECP256k1); + EXPECT_EQ(valid, false); + } + { + auto privKeyData = parse_hex("0000000000000000000000000000000000000000000000000000000000000000"); + auto valid = PrivateKey::isValid(privKeyData, TWCurveSECP256k1); + EXPECT_EQ(valid, false); + } +} + +string TestInvalidExtended(const Data& data, const Data& ext, const Data& chainCode, const Data& data2, const Data& ext2, const Data& chainCode2) { + try { + auto privateKey = PrivateKey(data, ext, chainCode, data2, ext2, chainCode2); + return hex(privateKey.bytes); + } catch (invalid_argument& ex) { + // expected exception + return string("EXCEPTION: ") + string(ex.what()); + } +} + +TEST(PrivateKey, CreateExtendedInvalid) { + { + string res = TestInvalidExtended( + parse_hex("deadbeed"), + parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), + parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111")); + EXPECT_EQ("EXCEPTION: Invalid private key or extended key data", res); + } + { + string res = TestInvalidExtended( + parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), + parse_hex("deadbeed"), + parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111")); + EXPECT_EQ("EXCEPTION: Invalid private key or extended key data", res); + } + { + string res = TestInvalidExtended( + parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), + parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), + parse_hex("deadbeed"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111")); + EXPECT_EQ("EXCEPTION: Invalid private key or extended key data", res); + } + { + string res = TestInvalidExtended( + parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), + parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), + parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4"), + parse_hex("deadbeed"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111"), + parse_hex("1111111111111111111111111111111111111111111111111111111111111111")); + EXPECT_EQ("EXCEPTION: Invalid private key or extended key data", res); + } +} + +TEST(PrivateKey, Valid) { + Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveSECP256k1)); + EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveED25519)); +} + +TEST(PrivateKey, PublicKey) { + Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = PrivateKey(privKeyData); + { + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + EXPECT_EQ( + "4870d56d074c50e891506d78faa4fb69ca039cc5f131eb491e166b975880e867", + hex(publicKey.bytes)); + } + { + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ( + "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1", + hex(publicKey.bytes)); + } + { + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ( + "0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91", + hex(publicKey.bytes)); + } + { + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1Extended); + EXPECT_EQ( + "046d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab918b4fe46ccbf56701fb210d67d91c5779468f6b3fdc7a63692b9b62543f47ae", + hex(publicKey.bytes)); + } +} + +TEST(PrivateKey, Cleanup) { + Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = new PrivateKey(privKeyData); + auto ptr = privateKey->bytes.data(); + ASSERT_EQ(hex(privKeyData), hex(data(ptr, 32))); + + privateKey->cleanup(); + + // Memory cleaned (filled with 0s). They may be overwritten by something else; we check that it is not equal to original, most of it has changed. + ASSERT_EQ(hex(data(ptr, 32)), "0000000000000000000000000000000000000000000000000000000000000000"); + + delete privateKey; + + // Note: it would be good to check the memory area after deletion of the object, but this is not possible +} + +TEST(PrivateKey, GetType) { + EXPECT_EQ(PrivateKey::getType(TWCurveSECP256k1), TWPrivateKeyTypeDefault); + EXPECT_EQ(PrivateKey::getType(TWCurveNIST256p1), TWPrivateKeyTypeDefault); + EXPECT_EQ(PrivateKey::getType(TWCurveED25519), TWPrivateKeyTypeDefault); + EXPECT_EQ(PrivateKey::getType(TWCurveCurve25519), TWPrivateKeyTypeDefault); + + EXPECT_EQ(PrivateKey::getType(TWCurveED25519ExtendedCardano), TWPrivateKeyTypeCardano); +} + +TEST(PrivateKey, PrivateKeyExtended) { + // Non-extended: both keys are 32 bytes. + auto privateKeyNonext = PrivateKey(parse_hex( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + EXPECT_EQ("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5", hex(privateKeyNonext.bytes)); + auto publicKeyNonext = privateKeyNonext.getPublicKey(TWPublicKeyTypeED25519); + EXPECT_EQ(32ul, publicKeyNonext.bytes.size()); + + const auto fullkey = + "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744" + "309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff" + "bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4" + "639aadd8b6499ae39b78018b79255fbd8f585cbda9cbb9e907a72af86afb7a05" + "d41a57c2dec9a6a19d6bf3b1fa784f334f3a0048d25ccb7b78a7b44066f9ba7b" + "ed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a"; + // Extended keys: private key is 2x3x32 bytes, public key is 2x64 bytes + auto privateKeyExt = PrivateKey(parse_hex(fullkey)); + EXPECT_EQ(fullkey, hex(privateKeyExt.bytes)); + EXPECT_EQ("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744", hex(privateKeyExt.key())); + EXPECT_EQ("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff", hex(privateKeyExt.extension())); + EXPECT_EQ("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4", hex(privateKeyExt.chainCode())); + EXPECT_EQ("639aadd8b6499ae39b78018b79255fbd8f585cbda9cbb9e907a72af86afb7a05", hex(privateKeyExt.secondKey())); + EXPECT_EQ("d41a57c2dec9a6a19d6bf3b1fa784f334f3a0048d25ccb7b78a7b44066f9ba7b", hex(privateKeyExt.secondExtension())); + EXPECT_EQ("ed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a", hex(privateKeyExt.secondChainCode())); + + auto publicKeyExt = privateKeyExt.getPublicKey(TWPublicKeyTypeED25519Cardano); + EXPECT_EQ(2 * 64ul, publicKeyExt.bytes.size()); + + // Try other constructor for extended key + auto privateKeyExtOne = PrivateKey( + parse_hex("b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744"), + parse_hex("309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71eff"), + parse_hex("bf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4"), + parse_hex("639aadd8b6499ae39b78018b79255fbd8f585cbda9cbb9e907a72af86afb7a05"), + parse_hex("d41a57c2dec9a6a19d6bf3b1fa784f334f3a0048d25ccb7b78a7b44066f9ba7b"), + parse_hex("ed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a")); + EXPECT_EQ(fullkey, hex(privateKeyExt.bytes)); +} + +TEST(PrivateKey, PrivateKeyExtendedError) { + // TWPublicKeyTypeED25519Cardano pubkey with non-extended private: error + auto privateKeyNonext = PrivateKey(parse_hex( + "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + try { + auto publicKeyError = privateKeyNonext.getPublicKey(TWPublicKeyTypeED25519Cardano); + } catch (invalid_argument& ex) { + // expected exception + return; + } + FAIL() << "Should throw Invalid empty key extension"; +} + +TEST(PrivateKey, getSharedKey) { + Data privKeyData = parse_hex("9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0"); + EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveSECP256k1)); + auto privateKey = PrivateKey(privKeyData); + + const Data pubKeyData = parse_hex("02a18a98316b5f52596e75bfa5ca9fa9912edd0c989b86b73d41bb64c9c6adb992"); + EXPECT_TRUE(PublicKey::isValid(pubKeyData, TWPublicKeyTypeSECP256k1)); + PublicKey publicKey(pubKeyData, TWPublicKeyTypeSECP256k1); + EXPECT_TRUE(publicKey.isCompressed()); + + const Data derivedKeyData = privateKey.getSharedKey(publicKey, TWCurveSECP256k1); + + EXPECT_EQ( + "ef2cf705af8714b35c0855030f358f2bee356ff3579cea2607b2025d80133c3a", + hex(derivedKeyData)); +} + +/** + * Valid test vector from Wycherproof project + * Source: https://github.com/google/wycheproof/blob/master/testvectors/ecdh_secp256k1_test.json#L31 + */ +TEST(PrivateKey, getSharedKeyWycherproof) { + // Stripped left-padded zeroes from: `00f4b7ff7cccc98813a69fae3df222bfe3f4e28f764bf91b4a10d8096ce446b254` + Data privKeyData = parse_hex("f4b7ff7cccc98813a69fae3df222bfe3f4e28f764bf91b4a10d8096ce446b254"); + EXPECT_TRUE(PrivateKey::isValid(privKeyData, TWCurveSECP256k1)); + auto privateKey = PrivateKey(privKeyData); + + // Decoded from ASN.1 & uncompressed `3056301006072a8648ce3d020106052b8104000a03420004d8096af8a11e0b80037e1ee68246b5dcbb0aeb1cf1244fd767db80f3fa27da2b396812ea1686e7472e9692eaf3e958e50e9500d3b4c77243db1f2acd67ba9cc4` + const Data pubKeyData = parse_hex("02d8096af8a11e0b80037e1ee68246b5dcbb0aeb1cf1244fd767db80f3fa27da2b"); + EXPECT_TRUE(PublicKey::isValid(pubKeyData, TWPublicKeyTypeSECP256k1)); + PublicKey publicKey(pubKeyData, TWPublicKeyTypeSECP256k1); + EXPECT_TRUE(publicKey.isCompressed()); + + const Data derivedKeyData = privateKey.getSharedKey(publicKey, TWCurveSECP256k1); + + // SHA-256 of encoded x-coordinate `02544dfae22af6af939042b1d85b71a1e49e9a5614123c4d6ad0c8af65baf87d65` + EXPECT_EQ( + "81165066322732362ca5d3f0991d7f1f7d0aad7ea533276496785d369e35159a", + hex(derivedKeyData)); +} + +TEST(PrivateKey, getSharedKeyBidirectional) { + Data privKeyData1 = parse_hex("9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0"); + EXPECT_TRUE(PrivateKey::isValid(privKeyData1, TWCurveSECP256k1)); + auto privateKey1 = PrivateKey(privKeyData1); + auto publicKey1 = privateKey1.getPublicKey(TWPublicKeyTypeSECP256k1); + + Data privKeyData2 = parse_hex("ef2cf705af8714b35c0855030f358f2bee356ff3579cea2607b2025d80133c3a"); + EXPECT_TRUE(PrivateKey::isValid(privKeyData2, TWCurveSECP256k1)); + auto privateKey2 = PrivateKey(privKeyData2); + auto publicKey2 = privateKey2.getPublicKey(TWPublicKeyTypeSECP256k1); + + const Data derivedKeyData1 = privateKey1.getSharedKey(publicKey2, TWCurveSECP256k1); + const Data derivedKeyData2 = privateKey2.getSharedKey(publicKey1, TWCurveSECP256k1); + + EXPECT_EQ(hex(derivedKeyData1), hex(derivedKeyData2)); +} + +TEST(PrivateKey, getSharedKeyError) { + Data privKeyData = parse_hex("9cd3b16e10bd574fed3743d8e0de0b7b4e6c69f3245ab5a168ef010d22bfefa0"); + auto privateKey = PrivateKey(privKeyData); + + const Data pubKeyData = parse_hex("02a18a98316b5f52596e75bfa5ca9fa9912edd0c989b86b73d41bb64c9c6adb992"); + PublicKey publicKey(pubKeyData, TWPublicKeyTypeSECP256k1); + + const Data derivedKeyData = privateKey.getSharedKey(publicKey, TWCurveCurve25519); + const Data expected = {}; + + EXPECT_EQ(expected, derivedKeyData); +} + +TEST(PrivateKey, SignSECP256k1) { + Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = PrivateKey(privKeyData); + Data messageData = TW::data("hello"); + Data hash = Hash::keccak256(messageData); + Data actual = privateKey.sign(hash, TWCurveSECP256k1); + + EXPECT_EQ( + "8720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba624d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de901", + hex(actual)); +} + +TEST(PrivateKey, SignExtended) { + const auto privateKeyExt = PrivateKey(parse_hex( + "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4" + "639aadd8b6499ae39b78018b79255fbd8f585cbda9cbb9e907a72af86afb7a05d41a57c2dec9a6a19d6bf3b1fa784f334f3a0048d25ccb7b78a7b44066f9ba7bed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a")); + Data messageData = TW::data("hello"); + Data hash = Hash::keccak256(messageData); + Data actual = privateKeyExt.sign(hash, TWCurveED25519ExtendedCardano); + + EXPECT_EQ( + "375df53b6a4931dcf41e062b1c64288ed4ff3307f862d5c1b1c71964ce3b14c99422d0fdfeb2807e9900a26d491d5e8a874c24f98eec141ed694d7a433a90f08", + hex(actual)); +} + +TEST(PrivateKey, SignSchnorr) { + const auto privateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + const Data messageData = TW::data("hello schnorr"); + const Data digest = Hash::sha256(messageData); + const auto signature = privateKey.signZilliqa(digest); + EXPECT_EQ(hex(signature), + "b8118ccb99563fe014279c957b0a9d563c1666e00367e9896fe541765246964f64a53052513da4e6dc20fdaf69ef0d95b4ca51c87ad3478986cf053c2dd0b853"); +} + +TEST(PrivateKey, SignNIST256p1) { + Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = PrivateKey(privKeyData); + Data messageData = TW::data("hello"); + Data hash = Hash::keccak256(messageData); + Data actual = privateKey.sign(hash, TWCurveNIST256p1); + + EXPECT_EQ( + "8859e63a0c0cc2fc7f788d7e78406157b288faa6f76f76d37c4cd1534e8d83c468f9fd6ca7dde378df594625dcde98559389569e039282275e3d87c26e36447401", + hex(actual)); +} + +int isCanonical([[maybe_unused]] uint8_t by, [[maybe_unused]] uint8_t sig[64]) { + return 1; +} + +TEST(PrivateKey, SignCanonicalSECP256k1) { + Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = PrivateKey(privKeyData); + Data messageData = TW::data("hello"); + Data hash = Hash::keccak256(messageData); + Data actual = privateKey.sign(hash, TWCurveSECP256k1, isCanonical); + + EXPECT_EQ( + "208720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba624d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de9", + hex(actual)); +} + +TEST(PrivateKey, SignShortDigest) { + Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = PrivateKey(privKeyData); + Data shortDigest = TW::data("12345"); + { + Data actual = privateKey.sign(shortDigest, TWCurveSECP256k1); + EXPECT_EQ(actual.size(), 0ul); + } + { + Data actual = privateKey.sign(shortDigest, TWCurveNIST256p1); + EXPECT_EQ(actual.size(), 0ul); + } + { + Data actual = privateKey.sign(shortDigest, TWCurveSECP256k1, isCanonical); + EXPECT_EQ(actual.size(), 0ul); + } +} + +} // namespace TW::tests diff --git a/tests/common/PublicKeyTests.cpp b/tests/common/PublicKeyTests.cpp new file mode 100644 index 00000000000..1dae03fb237 --- /dev/null +++ b/tests/common/PublicKeyTests.cpp @@ -0,0 +1,356 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "PublicKey.h" + +#include "Hash.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "TestUtilities.h" + +#include + +using namespace TW; + +TEST(PublicKeyTests, CreateFromPrivateSecp256k1) { + const Data key = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = PrivateKey(key); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(publicKey.bytes.size(), 33ul); + EXPECT_EQ(hex(publicKey.bytes), "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); + EXPECT_EQ(publicKey.isCompressed(), true); + EXPECT_TRUE(PublicKey::isValid(publicKey.bytes, TWPublicKeyTypeSECP256k1)); +} + +TEST(PublicKeyTests, CreateFromDataSecp256k1) { + const Data key = parse_hex("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); + PublicKey publicKey(key, TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(publicKey.bytes), hex(key)); +} + +TEST(PublicKeyTests, CreateInvalid) { + const Data keyInvalid = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32af"); // too short + try { + PublicKey publicKey(keyInvalid, TWPublicKeyTypeSECP256k1); + } catch (const std::invalid_argument&) { + return; // OK + } + FAIL() << "Missing expected exception"; +} + +TEST(PublicKeyTests, CreateBlake) { + const auto privateKeyHex = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + const auto publicKeyKeyHex = "b689ab808542e13f3d2ec56fe1efe43a1660dcadc73ce489fde7df98dd8ce5d9"; + { + auto publicKey = PrivateKey(parse_hex(privateKeyHex)).getPublicKey(TWPublicKeyTypeED25519Blake2b); + EXPECT_EQ(hex(publicKey.bytes), publicKeyKeyHex); + EXPECT_EQ(publicKey.bytes.size(), 32ul); + } + { + const auto publicKey = PublicKey(parse_hex(publicKeyKeyHex), TWPublicKeyTypeED25519Blake2b); + EXPECT_EQ(hex(publicKey.bytes), publicKeyKeyHex); + } +} + +TEST(PublicKeyTests, CompressedExtended) { + const Data key = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = PrivateKey(key); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(publicKey.type, TWPublicKeyTypeSECP256k1); + EXPECT_EQ(publicKey.bytes.size(), 33ul); + EXPECT_EQ(publicKey.isCompressed(), true); + EXPECT_TRUE(PublicKey::isValid(publicKey.bytes, TWPublicKeyTypeSECP256k1)); + EXPECT_EQ(hex(publicKey.bytes), std::string("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1")); + + auto extended = publicKey.extended(); + EXPECT_EQ(extended.type, TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(extended.bytes.size(), 65ul); + EXPECT_EQ(extended.isCompressed(), false); + EXPECT_TRUE(PublicKey::isValid(extended.bytes, TWPublicKeyTypeSECP256k1Extended)); + EXPECT_EQ(hex(extended.bytes), std::string("0499c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c166b489a4b7c491e7688e6ebea3a71fc3a1a48d60f98d5ce84c93b65e423fde91")); + + auto compressed = extended.compressed(); + EXPECT_EQ(compressed.type, TWPublicKeyTypeSECP256k1); + EXPECT_TRUE(compressed == publicKey); + EXPECT_EQ(compressed.bytes.size(), 33ul); + EXPECT_EQ(compressed.isCompressed(), true); + EXPECT_TRUE(PublicKey::isValid(compressed.bytes, TWPublicKeyTypeSECP256k1)); + EXPECT_EQ(hex(compressed.bytes), std::string("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1")); + + auto extended2 = extended.extended(); + EXPECT_EQ(extended2.type, TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(extended2.bytes.size(), 65ul); + EXPECT_EQ(extended2.isCompressed(), false); + + auto compressed2 = compressed.compressed(); + EXPECT_EQ(compressed2.type, TWPublicKeyTypeSECP256k1); + EXPECT_TRUE(compressed2 == publicKey); + EXPECT_EQ(compressed2.bytes.size(), 33ul); + EXPECT_EQ(compressed2.isCompressed(), true); +} + +TEST(PublicKeyTests, CompressedExtendedNist) { + const Data key = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = PrivateKey(key); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1); + EXPECT_EQ(publicKey.type, TWPublicKeyTypeNIST256p1); + EXPECT_EQ(publicKey.bytes.size(), 33ul); + EXPECT_EQ(publicKey.isCompressed(), true); + EXPECT_TRUE(PublicKey::isValid(publicKey.bytes, TWPublicKeyTypeNIST256p1)); + EXPECT_EQ(hex(publicKey.bytes), std::string("026d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab")); + + auto extended = publicKey.extended(); + EXPECT_EQ(extended.type, TWPublicKeyTypeNIST256p1Extended); + EXPECT_EQ(extended.bytes.size(), 65ul); + EXPECT_EQ(extended.isCompressed(), false); + EXPECT_TRUE(PublicKey::isValid(extended.bytes, TWPublicKeyTypeNIST256p1Extended)); + EXPECT_EQ(hex(extended.bytes), std::string("046d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab918b4fe46ccbf56701fb210d67d91c5779468f6b3fdc7a63692b9b62543f47ae")); + + auto compressed = extended.compressed(); + EXPECT_EQ(compressed.type, TWPublicKeyTypeNIST256p1); + EXPECT_TRUE(compressed == publicKey); + EXPECT_EQ(compressed.bytes.size(), 33ul); + EXPECT_EQ(compressed.isCompressed(), true); + EXPECT_TRUE(PublicKey::isValid(compressed.bytes, TWPublicKeyTypeNIST256p1)); + EXPECT_EQ(hex(compressed.bytes), std::string("026d786ab8fda678cf50f71d13641049a393b325063b8c0d4e5070de48a2caf9ab")); + + auto extended2 = extended.extended(); + EXPECT_EQ(extended2.type, TWPublicKeyTypeNIST256p1Extended); + EXPECT_EQ(extended2.bytes.size(), 65ul); + EXPECT_EQ(extended2.isCompressed(), false); + + auto compressed2 = compressed.compressed(); + EXPECT_EQ(compressed2.type, TWPublicKeyTypeNIST256p1); + EXPECT_TRUE(compressed2 == publicKey); + EXPECT_EQ(compressed2.bytes.size(), 33ul); + EXPECT_EQ(compressed2.isCompressed(), true); +} + +TEST(PublicKeyTests, CompressedExtendedED25519) { + const Data key = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"); + auto privateKey = PrivateKey(key); + auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + EXPECT_EQ(publicKey.type, TWPublicKeyTypeED25519); + EXPECT_EQ(publicKey.bytes.size(), 32ul); + EXPECT_EQ(publicKey.isCompressed(), true); + EXPECT_TRUE(PublicKey::isValid(publicKey.bytes, TWPublicKeyTypeED25519)); + EXPECT_EQ(hex(publicKey.bytes), std::string("4870d56d074c50e891506d78faa4fb69ca039cc5f131eb491e166b975880e867")); + + auto extended = publicKey.extended(); + EXPECT_EQ(extended.type, TWPublicKeyTypeED25519); + EXPECT_TRUE(extended == publicKey); + EXPECT_EQ(extended.bytes.size(), 32ul); + EXPECT_EQ(extended.isCompressed(), true); + + auto compressed = publicKey.compressed(); + EXPECT_EQ(compressed.type, TWPublicKeyTypeED25519); + EXPECT_TRUE(compressed == publicKey); + EXPECT_EQ(compressed.bytes.size(), 32ul); + EXPECT_EQ(compressed.isCompressed(), true); +} + +TEST(PublicKeyTests, IsValidWrongType) { + EXPECT_FALSE(PublicKey::isValid(parse_hex("deadbeef"), (enum TWPublicKeyType)99)); +} + +TEST(PublicKeyTests, Verify) { + const auto privateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + + const char* message = "Hello"; + const Data messageData = TW::data(message); + const Data digest = Hash::sha256(messageData); + + { + const auto signature = privateKey.sign(digest, TWCurveSECP256k1); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_TRUE(publicKey.verify(signature, digest)); + EXPECT_EQ(hex(signature), "0f5d5a9e5fc4b82a625312f3be5d3e8ad017d882de86c72c92fcefa924e894c12071772a14201a3a0debf381b5e8dea39fadb9bcabdc02ee71ab018f55bf717f01"); + } + { + const auto signature = privateKey.sign(digest, TWCurveED25519); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519); + EXPECT_TRUE(publicKey.verify(signature, digest)); + EXPECT_EQ(hex(signature), "42848abf2641a731e18b8a1fb80eff341a5acebdc56faeccdcbadb960aef775192842fccec344679446daa4d02d264259c8f9aa364164ebe0ebea218581e2e03"); + } + { + const auto signature = privateKey.sign(digest, TWCurveED25519Blake2bNano); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Blake2b); + EXPECT_TRUE(publicKey.verify(signature, digest)); + EXPECT_EQ(hex(signature), "5c1473944cd0234ebc5a91b2966b9e707a33b936dadd149417a2e53b6b3fc97bef17b767b1690708c74d7b4c8fe48703fd44a6ef59d4cc5b9f88ba992db0a003"); + } + { + const auto signature = privateKey.sign(digest, TWCurveNIST256p1); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1Extended); + EXPECT_TRUE(publicKey.verify(signature, digest)); + EXPECT_EQ(hex(signature), "2e4655831f0c60729583595c103bf0d862af6313e4326f03f512682106c792822f5a9cd21e7d4a3316c2d337e5eee649b09c34f7b4407344f0d32e8d33167d8901"); + } +} + +TEST(PublicKeyTests, VerifyAsDER) { + const auto privateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + + const char* message = "Hello"; + const Data messageData = TW::data(message); + const Data digest = Hash::sha256(messageData); + + const auto signature = privateKey.signAsDER(digest); + EXPECT_EQ(signature.size(), 70ul); + EXPECT_EQ(hex(signature), "304402200f5d5a9e5fc4b82a625312f3be5d3e8ad017d882de86c72c92fcefa924e894c102202071772a14201a3a0debf381b5e8dea39fadb9bcabdc02ee71ab018f55bf717f"); + + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(publicKey.bytes), "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); + + EXPECT_TRUE(publicKey.verifyAsDER(signature, digest)); + + EXPECT_FALSE(publicKey.verify(signature, digest)); + + { // Negative: wrong key type + const auto publicKeyWrong = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1Extended); + EXPECT_FALSE(publicKeyWrong.verifyAsDER(signature, digest)); + } +} + +TEST(PublicKeyTests, VerifyEd25519Extended) { + const auto privateKey = PrivateKey(parse_hex("e8c8c5b2df13f3abed4e6b1609c808e08ff959d7e6fc3d849e3f2880550b574437aa559095324d78459b9bb2da069da32337e1cc5da78f48e1bd084670107f3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26fae0d152bb611cb9ff34e945e4ff627e6fba81da687a601a879759cd76530b5744424db69a75edd4780a5fbc05d1a3c84ac4166ff8e424808481dd8e77627ce5f5bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + + const auto message = TW::data("Hello"); + const auto digest = Hash::sha256(message); + const auto signature = privateKey.sign(digest, TWCurveED25519ExtendedCardano); + const auto valid = publicKey.verify(signature, digest); + + EXPECT_TRUE(valid); +} + +TEST(PublicKeyTests, VerifySchnorr) { + const auto key = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + const auto privateKey = PrivateKey(key); + + const Data messageData = TW::data("hello schnorr"); + const Data digest = Hash::sha256(messageData); + + const auto signature = privateKey.signZilliqa(digest); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_TRUE(publicKey.verifyZilliqa(signature, digest)); + EXPECT_EQ(hex(signature), "b8118ccb99563fe014279c957b0a9d563c1666e00367e9896fe541765246964f64a53052513da4e6dc20fdaf69ef0d95b4ca51c87ad3478986cf053c2dd0b853"); +} + +TEST(PublicKeyTests, VerifySchnorrWrongType) { + const auto key = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + const auto privateKey = PrivateKey(key); + + const Data messageData = TW::data("hello schnorr"); + const Data digest = Hash::sha256(messageData); + + const auto signature = privateKey.signZilliqa(digest); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeNIST256p1); + EXPECT_FALSE(publicKey.verifyZilliqa(signature, digest)); +} + +TEST(PublicKeyTests, RecoverRaw) { + { + const auto message = parse_hex("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb11432"); + const auto signature = parse_hex("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); + { + const auto publicKey = PublicKey::recoverRaw(signature, 1ul, message); + EXPECT_EQ(publicKey.type, TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(hex(publicKey.bytes), "0456d8089137b1fd0d890f8c7d4a04d0fd4520a30b19518ee87bd168ea12ed8090329274c4c6c0d9df04515776f2741eeffc30235d596065d718c3973e19711ad0"); + } + { // same data but different recId -> different result + const auto publicKey = PublicKey::recoverRaw(signature, 0ul, message); + EXPECT_EQ(publicKey.type, TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(hex(publicKey.bytes), "043fc5bf5fec35b6ffe6fd246226d312742a8c296bfa57dd22da509a2e348529b7ddb9faf8afe1ecda3c05e7b2bda47ee1f5a87e952742b22afca560b29d972fcf"); + } + } + { + const auto message = parse_hex("6468eb103d51c9a683b51818fdb73390151c9973831d2cfb4e9587ad54273155"); + const auto signature = parse_hex("92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c646487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e58"); + const auto recovered = PublicKey::recoverRaw(signature, 0ul, message); + EXPECT_EQ(hex(recovered.bytes), "0463ade8ebc212b85e7e4278dc3dcb4f9cc18aab912ef5d302b5d1940e772e9e1a9213522efddad487bbd5dd7907e8e776f918e9a5e4cb51893724e9fe76792a4f"); + } +} + +TEST(PublicKeyTests, SignAndRecoverRaw) { + const auto privateKey = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(hex(publicKey.bytes), "0463ade8ebc212b85e7e4278dc3dcb4f9cc18aab912ef5d302b5d1940e772e9e1a9213522efddad487bbd5dd7907e8e776f918e9a5e4cb51893724e9fe76792a4f"); + const auto message = parse_hex("6468eb103d51c9a683b51818fdb73390151c9973831d2cfb4e9587ad54273155"); + + // sign + const auto signature = privateKey.sign(message, TWCurveSECP256k1); + EXPECT_EQ(hex(signature), "92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c646487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e5800"); + + // revocer + const auto pubkeyRecovered = PublicKey::recoverRaw(signature, signature[64], message); + EXPECT_EQ(hex(pubkeyRecovered.bytes), hex(publicKey.bytes)); + EXPECT_EQ(hex(pubkeyRecovered.bytes), "0463ade8ebc212b85e7e4278dc3dcb4f9cc18aab912ef5d302b5d1940e772e9e1a9213522efddad487bbd5dd7907e8e776f918e9a5e4cb51893724e9fe76792a4f"); +} + +TEST(PublicKeyTests, RecoverRawNegative) { + const auto message = parse_hex("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb11432"); + const auto signature = parse_hex("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); + // recid >= 4 + EXPECT_EXCEPTION(PublicKey::recoverRaw(signature, 4ul, message), "Invalid recId"); + // signature too short + EXPECT_EXCEPTION(PublicKey::recoverRaw(parse_hex("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd"), 1ul, message), + "signature too short"); + // Digest too short + EXPECT_EXCEPTION(PublicKey::recoverRaw(signature, 1ul, parse_hex("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb114")), + "digest too short"); +} + +TEST(PublicKeyTests, Recover) { + { + const auto message = parse_hex("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb11432"); + const auto signature = parse_hex("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef80"); + const auto publicKey = PublicKey::recover(signature, message); + EXPECT_EQ(publicKey.type, TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(hex(publicKey.bytes), + "0456d8089137b1fd0d890f8c7d4a04d0fd4520a30b19518ee87bd168ea12ed8090329274c4c6c0d9df04515776f2741eeffc30235d596065d718c3973e19711ad0"); + } + + const auto privateKey = PrivateKey(parse_hex("4f96ed80e9a7555a6f74b3d658afdd9c756b0a40d4ca30c42c2039eb449bb904")); + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + EXPECT_EQ(hex(publicKey.bytes), "0463ade8ebc212b85e7e4278dc3dcb4f9cc18aab912ef5d302b5d1940e772e9e1a9213522efddad487bbd5dd7907e8e776f918e9a5e4cb51893724e9fe76792a4f"); + { + const auto message = parse_hex("6468eb103d51c9a683b51818fdb73390151c9973831d2cfb4e9587ad54273155"); + const auto signature = parse_hex("92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c646487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e5800"); + const auto recovered = PublicKey::recover(signature, message); + EXPECT_EQ(hex(recovered.bytes), hex(publicKey.bytes)); + } + { // same with v=27 + const auto message = parse_hex("6468eb103d51c9a683b51818fdb73390151c9973831d2cfb4e9587ad54273155"); + const auto signature = parse_hex("92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c646487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e581b"); + const auto recovered = PublicKey::recover(signature, message); + EXPECT_EQ(hex(recovered.bytes), hex(publicKey.bytes)); + } + { // same with v=35+2 + const auto message = parse_hex("6468eb103d51c9a683b51818fdb73390151c9973831d2cfb4e9587ad54273155"); + const auto signature = parse_hex("92c336138f7d0231fe9422bb30ee9ef10bf222761fe9e04442e3a11e88880c646487026011dae03dc281bc21c7d7ede5c2226d197befb813a4ecad686b559e5825"); + const auto recovered = PublicKey::recover(signature, message); + EXPECT_EQ(hex(recovered.bytes), hex(publicKey.bytes)); + } +} + +TEST(PublicKeyTests, isValidED25519) { + EXPECT_TRUE(PublicKey::isValid(parse_hex("beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519)); + EXPECT_TRUE(PublicKey(parse_hex("beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_TRUE(PublicKey::isValid(parse_hex("fc8c425a8a94a55ce42f2c24b2fb2ef5ab4a69142d2d97f6c11e0106c84136d5"), TWPublicKeyTypeED25519)); + EXPECT_TRUE(PublicKey(parse_hex("fc8c425a8a94a55ce42f2c24b2fb2ef5ab4a69142d2d97f6c11e0106c84136d5"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_TRUE(PublicKey::isValid(parse_hex("01beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519)); + EXPECT_TRUE(PublicKey(parse_hex("01beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519).isValidED25519()); + // Following 32 bytes are not valid public keys (not on the curve) + EXPECT_TRUE(PublicKey::isValid(parse_hex("8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"), TWPublicKeyTypeED25519)); + EXPECT_FALSE(PublicKey(parse_hex("8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"), TWPublicKeyTypeED25519).isValidED25519()); + EXPECT_TRUE(PublicKey::isValid(parse_hex("51fdd5feae59d7dcbf5ebea99c05593ebee302577a5486ceac706ed568aa1e0e"), TWPublicKeyTypeED25519)); + EXPECT_FALSE(PublicKey(parse_hex("51fdd5feae59d7dcbf5ebea99c05593ebee302577a5486ceac706ed568aa1e0e"), TWPublicKeyTypeED25519).isValidED25519()); + // invalid input size/format + EXPECT_FALSE(PublicKey::isValid(parse_hex("1234"), TWPublicKeyTypeED25519)); + EXPECT_FALSE(PublicKey::isValid(parse_hex("beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa5279"), TWPublicKeyTypeED25519)); + EXPECT_FALSE(PublicKey::isValid(parse_hex("02beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519)); + EXPECT_FALSE(PublicKey::isValid(parse_hex("0101beff0e5d6f6e6e6d573d3044f3e2bfb353400375dc281da3337468d4aa527908"), TWPublicKeyTypeED25519)); + EXPECT_FALSE(PublicKey(parse_hex("0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"), TWPublicKeyTypeSECP256k1).isValidED25519()); +} diff --git a/tests/common/TestUtilities.cpp b/tests/common/TestUtilities.cpp new file mode 100644 index 00000000000..76e34fd35f1 --- /dev/null +++ b/tests/common/TestUtilities.cpp @@ -0,0 +1,27 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include +#include + +using namespace std; + +/// Return a writable temp dir which can be used to create files during testing +string getTestTempDir(void) { + // In general, tests should not use hardcoded "/tmp", but TEST_TMPDIR env var. + const char* fromEnvironment = getenv("TEST_TMPDIR"); + if (fromEnvironment == NULL || fromEnvironment[0] == '\0') { return "/tmp"; } + return string(fromEnvironment); +} + +nlohmann::json loadJson(std::string path) { + std::ifstream stream(path); + nlohmann::json json; + stream >> json; + return json; +} diff --git a/tests/common/TestUtilities.h b/tests/common/TestUtilities.h new file mode 100644 index 00000000000..78c0d89cb39 --- /dev/null +++ b/tests/common/TestUtilities.h @@ -0,0 +1,88 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include + +#include +#include +#include + +#include + +#define WRAP(type, x) std::shared_ptr(x, type##Delete) +#define WRAPD(x) std::shared_ptr(x, TWDataDelete) +#define WRAPS(x) std::shared_ptr(x, TWStringDelete) +#define STRING(x) std::shared_ptr(TWStringCreateWithUTF8Bytes(x), TWStringDelete) +#define DATA(x) std::shared_ptr(TWDataCreateWithHexString(STRING(x).get()), TWDataDelete) + +inline void assertStringsEqual(const std::shared_ptr& string, const char* expected) { + ASSERT_STREQ(TWStringUTF8Bytes(string.get()), expected); +} + +inline void assertHexEqual(const std::shared_ptr& data, const char* expected) { + auto hex = WRAPS(TWStringCreateWithHexData(data.get())); + assertStringsEqual(hex, expected); +} + + +inline void assertJSONEqual(const nlohmann::json& lhs, const nlohmann::json& rhs) { + ASSERT_EQ(lhs, rhs); +} + +inline void assertJSONEqual(const std::string& lhs, const char* expected) { + auto lhsJson = nlohmann::json::parse(lhs); + auto rhsJson = nlohmann::json::parse(std::string(expected)); + return assertJSONEqual(lhsJson, rhsJson); +} + +inline std::vector* dataFromTWData(TWData* data) { + return const_cast*>(reinterpret_cast*>(data)); +} + +/// Return a writable temp dir which can be used to create files during testing +std::string getTestTempDir(void); + +#define ANY_SIGN(input, coin) \ + {\ + auto inputData = input.SerializeAsString();\ + auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData.data(), inputData.size()));\ + auto outputTWData = WRAPD(TWAnySignerSign(inputTWData.get(), coin));\ + output.ParseFromArray(TWDataBytes(outputTWData.get()), static_cast(TWDataSize(outputTWData.get())));\ + } +#define ANY_PLAN(input, output, coin) \ + {\ + auto inputData = input.SerializeAsString();\ + auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData.data(), inputData.size()));\ + auto outputTWData = WRAPD(TWAnySignerPlan(inputTWData.get(), coin));\ + output.ParseFromArray(TWDataBytes(outputTWData.get()), static_cast(TWDataSize(outputTWData.get())));\ + } +#define DUMP_PROTO(input) \ + { \ + std::string json; \ + google::protobuf::util::MessageToJsonString(input, &json); \ + std::cout<<"dump proto: "< +#include "TransactionCompiler.h" +#include "Coin.h" +#include "proto/Common.pb.h" +#include "proto/Binance.pb.h" +#include "proto/Bitcoin.pb.h" +#include "proto/Ethereum.pb.h" +#include "proto/TransactionCompiler.pb.h" + +#include +#include "Bitcoin/Script.h" +#include "Bitcoin/SegwitAddress.h" + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "uint256.h" +#include + +#include "TestUtilities.h" +#include + +using namespace TW; + +TEST(TransactionCompiler, BinanceCompileWithSignatures) { + /// Step 1: Prepare transaction input (protobuf) + const auto coin = TWCoinTypeBinance; + const auto txInputData = TransactionCompiler::buildInput( + coin, + "bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2", // from + "bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5", // to + "1", // amount + "BNB", // asset + "", // memo + "Binance-Chain-Nile" // testnet chainId + ); + + { + // Check, by parsing + EXPECT_EQ(txInputData.size(), 88ul); + Binance::Proto::SigningInput input; + ASSERT_TRUE(input.ParseFromArray(txInputData.data(), (int)txInputData.size())); + EXPECT_EQ(input.chain_id(), "Binance-Chain-Nile"); + EXPECT_TRUE(input.has_send_order()); + ASSERT_EQ(input.send_order().inputs_size(), 1); + EXPECT_EQ(hex(data(input.send_order().inputs(0).address())), "40c2979694bbc961023d1d27be6fc4d21a9febe6"); + } + + /// Step 2: Obtain preimage hash + const auto preImageHashes = TransactionCompiler::preImageHashes(coin, txInputData); + ASSERT_GT(preImageHashes.size(), 0ul); + + TxCompiler::Proto::PreSigningOutput preSigningOutput; + ASSERT_TRUE(preSigningOutput.ParseFromArray(preImageHashes.data(), int(preImageHashes.size()))); + ASSERT_EQ(preSigningOutput.error(), 0); + + auto preImageHash = data(preSigningOutput.data_hash()); + EXPECT_EQ(hex(preImageHash), "3f3fece9059e714d303a9a1496ddade8f2c38fa78fc4cc2e505c5dbb0ea678d1"); + + // Simulate signature, normally obtained from signature server + const auto publicKeyData = parse_hex("026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502"); + const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeSECP256k1); + const auto signature = parse_hex("1b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679"); + + // Verify signature (pubkey & hash & signature) + { + EXPECT_TRUE(publicKey.verify(signature, preImageHash)); + } + + /// Step 3: Compile transaction info + const Data outputData = TransactionCompiler::compileWithSignatures(coin, txInputData, {signature}, {publicKeyData}); + + const auto ExpectedTx = "b801f0625dee0a462a2c87fa0a1f0a1440c2979694bbc961023d1d27be6fc4d21a9febe612070a03424e421001121f0a14bffe47abfaede50419c577f1074fee6dd1535cd112070a03424e421001126a0a26eb5ae98721026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e50212401b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679"; + { + EXPECT_EQ(outputData.size(), 189ul); + Binance::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + + EXPECT_EQ(hex(output.encoded()), ExpectedTx); + } + + { // Double check: check if simple signature process gives the same result. Note that private keys were not used anywhere up to this point. + Binance::Proto::SigningInput input; + ASSERT_TRUE(input.ParseFromArray(txInputData.data(), (int)txInputData.size())); + auto key = parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"); + input.set_private_key(key.data(), key.size()); + + Binance::Proto::SigningOutput output; + ANY_SIGN(input, coin); + + ASSERT_EQ(hex(output.encoded()), ExpectedTx); + } +} + +TEST(TransactionCompiler, BitcoinCompileWithSignatures) { + // Test external signining with a Bitcoin transaction with 3 input UTXOs, all used, but only using 2 public keys. + // Three signatures are neeeded. This illustrates that order of UTXOs/hashes is not always the same. + + const auto revUtxoHash0 = parse_hex("07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa8"); + const auto revUtxoHash1 = parse_hex("d6892a5aa54e3b8fe430efd23f49a8950733aaa9d7c915d9989179f48dd1905e"); + const auto revUtxoHash2 = parse_hex("6021efcf7555f90627364339fc921139dd40a06ccb2cb2a2a4f8f4ea7a2dc74d"); + const auto inPubKey0 = parse_hex("024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"); + const auto inPubKey1 = parse_hex("0217142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc"); + const auto inPubKeyHash0 = parse_hex("bd92088bb7e82d611a9b94fbb74a0908152b784f"); + const auto inPubKeyHash1 = parse_hex("6641abedacf9483b793afe1718689cc9420bbb1c"); + + // Test data: Input UTXO infos + struct UtxoInfo { + Data revUtxoHash; + Data publicKey; + long amount; + int index; + }; + std::vector utxoInfos = { + // first + UtxoInfo {revUtxoHash0, inPubKey0, 600'000, 0}, + // second UTXO, with same pubkey + UtxoInfo {revUtxoHash1, inPubKey0, 500'000, 1}, + // third UTXO, with different pubkey + UtxoInfo {revUtxoHash2, inPubKey1, 400'000, 0}, + }; + + // Signature infos, indexed by pubkeyhash+hash + struct SignatureInfo { + Data signature; + Data publicKey; + }; + std::map signatureInfos = { + { + hex(inPubKeyHash0) + "+" + "a296bead4172007be69b21971a790e076388666c162a9505698415f1b003ebd7", + { + parse_hex("304402201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f2a40"), + inPubKey0, + } + }, + { + hex(inPubKeyHash1) + "+" + "505f527f00e15fcc5a2d2416c9970beb57dfdfaca99e572a01f143b24dd8fab6", + { + parse_hex("3044022041294880caa09bb1b653775310fcdd1458da6b8e7d7fae34e37966414fe115820220646397c9d2513edc5974ecc336e9b287de0cdf071c366f3b3dc3ff309213e4e4"), + inPubKey1, + } + }, + { + hex(inPubKeyHash0) + "+" + "60ed6e9371e5ddc72fd88e46a12cb2f68516ebd307c0fd31b1b55cf767272101", + { + parse_hex("30440220764e3d5b3971c4b3e70b23fb700a7462a6fe519d9830e863a1f8388c402ad0b102207e777f7972c636961f92375a2774af3b7a2a04190251bbcb31d19c70927952dc"), + inPubKey0, + } + }, + }; + + const auto coin = TWCoinTypeBitcoin; + const auto ownAddress = "bc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z00ppggv"; + + // Setup input for Plan + Bitcoin::Proto::SigningInput signingInput; + signingInput.set_coin_type(coin); + signingInput.set_hash_type(TWBitcoinSigHashTypeAll); + signingInput.set_amount(1'200'000); + signingInput.set_use_max_amount(false); + signingInput.set_byte_fee(1); + signingInput.set_to_address("bc1q2dsdlq3343vk29runkgv4yc292hmq53jedfjmp"); + signingInput.set_change_address(ownAddress); + + // process UTXOs + int count = 0; + for (auto& u: utxoInfos) { + const auto publicKey = PublicKey(u.publicKey, TWPublicKeyTypeSECP256k1); + const auto address = Bitcoin::SegwitAddress(publicKey, "bc"); + if (count == 0) EXPECT_EQ(address.string(), ownAddress); + if (count == 1) EXPECT_EQ(address.string(), ownAddress); + if (count == 2) EXPECT_EQ(address.string(), "bc1qveq6hmdvl9yrk7f6lct3s6yue9pqhwcuxedggg"); + + const auto utxoScript = Bitcoin::Script::lockScriptForAddress(address.string(), coin); + if (count == 0) EXPECT_EQ(hex(utxoScript.bytes), "0014bd92088bb7e82d611a9b94fbb74a0908152b784f"); + if (count == 1) EXPECT_EQ(hex(utxoScript.bytes), "0014bd92088bb7e82d611a9b94fbb74a0908152b784f"); + if (count == 2) EXPECT_EQ(hex(utxoScript.bytes), "00146641abedacf9483b793afe1718689cc9420bbb1c"); + + Data keyHash; + EXPECT_TRUE(utxoScript.matchPayToWitnessPublicKeyHash(keyHash)); + if (count == 0) EXPECT_EQ(hex(keyHash), hex(inPubKeyHash0)); + if (count == 1) EXPECT_EQ(hex(keyHash), hex(inPubKeyHash0)); + if (count == 2) EXPECT_EQ(hex(keyHash), hex(inPubKeyHash1)); + + const auto redeemScript = Bitcoin::Script::buildPayToPublicKeyHash(keyHash); + if (count == 0) EXPECT_EQ(hex(redeemScript.bytes), "76a914bd92088bb7e82d611a9b94fbb74a0908152b784f88ac"); + if (count == 1) EXPECT_EQ(hex(redeemScript.bytes), "76a914bd92088bb7e82d611a9b94fbb74a0908152b784f88ac"); + if (count == 2) EXPECT_EQ(hex(redeemScript.bytes), "76a9146641abedacf9483b793afe1718689cc9420bbb1c88ac"); + (*signingInput.mutable_scripts())[hex(keyHash)] = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); + + auto utxo = signingInput.add_utxo(); + utxo->set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo->set_amount(u.amount); + utxo->mutable_out_point()->set_hash(std::string(u.revUtxoHash.begin(), u.revUtxoHash.end())); + utxo->mutable_out_point()->set_index(u.index); + utxo->mutable_out_point()->set_sequence(UINT32_MAX); + + ++count; + } + EXPECT_EQ(count, 3); + EXPECT_EQ(signingInput.utxo_size(), 3); + + // Plan + Bitcoin::Proto::TransactionPlan plan; + ANY_PLAN(signingInput, plan, coin); + + // At this point plan can be checked, assume it is accepted unmodified + EXPECT_EQ(plan.amount(), 1'200'000); + EXPECT_EQ(plan.fee(), 277); + EXPECT_EQ(plan.change(), 299'723); + ASSERT_EQ(plan.utxos_size(), 3); + // Note that UTXOs happen to be in reverse order compared to the input + EXPECT_EQ(hex(plan.utxos(0).out_point().hash()), hex(revUtxoHash2)); + EXPECT_EQ(hex(plan.utxos(1).out_point().hash()), hex(revUtxoHash1)); + EXPECT_EQ(hex(plan.utxos(2).out_point().hash()), hex(revUtxoHash0)); + + // Extend input with accepted plan + *signingInput.mutable_plan() = plan; + + // Serialize input + const auto txInputData = data(signingInput.SerializeAsString()); + EXPECT_EQ((int)txInputData.size(), 692); + + /// Step 2: Obtain preimage hashes + const auto preImageHashes = TransactionCompiler::preImageHashes(coin, txInputData); + TW::Bitcoin::Proto::PreSigningOutput preSigningOutput; + ASSERT_TRUE(preSigningOutput.ParseFromArray(preImageHashes.data(), (int)preImageHashes.size())); + + ASSERT_EQ(preSigningOutput.error(), 0); + EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[0].data_hash()), "505f527f00e15fcc5a2d2416c9970beb57dfdfaca99e572a01f143b24dd8fab6"); + EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[1].data_hash()), "a296bead4172007be69b21971a790e076388666c162a9505698415f1b003ebd7"); + EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[2].data_hash()), "60ed6e9371e5ddc72fd88e46a12cb2f68516ebd307c0fd31b1b55cf767272101"); + EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[0].public_key_hash()), hex(inPubKeyHash1)); + EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[1].public_key_hash()), hex(inPubKeyHash0)); + EXPECT_EQ(hex(preSigningOutput.hash_public_keys()[2].public_key_hash()), hex(inPubKeyHash0)); + + // Simulate signatures, normally they are obtained from external source, e.g. a signature server. + std::vector signatureVec; + std::vector pubkeyVec; + for (const auto& h: preSigningOutput.hash_public_keys()) { + const auto& preImageHash = h.data_hash(); + const auto& pubkeyhash = h.public_key_hash(); + + const std::string key = hex(pubkeyhash) + "+" + hex(preImageHash); + const auto sigInfoFind = signatureInfos.find(key); + ASSERT_TRUE(sigInfoFind != signatureInfos.end()); + const auto& sigInfo = std::get<1>(*sigInfoFind); + const auto& publicKeyData = sigInfo.publicKey; + const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeSECP256k1); + const auto signature = sigInfo.signature; + + signatureVec.push_back(signature); + pubkeyVec.push_back(publicKeyData); + + // Verify signature (pubkey & hash & signature) + EXPECT_TRUE(publicKey.verifyAsDER(signature, TW::Data(preImageHash.begin(), preImageHash.end()))); + } + + /// Step 3: Compile transaction info + const Data compileWithSignatures = TransactionCompiler::compileWithSignatures(coin, txInputData, signatureVec, pubkeyVec); + + const auto ExpectedTx = "010000000001036021efcf7555f90627364339fc921139dd40a06ccb2cb2a2a4f8f4ea7a2dc74d0000000000ffffffffd6892a5aa54e3b8fe430efd23f49a8950733aaa9d7c915d9989179f48dd1905e0100000000ffffffff07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa80000000000ffffffff02804f1200000000001600145360df8231ac5965147c9d90ca930a2aafb05232cb92040000000000160014bd92088bb7e82d611a9b94fbb74a0908152b784f02473044022041294880caa09bb1b653775310fcdd1458da6b8e7d7fae34e37966414fe115820220646397c9d2513edc5974ecc336e9b287de0cdf071c366f3b3dc3ff309213e4e401210217142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc0247304402201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f2a400121024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382024730440220764e3d5b3971c4b3e70b23fb700a7462a6fe519d9830e863a1f8388c402ad0b102207e777f7972c636961f92375a2774af3b7a2a04190251bbcb31d19c70927952dc0121024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb49338200000000"; + { + EXPECT_EQ(compileWithSignatures.size(), 786ul); + Bitcoin::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(compileWithSignatures.data(), (int)compileWithSignatures.size())); + + EXPECT_EQ(output.encoded().size(), 518ul); + EXPECT_EQ(hex(output.encoded()), ExpectedTx); + } + + { // Double check: check if simple signature process gives the same result. Note that private keys were not used anywhere up to this point. + Bitcoin::Proto::SigningInput input; + ASSERT_TRUE(input.ParseFromArray(txInputData.data(), (int)txInputData.size())); + + // 2 private keys are needed (despite >2 UTXOs) + auto key0 = parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + auto key1 = parse_hex("7878787878787878787878787878787878787878787878787878787878787878"); + EXPECT_EQ(hex(PrivateKey(key0).getPublicKey(TWPublicKeyTypeSECP256k1).bytes), hex(inPubKey0)); + EXPECT_EQ(hex(PrivateKey(key1).getPublicKey(TWPublicKeyTypeSECP256k1).bytes), hex(inPubKey1)); + *input.add_private_key() = std::string(key0.begin(), key0.end()); + *input.add_private_key() = std::string(key1.begin(), key1.end()); + + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(input, coin); + + ASSERT_EQ(hex(output.encoded()), ExpectedTx); + } + + { // Negative: not enough signatures + const Data outputData = TransactionCompiler::compileWithSignatures(coin, txInputData, {signatureVec[0]}, pubkeyVec); + EXPECT_GT(outputData.size(), 1ul); + Bitcoin::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + EXPECT_EQ(output.encoded().size(), 0ul); + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_params); + } + { // Negative: invalid public key + const auto publicKeyBlake = parse_hex("b689ab808542e13f3d2ec56fe1efe43a1660dcadc73ce489fde7df98dd8ce5d9"); + EXPECT_EXCEPTION(TransactionCompiler::compileWithSignatures(coin, txInputData, signatureVec, + {pubkeyVec[0], pubkeyVec[1], publicKeyBlake}), "Invalid public key"); + } + { // Negative: wrong signature (formally valid) + const Data outputData = TransactionCompiler::compileWithSignatures(coin, txInputData, + {parse_hex("415502201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f3b51"), + signatureVec[1], signatureVec[2]}, + pubkeyVec); + EXPECT_EQ(outputData.size(), 2ul); + Bitcoin::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + EXPECT_EQ(output.encoded().size(), 0ul); + EXPECT_EQ(output.error(), Common::Proto::Error_signing); + } +} + +TEST(TransactionCompiler, EthereumCompileWithSignatures) { + /// Step 1: Prepare transaction input (protobuf) + const auto coin = TWCoinTypeEthereum; + const auto txInputData0 = TransactionCompiler::buildInput( + coin, + "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", // from + "0x3535353535353535353535353535353535353535", // to + "1000000000000000000", // amount + "ETH", // asset + "", // memo + "" // chainId + ); + + // Check, by parsing + EXPECT_EQ((int)txInputData0.size(), 61); + Ethereum::Proto::SigningInput signingInput; + ASSERT_TRUE(signingInput.ParseFromArray(txInputData0.data(), (int)txInputData0.size())); + EXPECT_EQ(hex(signingInput.chain_id()), "01"); + EXPECT_EQ(signingInput.to_address(), "0x3535353535353535353535353535353535353535"); + ASSERT_TRUE(signingInput.transaction().has_transfer()); + EXPECT_EQ(hex(signingInput.transaction().transfer().amount()), "0de0b6b3a7640000"); + + // Set a few other values + const auto nonce = store(uint256_t(11)); + const auto gasPrice = store(uint256_t(20000000000)); + const auto gasLimit = store(uint256_t(21000)); + signingInput.set_nonce(nonce.data(), nonce.size()); + signingInput.set_gas_price(gasPrice.data(), gasPrice.size()); + signingInput.set_gas_limit(gasLimit.data(), gasLimit.size()); + signingInput.set_tx_mode(Ethereum::Proto::Legacy); + + // Serialize back, this shows how to serialize input protobuf to byte array + const auto txInputData = data(signingInput.SerializeAsString()); + EXPECT_EQ((int)txInputData.size(), 75); + + /// Step 2: Obtain preimage hash + const auto preImageHashes = TransactionCompiler::preImageHashes(coin, txInputData); + ASSERT_GT(preImageHashes.size(), 0ul); + + TxCompiler::Proto::PreSigningOutput preSigningOutput; + ASSERT_TRUE(preSigningOutput.ParseFromArray(preImageHashes.data(), int(preImageHashes.size()))); + ASSERT_EQ(preSigningOutput.error(), 0); + + auto preImageHash = data(preSigningOutput.data_hash()); + EXPECT_EQ(hex(preImageHash), "15e180a6274b2f6a572b9b51823fce25ef39576d10188ecdcd7de44526c47217"); + + // Simulate signature, normally obtained from signature server + const Data publicKeyData = parse_hex("044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a"); + const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeSECP256k1Extended); + const auto signature = parse_hex("360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07b53bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c9796677900"); + + // Verify signature (pubkey & hash & signature) + { + EXPECT_TRUE(publicKey.verify(signature, preImageHash)); + } + + /// Step 3: Compile transaction info + const Data outputData = TransactionCompiler::compileWithSignatures(coin, txInputData, {signature}, {publicKeyData}); + + const auto ExpectedTx = "f86c0b8504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a0360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07ba053bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c97966779"; + { + EXPECT_EQ(outputData.size(), 183ul); + Ethereum::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + + EXPECT_EQ(output.encoded().size(), 110ul); + EXPECT_EQ(hex(output.encoded()), ExpectedTx); + } + + { // Double check: check if simple signature process gives the same result. Note that private keys were not used anywhere up to this point. + Ethereum::Proto::SigningInput input; + ASSERT_TRUE(input.ParseFromArray(txInputData.data(), (int)txInputData.size())); + auto key = parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + input.set_private_key(key.data(), key.size()); + + Ethereum::Proto::SigningOutput output; + ANY_SIGN(input, coin); + + ASSERT_EQ(hex(output.encoded()), ExpectedTx); + } +} + +TEST(TransactionCompiler, EthereumBuildTransactionInput) { + const auto coin = TWCoinTypeEthereum; + const auto txInputData0 = TransactionCompiler::buildInput( + coin, + "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", // from + "0x3535353535353535353535353535353535353535", // to + "1000000000000000000", // amount + "ETH", // asset + "Memo", // memo + "05" // chainId + ); + + // Check, by parsing + EXPECT_EQ((int)txInputData0.size(), 61); + Ethereum::Proto::SigningInput input; + ASSERT_TRUE(input.ParseFromArray(txInputData0.data(), (int)txInputData0.size())); + EXPECT_EQ(hex(input.chain_id()), "05"); + EXPECT_EQ(input.to_address(), "0x3535353535353535353535353535353535353535"); + ASSERT_TRUE(input.transaction().has_transfer()); + EXPECT_EQ(hex(input.transaction().transfer().amount()), "0de0b6b3a7640000"); +} + +TEST(TransactionCompiler, EthereumBuildTransactionInputInvalidAddress) { + const auto coin = TWCoinTypeEthereum; + EXPECT_EXCEPTION(TransactionCompiler::buildInput( + coin, + "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F", // from + "__INVALID_ADDRESS__", // to + "1000000000000000000", // amount + "ETH", // asset + "", // memo + "" // chainId + ), "Invalid to address"); +} diff --git a/tests/common/Uint256Tests.cpp b/tests/common/Uint256Tests.cpp new file mode 100644 index 00000000000..a396e1e0318 --- /dev/null +++ b/tests/common/Uint256Tests.cpp @@ -0,0 +1,114 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "uint256.h" +#include "HexCoding.h" + +#include +#include + +#include + +namespace TW { + +// Test data: uint256_t, hex binary representation, string representation +const std::vector> testData = { + {0, "00", "0"}, + {1, "01", "1"}, + {7, "07", "7"}, + {100, "64", "100"}, + {255, "ff", "255"}, + {256, "0100", "256"}, + {65535, "ffff", "65535"}, + {1'000'000, "0f4240", "1000000"}, + { + load(parse_hex("123456789abcdef0")), + "123456789abcdef0", + "1311768467463790320" + }, + { + load(parse_hex("123456789abcdef123456789abcdef")), + "123456789abcdef123456789abcdef", + "94522879700260683142460330790866415" + }, + { + load(parse_hex("1000000000000000000000000000000000000000")), + "1000000000000000000000000000000000000000", + "91343852333181432387730302044767688728495783936" + }, +}; + +TEST(Uint256, storeLoadBasic) { + EXPECT_EQ(hex(store(uint256_t(3))), "03"); + EXPECT_EQ(load(parse_hex("03")), uint256_t(3)); +} + +TEST(Uint256, storeLoadStore) { + for(const auto& testi: testData) { + const uint256_t dataI = std::get<0>(testi); + const char* dataD = std::get<1>(testi); + + const uint256_t i = dataI; + + const Data d = store(i); + EXPECT_EQ(hex(d), dataD); + + const uint256_t i2 = load(d); + EXPECT_EQ(i2, dataI); + + const Data d2 = store(i2); + EXPECT_EQ(hex(d2), dataD); + } +} + +TEST(Uint256, toString) { + for(const auto& testi: testData) { + const uint256_t dataI = std::get<0>(testi); + const char* dataS = std::get<2>(testi); + + EXPECT_EQ(toString(dataI), dataS); + } +} + +TEST(Uint256, storePadding) { + EXPECT_EQ(hex(store(uint256_t(3))), "03"); + EXPECT_EQ(hex(store(uint256_t(1'000'000))), "0f4240"); + + EXPECT_EQ(hex(store(uint256_t(3), 4)), "00000003"); + EXPECT_EQ(hex(store(uint256_t(3), 32)), "0000000000000000000000000000000000000000000000000000000000000003"); + EXPECT_EQ(hex(store(uint256_t(3), 64)), "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003"); + + EXPECT_EQ(hex(store(uint256_t(1'000'000), 32)), "00000000000000000000000000000000000000000000000000000000000f4240"); + EXPECT_EQ(hex(store(uint256_t(1'000'000), 2)), "0f4240"); +} + +TEST(Uint256, loadWithLeadingZero) { + EXPECT_EQ(load(parse_hex("0f4240")), uint256_t(1'000'000)); + EXPECT_EQ(load(parse_hex("000f4240")), uint256_t(1'000'000)); + EXPECT_EQ(load(parse_hex("000000000f4240")), uint256_t(1'000'000)); + EXPECT_EQ(load(parse_hex("0000000000000000000000000000000000000000000000000000000000000003")), uint256_t(3)); + EXPECT_EQ(load(parse_hex("00000000000000000000000000000000000000000000000000000000000f4240")), uint256_t(1'000'000)); +} + +TEST(Uint256, LoadEmpty) { + EXPECT_EQ(load(parse_hex("")), uint256_t(0)); + EXPECT_EQ(load(parse_hex("00")), uint256_t(0)); + EXPECT_EQ(load(parse_hex("0000")), uint256_t(0)); +} + +TEST(Uint256, LoadWithOffset) { + EXPECT_EQ(loadWithOffset(parse_hex("0000000000000000000000000000000000000000000000000000000000000003"), 0), uint256_t(3)); + EXPECT_EQ(loadWithOffset(parse_hex("abcdef0000000000000000000000000000000000000000000000000000000000000003"), 3), uint256_t(3)); + EXPECT_EQ(loadWithOffset(parse_hex("0000000000000000000000000000000000000000000000000000000000000003"), 1), uint256_t(0)); // not enough bytes +} + +TEST(Uint256, loadStringProtobuf) { + const Data data = parse_hex("03"); + const std::string str = std::string(reinterpret_cast(data.data()), data.size()); + EXPECT_EQ(load(str), uint256_t(3)); +} + +} // namespace diff --git a/tests/WalletConsoleTests.cpp b/tests/common/WalletConsoleTests.cpp similarity index 97% rename from tests/WalletConsoleTests.cpp rename to tests/common/WalletConsoleTests.cpp index 3241f2f78a3..e6bdfc9be62 100644 --- a/tests/WalletConsoleTests.cpp +++ b/tests/common/WalletConsoleTests.cpp @@ -1,4 +1,4 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the @@ -6,29 +6,29 @@ #include "../walletconsole/lib/CommandExecutor.h" #include "../walletconsole/lib/WalletConsole.h" -#include "../walletconsole/lib/Util.h" #include #include #include -using namespace TW; -using namespace TW::WalletConsole; -using namespace std; +namespace TW::WalletConsole::tests { -// Test some command execution +using namespace std; static stringstream outputss; static CommandExecutor cmd(outputss); -static int staticInit() { cmd.init(); return 0; } +static int staticInit() { + cmd.init(); + return 0; +} static int dummyStatic = staticInit(); static const string mnemonic1 = "edge defense waste choose enrich upon flee junk siren film clown finish luggage leader kid quick brick print evidence swap drill paddle truly occur"; int countLines(const string& text) { int lines = 0; - for(int i = 0; i < text.length(); ++i) - { - if (text[i] == '\n') ++lines; + for (auto i = 0ul; i < text.length(); ++i) { + if (text[i] == '\n') + ++lines; } return lines; } @@ -41,7 +41,9 @@ TEST(WalletConsole, loopExit) { stringstream inss; stringstream outss; - inss << "coin eth" << endl << "newKey" << endl << "exit" << endl; + inss << "coin eth" << endl + << "newKey" << endl + << "exit" << endl; TW::WalletConsole::WalletConsole console(inss, outss); console.loop(); string res = outss.str(); @@ -90,7 +92,7 @@ TEST(WalletConsole, coin) { } { auto pos = outputss.str().length(); - cmd.executeLine("coin eth"); + cmd.executeLine("coin ethereum"); string res = outputss.str().substr(pos); EXPECT_TRUE(res.find("Set active coin to: ethereum") != string::npos); } @@ -285,7 +287,6 @@ TEST(WalletConsole, dumpdp) { } } - TEST(WalletConsole, dumpXpub) { cmd.executeLine("coin btc"); auto pos1 = outputss.str().length(); @@ -444,7 +445,6 @@ TEST(WalletConsole, fileWriteRead) { string res1 = outputss.str().substr(pos1); EXPECT_TRUE(res1.find("Written to ") != string::npos); - auto pos2 = outputss.str().length(); cmd.executeLine("fileR " + fileName); string res2 = outputss.str().substr(pos2); @@ -458,14 +458,11 @@ TEST(WalletConsole, fileWriteRead) { EXPECT_TRUE(res3.find("already exists, not overwriting") != string::npos); // clean up created file - try - { + try { std::remove(fileName.c_str()); + } catch (...) { } - catch(...) - { - } - + auto pos4 = outputss.str().length(); cmd.executeLine("fileR __NO_SUCH_FILE__"); string res4 = outputss.str().substr(pos4); @@ -485,3 +482,5 @@ TEST(WalletConsole, harmonyAddressDerivation) { EXPECT_TRUE(res1.find("Result") != string::npos); EXPECT_TRUE(res1.find("rror") == string::npos); } + +} // namespace TW::WalletConsole::tests diff --git a/tests/common/algorithm/erase_tests.cpp b/tests/common/algorithm/erase_tests.cpp new file mode 100644 index 00000000000..6a0ca0c41aa --- /dev/null +++ b/tests/common/algorithm/erase_tests.cpp @@ -0,0 +1,27 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "algorithm/erase.h" + +#include "gtest/gtest.h" +#include // std::iota + +TEST(Algorithm, Erase) { + std::vector cnt(10); + std::iota(cnt.begin(), cnt.end(), '0'); + cnt.back() = '3'; + std::size_t nbElementsErased = TW::erase(cnt, '3'); + ASSERT_EQ(cnt.size(), 8ul); + ASSERT_EQ(nbElementsErased, 2ul); +} + +TEST(Algorithm, EraseIf) { + std::vector cnt(10); + std::iota(cnt.begin(), cnt.end(), '0'); + auto erased = TW::erase_if(cnt, [](char x) { return (x - '0') % 2 == 0; }); + ASSERT_EQ(cnt.size(), 5ul); + ASSERT_EQ(erased, 5ul); +} diff --git a/tests/common/algorithm/sort_copy_tests.cpp b/tests/common/algorithm/sort_copy_tests.cpp new file mode 100644 index 00000000000..2603ee27b5a --- /dev/null +++ b/tests/common/algorithm/sort_copy_tests.cpp @@ -0,0 +1,25 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "algorithm/sort_copy.h" + +#include "gtest/gtest.h" + +using namespace TW; + +struct Amount { + int value; +}; + +TEST(SortCopy, IsSorted) { + std::vector data{9, 1, 2, 4, 5}; + const auto sorted = sortCopy(data); + std::vector anotherData{Amount{.value = 9}, Amount{.value = 1}, Amount{.value = 0}}; + auto sortFunctor = [](auto&& lhs, auto&& rhs) { return lhs.value < rhs.value; }; + const auto anotherSorted = sortCopy(anotherData, sortFunctor); + ASSERT_TRUE(std::is_sorted(cbegin(sorted), cend(sorted))); + ASSERT_TRUE(std::is_sorted(cbegin(anotherSorted), cend(anotherSorted), sortFunctor)); +} \ No newline at end of file diff --git a/tests/common/algorithm/to_array_tests.cpp b/tests/common/algorithm/to_array_tests.cpp new file mode 100644 index 00000000000..58d181ad663 --- /dev/null +++ b/tests/common/algorithm/to_array_tests.cpp @@ -0,0 +1,23 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "algorithm/to_array.h" + +#include "gtest/gtest.h" + +using namespace TW; + +TEST(Algorithms, ToArray) { + std::string str{"foo"}; + auto value = TW::to_array(str); + auto expected = std::array{"foo"}; + ASSERT_EQ(value, expected); + + std::vector ints{0, 1, 2}; + auto another_value = TW::to_array(ints); + auto expected_ints = std::array{0, 1, 2}; + ASSERT_EQ(another_value, expected_ints); +} diff --git a/tests/common/memory/memzero_tests.cpp b/tests/common/memory/memzero_tests.cpp new file mode 100644 index 00000000000..47fba2f4697 --- /dev/null +++ b/tests/common/memory/memzero_tests.cpp @@ -0,0 +1,23 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "memory/memzero_wrapper.h" + +#include "gtest/gtest.h" + +struct int_wrapper { + int value; +}; + +TEST(Memory, Memzero) { + int_wrapper obj{.value = 42}; + TW::memzero(&obj); + ASSERT_EQ(obj.value, 0); + obj.value = 42; + ASSERT_EQ(obj.value, 42); + TW::memzero(&obj, sizeof(int_wrapper)); + ASSERT_EQ(obj.value, 0); +} diff --git a/tests/common/operators/equality_comparable_tests.cpp b/tests/common/operators/equality_comparable_tests.cpp new file mode 100644 index 00000000000..134d172968e --- /dev/null +++ b/tests/common/operators/equality_comparable_tests.cpp @@ -0,0 +1,23 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "operators/equality_comparable.h" + +#include "gtest/gtest.h" + +namespace TW::operators::tests { + +struct Amount : equality_comparable { + int value; + friend bool operator==(const Amount& lhs, const Amount& rhs) { return lhs.value == rhs.value; } +}; + +TEST(Operators, EqualityComparable) { + ASSERT_TRUE(Amount{.value = 1} != Amount{.value = 2}); + ASSERT_TRUE(Amount{.value = 1} == Amount{.value = 1}); +} + +} // namespace TW::operators::tests diff --git a/tests/interface/TWAESTests.cpp b/tests/interface/TWAESTests.cpp index 8f1229ae5ca..0ee04dfdc5b 100644 --- a/tests/interface/TWAESTests.cpp +++ b/tests/interface/TWAESTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/interface/TWAccountTests.cpp b/tests/interface/TWAccountTests.cpp new file mode 100644 index 00000000000..5a8bcf4661d --- /dev/null +++ b/tests/interface/TWAccountTests.cpp @@ -0,0 +1,37 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include + +#include + +TEST(TWAccount, Create) { + const auto addressAdd = "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"; + const auto coin = TWCoinTypeBitcoin; + const auto derivationPath = "m/84'/0'/0'/0/0"; + const auto extPubKeyAdd = "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"; + const auto pubKey = "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006"; + + const auto account = WRAP( + TWAccount, + TWAccountCreate( + WRAPS(TWStringCreateWithUTF8Bytes(addressAdd)).get(), + coin, + TWDerivationDefault, + WRAPS(TWStringCreateWithUTF8Bytes(derivationPath)).get(), + WRAPS(TWStringCreateWithUTF8Bytes(pubKey)).get(), + WRAPS(TWStringCreateWithUTF8Bytes(extPubKeyAdd)).get() + ) + ); + + assertStringsEqual(WRAPS(TWAccountAddress(account.get())), addressAdd); + EXPECT_EQ(coin, TWAccountCoin(account.get())); + EXPECT_EQ(TWAccountDerivation(account.get()), TWDerivationDefault); + assertStringsEqual(WRAPS(TWAccountDerivationPath(account.get())), derivationPath); + assertStringsEqual(WRAPS(TWAccountExtendedPublicKey(account.get())), extPubKeyAdd); + assertStringsEqual(WRAPS(TWAccountPublicKey(account.get())), pubKey); +} diff --git a/tests/interface/TWAnyAddressTests.cpp b/tests/interface/TWAnyAddressTests.cpp index ada6aa5ce0f..c0bef30ab93 100644 --- a/tests/interface/TWAnyAddressTests.cpp +++ b/tests/interface/TWAnyAddressTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include "HexCoding.h" #include @@ -32,6 +32,20 @@ TEST(AnyAddress, Data) { auto keyHash = WRAPD(TWAnyAddressData(addr.get())); assertHexEqual(keyHash, "4e5b2e1dc63f6b91cb6cd759936495434c7e972f"); } + // smartBCH + { + auto string = STRING("0x4E5B2e1dc63F6b91cb6Cd759936495434C7e972F"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeSmartBitcoinCash)); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "4e5b2e1dc63f6b91cb6cd759936495434c7e972f"); + } + // KuCoin Community Chain + { + auto string = STRING("0x4E5B2e1dc63F6b91cb6Cd759936495434C7e972F"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeKuCoinCommunityChain)); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "4e5b2e1dc63f6b91cb6cd759936495434c7e972f"); + } // bnb address key hash { auto string = STRING("bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5"); @@ -53,7 +67,7 @@ TEST(AnyAddress, Data) { auto witness = WRAPD(TWAnyAddressData(addr.get())); assertHexEqual(witness, "751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6"); } - // cashaddr + // bitcoincashaddr { auto string = STRING("bitcoincash:qzxf0wl63ahx6jsxu8uuldcw7n5aatwppvnteraqaw"); auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeBitcoinCash)); @@ -114,10 +128,10 @@ TEST(AnyAddress, Data) { } // cardano { - auto string = STRING("addr1s3xuxwfetyfe7q9u3rfn6je9stlvcgmj8rezd87qjjegdtxm3y3f2mgtn87mrny9r77gm09h6ecslh3gmarrvrp9n4yzmdnecfxyu59jz29g8j"); + auto string = STRING("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23"); auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeCardano)); auto pubkey = WRAPD(TWAnyAddressData(addr.get())); - assertHexEqual(pubkey, "0104204dc3393959139f00bc88d33d4b2582fecc237238f2269fc094b286acdb892295206d0b99fdb1cc851fbc8dbcb7d6710fde28df46360c259d482db679c24c4e50b2"); + assertHexEqual(pubkey, "01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b"); } // neo { @@ -140,4 +154,18 @@ TEST(AnyAddress, Data) { auto pubkey = WRAPD(TWAnyAddressData(addr.get())); assertHexEqual(pubkey, "3b83b07cab54824a59c3d3f2e203a7cd913b7fcdc4439595983e2402c2cf791d"); } + // ecashaddr + { + auto string = STRING("ecash:qzxf0wl63ahx6jsxu8uuldcw7n5aatwppv2xdgx6me"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeECash)); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "8c97bbfa8f6e6d4a06e1f9cfb70ef4e9deadc10b"); + } + // solana + { + auto string = STRING("2gVkYWexTHR5Hb2aLeQN3tnngvWzisFKXDUPrgMHpdST"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeSolana)); + auto keyHash = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(keyHash, "18f9d8d877393bbbe8d697a8a2e52879cc7e84f467656d1cce6bab5a8d2637ec"); + } } diff --git a/tests/interface/TWBase32Tests.cpp b/tests/interface/TWBase32Tests.cpp new file mode 100644 index 00000000000..a8582fa23f5 --- /dev/null +++ b/tests/interface/TWBase32Tests.cpp @@ -0,0 +1,67 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include "Data.h" +#include + +#include + +TEST(TWBase32, InvalidDecode) { + const auto encodedInput = STRING("JBSWY3DPK5XXE3DE======="); + auto result = WRAPD(TWBase32Decode(encodedInput.get())); + ASSERT_EQ(result, nullptr); +} + +TEST(TWBase32, Decode) { + const auto encodedInput = STRING("JBSWY3DPK5XXE3DE"); + auto result = WRAPD(TWBase32Decode(encodedInput.get())); + + ASSERT_NE(result, nullptr); + ASSERT_EQ(TWDataSize(result.get()), 10ul); + + auto data = *reinterpret_cast(result.get()); + std::string str(data.begin(), data.end()); + + ASSERT_EQ(str, "HelloWorld"); +} + +TEST(TWBase32, DecodeWithAlphabet) { + const auto encodedInput = STRING("g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i"); + const auto filecoinAlphabet = STRING("abcdefghijklmnopqrstuvwxyz234567"); + auto result = WRAPD(TWBase32DecodeWithAlphabet(encodedInput.get(), filecoinAlphabet.get())); + + ASSERT_NE(result, nullptr); + ASSERT_EQ(TWDataSize(result.get()), 39ul); + + auto data = *reinterpret_cast(result.get()); + std::string str(data.begin(), data.end()); + + ASSERT_EQ(str, "7uoq6tp427uzv7fztkbsnn64iwotfrristwpryy"); +} + +TEST(TWBase32, Encode) { + TW::Data data{'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd'}; + auto encodedStr = TWBase32Encode(&data); + std::string result = TWStringUTF8Bytes(encodedStr); + + ASSERT_EQ(result, "JBSWY3DPK5XXE3DE"); + + TWStringDelete(encodedStr); +} + +TEST(TWBase32, EncodeWithAlphabet) { + const auto filecoinAlphabet = STRING("abcdefghijklmnopqrstuvwxyz234567"); + TW::Data data{'7', 'u', 'o', 'q', '6', 't', 'p', '4', '2', '7', 'u', 'z', 'v', '7', 'f', + 'z', 't', 'k', 'b', 's', 'n', 'n', '6', '4', 'i', 'w', 'o', 't', 'f', 'r', 'r', 'i', 's', 't', 'w', 'p', 'r', 'y', 'y'}; + auto encodedStr = TWBase32EncodeWithAlphabet(&data, filecoinAlphabet.get()); + std::string result = TWStringUTF8Bytes(encodedStr); + + ASSERT_EQ(result, "g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i"); + + TWStringDelete(encodedStr); +} diff --git a/tests/interface/TWBase58Tests.cpp b/tests/interface/TWBase58Tests.cpp index 79542447036..f2c35aa11fe 100644 --- a/tests/interface/TWBase58Tests.cpp +++ b/tests/interface/TWBase58Tests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/interface/TWBase64Tests.cpp b/tests/interface/TWBase64Tests.cpp new file mode 100644 index 00000000000..fc5332dfdd5 --- /dev/null +++ b/tests/interface/TWBase64Tests.cpp @@ -0,0 +1,56 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include "Data.h" +#include + +#include + +TEST(TWBase64, Decode) { + const auto encodedInput = STRING("Kyc/YWI="); + auto result = WRAPD(TWBase64Decode(encodedInput.get())); + + ASSERT_EQ(TWDataSize(result.get()), 5ul); + + auto data = *reinterpret_cast(result.get()); + std::string str(data.begin(), data.end()); + + ASSERT_EQ(str, "+\'?ab"); +} + +TEST(TWBase64, Encode) { + TW::Data data{'+', '\'', '?', 'a', 'b'}; + auto encodedStr = TWBase64Encode(&data); + std::string result = TWStringUTF8Bytes(encodedStr); + + ASSERT_EQ(result, "Kyc/YWI="); + + TWStringDelete(encodedStr); +} + +TEST(TWBase64, DecodeUrl) { + const auto encodedInput = STRING("Kyc_YWI="); + auto result = WRAPD(TWBase64DecodeUrl(encodedInput.get())); + + ASSERT_EQ(TWDataSize(result.get()), 5ul); + + auto data = *reinterpret_cast(result.get()); + std::string str(data.begin(), data.end()); + + ASSERT_EQ(str, "+\'?ab"); +} + +TEST(TWBase64, EncodeUrl) { + TW::Data data{'+', '\'', '?', 'a', 'b'}; + auto encodedStr = TWBase64EncodeUrl(&data); + std::string result = TWStringUTF8Bytes(encodedStr); + + ASSERT_EQ(result, "Kyc_YWI="); + + TWStringDelete(encodedStr); +} diff --git a/tests/interface/TWCoinTypeTests.cpp b/tests/interface/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..5fd55733ef8 --- /dev/null +++ b/tests/interface/TWCoinTypeTests.cpp @@ -0,0 +1,162 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include + +#include + +TEST(TWCoinType, TWPurpose) { + ASSERT_EQ(TWPurposeBIP84, TWCoinTypePurpose(TWCoinTypeBitcoin)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeBitcoinCash)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeBinance)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeCosmos)); + ASSERT_EQ(TWPurposeBIP84, TWCoinTypePurpose(TWCoinTypeDigiByte)); + ASSERT_EQ(TWPurposeBIP84, TWCoinTypePurpose(TWCoinTypeLitecoin)); + ASSERT_EQ(TWPurposeBIP84, TWCoinTypePurpose(TWCoinTypeGroestlcoin)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeIoTeX)); + ASSERT_EQ(TWPurposeBIP84, TWCoinTypePurpose(TWCoinTypeViacoin)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeQtum)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeZilliqa)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTerra)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeMonacoin)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeKava)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeBandChain)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeBluzelle)); + ASSERT_EQ(TWPurposeBIP1852, TWCoinTypePurpose(TWCoinTypeCardano)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeElrond)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeOasis)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTHORChain)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeCryptoOrg)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeOsmosis)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeECash)); + + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeAion)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeCallisto)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeDash)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeDecred)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeDogecoin)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeEOS)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeEthereum)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeEthereumClassic)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeGoChain)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeICON)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeKin)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeNULS)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeNano)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeNimiq)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeOntology)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypePOANetwork)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeXRP)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeStellar)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTezos)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTheta)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeThunderToken)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTomoChain)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeTron)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeVeChain)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeWanchain)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeZcash)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeFiro)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeZelcash)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeRavencoin)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeWaves)); + ASSERT_EQ(TWPurposeBIP44, TWCoinTypePurpose(TWCoinTypeNEO)); +} + +TEST(TWCoinType, TWHDVersion) { + ASSERT_EQ(TWHDVersionZPUB, TWCoinTypeXpubVersion(TWCoinTypeBitcoin)); + ASSERT_EQ(TWHDVersionZPRV, TWCoinTypeXprvVersion(TWCoinTypeBitcoin)); + + ASSERT_EQ(TWHDVersionXPUB, TWCoinTypeXpubVersion(TWCoinTypeBitcoinCash)); + ASSERT_EQ(TWHDVersionXPRV, TWCoinTypeXprvVersion(TWCoinTypeBitcoinCash)); + + ASSERT_EQ(TWHDVersionDPUB, TWCoinTypeXpubVersion(TWCoinTypeDecred)); + ASSERT_EQ(TWHDVersionDPRV, TWCoinTypeXprvVersion(TWCoinTypeDecred)); + + ASSERT_EQ(TWHDVersionDGUB, TWCoinTypeXpubVersion(TWCoinTypeDogecoin)); + ASSERT_EQ(TWHDVersionDGPV, TWCoinTypeXprvVersion(TWCoinTypeDogecoin)); +} + +TEST(TWCoinType, TWPublicKeyType) { + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeBitcoin)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeBitcoinCash)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeBinance)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeCosmos)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeDigiByte)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeLitecoin)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeGroestlcoin)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeIoTeX)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeViacoin)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeQtum)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeZilliqa)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeTerra)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeMonacoin)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeKava)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeBandChain)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeBluzelle)); + ASSERT_EQ(TWPublicKeyTypeED25519Cardano, TWCoinTypePublicKeyType(TWCoinTypeCardano)); + ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeElrond)); + ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeOasis)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeTHORChain)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeCryptoOrg)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeOsmosis)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeECash)); + + ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeAion)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeCallisto)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeDash)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeDecred)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeDogecoin)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeEOS)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeEthereum)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeEthereumClassic)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeGoChain)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeICON)); + ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeKin)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeNULS)); + ASSERT_EQ(TWPublicKeyTypeED25519Blake2b, TWCoinTypePublicKeyType(TWCoinTypeNano)); + ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeNimiq)); + ASSERT_EQ(TWPublicKeyTypeNIST256p1, TWCoinTypePublicKeyType(TWCoinTypeOntology)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypePOANetwork)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeXRP)); + ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeStellar)); + ASSERT_EQ(TWPublicKeyTypeED25519, TWCoinTypePublicKeyType(TWCoinTypeTezos)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeTheta)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeThunderToken)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeTomoChain)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeTron)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeVeChain)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1Extended, TWCoinTypePublicKeyType(TWCoinTypeWanchain)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeZcash)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeFiro)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeZelcash)); + ASSERT_EQ(TWPublicKeyTypeSECP256k1, TWCoinTypePublicKeyType(TWCoinTypeRavencoin)); + ASSERT_EQ(TWPublicKeyTypeCURVE25519, TWCoinTypePublicKeyType(TWCoinTypeWaves)); + ASSERT_EQ(TWPublicKeyTypeNIST256p1, TWCoinTypePublicKeyType(TWCoinTypeNEO)); +} + +TEST(TWCoinType, TWCoinTypeDerivationPath) { + auto res = TWCoinTypeDerivationPath(TWCoinTypeBitcoin); + auto result = *reinterpret_cast(res); + ASSERT_EQ(result, "m/84'/0'/0'/0/0"); + TWStringDelete(res); +} + +TEST(TWCoinType, TWCoinTypeDerivationPathWithDerivation) { + auto res = TWCoinTypeDerivationPathWithDerivation(TWCoinTypeBitcoin, TWDerivationBitcoinLegacy); + auto result = *reinterpret_cast(res); + ASSERT_EQ(result, "m/44'/0'/0'/0/0"); + TWStringDelete(res); +} + +TEST(TWCoinType, TWCoinTypeDerivationPathWithDerivationSolana) { + auto res = TWCoinTypeDerivationPathWithDerivation(TWCoinTypeSolana, TWDerivationSolanaSolana); + auto result = *reinterpret_cast(res); + ASSERT_EQ(result, "m/44'/501'/0'/0'"); + TWStringDelete(res); +} diff --git a/tests/interface/TWDataTests.cpp b/tests/interface/TWDataTests.cpp index 6e648769c57..015c5e12b00 100644 --- a/tests/interface/TWDataTests.cpp +++ b/tests/interface/TWDataTests.cpp @@ -5,14 +5,14 @@ // file LICENSE at the root of the source code distribution tree. #include -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include TEST(TWData, CreateWithHexString) { { const auto data = WRAPD(TWDataCreateWithHexString(STRING("deadbeef").get())); - ASSERT_EQ(TWDataSize(data.get()), 4); + ASSERT_EQ(TWDataSize(data.get()), 4ul); EXPECT_EQ(TWDataBytes(data.get())[0], 0xde); EXPECT_EQ(TWDataBytes(data.get())[1], 0xad); EXPECT_EQ(TWDataBytes(data.get())[2], 0xbe); @@ -22,7 +22,7 @@ TEST(TWData, CreateWithHexString) { { const auto data = WRAPD(TWDataCreateWithHexString(STRING("00").get())); - ASSERT_EQ(TWDataSize(data.get()), 1); + ASSERT_EQ(TWDataSize(data.get()), 1ul); EXPECT_EQ(TWDataBytes(data.get())[0], 0); assertHexEqual(data, "00"); } @@ -60,10 +60,10 @@ TEST(TWData, CreateWithBytes) { } TEST(TWData, CreateWithSize) { - int n = 12; + std::size_t n = 12; const auto data = WRAPD(TWDataCreateWithSize(n)); ASSERT_EQ(TWDataSize(data.get()), n); - for (int i = 0; i < n; ++i) { + for (auto i = 0ul; i < n; ++i) { EXPECT_EQ(TWDataBytes(data.get())[i], 0); } } diff --git a/tests/interface/TWDataVectorTests.cpp b/tests/interface/TWDataVectorTests.cpp new file mode 100644 index 00000000000..36a984c2e2c --- /dev/null +++ b/tests/interface/TWDataVectorTests.cpp @@ -0,0 +1,83 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include "HexCoding.h" +#include "TestUtilities.h" + +#include + +using namespace TW; + + +TEST(TWDataVector, CreateDelete) { + auto vec = TWDataVectorCreate(); + + ASSERT_TRUE(vec != nullptr); + EXPECT_EQ(TWDataVectorSize(vec), 0ul); + + TWDataVectorDelete(vec); +} + +TEST(TWDataVector, CreateWrapAutoDelete) { + auto vec = WRAP(TWDataVector, TWDataVectorCreate()); + + ASSERT_TRUE(vec.get() != nullptr); + EXPECT_EQ(TWDataVectorSize(vec.get()), 0ul); +} + +TEST(TWDataVector, CreateWithData) { + const auto elem1d = parse_hex("deadbeef"); + const auto elem1 = WRAPD(TWDataCreateWithBytes(elem1d.data(), elem1d.size())); + const auto vec = WRAP(TWDataVector, TWDataVectorCreateWithData(elem1.get())); + + ASSERT_TRUE(vec.get() != nullptr); + ASSERT_EQ(TWDataVectorSize(vec.get()), 1ul); + + const auto readElem1 = WRAPD(TWDataVectorGet(vec.get(), 0)); + EXPECT_EQ(hex(*static_cast(readElem1.get())), "deadbeef"); +} + +TEST(TWDataVector, Add) { + const auto vec = WRAP(TWDataVector, TWDataVectorCreate()); + + ASSERT_TRUE(vec.get() != nullptr); + EXPECT_EQ(TWDataVectorSize(vec.get()), 0ul); + + const auto elem1d = parse_hex("deadbeef"); + const auto elem1 = WRAPD(TWDataCreateWithBytes(elem1d.data(), elem1d.size())); + TWDataVectorAdd(vec.get(), elem1.get()); + + ASSERT_EQ(TWDataVectorSize(vec.get()), 1ul); + const auto readElem1 = WRAPD(TWDataVectorGet(vec.get(), 0)); + EXPECT_EQ(hex(*static_cast(readElem1.get())), "deadbeef"); + + const auto elem2d = parse_hex("0202"); + const auto elem2 = WRAPD(TWDataCreateWithBytes(elem2d.data(), elem2d.size())); + TWDataVectorAdd(vec.get(), elem2.get()); + + ASSERT_EQ(TWDataVectorSize(vec.get()), 2ul); + const auto readElem2 = WRAPD(TWDataVectorGet(vec.get(), 1)); + EXPECT_EQ(hex(*static_cast(readElem2.get())), "0202"); +} + +TEST(TWDataVector, Get) { + const auto elem1d = parse_hex("deadbeef"); + const auto elem1 = WRAPD(TWDataCreateWithBytes(elem1d.data(), elem1d.size())); + const auto vec = WRAP(TWDataVector, TWDataVectorCreateWithData(elem1.get())); + + ASSERT_TRUE(vec.get() != nullptr); + ASSERT_EQ(TWDataVectorSize(vec.get()), 1ul); + + { // Get element + const auto readElem1 = WRAPD(TWDataVectorGet(vec.get(), 0)); + EXPECT_EQ(hex(*static_cast(readElem1.get())), "deadbeef"); + } + { // Get with bad index + const auto readElem = TWDataVectorGet(vec.get(), 666); + EXPECT_EQ(readElem, nullptr); + } +} diff --git a/tests/interface/TWDerivationPathTests.cpp b/tests/interface/TWDerivationPathTests.cpp new file mode 100644 index 00000000000..15db6b2645e --- /dev/null +++ b/tests/interface/TWDerivationPathTests.cpp @@ -0,0 +1,64 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" +#include "TrustWalletCore/TWDerivation.h" +#include "TrustWalletCore/TWPurpose.h" +#include +#include + +#include + +TEST(TWDerivationPath, CreateWithString) { + const auto derivationPath = STRING("m/84'/1'/2'/3/4"); + + const auto path = WRAP(TWDerivationPath, TWDerivationPathCreateWithString(derivationPath.get())); + + EXPECT_EQ(5u, TWDerivationPathIndicesCount(path.get())); + EXPECT_EQ(TWPurposeBIP84, TWDerivationPathPurpose(path.get())); + EXPECT_EQ(1u, TWDerivationPathCoin(path.get())); + EXPECT_EQ(2u, TWDerivationPathAccount(path.get())); + EXPECT_EQ(3u, TWDerivationPathChange(path.get())); + EXPECT_EQ(4u, TWDerivationPathAddress(path.get())); + assertStringsEqual(WRAPS(TWDerivationPathDescription(path.get())), "m/84'/1'/2'/3/4"); + + const auto index0 = WRAP(TWDerivationPathIndex, TWDerivationPathIndexAt(path.get(), 0)); + const auto index3 = WRAP(TWDerivationPathIndex, TWDerivationPathIndexAt(path.get(), 3)); + + EXPECT_EQ(NULL, TWDerivationPathIndexAt(path.get(), 10)); + EXPECT_EQ(TWPurposeBIP84, TWDerivationPathIndexValue(index0.get())); + EXPECT_TRUE(TWDerivationPathIndexHardened(index0.get())); + + EXPECT_EQ(3u, TWDerivationPathIndexValue(index3.get())); + EXPECT_FALSE(TWDerivationPathIndexHardened(index3.get())); + assertStringsEqual(WRAPS(TWDerivationPathIndexDescription(index0.get())), "84'"); + + const auto path2 = WRAP(TWDerivationPath, TWDerivationPathCreateWithString(STRING("m/44'/501'").get())); + + EXPECT_EQ(2u, TWDerivationPathIndicesCount(path2.get())); + EXPECT_EQ(TWPurposeBIP44, TWDerivationPathPurpose(path2.get())); + EXPECT_EQ(501u, TWDerivationPathCoin(path2.get())); + EXPECT_EQ(0u, TWDerivationPathAccount(path2.get())); + EXPECT_EQ(0u, TWDerivationPathChange(path2.get())); + EXPECT_EQ(0u, TWDerivationPathAddress(path2.get())); +} + +TEST(TWDerivationPath, CreateWithCoin) { + + const auto path = WRAP(TWDerivationPath, TWDerivationPathCreate(TWPurposeBIP44, 60, 0, 0, 0)); + + EXPECT_EQ(5u, TWDerivationPathIndicesCount(path.get())); + EXPECT_EQ(TWPurposeBIP44, TWDerivationPathPurpose(path.get())); + EXPECT_EQ(60u, TWDerivationPathCoin(path.get())); + EXPECT_EQ(0u, TWDerivationPathAccount(path.get())); + EXPECT_EQ(0u, TWDerivationPathChange(path.get())); + EXPECT_EQ(0u, TWDerivationPathAddress(path.get())); + assertStringsEqual(WRAPS(TWDerivationPathDescription(path.get())), "m/44'/60'/0'/0/0"); + + const auto index = WRAP(TWDerivationPathIndex, TWDerivationPathIndexCreate(44, true)); + const auto description = WRAPS(TWDerivationPathIndexDescription(index.get())); + assertStringsEqual(description, "44'"); +} diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index a3ca7f87b91..63fc42fec3c 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include "Coin.h" @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "HexCoding.h" @@ -23,9 +24,11 @@ #include #include +using namespace TW; + const auto wordsStr = "ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal"; -const auto words = STRING(wordsStr); -const auto passphrase = STRING("TREZOR"); +const auto gWords = STRING(wordsStr); +const auto gPassphrase = STRING("TREZOR"); const auto seedHex = "7ae6f661157bda6492f6162701e570097fc726b6235011ea5ad09bf04986731ed4d92bc43cbdee047b60ea0dd1b1fa4274377c9bf5bd14ab1982c272d8076f29"; const auto entropyHex = "ba5821e8c356c05ba5f025d9532fe0f21f65d594"; @@ -46,31 +49,31 @@ inline void assertEntropyEq(const std::shared_ptr& wallet, const cha } TEST(HDWallet, CreateFromMnemonic) { - const auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + const auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); assertMnemonicEq(wallet, wordsStr); assertEntropyEq(wallet, entropyHex); assertSeedEq(wallet, seedHex); } TEST(HDWallet, CreateFromEntropy) { - const auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithEntropy(DATA(entropyHex).get(), passphrase.get())); + const auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithEntropy(DATA(entropyHex).get(), gPassphrase.get())); assertMnemonicEq(wallet, wordsStr); assertSeedEq(wallet, seedHex); assertEntropyEq(wallet, entropyHex); } TEST(HDWallet, Generate) { - const auto wallet = WRAP(TWHDWallet, TWHDWalletCreate(128, passphrase.get())); + const auto wallet = WRAP(TWHDWallet, TWHDWalletCreate(128, gPassphrase.get())); EXPECT_TRUE(TWMnemonicIsValid(WRAPS(TWHDWalletMnemonic(wallet.get())).get())); } TEST(HDWallet, SeedWithExtraSpaces) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); assertSeedEq(wallet, "7ae6f661157bda6492f6162701e570097fc726b6235011ea5ad09bf04986731ed4d92bc43cbdee047b60ea0dd1b1fa4274377c9bf5bd14ab1982c272d8076f29"); } TEST(HDWallet, CreateFromMnemonicNoPassword) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), STRING("").get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), STRING("").get())); assertSeedEq(wallet, "354c22aedb9a37407adc61f657a6f00d10ed125efa360215f36c6919abd94d6dbc193a5f9c495e21ee74118661e327e84a5f5f11fa373ec33b80897d4697557d"); } @@ -85,7 +88,7 @@ TEST(HDWallet, CreateFromMnemonicInvalid) { } TEST(HDWallet, MasterPrivateKey) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), STRING("").get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), STRING("").get())); auto key1 = WRAP(TWPrivateKey, TWHDWalletGetMasterKey(wallet.get(), TWCurveSECP256k1)); auto hexKey1 = WRAPD(TWPrivateKeyData(key1.get())); @@ -99,7 +102,7 @@ TEST(HDWallet, MasterPrivateKey) { TEST(HDWallet, Derive) { const auto derivationPath = TW::derivationPath(TWCoinTypeEthereum); - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key0 = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeEthereum)); auto publicKey0 = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(key0.get(), false)); @@ -109,7 +112,7 @@ TEST(HDWallet, Derive) { } TEST(HDWallet, DeriveBitcoinNonextended) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeBitcoin)); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(key.get(), false)); auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); @@ -119,7 +122,7 @@ TEST(HDWallet, DeriveBitcoinNonextended) { } TEST(HDWallet, DeriveBitcoinExtended) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeBitcoin)); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(key.get(), true)); auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); @@ -131,13 +134,13 @@ TEST(HDWallet, DeriveBitcoinExtended) { } TEST(HDWallet, DeriveAddressBitcoin) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto address = WRAP(TWString, TWHDWalletGetAddressForCoin(wallet.get(), TWCoinTypeBitcoin)); assertStringsEqual(address, "bc1qumwjg8danv2vm29lp5swdux4r60ezptzz7ce85"); } TEST(HDWallet, DeriveEthereum) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeEthereum)); auto key2 = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeSmartChain)); @@ -154,7 +157,7 @@ TEST(HDWallet, DeriveEthereum) { } TEST(HDWallet, DeriveAddressEthereum) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto address = WRAP(TWString, TWHDWalletGetAddressForCoin(wallet.get(), TWCoinTypeEthereum)); assertStringsEqual(address, "0x27Ef5cDBe01777D62438AfFeb695e33fC2335979"); } @@ -176,7 +179,7 @@ TEST(HDWallet, DeriveCosmos) { } TEST(HDWallet, DeriveNimiq) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeNimiq)); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519(key.get())); auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); @@ -185,7 +188,7 @@ TEST(HDWallet, DeriveNimiq) { } TEST(HDWallet, DeriveTezos) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeTezos)); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519(key.get())); auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); @@ -194,7 +197,7 @@ TEST(HDWallet, DeriveTezos) { } TEST(HDWallet, DeriveDoge) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeDogecoin)); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(key.get(), true)); auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); @@ -209,7 +212,7 @@ TEST(HDWallet, DeriveDoge) { } TEST(HDWallet, DeriveZilliqa) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeZilliqa)); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(key.get(), true)); auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); @@ -241,7 +244,7 @@ TEST(HDWallet, DeriveFIO) { } TEST(HDWallet, DeriveAlgorand) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeAlgorand)); auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeAlgorand, privateKey.get())); @@ -250,7 +253,7 @@ TEST(HDWallet, DeriveAlgorand) { } TEST(HDWallet, DeriveElrond) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeElrond)); auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeElrond, privateKey.get())); @@ -260,7 +263,7 @@ TEST(HDWallet, DeriveElrond) { } TEST(HDWallet, DeriveBinance) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeBinance)); auto key2 = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeSmartChain)); auto keyData = WRAPD(TWPrivateKeyData(key.get())); @@ -274,7 +277,7 @@ TEST(HDWallet, DeriveBinance) { } TEST(HDWallet, DeriveAvalancheCChain) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); auto key = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeAvalancheCChain)); auto keyData = WRAPD(TWPrivateKeyData(key.get())); @@ -283,6 +286,17 @@ TEST(HDWallet, DeriveAvalancheCChain) { assertHexEqual(keyData, expectedETH); } +TEST(HDWallet, DeriveCardano) { + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeCardano)); + auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); + EXPECT_EQ(TWDataSize(privateKeyData.get()), 192ul); + auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeCardano, privateKey.get())); + + assertHexEqual(privateKeyData, "f8a3b8ad30e62c369b939336c2035aba26d1ffad135e6f346f2a370517a14952e73d20aeadf906bc8b531900fb6c3ed4a05b16973c10ae24650b68b26fae4ee5d97418ba7f3b2707fae963041ff5f174195d1578da09478ad2d17a1ecc00cad478a8ca3be214870accd41f008d70e3b4b59b5981ca933d6d3f389ad317a14952166d8fd329ae3fab4712da739efc2ded9b3eef2b1a8e225dd3dddeb4f065a729b297d9fa76b8852eef235c25aac8f0ff6209ab7251f2a84c83b3b5f1161f7c59"); + assertStringsEqual(address, "addr1q9zz5nj4rqdvteauvdtc834f2vtzyrdrrdmnwdyp4s6huuz5fp33p9cq4xwpqtgguaxknzurmglv58yn9wrv6angpvvq9u36ya"); +} + TEST(HDWallet, ExtendedKeys) { auto words = STRING("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"); auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), STRING("").get())); @@ -309,6 +323,21 @@ TEST(HDWallet, ExtendedKeys) { assertStringsEqual(emptyPub, ""); } +TEST(HDWallet, ExtendedKeysCustomAccount) { + auto words = STRING("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), STRING("").get())); + + auto zprv0 = WRAPS(TWHDWalletGetExtendedPrivateKeyAccount(wallet.get(), TWPurposeBIP84, TWCoinTypeBitcoin, TWDerivationBitcoinSegwit, TWHDVersionZPRV, 0)); + assertStringsEqual(zprv0, "zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE"); + auto zprv1 = WRAPS(TWHDWalletGetExtendedPrivateKeyAccount(wallet.get(), TWPurposeBIP84, TWCoinTypeBitcoin, TWDerivationBitcoinSegwit, TWHDVersionZPRV, 1)); + assertStringsEqual(zprv1, "zprvAdG4iTXWBoAS2cCGuaGevCvH54GCunrvLJb2hoWCSuE3D9LS42XVg3c6sPm64w6VMq3w18vJf8nF3cBA2kUMkyWHsq6enWVXivzw42UrVHG"); + + auto zpub0 = WRAPS(TWHDWalletGetExtendedPublicKeyAccount(wallet.get(), TWPurposeBIP84, TWCoinTypeBitcoin, TWDerivationBitcoinSegwit, TWHDVersionZPUB, 0)); + assertStringsEqual(zpub0, "zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs"); + auto zpub1 = WRAPS(TWHDWalletGetExtendedPublicKeyAccount(wallet.get(), TWPurposeBIP84, TWCoinTypeBitcoin, TWDerivationBitcoinSegwit, TWHDVersionZPUB, 1)); + assertStringsEqual(zpub1, "zpub6rFR7y4Q2AijF6Gk1bofHLs1d66hKFamhXWdWBup1Em25wfabZqkDqvaieV63fDQFaYmaatCG7jVNUpUiM2hAMo6SAVHcrUpSnHDpNzucB7"); +} + TEST(HDWallet, PublicKeyFromX) { auto xpub = STRING("xpub6BosfCnifzxcFwrSzQiqu2DBVTshkCXacvNsWGYJVVhhawA7d4R5WSWGFNbi8Aw6ZRc1brxMyWMzG3DSSSSoekkudhUd9yLb6qx39T9nMdj"); auto xpubAddr2 = WRAP(TWPublicKey, TWHDWalletGetPublicKeyFromExtended(xpub.get(), TWCoinTypeBitcoinCash, STRING("m/44'/145'/0'/0/2").get())); @@ -420,8 +449,75 @@ TEST(HDWallet, MultipleThreads) { } TEST(HDWallet, GetDerivedKey) { - auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), gPassphrase.get())); const auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetDerivedKey(wallet.get(), TWCoinTypeBitcoin, 0, 0, 0)); const auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); assertHexEqual(privateKeyData, "1901b5994f075af71397f65bd68a9fff8d3025d65f5a2c731cf90f5e259d6aac"); } + +TEST(HDWallet, GetKeyByCurve) { + const auto derivPath = STRING("m/44'/539'/0'/0/0"); + + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), STRING("").get())); + const auto privateKey1 = WRAP(TWPrivateKey, TWHDWalletGetKeyByCurve(wallet.get(), TWCurveSECP256k1, derivPath.get())); + const auto privateKeyData1 = WRAPD(TWPrivateKeyData(privateKey1.get())); + assertHexEqual(privateKeyData1, "4fb8657d6464adcaa086d6758d7f0b6b6fc026c98dc1671fcc6460b5a74abc62"); + + const auto privateKey2 = WRAP(TWPrivateKey, TWHDWalletGetKeyByCurve(wallet.get(), TWCurveNIST256p1, derivPath.get())); + const auto privateKeyData2 = WRAPD(TWPrivateKeyData(privateKey2.get())); + assertHexEqual(privateKeyData2, "a13df52d5a5b438bbf921bbf86276e4347fe8e2f2ed74feaaee12b77d6d26f86"); +} + +TEST(TWHDWallet, Derive_XpubPub_vs_PrivPub) { + // Test different routes for deriving address from mnemonic, result should be the same: + // - Direct: mnemonic -> seed -> privateKey -> publicKey -> address + // - Extended Public: mnemonic -> seed -> zpub -> publicKey -> address + + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(gWords.get(), STRING("").get())); + const auto coin = TWCoinTypeBitcoin; + const auto derivPath1 = STRING("m/84'/0'/0'/0/0"); + const auto derivPath2 = STRING("m/84'/0'/0'/0/2"); + const auto expectedPublicKey1 = "02df9ef2a7a5552765178b181e1e1afdefc7849985c7dfe9647706dd4fa40df6ac"; + const auto expectedPublicKey2 = "031e1f64d2f6768dccb6814545b2e2d58e26ad5f91b7cbaffe881ed572c65060db"; + const auto expectedAddress1 = "bc1qpsp72plnsqe6e2dvtsetxtww2cz36ztmfxghpd"; + const auto expectedAddress2 = "bc1q7zddsunzaftf4zlsg9exhzlkvc5374a6v32jf6"; + + // -> privateKey -> publicKey + { + const auto privateKey1 = WRAP(TWPrivateKey, TWHDWalletGetKey(wallet.get(), coin, derivPath1.get())); + const auto publicKey1 = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey1.get(), true)); + const auto publicKey1Data = WRAPD(TWPublicKeyData(publicKey1.get())); + EXPECT_EQ(hex(TW::data(TWDataBytes(publicKey1Data.get()), TWDataSize(publicKey1Data.get()))), expectedPublicKey1); + const auto address1 = WRAP(TWSegwitAddress, TWSegwitAddressCreateWithPublicKey(TWHRPBitcoin, publicKey1.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(WRAPS(TWSegwitAddressDescription(address1.get())).get())), expectedAddress1); + } + { + const auto privateKey2 = WRAP(TWPrivateKey, TWHDWalletGetKey(wallet.get(), coin, derivPath2.get())); + const auto publicKey2 = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey2.get(), true)); + const auto publicKey2Data = WRAPD(TWPublicKeyData(publicKey2.get())); + EXPECT_EQ(hex(TW::data(TWDataBytes(publicKey2Data.get()), TWDataSize(publicKey2Data.get()))), expectedPublicKey2); + const auto address2 = WRAP(TWSegwitAddress, TWSegwitAddressCreateWithPublicKey(TWHRPBitcoin, publicKey2.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(WRAPS(TWSegwitAddressDescription(address2.get())).get())), expectedAddress2); + } + + // zpub -> publicKey + const auto zpub = WRAPS(TWHDWalletGetExtendedPublicKey(wallet.get(), TWPurposeBIP84, coin, TWHDVersionZPUB)); + EXPECT_EQ(std::string(TWStringUTF8Bytes(zpub.get())), "zpub6rNUNtxSa9Gxvm4Bdxf1MPMwrvkzwDx6vP96Hkzw3jiQKdg3fhXBStxjn12YixQB8h88B3RMSRscRstf9AEVaYr3MAqVBEWBDuEJU4PGaT9"); + + { + const auto publicKey1 = WRAP(TWPublicKey, TWHDWalletGetPublicKeyFromExtended(zpub.get(), coin, derivPath1.get())); + EXPECT_NE(publicKey1.get(), nullptr); + const auto publicKey1Data = WRAPD(TWPublicKeyData(publicKey1.get())); + EXPECT_EQ(hex(TW::data(TWDataBytes(publicKey1Data.get()), TWDataSize(publicKey1Data.get()))), expectedPublicKey1); + const auto address1 = WRAP(TWSegwitAddress, TWSegwitAddressCreateWithPublicKey(TWHRPBitcoin, publicKey1.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(WRAPS(TWSegwitAddressDescription(address1.get())).get())), expectedAddress1); + } + { + const auto publicKey2 = WRAP(TWPublicKey, TWHDWalletGetPublicKeyFromExtended(zpub.get(), coin, derivPath2.get())); + EXPECT_NE(publicKey2.get(), nullptr); + const auto publicKey2Data = WRAPD(TWPublicKeyData(publicKey2.get())); + EXPECT_EQ(hex(TW::data(TWDataBytes(publicKey2Data.get()), TWDataSize(publicKey2Data.get()))), expectedPublicKey2); + const auto address2 = WRAP(TWSegwitAddress, TWSegwitAddressCreateWithPublicKey(TWHRPBitcoin, publicKey2.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(WRAPS(TWSegwitAddressDescription(address2.get())).get())), expectedAddress2); + } +} diff --git a/tests/interface/TWHRPTests.cpp b/tests/interface/TWHRPTests.cpp index 562d95ec3e1..a24bda956ff 100644 --- a/tests/interface/TWHRPTests.cpp +++ b/tests/interface/TWHRPTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include #include @@ -34,6 +34,7 @@ TEST(TWHRP, StringForHRP) { ASSERT_STREQ(stringForHRP(TWHRPOasis), "oasis"); ASSERT_STREQ(stringForHRP(TWHRPTHORChain), "thor"); ASSERT_STREQ(stringForHRP(TWHRPCryptoOrg), "cro"); + ASSERT_STREQ(stringForHRP(TWHRPOsmosis), "osmo"); } TEST(TWHRP, HRPForString) { @@ -59,6 +60,8 @@ TEST(TWHRP, HRPForString) { ASSERT_EQ(hrpForString("thor"), TWHRPTHORChain); ASSERT_EQ(hrpForString("bluzelle"), TWHRPBluzelle); ASSERT_EQ(hrpForString("cro"), TWHRPCryptoOrg); + ASSERT_EQ(hrpForString("osmo"), TWHRPOsmosis); + ASSERT_EQ(hrpForString("ecash"), TWHRPECash); } TEST(TWHPR, HPRByCoinType) { @@ -83,6 +86,8 @@ TEST(TWHPR, HPRByCoinType) { ASSERT_EQ(TWHRPOasis, TWCoinTypeHRP(TWCoinTypeOasis)); ASSERT_EQ(TWHRPTHORChain, TWCoinTypeHRP(TWCoinTypeTHORChain)); ASSERT_EQ(TWHRPCryptoOrg, TWCoinTypeHRP(TWCoinTypeCryptoOrg)); + ASSERT_EQ(TWHRPOsmosis, TWCoinTypeHRP(TWCoinTypeOsmosis)); + ASSERT_EQ(TWHRPECash, TWCoinTypeHRP(TWCoinTypeECash)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeAion)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeCallisto)); @@ -110,7 +115,7 @@ TEST(TWHPR, HPRByCoinType) { ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeVeChain)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeWanchain)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeZcash)); - ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeZcoin)); + ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeFiro)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeZelcash)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeRavencoin)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeWaves)); diff --git a/tests/interface/TWHashTests.cpp b/tests/interface/TWHashTests.cpp index 5ead9c659fd..e67d0aeb36f 100644 --- a/tests/interface/TWHashTests.cpp +++ b/tests/interface/TWHashTests.cpp @@ -9,7 +9,7 @@ #include -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include using namespace std; @@ -176,38 +176,6 @@ TEST(TWHashTests, Groestl512) { } } -TEST(TWHashTests, XXHash64) { - auto tests = { - make_tuple(string(""), string("99e9d85137db46ef"), 0), - make_tuple(brownFox, string("bc71da1f362d240b"), 0), - make_tuple(brownFoxDot, string("73ad51577033ad44"), 0), - make_tuple(string("123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF"), - string("fd84b6962fcb8d09"), 0), - make_tuple(string("123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF"), - string("7ea0f7af4b4f9cf4"), 1), - }; - for (auto &test: tests) { - const auto inData = WRAPD(TWDataCreateWithBytes(reinterpret_cast(get<0>(test).c_str()), get<0>(test).length())); - const auto hash = WRAPD(TWHashXXHash64(inData.get(), get<2>(test))); - EXPECT_EQ(hex(data(TWDataBytes(hash.get()), TWDataSize(hash.get()))), get<1>(test)); - } -} - -TEST(TWHashTests, XXHash64Concat) { - auto tests = { - make_tuple(string(""), string("99e9d85137db46ef4bbea33613baafd5")), - make_tuple(brownFox, string("bc71da1f362d240bdbc6d2dab69150df")), - make_tuple(brownFoxDot, string("73ad51577033ad44269e8e5ef42d32d2")), - make_tuple(string("Balances"), string("c2261276cc9d1f8598ea4b6a74b15c2f")), - make_tuple(string("FreeBalance"), string("6482b9ade7bc6657aaca787ba1add3b4")), - }; - for (auto &test: tests) { - const auto inData = WRAPD(TWDataCreateWithBytes(reinterpret_cast(get<0>(test).c_str()), get<0>(test).length())); - const auto hash = WRAPD(TWHashTwoXXHash64Concat(inData.get())); - EXPECT_EQ(hex(data(TWDataBytes(hash.get()), TWDataSize(hash.get()))), get<1>(test)); - } -} - TEST(TWHashTests, SHA256SHA256) { auto tests = { make_tuple(string(""), string("5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456")), diff --git a/tests/interface/TWMnemonicTests.cpp b/tests/interface/TWMnemonicTests.cpp index 6ed7d3b1a9c..df507c553a9 100644 --- a/tests/interface/TWMnemonicTests.cpp +++ b/tests/interface/TWMnemonicTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/interface/TWPBKDF2Tests.cpp b/tests/interface/TWPBKDF2Tests.cpp new file mode 100644 index 00000000000..57c48c73083 --- /dev/null +++ b/tests/interface/TWPBKDF2Tests.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "TestUtilities.h" + +#include + +#include + +TEST(TWPBKDF2, Sha256_sha512) { + auto password = DATA("50617373776f7264"); // Password + auto salt = DATA("4e61436c"); // NaCl + + auto sha256Result = WRAPD(TWPBKDF2HmacSha256(password.get(), salt.get(), 80000, 128)); + assertHexEqual(sha256Result, "4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d62aae85a11cdde829d89cb6ffd1ab0e63a981f8747d2f2f9fe5874165c83c168d2eed1d2d5ca4052dec2be5715623da019b8c0ec87dc36aa751c38f9893d15c3"); + + auto sha512Result = WRAPD(TWPBKDF2HmacSha512(password.get(), salt.get(), 80000, 128)); + assertHexEqual(sha512Result, "e6337d6fbeb645c794d4a9b5b75b7b30dac9ac50376a91df1f4460f6060d5addb2c1fd1f84409abacc67de7eb4056e6bb06c2d82c3ef4ccd1bded0f675ed97c65c33d39f81248454327aa6d03fd049fc5cbb2b5e6dac08e8ace996cdc960b1bd4530b7e754773d75f67a733fdb99baf6470e42ffcb753c15c352d4800fb6f9d6"); +} diff --git a/tests/interface/TWPrivateKeyTests.cpp b/tests/interface/TWPrivateKeyTests.cpp index d6fe4ea8f36..2ce9d6be10c 100644 --- a/tests/interface/TWPrivateKeyTests.cpp +++ b/tests/interface/TWPrivateKeyTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include "PrivateKey.h" #include "PublicKey.h" @@ -167,7 +167,7 @@ TEST(TWPrivateKeyTests, SignAsDER) { const auto data = WRAPD(TWDataCreateWithBytes((uint8_t *)message, strlen(message))); const auto hash = WRAPD(TWHashKeccak256(data.get())); - auto actual = WRAPD(TWPrivateKeySignAsDER(privateKey.get(), hash.get(), TWCurveSECP256k1)); + auto actual = WRAPD(TWPrivateKeySignAsDER(privateKey.get(), hash.get())); ASSERT_EQ(TW::hex(*((TW::Data*)actual.get())), "30450221008720a46b5b3963790d94bcc61ad57ca02fd153584315bfa161ed3455e336ba6202204d68df010ed934b8792c5b6a57ba86c3da31d039f9612b44d1bf054132254de9"); diff --git a/tests/interface/TWPublicKeyTests.cpp b/tests/interface/TWPublicKeyTests.cpp index b0ec026e983..64d712cc96a 100644 --- a/tests/interface/TWPublicKeyTests.cpp +++ b/tests/interface/TWPublicKeyTests.cpp @@ -1,10 +1,10 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include "PublicKey.h" #include "PrivateKey.h" @@ -49,20 +49,20 @@ TEST(TWPublicKeyTests, CompressedExtended) { const auto privateKey = WRAP(TWPrivateKey, new TWPrivateKey{ key }); auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), true)); EXPECT_EQ(TWPublicKeyKeyType(publicKey.get()), TWPublicKeyTypeSECP256k1); - EXPECT_EQ(publicKey.get()->impl.bytes.size(), 33); + EXPECT_EQ(publicKey.get()->impl.bytes.size(), 33ul); EXPECT_EQ(TWPublicKeyIsCompressed(publicKey.get()), true); EXPECT_TRUE(TWPublicKeyIsValid(publicKey.get(), TWPublicKeyTypeSECP256k1)); auto extended = WRAP(TWPublicKey, TWPublicKeyUncompressed(publicKey.get())); EXPECT_EQ(TWPublicKeyKeyType(extended.get()), TWPublicKeyTypeSECP256k1Extended); - EXPECT_EQ(extended.get()->impl.bytes.size(), 65); + EXPECT_EQ(extended.get()->impl.bytes.size(), 65ul); EXPECT_EQ(TWPublicKeyIsCompressed(extended.get()), false); EXPECT_TRUE(TWPublicKeyIsValid(extended.get(), TWPublicKeyTypeSECP256k1Extended)); auto compressed = WRAP(TWPublicKey, TWPublicKeyCompressed(extended.get())); //EXPECT_TRUE(compressed == publicKey.get()); EXPECT_EQ(TWPublicKeyKeyType(compressed.get()), TWPublicKeyTypeSECP256k1); - EXPECT_EQ(compressed.get()->impl.bytes.size(), 33); + EXPECT_EQ(compressed.get()->impl.bytes.size(), 33ul); EXPECT_EQ(TWPublicKeyIsCompressed(compressed.get()), true); EXPECT_TRUE(TWPublicKeyIsValid(compressed.get(), TWPublicKeyTypeSECP256k1)); } @@ -81,6 +81,23 @@ TEST(TWPublicKeyTests, Verify) { ASSERT_TRUE(TWPublicKeyVerify(publicKey.get(), signature.get(), digest.get())); } +TEST(TWPublicKeyTests, VerifyAsDER) { + const PrivateKey key = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + const auto privateKey = WRAP(TWPrivateKey, new TWPrivateKey{ key }); + + const char* message = "Hello"; + auto messageData = WRAPD(TWDataCreateWithBytes((const uint8_t*)message, strlen(message))); + auto digest = WRAPD(TWHashKeccak256(messageData.get())); + + auto signature = WRAPD(TWPrivateKeySignAsDER(privateKey.get(), digest.get())); + + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), false)); + + ASSERT_TRUE(TWPublicKeyVerifyAsDER(publicKey.get(), signature.get(), digest.get())); + + ASSERT_FALSE(TWPublicKeyVerify(publicKey.get(), signature.get(), digest.get())); +} + TEST(TWPublicKeyTests, VerifyEd25519) { const PrivateKey key(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); const auto privateKey = WRAP(TWPrivateKey, new TWPrivateKey{ key }); diff --git a/tests/interface/TWStoredKeyTests.cpp b/tests/interface/TWStoredKeyTests.cpp index ff498e4f6cb..6cfa96c04c8 100644 --- a/tests/interface/TWStoredKeyTests.cpp +++ b/tests/interface/TWStoredKeyTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include #include @@ -14,6 +14,7 @@ #include "../src/HexCoding.h" #include +#include #include @@ -40,7 +41,7 @@ struct std::shared_ptr createDefaultStoredKey() { } TEST(TWStoredKey, loadPBKDF2Key) { - const auto filename = WRAPS(TWStringCreateWithUTF8Bytes((TESTS_ROOT + "/Keystore/Data/pbkdf2.json").c_str())); + const auto filename = WRAPS(TWStringCreateWithUTF8Bytes((TESTS_ROOT + "/common/Keystore/Data/pbkdf2.json").c_str())); const auto key = WRAP(TWStoredKey, TWStoredKeyLoad(filename.get())); const auto keyId = WRAPS(TWStoredKeyIdentifier(key.get())); EXPECT_EQ(string(TWStringUTF8Bytes(keyId.get())), "3198bc9c-6672-5ab3-d995-4942343ae5b6"); @@ -56,7 +57,7 @@ TEST(TWStoredKey, createWallet) { const auto name = WRAPS(TWStringCreateWithUTF8Bytes("name")); const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password")); const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get()))); - const auto key = WRAP(TWStoredKey, TWStoredKeyCreate(name.get(), password.get())); + const auto key = WRAP(TWStoredKey, TWStoredKeyCreateLevel(name.get(), password.get(), TWStoredKeyEncryptionLevelDefault)); const auto name2 = WRAPS(TWStoredKeyName(key.get())); EXPECT_EQ(string(TWStringUTF8Bytes(name2.get())), "name"); const auto mnemonic = WRAPS(TWStoredKeyDecryptMnemonic(key.get(), password.get())); @@ -108,27 +109,73 @@ TEST(TWStoredKey, addressAddRemove) { const auto accountAddress = WRAPS(TWAccountAddress(accountCoin.get())); EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress.get())), "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); - EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 1); + EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 1ul); const auto accountIdx = WRAP(TWAccount, TWStoredKeyAccount(key.get(), 0)); TWStoredKeyRemoveAccountForCoin(key.get(), coin); - EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 0); + EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 0ul); const auto addressAdd = "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"; const auto derivationPath = "m/84'/0'/0'/0/0"; const auto extPubKeyAdd = "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"; + const auto pubKey = "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006"; TWStoredKeyAddAccount(key.get(), WRAPS(TWStringCreateWithUTF8Bytes(addressAdd)).get(), TWCoinTypeBitcoin, WRAPS(TWStringCreateWithUTF8Bytes(derivationPath)).get(), + WRAPS(TWStringCreateWithUTF8Bytes(pubKey)).get(), WRAPS(TWStringCreateWithUTF8Bytes(extPubKeyAdd)).get()); - EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 1); + EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 1ul); // invalid account index EXPECT_EQ(TWStoredKeyAccount(key.get(), 1001), nullptr); } +TEST(TWStoredKey, addressAddRemoveDerivationPath) { + const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password")); + const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get()))); + + const auto coin = TWCoinTypeBitcoin; + const auto key = createAStoredKey(coin, password.get()); + + const auto wallet = WRAP(TWHDWallet, TWStoredKeyWallet(key.get(), password.get())); + const auto accountCoin = WRAP(TWAccount, TWStoredKeyAccountForCoin(key.get(), coin, wallet.get())); + const auto accountAddress = WRAPS(TWAccountAddress(accountCoin.get())); + EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress.get())), "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + + EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 1ul); + const auto accountIdx = WRAP(TWAccount, TWStoredKeyAccount(key.get(), 0)); + + const auto derivationPath0 = "m/84'/0'/0'/0/0"; + const auto derivationPath1 = "m/84'/0'/0'/1/0"; + + TWStoredKeyRemoveAccountForCoinDerivationPath(key.get(), coin, WRAPS(TWStringCreateWithUTF8Bytes(derivationPath1)).get()); + EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 1ul); + + TWStoredKeyRemoveAccountForCoinDerivationPath(key.get(), coin, WRAPS(TWStringCreateWithUTF8Bytes(derivationPath0)).get()); + EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 0ul); +} + +TEST(TWStoredKey, addressAddDerivation) { + const auto passwordString = WRAPS(TWStringCreateWithUTF8Bytes("password")); + const auto password = WRAPD(TWDataCreateWithBytes(reinterpret_cast(TWStringUTF8Bytes(passwordString.get())), TWStringSize(passwordString.get()))); + + const auto coin = TWCoinTypeBitcoin; + const auto key = createAStoredKey(coin, password.get()); + const auto wallet = WRAP(TWHDWallet, TWStoredKeyWallet(key.get(), password.get())); + + const auto accountCoin1 = WRAP(TWAccount, TWStoredKeyAccountForCoinDerivation(key.get(), coin, TWDerivationDefault, wallet.get())); + const auto accountAddress1 = WRAPS(TWAccountAddress(accountCoin1.get())); + EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress1.get())), "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + + const auto accountCoin2 = WRAP(TWAccount, TWStoredKeyAccountForCoinDerivation(key.get(), coin, TWDerivationBitcoinLegacy, wallet.get())); + const auto accountAddress2 = WRAPS(TWAccountAddress(accountCoin2.get())); + EXPECT_EQ(string(TWStringUTF8Bytes(accountAddress2.get())), "1NyRyFewhZcWMa9XCj3bBxSXPXyoSg8dKz"); + + EXPECT_EQ(TWStoredKeyAccountCount(key.get()), 2ul); +} + TEST(TWStoredKey, exportJSON) { const auto key = createDefaultStoredKey(); const auto json = WRAPD(TWStoredKeyExportJSON(key.get())); @@ -155,7 +202,7 @@ TEST(TWStoredKey, storeAndImportJSON) { Data json(length); size_t idx = 0; // read the slow way, ifs.read gave some false warnings with codacy - while (!ifs.eof() && idx < length) { char c = ifs.get(); json[idx++] = (uint8_t)c; } + while (!ifs.eof() && idx < static_cast(length)) { char c = ifs.get(); json[idx++] = (uint8_t)c; } const auto key2 = WRAP(TWStoredKey, TWStoredKeyImportJSON(WRAPD(TWDataCreateWithData(&json)).get())); const auto name2 = WRAPS(TWStoredKeyName(key2.get())); @@ -203,11 +250,11 @@ TEST(TWStoredKey, removeAccountForCoin) { ASSERT_NE(WRAP(TWAccount, TWStoredKeyAccountForCoin(key.get(), TWCoinTypeEthereum, wallet.get())).get(), nullptr); ASSERT_NE(WRAP(TWAccount, TWStoredKeyAccountForCoin(key.get(), TWCoinTypeBitcoin, wallet.get())).get(), nullptr); - ASSERT_EQ(TWStoredKeyAccountCount(key.get()), 2); + ASSERT_EQ(TWStoredKeyAccountCount(key.get()), 2ul); TWStoredKeyRemoveAccountForCoin(key.get(), TWCoinTypeBitcoin); - ASSERT_EQ(TWStoredKeyAccountCount(key.get()), 1); + ASSERT_EQ(TWStoredKeyAccountCount(key.get()), 1ul); ASSERT_NE(WRAP(TWAccount, TWStoredKeyAccountForCoin(key.get(), TWCoinTypeEthereum, nullptr)).get(), nullptr); ASSERT_EQ(WRAP(TWAccount, TWStoredKeyAccountForCoin(key.get(), TWCoinTypeBitcoin, nullptr)).get(), nullptr); @@ -221,7 +268,41 @@ TEST(TWStoredKey, getWalletPasswordInvalid) { const auto invalidString = WRAPS(TWStringCreateWithUTF8Bytes("_THIS_IS_INVALID_PASSWORD_")); const auto passwordInvalid = WRAPD(TWDataCreateWithBytes(reinterpret_cast(TWStringUTF8Bytes(invalidString.get())), TWStringSize(invalidString.get()))); - auto key = WRAP(TWStoredKey, TWStoredKeyCreate (name.get(), password.get())); + auto key = WRAP(TWStoredKey, TWStoredKeyCreate(name.get(), password.get())); ASSERT_NE(WRAP(TWHDWallet, TWStoredKeyWallet(key.get(), password.get())).get(), nullptr); ASSERT_EQ(WRAP(TWHDWallet, TWStoredKeyWallet(key.get(), passwordInvalid.get())).get(), nullptr); } + +TEST(TWStoredKey, encryptionParameters) { + const auto key = createDefaultStoredKey(); + const auto params = WRAPS(TWStoredKeyEncryptionParameters(key.get())); + + nlohmann::json jsonParams = nlohmann::json::parse(string(TWStringUTF8Bytes(params.get()))); + + // compare some specific parameters + EXPECT_EQ(jsonParams["kdfparams"]["n"], 16384); + EXPECT_EQ(std::string(jsonParams["cipherparams"]["iv"]).length(), 32ul); + + // compare all keys, except dynamic ones (like cipherparams/iv) + jsonParams["cipherparams"] = {}; + jsonParams["ciphertext"] = ""; + jsonParams["kdfparams"]["salt"] = ""; + jsonParams["mac"] = ""; + const auto params2 = jsonParams.dump(); + assertJSONEqual(params2, R"( + { + "cipher": "aes-128-ctr", + "cipherparams": null, + "ciphertext": "", + "kdf": "scrypt", + "kdfparams": { + "dklen": 32, + "n": 16384, + "p": 4, + "r": 8, + "salt": "" + }, + "mac": "" + } + )"); +} diff --git a/tests/interface/TWStringTests.cpp b/tests/interface/TWStringTests.cpp index 17d800b6782..e79755ba76d 100644 --- a/tests/interface/TWStringTests.cpp +++ b/tests/interface/TWStringTests.cpp @@ -4,7 +4,7 @@ // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. -#include "TWTestUtilities.h" +#include "TestUtilities.h" #include diff --git a/tests/interface/TWTestUtilities.cpp b/tests/interface/TWTestUtilities.cpp deleted file mode 100644 index 8ca953eb666..00000000000 --- a/tests/interface/TWTestUtilities.cpp +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#include "TWTestUtilities.h" - -#include -#include - -using namespace std; - -/// Return a writable temp dir which can be used to create files during testing -string getTestTempDir(void) { - // In general, tests should not use hardcoded "/tmp", but TEST_TMPDIR env var. - const char* fromEnvironment = getenv("TEST_TMPDIR"); -#ifdef _WIN32 - if (fromEnvironment == NULL || fromEnvironment[0] == '\0') { fromEnvironment = getenv("TMP"); } -#endif - if (fromEnvironment == NULL || fromEnvironment[0] == '\0') { return "/tmp"; } - return string(fromEnvironment); -} - -nlohmann::json loadJson(std::string path) { - std::ifstream stream(path); - nlohmann::json json; - stream >> json; - return json; -} diff --git a/tests/interface/TWTestUtilities.h b/tests/interface/TWTestUtilities.h deleted file mode 100644 index 1adc36b9ac0..00000000000 --- a/tests/interface/TWTestUtilities.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright © 2017-2020 Trust Wallet. -// -// This file is part of Trust. The full Trust copyright notice, including -// terms governing use, modification, and redistribution, is contained in the -// file LICENSE at the root of the source code distribution tree. - -#pragma once - -#include -#include - -#include -#include -#include - -#include - -#define WRAP(type, x) std::shared_ptr(x, type##Delete) -#define WRAPD(x) std::shared_ptr(x, TWDataDelete) -#define WRAPS(x) std::shared_ptr(x, TWStringDelete) -#define STRING(x) std::shared_ptr(TWStringCreateWithUTF8Bytes(x), TWStringDelete) -#define DATA(x) std::shared_ptr(TWDataCreateWithHexString(STRING(x).get()), TWDataDelete) - -inline void assertStringsEqual(const std::shared_ptr& string, const char* expected) { - ASSERT_STREQ(TWStringUTF8Bytes(string.get()), expected); -} - -inline void assertHexEqual(const std::shared_ptr& data, const char* expected) { - auto hex = WRAPS(TWStringCreateWithHexData(data.get())); - assertStringsEqual(hex, expected); -} - -inline void assertJSONEqual(const std::string& lhs, const char* expected) { - auto lhsJson = nlohmann::json::parse(lhs); - auto rhsJson = nlohmann::json::parse(std::string(expected)); - ASSERT_EQ(lhsJson.dump(), rhsJson.dump()); -} - -inline std::vector* dataFromTWData(TWData* data) { - return const_cast*>(reinterpret_cast*>(data)); -} - -/// Return a writable temp dir which can be used to create files during testing -std::string getTestTempDir(void); - -#define ANY_SIGN(input, coin) \ - {\ - auto inputData = input.SerializeAsString();\ - auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData.data(), inputData.size()));\ - auto outputTWData = WRAPD(TWAnySignerSign(inputTWData.get(), coin));\ - output.ParseFromArray(TWDataBytes(outputTWData.get()), static_cast(TWDataSize(outputTWData.get())));\ - } -#define ANY_PLAN(input, output, coin) \ - {\ - auto inputData = input.SerializeAsString();\ - auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData.data(), inputData.size()));\ - auto outputTWData = WRAPD(TWAnySignerPlan(inputTWData.get(), coin));\ - output.ParseFromArray(TWDataBytes(outputTWData.get()), static_cast(TWDataSize(outputTWData.get())));\ - } -#define DUMP_PROTO(input) \ - { \ - std::string json; \ - google::protobuf::util::MessageToJsonString(input, &json); \ - std::cout<<"dump proto: "< +#include +#include +#include "proto/Binance.pb.h" +#include "proto/Bitcoin.pb.h" +#include "proto/Ethereum.pb.h" +#include "proto/TransactionCompiler.pb.h" + +#include +#include "Bitcoin/Script.h" +#include "Bitcoin/SegwitAddress.h" + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "uint256.h" +#include + +#include "TestUtilities.h" +#include + +#include +#include +#include + +using namespace TW; + +TEST(TWTransactionCompiler, ExternalSignatureSignBinance) { + /// Step 1: Prepare transaction input (protobuf) + const auto coin = TWCoinTypeBinance; + const auto txInputData = WRAPD(TWTransactionCompilerBuildInput( + coin, + STRING("bnb1grpf0955h0ykzq3ar5nmum7y6gdfl6lxfn46h2").get(), // from + STRING("bnb1hlly02l6ahjsgxw9wlcswnlwdhg4xhx38yxpd5").get(), // to + STRING("1").get(), // amount + STRING("BNB").get(), // asset + STRING("").get(), // memo + STRING("Binance-Chain-Nile").get() // testnet chainId + )); + + { + // Check, by parsing + EXPECT_EQ((int)TWDataSize(txInputData.get()), 88); + Binance::Proto::SigningInput input; + ASSERT_TRUE(input.ParseFromArray(TWDataBytes(txInputData.get()), (int)TWDataSize(txInputData.get()))); + EXPECT_EQ(input.chain_id(), "Binance-Chain-Nile"); + EXPECT_TRUE(input.has_send_order()); + ASSERT_EQ(input.send_order().inputs_size(), 1); + EXPECT_EQ(hex(data(input.send_order().inputs(0).address())), "40c2979694bbc961023d1d27be6fc4d21a9febe6"); + } + + /// Step 2: Obtain preimage hash + const auto preImageHashes = WRAPD(TWTransactionCompilerPreImageHashes(coin, txInputData.get())); + auto preImageHash = data(TWDataBytes(preImageHashes.get()), TWDataSize(preImageHashes.get())); + + TxCompiler::Proto::PreSigningOutput preSigningOutput; + ASSERT_TRUE(preSigningOutput.ParseFromArray(preImageHash.data(), int(preImageHash.size()))); + ASSERT_EQ(preSigningOutput.error(), 0); + const auto preImageHashData = data(preSigningOutput.data_hash()); + + EXPECT_EQ(hex(preImageHashData), "3f3fece9059e714d303a9a1496ddade8f2c38fa78fc4cc2e505c5dbb0ea678d1"); + + // Simulate signature, normally obtained from signature server + const auto publicKeyData = parse_hex("026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e502"); + const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeSECP256k1); + const auto signature = parse_hex("1b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679"); + + // Verify signature (pubkey & hash & signature) + { + EXPECT_TRUE(publicKey.verify(signature, preImageHashData)); + } + + /// Step 3: Compile transaction info + const auto outputData = WRAPD(TWTransactionCompilerCompileWithSignatures( + coin, + txInputData.get(), + WRAP(TWDataVector, TWDataVectorCreateWithData((TWData*)&signature)).get(), + WRAP(TWDataVector, TWDataVectorCreateWithData((TWData*)&publicKeyData)).get()) + ); + + const auto ExpectedTx = "b801f0625dee0a462a2c87fa0a1f0a1440c2979694bbc961023d1d27be6fc4d21a9febe612070a03424e421001121f0a14bffe47abfaede50419c577f1074fee6dd1535cd112070a03424e421001126a0a26eb5ae98721026a35920088d98c3888ca68c53dfc93f4564602606cbb87f0fe5ee533db38e50212401b1181faec30b60a2ddaa2804c253cf264c69180ec31814929b5de62088c0c5a45e8a816d1208fc5366bb8b041781a6771248550d04094c3d7a504f9e8310679"; + { + EXPECT_EQ(TWDataSize(outputData.get()), 189ul); + Binance::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(TWDataBytes(outputData.get()), (int)TWDataSize(outputData.get()))); + + EXPECT_EQ(hex(output.encoded()), ExpectedTx); + } + + { // Double check: check if simple signature process gives the same result. Note that private keys were not used anywhere up to this point. + Binance::Proto::SigningInput input; + ASSERT_TRUE(input.ParseFromArray(TWDataBytes(txInputData.get()), (int)TWDataSize(txInputData.get()))); + auto key = parse_hex("95949f757db1f57ca94a5dff23314accbe7abee89597bf6a3c7382c84d7eb832"); + input.set_private_key(key.data(), key.size()); + + Binance::Proto::SigningOutput output; + ANY_SIGN(input, coin); + + ASSERT_EQ(hex(output.encoded()), ExpectedTx); + } +} + +TEST(TWTransactionCompiler, ExternalSignatureSignEthereum) { + /// Step 1: Prepare transaction input (protobuf) + const auto coin = TWCoinTypeEthereum; + const auto txInputData0 = WRAPD(TWTransactionCompilerBuildInput( + coin, + STRING("0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F").get(), // from + STRING("0x3535353535353535353535353535353535353535").get(), // to + STRING("1000000000000000000").get(), // amount + STRING("ETH").get(), // asset + STRING("").get(), // memo + STRING("").get() // chainId + )); + + // Check, by parsing + EXPECT_EQ((int)TWDataSize(txInputData0.get()), 61); + Ethereum::Proto::SigningInput signingInput; + ASSERT_TRUE(signingInput.ParseFromArray(TWDataBytes(txInputData0.get()), (int)TWDataSize(txInputData0.get()))); + EXPECT_EQ(hex(signingInput.chain_id()), "01"); + EXPECT_EQ(signingInput.to_address(), "0x3535353535353535353535353535353535353535"); + ASSERT_TRUE(signingInput.transaction().has_transfer()); + EXPECT_EQ(hex(signingInput.transaction().transfer().amount()), "0de0b6b3a7640000"); + + // Set a few other values + const auto nonce = store(uint256_t(11)); + const auto gasPrice = store(uint256_t(20000000000)); + const auto gasLimit = store(uint256_t(21000)); + signingInput.set_nonce(nonce.data(), nonce.size()); + signingInput.set_gas_price(gasPrice.data(), gasPrice.size()); + signingInput.set_gas_limit(gasLimit.data(), gasLimit.size()); + signingInput.set_tx_mode(Ethereum::Proto::Legacy); + + // Serialize back, this shows how to serialize SigningInput protobuf to byte array + const auto txInputDataData = data(signingInput.SerializeAsString()); + const auto txInputData = WRAPD(TWDataCreateWithBytes(txInputDataData.data(), txInputDataData.size())); + EXPECT_EQ((int)TWDataSize(txInputData.get()), 75); + + /// Step 2: Obtain preimage hash + const auto preImageHashes = WRAPD(TWTransactionCompilerPreImageHashes(coin, txInputData.get())); + auto preImageHash = data(TWDataBytes(preImageHashes.get()), TWDataSize(preImageHashes.get())); + + TxCompiler::Proto::PreSigningOutput preSigningOutput; + ASSERT_TRUE(preSigningOutput.ParseFromArray(preImageHash.data(), int(preImageHash.size()))); + ASSERT_EQ(preSigningOutput.error(), 0); + const auto preImageHashData = data(preSigningOutput.data_hash()); + EXPECT_EQ(hex(preImageHashData), "15e180a6274b2f6a572b9b51823fce25ef39576d10188ecdcd7de44526c47217"); + + // Simulate signature, normally obtained from signature server + const auto publicKeyData = parse_hex("044bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382ce28cab79ad7119ee1ad3ebcdb98a16805211530ecc6cfefa1b88e6dff99232a"); + const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeSECP256k1Extended); + const auto signature = parse_hex("360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07b53bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c9796677900"); + + // Verify signature (pubkey & hash & signature) + { + EXPECT_TRUE(publicKey.verify(signature, preImageHashData)); + } + + /// Step 3: Compile transaction info + const auto outputData = WRAPD(TWTransactionCompilerCompileWithSignatures( + coin, + txInputData.get(), + WRAP(TWDataVector, TWDataVectorCreateWithData((TWData*)&signature)).get(), + WRAP(TWDataVector, TWDataVectorCreateWithData((TWData*)&publicKeyData)).get()) + ); + + const auto ExpectedTx = "f86c0b8504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a0360a84fb41ad07f07c845fedc34cde728421803ebbaae392fc39c116b29fc07ba053bd9d1376e15a191d844db458893b928f3efbfee90c9febf51ab84c97966779"; + { + EXPECT_EQ(TWDataSize(outputData.get()), 183ul); + Ethereum::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(TWDataBytes(outputData.get()), (int)TWDataSize(outputData.get()))); + + EXPECT_EQ(output.encoded().size(), 110ul); + EXPECT_EQ(hex(output.encoded()), ExpectedTx); + } + + { // Double check: check if simple signature process gives the same result. Note that private keys were not used anywhere up to this point. + Ethereum::Proto::SigningInput input; + ASSERT_TRUE(input.ParseFromArray(TWDataBytes(txInputData.get()), (int)TWDataSize(txInputData.get()))); + auto key = parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + input.set_private_key(key.data(), key.size()); + + Ethereum::Proto::SigningOutput output; + ANY_SIGN(input, coin); + + ASSERT_EQ(hex(output.encoded()), ExpectedTx); + } +} + +// data conversion utility +Data dataFromTWData(std::shared_ptr data) { + return TW::data(TWDataBytes(data.get()), TWDataSize(data.get())); +} + +TEST(TWTransactionCompiler, ExternalSignatureSignBitcoin) { + // Test external signining with a Bitcoin transaction with 3 input UTXOs, all used, but only using 2 public keys. + // Three signatures are neeeded. This illustrates that order of UTXOs/hashes is not always the same. + + const auto revUtxoHash0 = parse_hex("07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa8"); + const auto revUtxoHash1 = parse_hex("d6892a5aa54e3b8fe430efd23f49a8950733aaa9d7c915d9989179f48dd1905e"); + const auto revUtxoHash2 = parse_hex("6021efcf7555f90627364339fc921139dd40a06ccb2cb2a2a4f8f4ea7a2dc74d"); + const auto inPubKey0 = parse_hex("024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382"); + const auto inPubKey1 = parse_hex("0217142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc"); + const auto inPubKeyHash0 = parse_hex("bd92088bb7e82d611a9b94fbb74a0908152b784f"); + const auto inPubKeyHash1 = parse_hex("6641abedacf9483b793afe1718689cc9420bbb1c"); + + // Input UTXO infos + struct UtxoInfo { + Data revUtxoHash; + Data publicKey; + long amount; + int index; + }; + std::vector utxoInfos = { + // first + UtxoInfo {revUtxoHash0, inPubKey0, 600'000, 0}, + // second UTXO, with same pubkey + UtxoInfo {revUtxoHash1, inPubKey0, 500'000, 1}, + // third UTXO, with different pubkey + UtxoInfo {revUtxoHash2, inPubKey1, 400'000, 0}, + }; + + // Signature infos, indexed by pubkeyhash+hash + struct SignatureInfo { + Data signature; + Data publicKey; + }; + std::map signatureInfos = { + { + hex(inPubKeyHash0) + "+" + "a296bead4172007be69b21971a790e076388666c162a9505698415f1b003ebd7", + { + parse_hex("304402201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f2a40"), + inPubKey0, + } + }, + { + hex(inPubKeyHash1) + "+" + "505f527f00e15fcc5a2d2416c9970beb57dfdfaca99e572a01f143b24dd8fab6", + { + parse_hex("3044022041294880caa09bb1b653775310fcdd1458da6b8e7d7fae34e37966414fe115820220646397c9d2513edc5974ecc336e9b287de0cdf071c366f3b3dc3ff309213e4e4"), + inPubKey1, + } + }, + { + hex(inPubKeyHash0) + "+" + "60ed6e9371e5ddc72fd88e46a12cb2f68516ebd307c0fd31b1b55cf767272101", + { + parse_hex("30440220764e3d5b3971c4b3e70b23fb700a7462a6fe519d9830e863a1f8388c402ad0b102207e777f7972c636961f92375a2774af3b7a2a04190251bbcb31d19c70927952dc"), + inPubKey0, + } + }, + }; + + const auto coin = TWCoinTypeBitcoin; + const auto ownAddress = "bc1qhkfq3zahaqkkzx5mjnamwjsfpq2jk7z00ppggv"; + + // Setup input for Plan + Bitcoin::Proto::SigningInput signingInput; + signingInput.set_coin_type(coin); + signingInput.set_hash_type(TWBitcoinSigHashTypeAll); + signingInput.set_amount(1'200'000); + signingInput.set_use_max_amount(false); + signingInput.set_byte_fee(1); + signingInput.set_to_address("bc1q2dsdlq3343vk29runkgv4yc292hmq53jedfjmp"); + signingInput.set_change_address(ownAddress); + + // process UTXOs + int count = 0; + for (auto& u: utxoInfos) { + const auto publicKey = PublicKey(u.publicKey, TWPublicKeyTypeSECP256k1); + const auto address = Bitcoin::SegwitAddress(publicKey, "bc"); + if (count == 0) EXPECT_EQ(address.string(), ownAddress); + if (count == 1) EXPECT_EQ(address.string(), ownAddress); + if (count == 2) EXPECT_EQ(address.string(), "bc1qveq6hmdvl9yrk7f6lct3s6yue9pqhwcuxedggg"); + + const auto utxoScript = Bitcoin::Script::lockScriptForAddress(address.string(), coin); + if (count == 0) EXPECT_EQ(hex(utxoScript.bytes), "0014bd92088bb7e82d611a9b94fbb74a0908152b784f"); + if (count == 1) EXPECT_EQ(hex(utxoScript.bytes), "0014bd92088bb7e82d611a9b94fbb74a0908152b784f"); + if (count == 2) EXPECT_EQ(hex(utxoScript.bytes), "00146641abedacf9483b793afe1718689cc9420bbb1c"); + + Data keyHash; + EXPECT_TRUE(utxoScript.matchPayToWitnessPublicKeyHash(keyHash)); + if (count == 0) EXPECT_EQ(hex(keyHash), hex(inPubKeyHash0)); + if (count == 1) EXPECT_EQ(hex(keyHash), hex(inPubKeyHash0)); + if (count == 2) EXPECT_EQ(hex(keyHash), hex(inPubKeyHash1)); + + const auto redeemScript = Bitcoin::Script::buildPayToPublicKeyHash(keyHash); + if (count == 0) EXPECT_EQ(hex(redeemScript.bytes), "76a914bd92088bb7e82d611a9b94fbb74a0908152b784f88ac"); + if (count == 1) EXPECT_EQ(hex(redeemScript.bytes), "76a914bd92088bb7e82d611a9b94fbb74a0908152b784f88ac"); + if (count == 2) EXPECT_EQ(hex(redeemScript.bytes), "76a9146641abedacf9483b793afe1718689cc9420bbb1c88ac"); + (*signingInput.mutable_scripts())[hex(keyHash)] = std::string(redeemScript.bytes.begin(), redeemScript.bytes.end()); + + auto utxo = signingInput.add_utxo(); + utxo->set_script(utxoScript.bytes.data(), utxoScript.bytes.size()); + utxo->set_amount(u.amount); + utxo->mutable_out_point()->set_hash(std::string(u.revUtxoHash.begin(), u.revUtxoHash.end())); + utxo->mutable_out_point()->set_index(u.index); + utxo->mutable_out_point()->set_sequence(UINT32_MAX); + + ++count; + } + EXPECT_EQ(count, 3); + EXPECT_EQ(signingInput.utxo_size(), 3); + + // Plan + Bitcoin::Proto::TransactionPlan plan; + ANY_PLAN(signingInput, plan, coin); + + // Plan is checked, assume it is accepted + EXPECT_EQ(plan.amount(), 1'200'000); + EXPECT_EQ(plan.fee(), 277); + EXPECT_EQ(plan.change(), 299'723); + ASSERT_EQ(plan.utxos_size(), 3); + // Note that UTXOs happen to be in reverse order compared to the input + EXPECT_EQ(hex(plan.utxos(0).out_point().hash()), hex(revUtxoHash2)); + EXPECT_EQ(hex(plan.utxos(1).out_point().hash()), hex(revUtxoHash1)); + EXPECT_EQ(hex(plan.utxos(2).out_point().hash()), hex(revUtxoHash0)); + + // Extend input with accepted plan + *signingInput.mutable_plan() = plan; + + // Serialize signingInput + const auto txInputDataData = data(signingInput.SerializeAsString()); + const auto txInputData = WRAPD(TWDataCreateWithBytes(txInputDataData.data(), txInputDataData.size())); + EXPECT_EQ((int)TWDataSize(txInputData.get()), 692); + + /// Step 2: Obtain preimage hashes + const auto preImageHashes = WRAPD(TWTransactionCompilerPreImageHashes(coin, txInputData.get())); + auto preImageHash = data(TWDataBytes(preImageHashes.get()), TWDataSize(preImageHashes.get())); + Bitcoin::Proto::PreSigningOutput preOutput; + ASSERT_TRUE(preOutput.ParseFromArray(preImageHash.data(), (int)preImageHash.size())); + ASSERT_EQ(preOutput.hash_public_keys_size(), 3); + auto hashes = preOutput.hash_public_keys(); + EXPECT_EQ(hex(hashes[0].data_hash()), "505f527f00e15fcc5a2d2416c9970beb57dfdfaca99e572a01f143b24dd8fab6"); + EXPECT_EQ(hex(hashes[1].data_hash()), "a296bead4172007be69b21971a790e076388666c162a9505698415f1b003ebd7"); + EXPECT_EQ(hex(hashes[2].data_hash()), "60ed6e9371e5ddc72fd88e46a12cb2f68516ebd307c0fd31b1b55cf767272101"); + EXPECT_EQ(hex(hashes[0].public_key_hash()), hex(inPubKeyHash1)); + EXPECT_EQ(hex(hashes[1].public_key_hash()), hex(inPubKeyHash0)); + EXPECT_EQ(hex(hashes[2].public_key_hash()), hex(inPubKeyHash0)); + + // Simulate signatures, normally obtained from signature server. + auto signatureVec = WRAP(TWDataVector, TWDataVectorCreate()); + auto pubkeyVec = WRAP(TWDataVector, TWDataVectorCreate()); + for (const auto& h: hashes) { + const auto& preImageHash_ = TW::data(h.data_hash()); + const auto& pubkeyhash = h.public_key_hash(); + + const std::string key = hex(pubkeyhash) + "+" + hex(preImageHash_); + const auto sigInfoFind = signatureInfos.find(key); + ASSERT_TRUE(sigInfoFind != signatureInfos.end()); + const auto& sigInfo = std::get<1>(*sigInfoFind); + const auto& publicKeyData = sigInfo.publicKey; + const PublicKey publicKey = PublicKey(publicKeyData, TWPublicKeyTypeSECP256k1); + const auto signature = sigInfo.signature; + + TWDataVectorAdd(signatureVec.get(), WRAPD(TWDataCreateWithBytes(signature.data(), signature.size())).get()); + TWDataVectorAdd(pubkeyVec.get(), WRAPD(TWDataCreateWithBytes(publicKeyData.data(), publicKeyData.size())).get()); + // Verify signature (pubkey & hash & signature) + EXPECT_TRUE(publicKey.verifyAsDER(signature, preImageHash_)); + } + /// Step 3: Compile transaction info + const auto outputData = WRAPD(TWTransactionCompilerCompileWithSignatures( + coin, txInputData.get(), signatureVec.get(), pubkeyVec.get() + )); + + const auto ExpectedTx = "010000000001036021efcf7555f90627364339fc921139dd40a06ccb2cb2a2a4f8f4ea7a2dc74d0000000000ffffffffd6892a5aa54e3b8fe430efd23f49a8950733aaa9d7c915d9989179f48dd1905e0100000000ffffffff07c42b969286be06fae38528c85f0a1ce508d4df837eb5ac4cf5f2a7a9d65fa80000000000ffffffff02804f1200000000001600145360df8231ac5965147c9d90ca930a2aafb05232cb92040000000000160014bd92088bb7e82d611a9b94fbb74a0908152b784f02473044022041294880caa09bb1b653775310fcdd1458da6b8e7d7fae34e37966414fe115820220646397c9d2513edc5974ecc336e9b287de0cdf071c366f3b3dc3ff309213e4e401210217142f69535e4dad0dc7060df645c55a174cc1bfa5b9eb2e59aad2ae96072dfc0247304402201857bc6e6e48b46046a4bd204136fc77e24c240943fb5a1f0e86387aae59b34902200a7f31478784e51c49f46ef072745a4f263d7efdbc9c6784aa2571ff4f6f2a400121024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb493382024730440220764e3d5b3971c4b3e70b23fb700a7462a6fe519d9830e863a1f8388c402ad0b102207e777f7972c636961f92375a2774af3b7a2a04190251bbcb31d19c70927952dc0121024bc2a31265153f07e70e0bab08724e6b85e217f8cd628ceb62974247bb49338200000000"; + { + EXPECT_EQ(TWDataSize(outputData.get()), 786ul); + Bitcoin::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(TWDataBytes(outputData.get()), (int)TWDataSize(outputData.get()))); + + EXPECT_EQ(output.encoded().size(), 518ul); + EXPECT_EQ(hex(output.encoded()), ExpectedTx); + } + + { // Double check: check if simple signature process gives the same result. Note that private keys were not used anywhere up to this point. + Bitcoin::Proto::SigningInput input; + ASSERT_TRUE(input.ParseFromArray(TWDataBytes(txInputData.get()), (int)TWDataSize(txInputData.get()))); + + // 2 private keys are needed (despite >2 UTXOs) + auto key0 = parse_hex("4646464646464646464646464646464646464646464646464646464646464646"); + auto key1 = parse_hex("7878787878787878787878787878787878787878787878787878787878787878"); + EXPECT_EQ(hex(PrivateKey(key0).getPublicKey(TWPublicKeyTypeSECP256k1).bytes), hex(inPubKey0)); + EXPECT_EQ(hex(PrivateKey(key1).getPublicKey(TWPublicKeyTypeSECP256k1).bytes), hex(inPubKey1)); + *input.add_private_key() = std::string(key0.begin(), key0.end()); + *input.add_private_key() = std::string(key1.begin(), key1.end()); + + Bitcoin::Proto::SigningOutput output; + ANY_SIGN(input, coin); + + ASSERT_EQ(hex(output.encoded()), ExpectedTx); + } +} diff --git a/tests/main.cpp b/tests/main.cpp index d75eab4a6bc..136462e0cce 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -1,11 +1,17 @@ -// Copyright © 2017-2020 Trust Wallet. +// Copyright © 2017-2022 Trust Wallet. // // This file is part of Trust. The full Trust copyright notice, including // terms governing use, modification, and redistribution, is contained in the // file LICENSE at the root of the source code distribution tree. - #include +#ifdef WIN32 +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +#include std::string TESTS_ROOT; int main(int argc, char **argv) { @@ -20,8 +26,36 @@ int main(int argc, char **argv) { std::cerr << "Please specify the tests root folder. '" << TESTS_ROOT << "' is not a valid directory." << std::endl; exit(1); } + std::cout<<"TESTS_ROOT: "< +#include + + +std::string TESTS_ROOT; + +int main(int argc, char** argv) { + // current path + auto path = std::filesystem::current_path(); + // executable path + path.append(argv[0]); + // normalize + path = std::filesystem::canonical(path); + std::cout<<"normaliz path: "< -#include - - -TEST(TWxDaiCoinType, TWCoinType) { - auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeXDai)); - auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x936798a1ef607c9e856d7861b15999c770c06f0887c4fc1f6acbf3bef09899c1")); - auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeXDai, txId.get())); - auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0x12d61a95CF55e18D267C2F1AA67d8e42ae1368f8")); - auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeXDai, accId.get())); - auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeXDai)); - auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeXDai)); - - ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeXDai), 18); - ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeXDai)); - ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeXDai)); - ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeXDai)); - assertStringsEqual(symbol, "xDAI"); - assertStringsEqual(txUrl, "https://blockscout.com/xdai/mainnet/tx/0x936798a1ef607c9e856d7861b15999c770c06f0887c4fc1f6acbf3bef09899c1"); - assertStringsEqual(accUrl, "https://blockscout.com/xdai/mainnet/address/0x12d61a95CF55e18D267C2F1AA67d8e42ae1368f8"); - assertStringsEqual(id, "xdai"); - assertStringsEqual(name, "xDai"); -} diff --git a/tools/android-test b/tools/android-test index 083f8f56724..576cb4dd659 100755 --- a/tools/android-test +++ b/tools/android-test @@ -17,7 +17,7 @@ SERIAL=emulator-${PORT} # We have to echo "no" because it will ask us if we want to use a custom hardware profile, and we don't. echo -e "\nCreating Android emulator...\n" -echo "no" | "$ANDROID_HOME/tools/bin/avdmanager" create avd \ +echo "no" | "$ANDROID_HOME/cmdline-tools/latest/bin/avdmanager" create avd \ -n "${AVD_NAME}" \ -k "system-images;android-26;google_apis;x86" \ -f diff --git a/tools/build-and-test b/tools/build-and-test new file mode 100644 index 00000000000..0bc769ce9b8 --- /dev/null +++ b/tools/build-and-test @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# +# Perform full build and runs the tests. +# Prerequisite: workspace with dependencies installed, see bootstrap.sh + +# Fail if any commands fails +set -e + +echo "#### Generating files... ####" +tools/generate-files + +echo "#### Building... ####" +cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ +make -Cbuild -j12 tests TrezorCryptoTests + +if [ -x "$(command -v clang-tidy)" ]; then + echo "#### Linting... ####" + tools/lint +fi + +echo "#### Running trezor-crypto tests... ####" +export CK_TIMEOUT_MULTIPLIER=4 +build/trezor-crypto/crypto/tests/TrezorCryptoTests + +echo "#### Running unit tests... ####" +FILTER="*" +build/tests/tests --gtest_filter="$FILTER" diff --git a/tools/coverage b/tools/coverage index 8a33e5f215d..648a1a81f8f 100755 --- a/tools/coverage +++ b/tools/coverage @@ -23,7 +23,7 @@ set -e function install_llvm_gcov() { cat << EOF > /tmp/llvm-gcov.sh #!/bin/bash -exec /usr/bin/llvm-cov-11 gcov "\$@" +exec /usr/bin/llvm-cov-14 gcov "\$@" EOF sudo chmod 755 /tmp/llvm-gcov.sh } diff --git a/tools/dependencies-version b/tools/dependencies-version new file mode 100644 index 00000000000..38f12b7c4a0 --- /dev/null +++ b/tools/dependencies-version @@ -0,0 +1,7 @@ +#!/bin/bash + +export GTEST_VERSION=1.11.0 +export CHECK_VERSION=0.15.2 +export JSON_VERSION=3.10.2 +export PROTOBUF_VERSION=3.19.2 +export SWIFT_PROTOBUF_VERSION=1.18.0 diff --git a/tools/download-dependencies b/tools/download-dependencies new file mode 100644 index 00000000000..bd138ca5904 --- /dev/null +++ b/tools/download-dependencies @@ -0,0 +1,61 @@ +#!/bin/bash + +set -e + +ROOT="$PWD" +PREFIX="${PREFIX:-$ROOT/build/local}" + +# Load dependencies version +BASE_DIR=$(cd `dirname $0`; pwd) +source ${BASE_DIR}/dependencies-version + +function download_gtest() { + echo "Downloading gtest..." + GTEST_DIR="$ROOT/build/local/src/gtest" + mkdir -p "$GTEST_DIR" + cd "$GTEST_DIR" + if [ ! -f release-$GTEST_VERSION.tar.gz ]; then + curl -fSsOL https://github.com/google/googletest/archive/release-$GTEST_VERSION.tar.gz + fi + tar xzf release-$GTEST_VERSION.tar.gz +} + +function download_libcheck() { + echo "Downloading libcheck..." + CHECK_DIR="$ROOT/build/local/src/check" + mkdir -p "$CHECK_DIR" + cd "$CHECK_DIR" + if [ ! -f check-$CHECK_VERSION.tar.gz ]; then + curl -fSsOL https://github.com/libcheck/check/releases/download/$CHECK_VERSION/check-$CHECK_VERSION.tar.gz + fi + tar xzf check-$CHECK_VERSION.tar.gz +} + +function download_nolhmann_json() { + echo "Downloading nolhmann_json..." + JSON_DIR="$ROOT/build/local/json" + mkdir -p "$JSON_DIR" + cd "$JSON_DIR" + if [ ! -f include.zip ]; then + curl -fSsOL https://github.com/nlohmann/json/releases/download/v$JSON_VERSION/include.zip + fi + unzip -qq -d "$PREFIX" -o include.zip +} + +function download_protobuf() { + echo "Downloading protobuf..." + PROTOBUF_DIR="$ROOT/build/local/src/protobuf" + mkdir -p "$PROTOBUF_DIR" + cd "$PROTOBUF_DIR" + if [ ! -f protobuf-java-$PROTOBUF_VERSION.tar.gz ]; then + curl -fSsOL https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOBUF_VERSION/protobuf-java-$PROTOBUF_VERSION.tar.gz + fi + tar xzf protobuf-java-$PROTOBUF_VERSION.tar.gz +} + +download_gtest +download_libcheck +download_nolhmann_json +download_protobuf + +echo "done." diff --git a/tools/doxygen_convert_comments b/tools/doxygen_convert_comments new file mode 100644 index 00000000000..91870ffb466 --- /dev/null +++ b/tools/doxygen_convert_comments @@ -0,0 +1,28 @@ +#!/bin/bash + +SWIFT_PARAMETER_PATTERN='s/\\param\s+([^\s]+)/\- Parameter $1:/g' +SWIFT_RETURN_PATTERN='s/\\return/\- Returns:/g' +SWIFT_NOTE_PATTERN='s/\\note/\- Note:/g' +SWIFT_SEE_PATTERN='s/\\see/\- SeeAlso:/g' +SWIFT_FOLDER_PATH="swift/Sources/Generated/*.swift" +SWIFT_FOLDER_PATH_BAK="swift/Sources/Generated/*.bak" + +function process_swift_comments() { + perl -pi.bak -e "$SWIFT_PARAMETER_PATTERN" "$1" + perl -pi.bak -e "$SWIFT_RETURN_PATTERN" "$1" + perl -pi.bak -e "$SWIFT_NOTE_PATTERN" "$1" + perl -pi.bak -e "$SWIFT_SEE_PATTERN" "$1" +} + + +function swift_convert() { + echo "Processing swift conversion" + + for d in $SWIFT_FOLDER_PATH ; do + process_swift_comments $d + done + + rm -rf $SWIFT_FOLDER_PATH_BAK +} + +swift_convert diff --git a/tools/generate-files b/tools/generate-files index 5df2a69f23e..aa694add2cc 100755 --- a/tools/generate-files +++ b/tools/generate-files @@ -49,16 +49,21 @@ codegen/bin/coins # Generate interface code codegen/bin/codegen +# Convert doxygen comments to appropriate format +tools/doxygen_convert_comments + # Generate Java, C++ and Swift Protobuf files if [ -x "$(command -v protoc-gen-swift)" ]; then - "$PROTOC" -I=$PREFIX/include -I=src/proto --cpp_out=src/proto --java_out=jni/java --swift_out=swift/Sources/Generated/Protobuf --swift_opt=Visibility=Public src/proto/*.proto + "$PROTOC" -I=$PREFIX/include -I=src/proto --cpp_out=src/proto --java_out=lite:jni/java --swift_out=swift/Sources/Generated/Protobuf --swift_opt=Visibility=Public src/proto/*.proto else - "$PROTOC" -I=$PREFIX/include -I=src/proto --cpp_out=src/proto --java_out=jni/java src/proto/*.proto + "$PROTOC" -I=$PREFIX/include -I=src/proto --cpp_out=src/proto --java_out=lite:jni/java src/proto/*.proto fi -# Generate internal message protocol Protobuf files -- not every time +# Generate internal message protocol Protobuf files "$PROTOC" -I=$PREFIX/include -I=src/Tron/Protobuf --cpp_out=src/Tron/Protobuf src/Tron/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=src/Zilliqa/Protobuf --cpp_out=src/Zilliqa/Protobuf src/Zilliqa/Protobuf/*.proto +"$PROTOC" -I=$PREFIX/include -I=src/Cosmos/Protobuf --cpp_out=src/Cosmos/Protobuf src/Cosmos/Protobuf/*.proto +"$PROTOC" -I=$PREFIX/include -I=tests/chains/Cosmos/Protobuf --cpp_out=tests/chains/Cosmos/Protobuf tests/chains/Cosmos/Protobuf/*.proto # Generate Proto interface file "$PROTOC" -I=$PREFIX/include -I=src/proto --plugin=$PREFIX/bin/protoc-gen-c-typedef --c-typedef_out include/TrustWalletCore src/proto/*.proto diff --git a/tools/gtest.patch b/tools/gtest.patch deleted file mode 100644 index 83138001530..00000000000 --- a/tools/gtest.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- CMakeLists.txt 2019-10-03 23:52:15.000000000 +0900 -+++ CMakeLists.txt.patched 2021-01-29 10:16:41.000000000 +0900 -@@ -1,7 +1,7 @@ - # Note: CMake support is community-based. The maintainers do not use CMake - # internally. - --cmake_minimum_required(VERSION 2.8.8) -+cmake_minimum_required(VERSION 2.8.12) - - if (POLICY CMP0048) - cmake_policy(SET CMP0048 NEW) diff --git a/tools/gtest_mock.patch b/tools/gtest_mock.patch deleted file mode 100644 index 2b9dfb81c62..00000000000 --- a/tools/gtest_mock.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- googlemock/CMakeLists.txt 2019-10-03 23:52:15.000000000 +0900 -+++ googlemock/CMakeLists.txt.patched 2021-01-29 10:28:32.000000000 +0900 -@@ -42,7 +42,7 @@ - cmake_policy(SET CMP0048 NEW) - project(gmock VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C) - endif() --cmake_minimum_required(VERSION 2.6.4) -+cmake_minimum_required(VERSION 2.8.12) - - if (COMMAND set_up_hermetic_build) - set_up_hermetic_build() diff --git a/tools/gtest_test.patch b/tools/gtest_test.patch deleted file mode 100644 index 09b0d659c79..00000000000 --- a/tools/gtest_test.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- googletest/CMakeLists.txt 2019-10-03 23:52:15.000000000 +0900 -+++ googletest/CMakeLists.txt.patched 2021-01-29 10:24:45.000000000 +0900 -@@ -53,7 +53,7 @@ - cmake_policy(SET CMP0048 NEW) - project(gtest VERSION ${GOOGLETEST_VERSION} LANGUAGES CXX C) - endif() --cmake_minimum_required(VERSION 2.6.4) -+cmake_minimum_required(VERSION 2.8.12) - - if (POLICY CMP0063) # Visibility - cmake_policy(SET CMP0063 NEW) diff --git a/tools/install-android-dependencies b/tools/install-android-dependencies new file mode 100644 index 00000000000..7f3d5c64b88 --- /dev/null +++ b/tools/install-android-dependencies @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e + +$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --verbose "cmake;3.18.1" "ndk;23.1.7779620" +$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "system-images;android-26;google_apis;x86" + +echo -e "y\ny\ny\ny\ny\n" | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses diff --git a/tools/install-dependencies b/tools/install-dependencies index 041930f4800..3a04f8434bd 100755 --- a/tools/install-dependencies +++ b/tools/install-dependencies @@ -6,6 +6,13 @@ ROOT="$PWD" PREFIX="${PREFIX:-$ROOT/build/local}" echo "PREFIX: $PREFIX" +CMAKE=cmake +MAKE=make + +# Load dependencies version +BASE_DIR=$(cd `dirname $0`; pwd) +source ${BASE_DIR}/dependencies-version + # Setup up folders export PATH="$PREFIX/bin":$PATH export LDFLAGS="-L$PREFIX/lib" @@ -14,78 +21,57 @@ export DYLD_LIBRARY_PATH="$PREFIX/lib" export LD_LIBRARY_PATH="$PREFIX/lib" export LD_RUN_PATH="$PREFIX/lib" -# Download Google Test -export GTEST_VERSION=1.10.0 -GTEST_DIR="$ROOT/build/local/src/gtest" -mkdir -p "$GTEST_DIR" -cd "$GTEST_DIR" -if [ ! -f release-$GTEST_VERSION.tar.gz ]; then - curl -fSsOL https://github.com/google/googletest/archive/release-$GTEST_VERSION.tar.gz -fi -tar xzf release-$GTEST_VERSION.tar.gz - -# Build gtest -cd googletest-release-$GTEST_VERSION -echo "patching cmake minimum version" -cp $ROOT/tools/*.patch . -patch CMakeLists.txt gtest.patch -patch googletest/CMakeLists.txt gtest_test.patch -patch googlemock/CMakeLists.txt gtest_mock.patch - -cmake -DCMAKE_INSTALL_PREFIX:PATH=$PREFIX -H. -make -j4 -make install -make clean - -# Download Check -export CHECK_VERSION=0.15.2 -CHECK_DIR="$ROOT/build/local/src/check" -mkdir -p "$CHECK_DIR" -cd "$CHECK_DIR" -if [ ! -f check-$CHECK_VERSION.tar.gz ]; then - curl -fSsOL https://github.com/libcheck/check/releases/download/$CHECK_VERSION/check-$CHECK_VERSION.tar.gz -fi -tar xzf check-$CHECK_VERSION.tar.gz - -# Build Check -cd check-$CHECK_VERSION -./configure --prefix="$PREFIX" -make -j4 -make install -make clean - -# Download Nlohmann JSON -export JSON_VERSION=3.10.2 -JSON_DIR="$ROOT/build/local/json" -mkdir -p "$JSON_DIR" -cd "$JSON_DIR" -if [ ! -f include.zip ]; then - curl -fSsOL https://github.com/nlohmann/json/releases/download/v$JSON_VERSION/include.zip -fi -unzip -d "$PREFIX" -o include.zip - -# Download Protobuf sources -export PROTOBUF_VERSION=3.14.0 -PROTOBUF_DIR="$ROOT/build/local/src/protobuf" -mkdir -p "$PROTOBUF_DIR" -cd "$PROTOBUF_DIR" -if [ ! -f protobuf-java-$PROTOBUF_VERSION.tar.gz ]; then - curl -fSsOL https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOBUF_VERSION/protobuf-java-$PROTOBUF_VERSION.tar.gz -fi -tar xzf protobuf-java-$PROTOBUF_VERSION.tar.gz - -# Build Protobuf -cd protobuf-$PROTOBUF_VERSION -./configure --prefix="$PREFIX" -make -j4 -make install -# after install, cleanup to save space (docker) -make clean -"$PREFIX/bin/protoc" --version - -if [[ -x "$(command -v swift)" && `uname` == "Darwin" ]]; then - # Download Swift Protobuf sources - export SWIFT_PROTOBUF_VERSION=1.18.0 +function download_dependencies() { + ${BASE_DIR}/download-dependencies +} + +function build_gtest() { + # Build gtest + GTEST_DIR="$ROOT/build/local/src/gtest" + cd ${GTEST_DIR}/googletest-release-$GTEST_VERSION + $CMAKE -DCMAKE_INSTALL_PREFIX:PATH=$PREFIX -H. + $MAKE -j4 + $MAKE install + $MAKE clean +} + +function build_libcheck() { + # Build Check + CHECK_DIR="$ROOT/build/local/src/check" + cd ${CHECK_DIR}/check-$CHECK_VERSION + $CMAKE -DCMAKE_INSTALL_PREFIX:PATH=$PREFIX -H. + $MAKE -j4 + $MAKE install + $MAKE clean +} + +function build_protobuf() { + # Build Protobuf + PROTOBUF_DIR="$ROOT/build/local/src/protobuf" + cd ${PROTOBUF_DIR}/protobuf-$PROTOBUF_VERSION + + ./configure --prefix="$PREFIX" + $MAKE -j4 + $MAKE install + + # after install, cleanup to save space (docker) + make clean + "$PREFIX/bin/protoc" --version + + # Protobuf plugins + cd "$ROOT/protobuf-plugin" + $CMAKE -H. -Bbuild -DCMAKE_INSTALL_PREFIX=$PREFIX + $MAKE -Cbuild -j12 + $MAKE -Cbuild install + rm -rf build + + if [[ -x "$(command -v swift)" && `uname` == "Darwin" ]]; then + build_swift_plugin + fi +} + +function build_swift_plugin() { + # Download Swift Protobuf sources SWIFT_PROTOBUF_DIR="$ROOT/build/local/src/swift-protobuf" mkdir -p "$SWIFT_PROTOBUF_DIR" cd "$SWIFT_PROTOBUF_DIR" @@ -99,13 +85,12 @@ if [[ -x "$(command -v swift)" && `uname` == "Darwin" ]]; then swift build --static-swift-stdlib -c release cp -f "$SWIFT_PROTOBUF_DIR/swift-protobuf-$SWIFT_PROTOBUF_VERSION/.build/release/protoc-gen-swift" "$PREFIX/bin" | true $PREFIX/bin/protoc-gen-swift --version -fi - -# Protobuf plugins -cd "$ROOT/protobuf-plugin" -cmake -H. -Bbuild -DCMAKE_INSTALL_PREFIX=$PREFIX -make -Cbuild -j12 -make -Cbuild install -rm -rf build +} + +download_dependencies + +build_gtest +build_libcheck +build_protobuf cd "$ROOT" diff --git a/tools/install-wasm-dependencies b/tools/install-wasm-dependencies new file mode 100644 index 00000000000..dcf17caa686 --- /dev/null +++ b/tools/install-wasm-dependencies @@ -0,0 +1,15 @@ +#!/bin/bash + +set -e + +emsdk_version=3.1.22 + +git clone https://github.com/emscripten-core/emsdk.git + +cd emsdk + + +./emsdk install $emsdk_version && ./emsdk activate $emsdk_version + +curl -fSsOL https://github.com/emscripten-ports/boost/releases/download/boost-1.75.0/boost-headers-1.75.0.zip +unzip boost-headers-1.75.0.zip -d upstream/emscripten/system/include diff --git a/tools/ios-build b/tools/ios-build index bf5fa47251a..e1b3945ecfc 100755 --- a/tools/ios-build +++ b/tools/ios-build @@ -23,7 +23,7 @@ init() { # build destination archivePath build() { echo "Building scheme: $1, destination: $2, path: $3" - xcodebuild archive -project swift/${FRAMEWORK}.xcodeproj -scheme "$1" -destination "$2" -archivePath "$BUILD_FOLDER/$3".xcarchive SKIP_INSTALL=NO | xcpretty + xcodebuild archive -project swift/${FRAMEWORK}.xcodeproj -scheme "$1" -destination "$2" -archivePath "$BUILD_FOLDER/$3".xcarchive SKIP_INSTALL=NO | xcbeautify } build_ios_arm64() { diff --git a/tools/ios-doc b/tools/ios-doc new file mode 100644 index 00000000000..9c004897a18 --- /dev/null +++ b/tools/ios-doc @@ -0,0 +1,22 @@ +#!/bin/bash + +# https://developer.apple.com/documentation/xcode/distributing-documentation-to-external-developers + +set -e +set -o pipefail + +pushd swift +mkdir -p build && rm -rf build/*.doccarchive + +export DOCC_JSON_PRETTYPRINT="YES" +xcodebuild -workspace TrustWalletCore.xcworkspace -derivedDataPath build/docsData -scheme WalletCore -destination 'platform=iOS Simulator,name=iPhone 13 Pro Max' -parallelizeTargets docbuild | xcbeautify + +pushd build + +mv `find docsData/Build/Products -type d -name "*.doccarchive"` . + +echo "Zipping docc archive..." +zip -rq WalletCore.doccarchive.zip WalletCore.doccarchive + +popd # build +popd # swift diff --git a/tools/ios-release b/tools/ios-release index 1499240cbff..85a70879c09 100755 --- a/tools/ios-release +++ b/tools/ios-release @@ -37,8 +37,9 @@ popd # upload to release download_url=$(wc_upload_asset ${release_url} ${filename}) +echo "download_url is $download_url" download_url=$(tr -d '"' <<< $download_url) -echo "download_url is ${download_url}" +echo "trimmed download_url is ${download_url}" # create podspec podspec_name=TrustWalletCore diff --git a/tools/ios-test b/tools/ios-test index 4839435c61a..62d09d61417 100755 --- a/tools/ios-test +++ b/tools/ios-test @@ -3,9 +3,15 @@ # This script runs the iOS tests. set -e +set -o pipefail pushd swift -xcodegen -pod install -fastlane scan --workspace TrustWalletCore.xcworkspace --scheme TrustWalletCore --sdk iphonesimulator --device='iPhone 13' --clean + +xcodegen && pod install +xcodebuild -workspace TrustWalletCore.xcworkspace \ + -scheme WalletCore \ + -sdk iphonesimulator \ + -destination "platform=iOS Simulator,name=iPhone 13" \ + test | xcbeautify + popd diff --git a/tools/ios-xcframework b/tools/ios-xcframework index c2cb0191073..d5d667f4b14 100755 --- a/tools/ios-xcframework +++ b/tools/ios-xcframework @@ -5,8 +5,10 @@ set -e -patch build/local/src/protobuf/protobuf-3.14.0/src/google/protobuf/stubs/common.cc swift/protobuf.patch +echo "Building Docc..." +tools/ios-doc +echo "Building xcframework..." pushd swift fastlane ios xcframework popd diff --git a/tools/ios-xcframework-release b/tools/ios-xcframework-release index a7843f6a69d..0ce859a8cc1 100755 --- a/tools/ios-xcframework-release +++ b/tools/ios-xcframework-release @@ -1,5 +1,6 @@ #!/bin/bash +set -o pipefail set -e # load release library code @@ -16,13 +17,19 @@ pushd ${base_dir}/../swift/build # archive xcframeworks core_zip_filename="WalletCore.xcframework.zip" +core_dsyms_filename="WalletCore.xcframework.dSYM.zip" protobuf_zip_filename="SwiftProtobuf.xcframework.zip" +protobuf_dsyms_filename="SwiftProtobuf.xcframework.dSYM.zip" -rm -rf ${core_zip_filename} ${protobuf_zip_filename} +rm -rf ${core_zip_filename} ${core_dsyms_filename} ${protobuf_zip_filename} ${protobuf_dsyms_filename} + +zip -r ${core_dsyms_filename} WalletCore.dSYMs zip -r ${core_zip_filename} WalletCore.xcframework core_hash=$(/usr/bin/shasum -a 256 ${core_zip_filename} | awk '{printf $1}') +zip -r ${protobuf_dsyms_filename} SwiftProtobuf.dSYMs + zip -r ${protobuf_zip_filename} SwiftProtobuf.xcframework protobuf_hash=$(/usr/bin/shasum -a 256 ${protobuf_zip_filename} | awk '{printf $1}') @@ -44,9 +51,8 @@ let package = Package( name: "WalletCore", platforms: [.iOS(.v13)], products: [ - .library( - name: "WalletCore", targets: ["WalletCore", "SwiftProtobuf"] - ) + .library(name: "WalletCore", targets: ["WalletCore"]), + .library(name: "SwiftProtobuf", targets: ["SwiftProtobuf"]) ], dependencies: [], targets: [ @@ -64,8 +70,19 @@ let package = Package( ) EOF -echo "Package.swift generated." - +echo "${package_swift} generated." cat $package_swift +package_swift_download_url=$(wc_upload_asset ${release_url} ${package_swift}) +echo "${package_swift} url is: ${package_swift_download_url}" + +protobuf_dsyms_url=$(wc_upload_asset ${release_url} ${protobuf_dsyms_filename}) +echo "protobuf dsyms url is: ${protobuf_dsyms_url}" + +core_dsyms_url=$(wc_upload_asset ${release_url} ${core_dsyms_filename}) +echo "core dsyms url is: ${core_dsyms_url}" + +docc_url=$(wc_upload_asset ${release_url} WalletCore.doccarchive.zip) +echo "docc url is: ${docc_url}" + popd diff --git a/tools/library b/tools/library index 58af6624a2c..46891c22dc9 100755 --- a/tools/library +++ b/tools/library @@ -11,7 +11,11 @@ wc_read_version() { wc_release_url() { tag="$1" - id=$(curl "https://api.github.com/repos/trustwallet/wallet-core/releases/tags/${tag}" | jq ".id") + id=$(curl -u ${GITHUB_USER}:${GITHUB_TOKEN} "https://api.github.com/repos/trustwallet/wallet-core/releases/tags/${tag}" | jq ".id") + if [[ $id == "null" ]]; then + echo "Failed to get release id for tag ${tag}" + exit 22 + fi release_url="https://uploads.github.com/repos/trustwallet/wallet-core/releases/${id}" echo ${release_url} } @@ -21,6 +25,6 @@ wc_upload_asset() { filename="$2" upload_url="${release_url}/assets?name=${filename}" - download_url=$(curl -u ${GITHUB_USER}:${GITHUB_TOKEN} -X POST -H "Content-Type: application/octet-stream" --data-binary @${filename} ${upload_url} | jq ".browser_download_url") + download_url=$(curl --progress-bar --retry 3 -u ${GITHUB_USER}:${GITHUB_TOKEN} -X POST -H "Content-Type: application/octet-stream" --data-binary @${filename} ${upload_url} | jq ".browser_download_url") echo ${download_url} } diff --git a/tools/lint-all b/tools/lint-all index e3e1eb781c8..c2afc4bc026 100755 --- a/tools/lint-all +++ b/tools/lint-all @@ -4,4 +4,5 @@ set -e -find src \( -name "*.cpp" -o -name "*.h" \) -not -path "./src/proto/*" -not -path "./src/Tron/Protobuf/*" -exec clang-tidy-11 -quiet -p=build '{}' \; +clang-tidy --version +find src \( -name "*.cpp" -o -name "*.h" \) -not -path "./src/proto/*" -not -path "./src/Tron/Protobuf/*" -exec clang-tidy -quiet -p=build '{}' \; diff --git a/tools/pvs-studio-analyze b/tools/pvs-studio-analyze new file mode 100644 index 00000000000..c219fbefc52 --- /dev/null +++ b/tools/pvs-studio-analyze @@ -0,0 +1,25 @@ +#!/bin/bash +# +# This script generate a build folder with pvs studio enabled and run an analyze +# You need to have ninja, pvs-studio-analyzer and llvm installed +# If you are on macOS set CC/CXX to clang/clang++ from brew + +base_dir=$( + cd $(dirname $0) + pwd +) +src_dir=${base_dir}/.. + +mkdir -p "${src_dir}"/build_pvs_studio +cd "${src_dir}"/build_pvs_studio + +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + cmake -GNinja -DTW_ENABLE_PVS_STUDIO=ON -DTW_IDE_VSCODE=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ../ +elif [[ "$OSTYPE" == "darwin"* ]]; then + llvm_bin="$(brew --prefix)/opt/llvm/bin" + cmake -GNinja -DTW_ENABLE_PVS_STUDIO=ON -DTW_IDE_VSCODE=ON -DCMAKE_C_COMPILER=$llvm_bin/clang -DCMAKE_CXX_COMPILER=$llvm_bin/clang++ ../ +fi + +ninja TrustWalletCore.analyze + +cd - diff --git a/tools/pvs-studio/config.cfg b/tools/pvs-studio/config.cfg new file mode 100644 index 00000000000..4823855e789 --- /dev/null +++ b/tools/pvs-studio/config.cfg @@ -0,0 +1,11 @@ +exclude-path=*/boost/* +exclude-path=*/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform* +exclude-path=*/src/proto* +exclude-path=*/src/Cosmos/Protobuf* +exclude-path=*/build/local/include* +exclude-path=*/build/local/* +exclude-path=*/build/local/src/google/protobuf/* +exclude-path=*/opt/homebrew/opt/llvm* +exclude-path=*/src/Zilliqa/Protobuf* +exclude-path=*/src/Tron/Protobuf* +exclude-path=*/opt/homebrew/Cellar/llvm* diff --git a/tools/wasm-build b/tools/wasm-build new file mode 100644 index 00000000000..2fa483726a2 --- /dev/null +++ b/tools/wasm-build @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +base_dir=$(cd `dirname $0`; pwd) +src_dir=${base_dir}/.. + +if [[ -z ${EMSDK} ]]; then + source ${src_dir}/emsdk/emsdk_env.sh +fi + +build_folder=${src_dir}/wasm-build +boost_dir=${EMSDK}/upstream/emscripten/system/include +pushd ${src_dir} + +# cmake +cmake -Bwasm-build -DBoost_INCLUDE_DIR=${boost_dir} -DTW_COMPILE_WASM=ON -DCMAKE_TOOLCHAIN_FILE=${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake + +# make +make -j8 -Cwasm-build + +popd diff --git a/tools/wasm-set-version b/tools/wasm-set-version new file mode 100644 index 00000000000..ca3828fd0c1 --- /dev/null +++ b/tools/wasm-set-version @@ -0,0 +1,13 @@ +#!/bin/bash + +base_dir=$(cd `dirname $0`; pwd) +src_dir=${base_dir}/.. +package_json=${src_dir}/wasm/package.json +version="$1" + +if [ -z "${version}" ]; then + version=`git describe --long --tags | cut -f 1 -d "-"` +fi + +new_package_json=$(jq --arg tag "${version}" '.version = $tag' ${package_json}) +echo ${new_package_json} | jq . > ${package_json} diff --git a/tools/windows-build-and-test.ps1 b/tools/windows-build-and-test.ps1 new file mode 100644 index 00000000000..815b73ab1ca --- /dev/null +++ b/tools/windows-build-and-test.ps1 @@ -0,0 +1,25 @@ +# Build Wallet Core + +# Run after installing `dependencies` and `generating` code +# > powershell .\tools\windows-build-and-test.ps1 + +# Builds Wallet Core in static release mode, to build the console wallet +# Builds Wallet Core in dynamic release and debug mode, to build a DLL for applications + + + +# Fail if any commands fails +$ErrorActionPreference = "Stop" + +#Set the position of the toolchain = $toolsPath +$toolsPath = $PSScriptRoot + +echo "#### Generating files... ####" +& $toolsPath\windows-generate.ps1 + + +echo "#### Building... ####" +& $toolsPath\windows-build.ps1 + +& $toolsPath\windows-tests.ps1 + diff --git a/tools/windows-build.ps1 b/tools/windows-build.ps1 index e80121b8a60..da19f546275 100644 --- a/tools/windows-build.ps1 +++ b/tools/windows-build.ps1 @@ -1,24 +1,13 @@ - -# Build Wallet Core - -# Run after installing dependencies and generating code -# > powershell .\tools\windows-build.ps1 - -# Builds Wallet Core in static release mode, to build the console wallet -# Builds Wallet Core in dynamic release and debug mode, to build a DLL for applications - -$ErrorActionPreference = "Stop" +# Load dependencies version +. $PSScriptRoot\windows-dependencies-version.ps1 $root = $pwd $prefix = Join-Path $pwd "build\local" $install = Join-Path $pwd "build\install" -$cmakeGenerator = "Visual Studio 17 2022" -$cmakePlatform = "x64" -$cmakeToolset = "v143" if (Test-Path -Path $install -PathType Container) { - Remove-Item –Path $install -Recurse + Remove-Item CPath $install -Recurse } cd build @@ -36,7 +25,7 @@ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } $libInstall = Join-Path $install "lib\TrustWalletCore.lib" -Remove-Item –Path $libInstall # Replaced with the shared lib afterwards +Remove-Item CPath $libInstall # Replaced with the shared lib afterwards cd .. if (-not(Test-Path -Path "shared" -PathType Container)) { @@ -59,7 +48,7 @@ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } $cppInclude = Join-Path $install "include\WalletCore" -Remove-Item –Path $cppInclude -Recurse # Useless from shared library +Remove-Item CPath $cppInclude -Recurse # Useless from shared library cd .. cd $root diff --git a/tools/windows-dependencies-version.ps1 b/tools/windows-dependencies-version.ps1 new file mode 100644 index 00000000000..00e6f353683 --- /dev/null +++ b/tools/windows-dependencies-version.ps1 @@ -0,0 +1,14 @@ + +$gtestVersion="1.11.0" +$checkVersion="0.15.2" +$jsonVersion="3.10.2" +$boostVersion = "1.80.0" +$protobufVersion="3.19.2" +$SWIFT_PROTOBUF_VERSION="1.18.0" + + + +$cmakeGenerator = "Visual Studio 16 2019" +$cmakePlatform = "x64" +$cmakeToolset = "v142" + diff --git a/tools/windows-dependencies.ps1 b/tools/windows-dependencies.ps1 index 3e92b23f098..8549e7f3e5c 100644 --- a/tools/windows-dependencies.ps1 +++ b/tools/windows-dependencies.ps1 @@ -12,188 +12,147 @@ # Downloads and builds Protobuf in static debug and release mode, with dynamic C runtime # Builds the Wallet Core protobuf plugins in release mode + $ErrorActionPreference = "Stop" $root = $pwd $prefix = Join-Path $pwd "build\local" $include = Join-Path $prefix "include" -$cmakeGenerator = "Visual Studio 17 2022" -$cmakePlatform = "x64" -$cmakeToolset = "v143" +# Load dependencies version +. $PSScriptRoot\windows-dependencies-version.ps1 -# GoogleTest -$gtestVersion = "1.11.0" -$gtestDir = Join-Path $prefix "src\gtest" -$gtestZip = "googletest-release-$gtestVersion.zip" -$gtestUrl = "https://github.com/google/googletest/archive/refs/tags/release-$gtestVersion.zip" - -# Download and extract -if (-not(Test-Path -Path $gtestDir -PathType Container)) { - mkdir $gtestDir | Out-Null -} -cd $gtestDir -if (-not(Test-Path -Path $gtestZip -PathType Leaf)) { - Invoke-WebRequest -Uri $gtestUrl -OutFile $gtestZip -} -if (Test-Path -Path googletest-release-$gtestVersion -PathType Container) { - Remove-Item –Path googletest-release-$gtestVersion -Recurse -} -Expand-Archive -LiteralPath $gtestZip -DestinationPath $gtestDir - -# Build debug and release libraries -cd googletest-release-$gtestVersion -mkdir build_msvc | Out-Null -cd build_msvc -cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_DEBUG_POSTFIX=d" "-DCMAKE_BUILD_TYPE=Release" "-Dgtest_force_shared_crt=ON" .. -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -cmake --build . --target INSTALL --config Debug -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -cmake --build . --target INSTALL --config Release -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -# Check -$checkVersion = "0.15.2" -$checkDir = Join-Path $prefix "src\check" -$checkZip = "check-$checkVersion.zip" -$checkUrl = "https://codeload.github.com/libcheck/check/zip/refs/tags/$checkVersion" - -# Download and extract -if (-not(Test-Path -Path $checkDir -PathType Container)) { - mkdir $checkDir | Out-Null -} -cd $checkDir -if (-not(Test-Path -Path $checkZip -PathType Leaf)) { - Invoke-WebRequest -Uri $checkUrl -OutFile $checkZip -} -if (Test-Path -Path check-$checkVersion -PathType Container) { - Remove-Item –Path check-$checkVersion -Recurse -} -Expand-Archive -LiteralPath $checkZip -DestinationPath $checkDir - -# Build debug and release libraries -cd check-$checkVersion -mkdir build_msvc | Out-Null -cd build_msvc -cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_DEBUG_POSTFIX=d" "-DCMAKE_BUILD_TYPE=Release" .. -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -cmake --build . --target INSTALL --config Debug -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -cmake --build . --target INSTALL --config Release -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -# Nlohmann JSON -$jsonVersion = "3.10.4" -$jsonDir = Join-Path $prefix "include\nlohmann" -$jsonUrl = "https://github.com/nlohmann/json/releases/download/v$jsonVersion/json.hpp" -$jsonFile = Join-Path $jsonDir "json.hpp" -if (Test-Path -Path $jsonFile -PathType Leaf) { - Remove-Item –Path $jsonFile -} -if (-not(Test-Path -Path $jsonDir -PathType Container)) { - mkdir $jsonDir | Out-Null -} -Invoke-WebRequest -Uri $jsonUrl -OutFile $jsonFile - -# Boost -$boostVersion = "1.77.0" -$boostVersionU = $boostVersion.Replace(".", "_") -$boostDir = Join-Path $prefix "src\boost" -$boostZip = "boost_$boostVersionU.zip" -$boostUrl = "https://nchc.dl.sourceforge.net/project/boost/boost/$boostVersion/$boostZip" - -# Download and extract -if (-not(Test-Path -Path $boostDir -PathType Container)) { - mkdir $boostDir | Out-Null -} -cd $boostDir -if (-not(Test-Path -Path $boostZip -PathType Leaf)) { - Invoke-WebRequest -Uri $boostUrl -OutFile $boostZip -} -if (Test-Path -Path boost_$boostVersionU -PathType Container) { - Remove-Item –Path boost_$boostVersionU -Recurse -} -# Expand-Archive -LiteralPath $boostZip -DestinationPath $boostDir -$boostZipPath = Join-Path $boostDir $boostZip -Add-Type -Assembly "System.IO.Compression.Filesystem" -[System.IO.Compression.ZipFile]::ExtractToDirectory($boostZipPath, $boostDir) -if (-not(Test-Path -Path $include -PathType Container)) { - mkdir $include | Out-Null -} -$boostInclude = Join-Path $include "boost" -if (Test-Path -Path $boostInclude -PathType Container) { - Remove-Item –Path $boostInclude -Recurse -} -$boostSrcInclude = Join-Path $boostDir "boost_$boostVersionU\boost" -move $boostSrcInclude $boostInclude - -# Protobuf -$protobufVersion = "3.19.1" -$protobufDir = Join-Path $prefix "src\protobuf" -$protobufZip = "protobuf-cpp-$protobufVersion.zip" -$protobufUrl = "https://github.com/protocolbuffers/protobuf/releases/download/v$protobufVersion/$protobufZip" - -# Download and extract -if (-not(Test-Path -Path $protobufDir -PathType Container)) { - mkdir $protobufDir | Out-Null -} -cd $protobufDir -if (-not(Test-Path -Path $protobufZip -PathType Leaf)) { - Invoke-WebRequest -Uri $protobufUrl -OutFile $protobufZip -} -if (Test-Path -Path protobuf-$protobufVersion -PathType Container) { - Remove-Item –Path protobuf-$protobufVersion -Recurse -} -Expand-Archive -LiteralPath $protobufZip -DestinationPath $protobufDir - -# Build debug and release libraries -cd protobuf-$protobufVersion -mkdir build_msvc | Out-Null -cd build_msvc -$protobufCMake = Get-Content ..\cmake\CMakeLists.txt # Bugfix -$protobufCMake = $protobufCMake.Replace("set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$:Debug>)", - "if (MSVC AND protobuf_MSVC_STATIC_RUNTIME)`r`nset(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$:Debug>)`r`nendif()") # Bugfix -$protobufCMake | Out-File -encoding UTF8 ..\cmake\CMakeLists.txt # Bugfix -cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_BUILD_TYPE=Release" "-Dprotobuf_WITH_ZLIB=OFF" "-Dprotobuf_MSVC_STATIC_RUNTIME=OFF" "-Dprotobuf_BUILD_TESTS=OFF" "-Dprotobuf_BUILD_SHARED_LIBS=OFF" ../cmake -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -cmake --build . --target INSTALL --config Debug -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE + +function download_dependencies +{ + & $PSScriptRoot\windows-download-dependencies.ps1 } -cmake --build . --target INSTALL --config Release -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE + +# GoogleTest +function build_gtest +{ + # Build debug and release libraries + $gtest_dir = Join-Path $root "build\local\src\gtest\googletest-release-$gtestVersion" + cd $gtest_dir + if (Test-Path -Path build_msvc -PathType Container) + { + Remove-Item CPath build_msvc -Recurse + } + mkdir build_msvc | Out-Null + cd build_msvc + cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_DEBUG_POSTFIX=d" "-DCMAKE_BUILD_TYPE=Release" "-Dgtest_force_shared_crt=ON" .. + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Debug + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Release + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } +} + +#check +function build_libcheck +{ + # Build debug and release libraries + $check_dir = Join-Path $root "build\local\src\check\check-$checkVersion" + cd $check_dir + if (-not(Test-Path -Path $check_dir\build_msvc -PathType Container)) + { + mkdir build_msvc | Out-Null + } + cd build_msvc + cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_DEBUG_POSTFIX=d" "-DCMAKE_BUILD_TYPE=Release" .. + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Debug + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Release + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } +} + +#protobuf +function build_protobuf +{ + # Build debug and release libraries + $protobuf_dir = Join-Path $root "build\local\src\protobuf\protobuf-$protobufVersion" + cd $protobuf_dir + if (Test-Path -Path build_msvc -PathType Container) + { + Remove-Item CPath build_msvc -Recurse + } + mkdir build_msvc | Out-Null + cd build_msvc + $protobufCMake = Get-Content ..\cmake\CMakeLists.txt # Bugfix + $protobufCMake = $protobufCMake.Replace("set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$:Debug>)", + "if (MSVC AND protobuf_MSVC_STATIC_RUNTIME)`r`nset(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$:Debug>)`r`nendif()") # Bugfix + $protobufCMake | Out-File -encoding UTF8 ..\cmake\CMakeLists.txt # Bugfix + cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_BUILD_TYPE=Release" "-Dprotobuf_WITH_ZLIB=OFF" "-Dprotobuf_MSVC_STATIC_RUNTIME=OFF" "-Dprotobuf_BUILD_TESTS=OFF" "-Dprotobuf_BUILD_SHARED_LIBS=OFF" ../cmake + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Debug + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Release + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + + build_swift_plugin } # Protobuf plugins -$pluginSrc = Join-Path $root "protobuf-plugin" -cd $pluginSrc -if (Test-Path -Path build -PathType Container) { - Remove-Item –Path build -Recurse -} -mkdir build | Out-Null -cd build -cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_BUILD_TYPE=Release" .. -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -cmake --build . --target INSTALL --config Release -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} +function build_swift_plugin +{ + $pluginSrc = Join-Path $root "protobuf-plugin" + cd $pluginSrc + if (Test-Path -Path build -PathType Container) + { + Remove-Item CPath build -Recurse + } + mkdir build | Out-Null + cd build + cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_BUILD_TYPE=Release" .. + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Release + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } +} + + + +download_dependencies + +build_gtest +build_libcheck +build_protobuf +echo "`n`n" +echo "--------------------done...." cd $root diff --git a/tools/windows-download-dependencies.ps1 b/tools/windows-download-dependencies.ps1 new file mode 100644 index 00000000000..90010186637 --- /dev/null +++ b/tools/windows-download-dependencies.ps1 @@ -0,0 +1,162 @@ + +# Downloads and builds dependencies for Windows + +#This script downloads third-party dependencies .By tools\Windows-dependencies.Ps1 calls. + + + +$ErrorActionPreference = "Stop" + + +$root = $pwd +$prefix = Join-Path $pwd "build\local" +$include = Join-Path $prefix "include" + +# Load dependencies version +. $PSScriptRoot\windows-dependencies-version.ps1 + + + +# GoogleTest +function download_gtest +{ + Write-Host "Downloading gtest..." + $gtestDir = Join-Path $prefix "src\gtest" + $gtestZip = "googletest-release-$gtestVersion.zip" + $gtestUrl = "https://github.com/google/googletest/archive/refs/tags/release-$gtestVersion.zip" + # Download and extract + if (-not(Test-Path -Path $gtestDir -PathType Container)) + { + mkdir $gtestDir | Out-Null + } + cd $gtestDir + if (-not(Test-Path -Path $gtestZip -PathType Leaf)) + { + Invoke-WebRequest -Uri $gtestUrl -OutFile $gtestZip + } + if (Test-Path -Path googletest-release-$gtestVersion -PathType Container) + { + Remove-Item CPath googletest-release-$gtestVersion -Recurse + } + Expand-Archive -LiteralPath $gtestZip -DestinationPath $gtestDir +} +# Check +function download_libcheck +{ + Write-Host "Downloading libcheck..." + $checkDir = Join-Path $prefix "src\check" + $checkZip = "check-$checkVersion.zip" + $checkUrl = "https://codeload.github.com/libcheck/check/zip/refs/tags/$checkVersion" + + # Download and extract + if (-not(Test-Path -Path $checkDir -PathType Container)) + { + mkdir $checkDir | Out-Null + } + cd $checkDir + if (-not(Test-Path -Path $checkZip -PathType Leaf)) + { + Invoke-WebRequest -Uri $checkUrl -OutFile $checkZip + } + if (Test-Path -Path check-$checkVersion -PathType Container) + { + Remove-Item CPath check-$checkVersion -Recurse + } + Expand-Archive -LiteralPath $checkZip -DestinationPath $checkDir + +} + +# Nlohmann JSON +function download_nolhmann_json +{ + + Write-Host "Downloading Nlohmann JSON..." + $jsonDir = Join-Path $prefix "include\nlohmann" + $jsonUrl = "https://github.com/nlohmann/json/releases/download/v$jsonVersion/json.hpp" + $jsonFile = Join-Path $jsonDir "json.hpp" + if (Test-Path -Path $jsonFile -PathType Leaf) { + Remove-Item CPath $jsonFile + } + if (-not(Test-Path -Path $jsonDir -PathType Container)) { + mkdir $jsonDir | Out-Null + } + Invoke-WebRequest -Uri $jsonUrl -OutFile $jsonFile +} + +# Boost +function download_and_move_include_boost +{ + Write-Host "Downloading boost..." + + $boostVersionU = $boostVersion.Replace(".", "_") + $boostDir = Join-Path $prefix "src\boost" + $boostZip = "boost_$boostVersionU.zip" + $boostUrl = "https://nchc.dl.sourceforge.net/project/boost/boost/$boostVersion/$boostZip" + + # Download and extract + if (-not(Test-Path -Path $boostDir -PathType Container)) + { + mkdir $boostDir | Out-Null + } + cd $boostDir + if (-not(Test-Path -Path $boostZip -PathType Leaf)) + { + Invoke-WebRequest -Uri $boostUrl -OutFile $boostZip + } + if (Test-Path -Path boost_$boostVersionU -PathType Container) + { + Remove-Item CPath boost_$boostVersionU -Recurse + } + + # Expand-Archive -LiteralPath $boostZip -DestinationPath $boostDir + $boostZipPath = Join-Path $boostDir $boostZip + Add-Type -Assembly "System.IO.Compression.Filesystem" + [System.IO.Compression.ZipFile]::ExtractToDirectory($boostZipPath, $boostDir) + + #move include + if (-not(Test-Path -Path $include -PathType Container)) + { + mkdir $include | Out-Null + } + $boostInclude = Join-Path $include "boost" + if (Test-Path -Path $boostInclude -PathType Container) + { + Remove-Item CPath $boostInclude -Recurse + } + $boostSrcInclude = Join-Path $boostDir "boost_$boostVersionU\boost" + move $boostSrcInclude $boostInclude + +} + +function download_protobuf +{ + Write-Host "Downloading Protobuf..." + + # Protobuf + $protobufVersion = "3.19.2" + $protobufDir = Join-Path $prefix "src\protobuf" + $protobufZip = "protobuf-cpp-$protobufVersion.zip" + $protobufUrl = "https://github.com/protocolbuffers/protobuf/releases/download/v$protobufVersion/$protobufZip" + + # Download and extract + if (-not(Test-Path -Path $protobufDir -PathType Container)) { + mkdir $protobufDir | Out-Null + } + cd $protobufDir + if (-not(Test-Path -Path $protobufZip -PathType Leaf)) { + Invoke-WebRequest -Uri $protobufUrl -OutFile $protobufZip + } + if (Test-Path -Path protobuf-$protobufVersion -PathType Container) { + Remove-Item CPath protobuf-$protobufVersion -Recurse + } + Expand-Archive -LiteralPath $protobufZip -DestinationPath $protobufDir +} + + +download_gtest +download_libcheck +download_nolhmann_json +download_protobuf +download_and_move_include_boost + +cd $root diff --git a/tools/windows-doxygen_convert_comments.ps1 b/tools/windows-doxygen_convert_comments.ps1 new file mode 100644 index 00000000000..0e7242ebe7f --- /dev/null +++ b/tools/windows-doxygen_convert_comments.ps1 @@ -0,0 +1,30 @@ +#perlҪ + + +$SWIFT_PARAMETER_PATTERN = 's/\\param\s+([^\s]+)/\- Parameter $1:/g' +$SWIFT_RETURN_PATTERN = 's/\\return/\- Returns:/g' +$SWIFT_NOTE_PATTERN = 's/\\note/\- Note:/g' +$SWIFT_SEE_PATTERN = 's/\\see/\- SeeAlso:/g' +$SWIFT_FOLDER_PATH = "swift\Sources\Generated\*.swift" +$SWIFT_FOLDER_PATH_BAK = "swift\Sources\Generated\*.bak" + +function process_swift_comments($path) +{ + perl '-pi.bak' -e $SWIFT_PARAMETER_PATTERN $path + perl '-pi.bak' -e $SWIFT_RETURN_PATTERN $path + perl '-pi.bak' -e $SWIFT_NOTE_PATTERN $path + perl '-pi.bak' -e $SWIFT_SEE_PATTERN $path +} + +function swift_convert +{ + echo "Processing swift convertion..." + + foreach($path in Get-ChildItem $SWIFT_FOLDER_PATH) + { + process_swift_comments($path) + } + Remove-Item $SWIFT_FOLDER_PATH_BAK + +} +swift_convert \ No newline at end of file diff --git a/tools/windows-generate.ps1 b/tools/windows-generate.ps1 index 55c9e3085bd..a9a3b9c6f66 100644 --- a/tools/windows-generate.ps1 +++ b/tools/windows-generate.ps1 @@ -9,6 +9,10 @@ $ErrorActionPreference = "Stop" +#Set the position of the toolchain = $toolsPath +$toolsPath = Join-Path $pwd "tools" + + $root = $pwd $prefix = Join-Path $pwd "build\local" $bin = Join-Path $prefix "bin" @@ -22,13 +26,13 @@ protoc.exe --version # Clean if (Test-Path -Path "swift\Sources\Generated" -PathType Container) { - Remove-Item –Path "swift\Sources\Generated" -Recurse + Remove-Item CPath "swift\Sources\Generated" -Recurse } if (Test-Path -Path "jni\java\wallet\core\jni" -PathType Container) { - Remove-Item –Path "jni\java\wallet\core\jni" -Recurse + Remove-Item CPath "jni\java\wallet\core\jni" -Recurse } if (Test-Path -Path "jni\cpp\generated" -PathType Container) { - Remove-Item –Path "jni\cpp\generated" -Recurse + Remove-Item CPath "jni\cpp\generated" -Recurse } mkdir swift\Sources\Generated\Protobuf | Out-Null @@ -46,8 +50,11 @@ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +# Convert doxygen comments to appropriate format +& $toolsPath\windows-doxygen_convert_comments.ps1 + # Generate Java, C++ and Swift Protobuf files -protoc.exe "-I=$include" -I=src/proto --cpp_out=src/proto --java_out=jni/java src/proto/*.proto +protoc.exe "-I=$include" -I=src/proto --cpp_out=src/proto --java_out=lite:jni/java src/proto/*.proto if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } @@ -62,6 +69,15 @@ if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +protoc.exe "-I=$include" -I=src/Cosmos/Protobuf --cpp_out=src/Cosmos/Protobuf src/Cosmos/Protobuf/*.proto +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +protoc.exe "-I=$include" -I=tests/chains/Cosmos/Protobuf --cpp_out=tests/chains/Cosmos/Protobuf tests/chains/Cosmos/Protobuf/*.proto +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + # Generate Proto interface file protoc.exe "-I=$include" -I=src/proto "--plugin=$bin\protoc-gen-c-typedef.exe" --c-typedef_out include/TrustWalletCore src/proto/*.proto if ($LASTEXITCODE -ne 0) { @@ -71,3 +87,5 @@ protoc.exe "-I=$include" -I=src/proto "--plugin=$bin\protoc-gen-swift-typealias. if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +echo "done..." \ No newline at end of file diff --git a/tools/windows-replace-file.pl b/tools/windows-replace-file.pl new file mode 100644 index 00000000000..2680505d77a --- /dev/null +++ b/tools/windows-replace-file.pl @@ -0,0 +1,115 @@ +#!/usr/bin/perl +if ($#ARGV+1 != 1 ) { + + print "Enter the command: -e or -r,\n"; + print " -e : extract command is used to extract the modified source code to a tools file.\n"; + print " -r : replace command is used to replace the source code file.\n"; + exit; +} + +$op=$ARGV[0]; + +$searchPoint = "windows-replace/"; +$searchPointLength = length($searchPoint); +$searchPointLength = $searchPointLength - 1; +sub recure{ + + my $file = shift @_; + + if( -d $file ){ + + my @sub_files = <$file/*>; + + + foreach $sub_file (@sub_files ) + { + if(($sub_file ne $powerShellDir) && ($sub_file ne $READMEfile) && ($sub_file ne $dragonFile) ) + { + &recure($sub_file); + } + } + }else { + $cnt_file++; + + $windows_replaceFiles[$cnt_file] = $sub_file; + } +} + +sub replace_head +{ + my @toolsFiles = @_; + @TWFiles; + for($a = 0;$a < $#toolsFiles + 1;$a = $a + 1) + { + $toolsFilesString = join("",$toolsFiles[$a]); + my $stringPos = rindex($toolsFilesString,$searchPoint); + $stringPos += $searchPointLength; + my $filesName = substr($toolsFilesString,$stringPos + 1); + my $TWFileNameString = join( "",$TWdir ,$filesName ); + $TWFiles[$a] = $TWFileNameString; + } + return @TWFiles; + +} + +sub copyFile +{ + my($fromPath,$toPath) = @_; + use File::Copy; + for($a = 1;$a < @$fromPath;$a = $a + 1) + { +=pod + print "\n"; + print "fromPath = ",$fromPath->[$a],"\n"; + print "toPath = ",$toPath->[$a],"\n"; + print "\n"; +=cut + copy $fromPath->[$a] , $toPath->[$a] or warn 'copy failed.'; + } + +} +#path--> /wallet-core-win +use Cwd; +$TWdir = getcwd; + +$windowsToolkitName = "windows-replace"; + +#path--> tools/$windowsToolkitName +$windows_replaceDir = join( "", $TWdir,"/tools/$windowsToolkitName" ); + +#path--> tools/$windowsToolkitName/powerShell/ +$powerShellDir = join( "", $TWdir,"/tools/$windowsToolkitName/powerShell" ); + +#file--> tools/$windowsToolkitName/README.md +$READMEfile = join( "", $TWdir,"/tools/$windowsToolkitName/README.md" ); + +#file--> tools/$windowsToolkitName/windows-dragon-king.pl +$dragonFile = join( "", $TWdir,"/tools/$windowsToolkitName/windows-dragon-king.pl" ); + +#file--> tools/$windowsToolkitName/*.* +@windows_replaceFiles; +&recure($windows_replaceDir); + + +#file--> tools/windows-*.ps1 & tools/windows-*.pl +@TWFiles = replace_head(@windows_replaceFiles); + +if ($op eq "-e") +{ + copyFile(\@TWFiles,\@windows_replaceFiles); + +} +elsif($op eq "-r") +{ + copyFile(\@windows_replaceFiles,\@TWFiles); +} +else{ + + print "Enter the command: -e or -r,\n"; + print " -e : extract command is used to extract the modified source code to a tools file.\n"; + print " -r : replace command is used to replace the source code file.\n"; + exit; +} + + + diff --git a/tools/windows-replace/CMakeLists.txt b/tools/windows-replace/CMakeLists.txt new file mode 100644 index 00000000000..0886e36252f --- /dev/null +++ b/tools/windows-replace/CMakeLists.txt @@ -0,0 +1,160 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) + +project(TrustWalletCore) + +if((NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) AND (NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC"))) + message(FATAL_ERROR "You should use clang or msvc compiler") +endif() + +if ("$ENV{PREFIX}" STREQUAL "") + set(PREFIX "${CMAKE_SOURCE_DIR}/build/local") +else () + set(PREFIX "$ENV{PREFIX}") +endif () + +include(GNUInstallDirs) +include(cmake/StandardSettings.cmake) +include(cmake/CompilerWarnings.cmake) +include(cmake/StaticAnalyzers.cmake) +include(cmake/FindHostPackage.cmake) + +add_library(${PROJECT_NAME}_INTERFACE INTERFACE) +target_include_directories(${PROJECT_NAME}_INTERFACE INTERFACE ${PREFIX}/include) +target_link_directories(${PROJECT_NAME}_INTERFACE INTERFACE ${PREFIX}/lib) +set_project_warnings(${PROJECT_NAME}_INTERFACE) + +if(WIN32) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) # Disable strcpy warnings +endif() +add_subdirectory(trezor-crypto) + +if (TW_COMPILE_WASM) + message(STATUS "Wasm build enabled") + add_subdirectory(wasm) +endif () + +find_host_package(Boost REQUIRED) + +include(ExternalProject) + +# Dependencies +if(WIN32) + # Use regular static lib on Windows + find_package(Protobuf REQUIRED) + include_directories(${Protobuf_INCLUDE_DIRS}) +else() + # Build as part of project otherwise + include(cmake/Protobuf.cmake) +endif() + +if(WIN32) + # Static library is required when building tools (walletconsole) that include the C++ headers + option(TW_STATIC_LIBRARY "Build static TrustWalletCore library" OFF) + if(TW_STATIC_LIBRARY) + add_definitions(-DTW_STATIC_LIBRARY) + endif() +endif() + + + +# Source files ** +if (${ANDROID}) + message("Configuring for JNI") + file(GLOB_RECURSE sources src/*.c src/*.cc src/*.cpp src/*.h jni/cpp/*.c jni/cpp/*.cpp jni/cpp/*.h jni/cpp/*.c) + add_library(TrustWalletCore SHARED ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) + find_library(log-lib log) + target_link_libraries(TrustWalletCore PUBLIC ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto ${Protobuf_LIBRARIES} ${log-lib} Boost::boost) +else () + file(GLOB_RECURSE sources src/*.c src/*.cc src/*.cpp src/*.h) + if(WIN32) + file(GLOB_RECURSE headers include/TrustWalletCore/*.h) + if(TW_STATIC_LIBRARY) + message("Configuring static for Windows") + add_library(TrustWalletCore ${sources} ${headers} ${PROTO_SRCS} ${PROTO_HDRS}) + else() + message("Configuring shared for Windows") + add_library(TrustWalletCore SHARED ${sources} ${headers} ${PROTO_SRCS} ${PROTO_HDRS}) + target_compile_definitions(TrustWalletCore PRIVATE TW_EXPORT_LIBRARY) + endif() + else() + message("Configuring standalone") + add_library(TrustWalletCore ${sources} ${PROTO_SRCS} ${PROTO_HDRS}) + endif() + +target_link_libraries(TrustWalletCore PUBLIC ${PROJECT_NAME}_INTERFACE PRIVATE TrezorCrypto ${Protobuf_LIBRARIES} Boost::boost) +endif () + +if (TW_CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_enable_coverage(TrustWalletCore) +endif () + +if(NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) + target_compile_options(TrustWalletCore PRIVATE "-Wall") +endif() + +if (TW_CLANG_ASAN) + target_enable_asan(TrustWalletCore) +endif () + +# Define headers for this library. PUBLIC headers are used for compiling the +# library, and will be added to consumers' build paths. +target_include_directories(TrustWalletCore + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/jni/cpp + ${CMAKE_CURRENT_SOURCE_DIR}/build/local/include + ) + +if (TW_UNIT_TESTS AND NOT (WIN32 AND NOT TW_STATIC_LIBRARY)) + add_subdirectory(tests) +endif () + +if (TW_BUILD_EXAMPLES AND NOT (WIN32 AND NOT TW_STATIC_LIBRARY)) + add_subdirectory(walletconsole/lib) + add_subdirectory(walletconsole) +endif () + +if (TW_ENABLE_PVS_STUDIO) + tw_add_pvs_studio_target(TrustWalletCore) +endif () + +if (TW_ENABLE_CLANG_TIDY) + tw_add_clang_tidy_target(TrustWalletCore) +endif () + +if (NOT ANDROID AND TW_UNITY_BUILD) + set_target_properties(TrustWalletCore PROPERTIES UNITY_BUILD ON) + file(GLOB_RECURSE PROTOBUF_SOURCE_FILES CONFIGURE_DEPENDS src/Cosmos/Protobuf/*.pb.cc src/proto/*.pb.cc) + foreach(file ${PROTOBUF_SOURCE_FILES}) + set_property(SOURCE ${file} PROPERTY SKIP_UNITY_BUILD_INCLUSION ON) + endforeach() + message(STATUS "Unity build activated") +endif() + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/swift/cpp.xcconfig.in ${CMAKE_CURRENT_SOURCE_DIR}/swift/cpp.xcconfig @ONLY) + +if(WIN32 AND NOT TW_STATIC_LIBRARY) + install(TARGETS TrustWalletCore RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() + +install(TARGETS TrustWalletCore + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/WalletCore + FILES_MATCHING PATTERN "*.h" +) + +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/tools/windows-replace/cmake/CompilerWarnings.cmake b/tools/windows-replace/cmake/CompilerWarnings.cmake new file mode 100644 index 00000000000..d7d54fd984e --- /dev/null +++ b/tools/windows-replace/cmake/CompilerWarnings.cmake @@ -0,0 +1,95 @@ +macro(target_enable_asan target) + message("-- ASAN Enabled, Configuring...") + target_compile_options(${target} PUBLIC + $<$,$>:-fsanitize=address -fno-omit-frame-pointer> + $<$,$>:-fsanitize=address -fno-omit-frame-pointer>) + target_link_options(${target} PUBLIC + $<$,$>:-fsanitize=address -fno-omit-frame-pointer> + $<$,$>:-fsanitize=address -fno-omit-frame-pointer>) +endmacro() + +macro(target_enable_coverage target) + message(STATUS "Code coverage ON") + # This option is used to compile and link code instrumented for coverage analysis. + # The option is a synonym for -fprofile-arcs -ftest-coverage (when compiling) and -lgcov (when linking). + # See the documentation for those options for more details. + # https://gcc.gnu.org/onlinedocs/gcc-9.3.0/gcc/Instrumentation-Options.html + if (TW_IDE_CLION) + message(STATUS "Code coverage for Clion ON") + target_compile_options(${target} PUBLIC + $<$,$>:-fprofile-instr-generate -fcoverage-mapping> + $<$,$>:-fprofile-instr-generate -fcoverage-mapping> + $<$,$>:-fprofile-instr-generate -fcoverage-mapping>) + target_link_options(${target} PUBLIC + $<$,$>:-fprofile-instr-generate -fcoverage-mapping> + $<$,$>:-fprofile-instr-generate -fcoverage-mapping> + $<$,$>:-fprofile-instr-generate -fcoverage-mapping>) + else() + target_compile_options(${target} PUBLIC + $<$,$>:--coverage> + $<$,$>:--coverage>) + target_link_options(${target} PUBLIC + $<$,$>:--coverage> + $<$,$>:--coverage>) + endif () +endmacro() + +add_library(tw_error_settings INTERFACE) +add_library(tw::error_settings ALIAS tw_error_settings) + +add_library(tw_defaults_features INTERFACE) +add_library(tw::defaults_features ALIAS tw_defaults_features) + +add_library(tw_optimize_settings INTERFACE) +add_library(tw::optimize_settings ALIAS tw_optimize_settings) + +if(NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) + target_compile_options( + tw_error_settings + INTERFACE + -Wall + -Wextra # reasonable and standard + -Wfatal-errors # short error report + -Wshadow # warn the user if a variable declaration shadows one from a + -Wshorten-64-to-32 + -Wno-nullability-completeness + # parent context + -Wnon-virtual-dtor # warn the user if a class with virtual functions has a + # non-virtual destructor. This helps catch hard to track down memory errors + -Wcast-align # warn for potential performance problem casts + #-Wunused # warn on anything being unused + -Woverloaded-virtual # warn if you overload (not override) a virtual + # function + -Wnull-dereference # warn if a null dereference is detected + -Wdouble-promotion # warn if float is implicit promoted to double + -Wformat=2 # warn on security issues around functions that format output + ) +endif () + + + + +if (TW_WARNINGS_AS_ERRORS) + target_compile_options( + tw_error_settings + INTERFACE + -Werror + ) +endif () + +target_compile_features(tw_defaults_features INTERFACE cxx_std_20) + +target_compile_options(tw_optimize_settings INTERFACE + $<$,$>:-O0 -g> + $<$,$>:-O0 -g> + $<$,$>:-O2> + $<$,$>:-O2> + ) + +function(set_project_warnings project_name) + target_link_libraries(${project_name} INTERFACE tw::error_settings tw::defaults_features tw::optimize_settings) + + if (NOT TARGET ${project_name}) + message(AUTHOR_WARNING "${project_name} is not a target, thus no compiler warning were added.") + endif () +endfunction() diff --git a/tools/windows-replace/cmake/Protobuf.cmake b/tools/windows-replace/cmake/Protobuf.cmake new file mode 100644 index 00000000000..891d734ba24 --- /dev/null +++ b/tools/windows-replace/cmake/Protobuf.cmake @@ -0,0 +1,220 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + +set(protobuf_SOURCE_DIR ${CMAKE_SOURCE_DIR}/build/local/src/protobuf/protobuf-3.19.2) +set(protobuf_source_dir ${CMAKE_SOURCE_DIR}/build/local/src/protobuf/protobuf-3.19.2) + +# sort + uniq -u +# https://github.com/protocolbuffers/protobuf/blob/master/cmake/libprotobuf.cmake +# https://github.com/protocolbuffers/protobuf/blob/master/cmake/libprotobuf-lite.cmake + +set(protobuf_SOURCE_FILES + ${protobuf_source_dir}/src/google/protobuf/any.cc + ${protobuf_source_dir}/src/google/protobuf/any.pb.cc + ${protobuf_source_dir}/src/google/protobuf/any_lite.cc + ${protobuf_source_dir}/src/google/protobuf/api.pb.cc + ${protobuf_source_dir}/src/google/protobuf/arena.cc + ${protobuf_source_dir}/src/google/protobuf/arenastring.cc + ${protobuf_source_dir}/src/google/protobuf/compiler/importer.cc + ${protobuf_source_dir}/src/google/protobuf/compiler/parser.cc + ${protobuf_source_dir}/src/google/protobuf/descriptor.cc + ${protobuf_source_dir}/src/google/protobuf/descriptor.pb.cc + ${protobuf_source_dir}/src/google/protobuf/descriptor_database.cc + ${protobuf_source_dir}/src/google/protobuf/duration.pb.cc + ${protobuf_source_dir}/src/google/protobuf/dynamic_message.cc + ${protobuf_source_dir}/src/google/protobuf/empty.pb.cc + ${protobuf_source_dir}/src/google/protobuf/extension_set.cc + ${protobuf_source_dir}/src/google/protobuf/extension_set_heavy.cc + ${protobuf_source_dir}/src/google/protobuf/field_mask.pb.cc + ${protobuf_source_dir}/src/google/protobuf/generated_enum_util.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_bases.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_reflection.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_table_driven.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_table_driven_lite.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_full.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_lite.cc + ${protobuf_source_dir}/src/google/protobuf/generated_message_util.cc + ${protobuf_source_dir}/src/google/protobuf/implicit_weak_message.cc + ${protobuf_source_dir}/src/google/protobuf/inlined_string_field.cc + ${protobuf_source_dir}/src/google/protobuf/io/coded_stream.cc + ${protobuf_source_dir}/src/google/protobuf/io/gzip_stream.cc + ${protobuf_source_dir}/src/google/protobuf/io/io_win32.cc + ${protobuf_source_dir}/src/google/protobuf/io/printer.cc + ${protobuf_source_dir}/src/google/protobuf/io/strtod.cc + ${protobuf_source_dir}/src/google/protobuf/io/tokenizer.cc + ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream.cc + ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream_impl.cc + ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream_impl_lite.cc + ${protobuf_source_dir}/src/google/protobuf/map.cc + ${protobuf_source_dir}/src/google/protobuf/map_field.cc + ${protobuf_source_dir}/src/google/protobuf/message.cc + ${protobuf_source_dir}/src/google/protobuf/message_lite.cc + ${protobuf_source_dir}/src/google/protobuf/parse_context.cc + ${protobuf_source_dir}/src/google/protobuf/reflection_ops.cc + ${protobuf_source_dir}/src/google/protobuf/repeated_field.cc + ${protobuf_source_dir}/src/google/protobuf/repeated_ptr_field.cc + ${protobuf_source_dir}/src/google/protobuf/service.cc + ${protobuf_source_dir}/src/google/protobuf/source_context.pb.cc + ${protobuf_source_dir}/src/google/protobuf/struct.pb.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/bytestream.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/common.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/int128.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/status.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/statusor.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/stringpiece.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/stringprintf.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/structurally_valid.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/strutil.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/substitute.cc + ${protobuf_source_dir}/src/google/protobuf/stubs/time.cc + ${protobuf_source_dir}/src/google/protobuf/text_format.cc + ${protobuf_source_dir}/src/google/protobuf/timestamp.pb.cc + ${protobuf_source_dir}/src/google/protobuf/type.pb.cc + ${protobuf_source_dir}/src/google/protobuf/unknown_field_set.cc + ${protobuf_source_dir}/src/google/protobuf/util/delimited_message_util.cc + ${protobuf_source_dir}/src/google/protobuf/util/field_comparator.cc + ${protobuf_source_dir}/src/google/protobuf/util/field_mask_util.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/datapiece.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/default_value_objectwriter.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/error_listener.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/field_mask_utility.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/json_escaping.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/json_objectwriter.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/json_stream_parser.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/object_writer.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/proto_writer.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectsource.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/protostream_objectwriter.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/type_info.cc + ${protobuf_source_dir}/src/google/protobuf/util/internal/utility.cc + ${protobuf_source_dir}/src/google/protobuf/util/json_util.cc + ${protobuf_source_dir}/src/google/protobuf/util/message_differencer.cc + ${protobuf_source_dir}/src/google/protobuf/util/time_util.cc + ${protobuf_source_dir}/src/google/protobuf/util/type_resolver_util.cc + ${protobuf_source_dir}/src/google/protobuf/wire_format.cc + ${protobuf_source_dir}/src/google/protobuf/wire_format_lite.cc + ${protobuf_source_dir}/src/google/protobuf/wrappers.pb.cc +) + +set(protobuf_HEADER_FILES + ${protobuf_source_dir}/src/google/protobuf/any.h + ${protobuf_source_dir}/src/google/protobuf/any.pb.h + ${protobuf_source_dir}/src/google/protobuf/api.pb.h + ${protobuf_source_dir}/src/google/protobuf/arena.h + ${protobuf_source_dir}/src/google/protobuf/arena_impl.h + ${protobuf_source_dir}/src/google/protobuf/arenastring.h + ${protobuf_source_dir}/src/google/protobuf/compiler/importer.h + ${protobuf_source_dir}/src/google/protobuf/compiler/parser.h + ${protobuf_source_dir}/src/google/protobuf/descriptor.h + ${protobuf_source_dir}/src/google/protobuf/descriptor.pb.h + ${protobuf_source_dir}/src/google/protobuf/descriptor_database.h + ${protobuf_source_dir}/src/google/protobuf/duration.pb.h + ${protobuf_source_dir}/src/google/protobuf/dynamic_message.h + ${protobuf_source_dir}/src/google/protobuf/empty.pb.h + ${protobuf_source_dir}/src/google/protobuf/explicitly_constructed.h + ${protobuf_source_dir}/src/google/protobuf/extension_set.h + ${protobuf_source_dir}/src/google/protobuf/extension_set_inl.h + ${protobuf_source_dir}/src/google/protobuf/field_access_listener.h + ${protobuf_source_dir}/src/google/protobuf/field_mask.pb.h + ${protobuf_source_dir}/src/google/protobuf/generated_enum_reflection.h + ${protobuf_source_dir}/src/google/protobuf/generated_enum_util.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_bases.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_reflection.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_table_driven.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_table_driven_lite.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_decl.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_impl.h + ${protobuf_source_dir}/src/google/protobuf/generated_message_tctable_impl.inc + ${protobuf_source_dir}/src/google/protobuf/generated_message_util.h + ${protobuf_source_dir}/src/google/protobuf/has_bits.h + ${protobuf_source_dir}/src/google/protobuf/implicit_weak_message.h + ${protobuf_source_dir}/src/google/protobuf/inlined_string_field.h + ${protobuf_source_dir}/src/google/protobuf/io/coded_stream.h + ${protobuf_source_dir}/src/google/protobuf/io/gzip_stream.h + ${protobuf_source_dir}/src/google/protobuf/io/io_win32.h + ${protobuf_source_dir}/src/google/protobuf/io/printer.h + ${protobuf_source_dir}/src/google/protobuf/io/strtod.h + ${protobuf_source_dir}/src/google/protobuf/io/tokenizer.h + ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream.h + ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream_impl.h + ${protobuf_source_dir}/src/google/protobuf/io/zero_copy_stream_impl_lite.h + ${protobuf_source_dir}/src/google/protobuf/map.h + ${protobuf_source_dir}/src/google/protobuf/map_entry.h + ${protobuf_source_dir}/src/google/protobuf/map_entry_lite.h + ${protobuf_source_dir}/src/google/protobuf/map_field.h + ${protobuf_source_dir}/src/google/protobuf/map_field_inl.h + ${protobuf_source_dir}/src/google/protobuf/map_field_lite.h + ${protobuf_source_dir}/src/google/protobuf/map_type_handler.h + ${protobuf_source_dir}/src/google/protobuf/message.h + ${protobuf_source_dir}/src/google/protobuf/message_lite.h + ${protobuf_source_dir}/src/google/protobuf/metadata.h + ${protobuf_source_dir}/src/google/protobuf/metadata_lite.h + ${protobuf_source_dir}/src/google/protobuf/parse_context.h + ${protobuf_source_dir}/src/google/protobuf/port.h + ${protobuf_source_dir}/src/google/protobuf/reflection.h + ${protobuf_source_dir}/src/google/protobuf/reflection_ops.h + ${protobuf_source_dir}/src/google/protobuf/repeated_field.h + ${protobuf_source_dir}/src/google/protobuf/repeated_ptr_field.h + ${protobuf_source_dir}/src/google/protobuf/service.h + ${protobuf_source_dir}/src/google/protobuf/source_context.pb.h + ${protobuf_source_dir}/src/google/protobuf/struct.pb.h + ${protobuf_source_dir}/src/google/protobuf/stubs/bytestream.h + ${protobuf_source_dir}/src/google/protobuf/stubs/callback.h + ${protobuf_source_dir}/src/google/protobuf/stubs/casts.h + ${protobuf_source_dir}/src/google/protobuf/stubs/common.h + ${protobuf_source_dir}/src/google/protobuf/stubs/hash.h + ${protobuf_source_dir}/src/google/protobuf/stubs/logging.h + ${protobuf_source_dir}/src/google/protobuf/stubs/macros.h + ${protobuf_source_dir}/src/google/protobuf/stubs/map_util.h + ${protobuf_source_dir}/src/google/protobuf/stubs/mutex.h + ${protobuf_source_dir}/src/google/protobuf/stubs/once.h + ${protobuf_source_dir}/src/google/protobuf/stubs/platform_macros.h + ${protobuf_source_dir}/src/google/protobuf/stubs/port.h + ${protobuf_source_dir}/src/google/protobuf/stubs/status.h + ${protobuf_source_dir}/src/google/protobuf/stubs/stl_util.h + ${protobuf_source_dir}/src/google/protobuf/stubs/stringpiece.h + ${protobuf_source_dir}/src/google/protobuf/stubs/strutil.h + ${protobuf_source_dir}/src/google/protobuf/stubs/template_util.h + ${protobuf_source_dir}/src/google/protobuf/text_format.h + ${protobuf_source_dir}/src/google/protobuf/timestamp.pb.h + ${protobuf_source_dir}/src/google/protobuf/type.pb.h + ${protobuf_source_dir}/src/google/protobuf/unknown_field_set.h + ${protobuf_source_dir}/src/google/protobuf/util/delimited_message_util.h + ${protobuf_source_dir}/src/google/protobuf/util/field_comparator.h + ${protobuf_source_dir}/src/google/protobuf/util/field_mask_util.h + ${protobuf_source_dir}/src/google/protobuf/util/json_util.h + ${protobuf_source_dir}/src/google/protobuf/util/message_differencer.h + ${protobuf_source_dir}/src/google/protobuf/util/time_util.h + ${protobuf_source_dir}/src/google/protobuf/util/type_resolver.h + ${protobuf_source_dir}/src/google/protobuf/util/type_resolver_util.h + ${protobuf_source_dir}/src/google/protobuf/wire_format.h + ${protobuf_source_dir}/src/google/protobuf/wire_format_lite.h + ${protobuf_source_dir}/src/google/protobuf/wrappers.pb.h +) + +#file(GLOB_RECURSE protobuf_HEADER_FILES ${protobuf_SOURCE_DIR}/src/**/*.h) +include_directories(${protobuf_source_dir}/src) + +add_library(protobuf ${protobuf_SOURCE_FILES} ${protobuf_HEADER_FILES}) +set_target_properties( + protobuf + PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + IMPORTED_CONFIGURATIONS Release + INCLUDE_DIRECTORIES ${protobuf_source_dir}/src + PUBLIC_HEADER "${protobuf_HEADER_FILES}" + LINK_FLAGS -no-undefined +) + +target_compile_options(protobuf PRIVATE -DHAVE_PTHREAD=1 -Wno-inconsistent-missing-override -Wno-shorten-64-to-32 -Wno-invalid-noreturn) + +install(TARGETS protobuf + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/protobuf +) +set(Protobuf_LIBRARIES protobuf) \ No newline at end of file diff --git a/tools/windows-replace/include/TrustWalletCore/TWAnySigner.h b/tools/windows-replace/include/TrustWalletCore/TWAnySigner.h new file mode 100644 index 00000000000..90276f3dea8 --- /dev/null +++ b/tools/windows-replace/include/TrustWalletCore/TWAnySigner.h @@ -0,0 +1,50 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +#pragma once + +#include "TWBase.h" +#include "TWCoinType.h" +#include "TWData.h" +#include "TWString.h" + +TW_EXTERN_C_BEGIN + +/// Represents a signer to sign transactions for any blockchain. +struct TWAnySigner; + +/// Signs a transaction specified by the signing input and coin type. +/// +/// \param input The serialized data of a signing input (e.g. TW.Bitcoin.Proto.SigningInput). +/// \param coin The given coin type to sign the transaction for. +/// \return The serialized data of a `SigningOutput` proto object. (e.g. TW.Bitcoin.Proto.SigningOutput). +TW_EXTERN +extern TWData *_Nonnull TWAnySignerSign(TWData *_Nonnull input, enum TWCoinType coin); + +/// Signs a transaction specified by the JSON representation of signing input, coin type and a private key, returning the JSON representation of the signing output. +/// +/// \param json JSON representation of a signing input +/// \param key The private key to sign with. +/// \param coin The given coin type to sign the transaction for. +/// \return The JSON representation of a `SigningOutput` proto object. +TW_EXTERN +extern TWString *_Nonnull TWAnySignerSignJSON(TWString *_Nonnull json, TWData *_Nonnull key, enum TWCoinType coin); + +/// Check if AnySigner supports signing JSON representation of signing input. +/// +/// \param coin The given coin type to sign the transaction for. +/// \return true if AnySigner supports signing JSON representation of signing input for a given coin. +TW_EXTERN +extern bool TWAnySignerSupportsJSON(enum TWCoinType coin); + +/// Plans a transaction (for UTXO chains only). +/// +/// \param input The serialized data of a signing input +/// \param coin The given coin type to plan the transaction for. +/// \return The serialized data of a `TransactionPlan` proto object. +TW_EXTERN +extern TWData *_Nonnull TWAnySignerPlan(TWData *_Nonnull input, enum TWCoinType coin); + +TW_EXTERN_C_END diff --git a/tools/windows-replace/include/TrustWalletCore/TWBase.h b/tools/windows-replace/include/TrustWalletCore/TWBase.h new file mode 100644 index 00000000000..db2f782dd9a --- /dev/null +++ b/tools/windows-replace/include/TrustWalletCore/TWBase.h @@ -0,0 +1,99 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#if !defined(TW_EXTERN_C_BEGIN) +#if defined(__cplusplus) +#define TW_EXTERN_C_BEGIN extern "C" { +#define TW_EXTERN_C_END } +#else +#define TW_EXTERN_C_BEGIN +#define TW_EXTERN_C_END +#endif +#endif + +#ifdef _WIN32 +#ifdef TW_STATIC_LIBRARY +#define TW_EXTERN +#else +#ifdef TW_EXPORT_LIBRARY +#define TW_EXTERN __declspec(dllexport) +#else +#define TW_EXTERN __declspec(dllimport) +#endif +#endif +#else +#define TW_EXTERN extern +#endif + +// Marker for default visibility +#ifdef _MSC_VER + #define TW_VISIBILITY_DEFAULT +#else + #define TW_VISIBILITY_DEFAULT __attribute__((visibility("default"))) +#endif + +// Marker for exported classes +#define TW_EXPORT_CLASS + +// Marker for exported structs +#define TW_EXPORT_STRUCT + +// Marker for exported enums +#define TW_EXPORT_ENUM(...) + +// Marker for exported functions +#define TW_EXPORT_FUNC TW_EXTERN + +// Marker for exported methods +#define TW_EXPORT_METHOD TW_EXTERN + +// Marker for exported properties +#define TW_EXPORT_PROPERTY TW_EXTERN + +// Marker for exported static methods +#define TW_EXPORT_STATIC_METHOD TW_EXTERN + +// Marker for exported static properties +#define TW_EXPORT_STATIC_PROPERTY TW_EXTERN + +// Marker for discardable result (static) method +#define TW_METHOD_DISCARDABLE_RESULT + +// Marker for Protobuf types to be serialized across the interface +#define PROTO(x) TWData * + +#ifdef _MSC_VER +#define TW_HAS_FEATURE(feature) 0 +#else +#define TW_HAS_FEATURE(feature) __has_feature(feature) +#endif + +#if TW_HAS_FEATURE(assume_nonnull) +#define TW_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") +#define TW_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") +#else +#define TW_ASSUME_NONNULL_BEGIN +#define TW_ASSUME_NONNULL_END +#endif + + +#if !TW_HAS_FEATURE(nullability) +#ifndef _Nullable +#define _Nullable +#endif +#ifndef _Nonnull +#define _Nonnull +#endif +#ifndef _Null_unspecified +#define _Null_unspecified +#endif +#endif + +#include +#include +#include +#include + diff --git a/tools/windows-replace/include/TrustWalletCore/TWString.h b/tools/windows-replace/include/TrustWalletCore/TWString.h new file mode 100644 index 00000000000..4ad92566126 --- /dev/null +++ b/tools/windows-replace/include/TrustWalletCore/TWString.h @@ -0,0 +1,73 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "TWBase.h" + +TW_EXTERN_C_BEGIN + +typedef const void TWData; + +/// Defines a resizable string. +/// +/// The implementantion of these methods should be language-specific to minimize translation +/// overhead. For instance it should be a `jstring` for Java and an `NSString` for Swift. Create +/// allocates memory, the delete call should be called at the end to release memory. +typedef const void TWString; + +/// Creates a TWString from a null-terminated UTF8 byte array. It must be deleted at the end. +/// +/// \param bytes a null-terminated UTF8 byte array. +TW_EXTERN +TWString* _Nonnull TWStringCreateWithUTF8Bytes(const char* _Nonnull bytes) TW_VISIBILITY_DEFAULT; + +/// Creates a string from a raw byte array and size. It must be deleted at the end. +/// +/// \param bytes a raw byte array. +/// \param size the size of the byte array. +TW_EXTERN +TWString* _Nonnull TWStringCreateWithRawBytes(const uint8_t* _Nonnull bytes, size_t size) TW_VISIBILITY_DEFAULT; + +/// Creates a hexadecimal string from a block of data. It must be deleted at the end. +/// +/// \param data a block of data. +TW_EXTERN +TWString* _Nonnull TWStringCreateWithHexData(TWData* _Nonnull data) TW_VISIBILITY_DEFAULT; + +/// Returns the string size in bytes. +/// +/// \param string a TWString pointer. +TW_EXTERN +size_t TWStringSize(TWString* _Nonnull string) TW_VISIBILITY_DEFAULT; + +/// Returns the byte at the provided index. +/// +/// \param string a TWString pointer. +/// \param index the index of the byte. +TW_EXTERN +char TWStringGet(TWString* _Nonnull string, size_t index) TW_VISIBILITY_DEFAULT; + +/// Returns the raw pointer to the string's UTF8 bytes (null-terminated). +/// +/// \param string a TWString pointer. +TW_EXTERN +const char* _Nonnull TWStringUTF8Bytes(TWString* _Nonnull string) TW_VISIBILITY_DEFAULT; + +/// Deletes a string created with a `TWStringCreate*` method and frees the memory. +/// +/// \param string a TWString pointer. +TW_EXTERN +void TWStringDelete(TWString* _Nonnull string) TW_VISIBILITY_DEFAULT; + +/// Determines whether two string blocks are equal. +/// +/// \param lhs a TWString pointer. +/// \param rhs another TWString pointer. +TW_EXTERN +bool TWStringEqual(TWString* _Nonnull lhs, TWString* _Nonnull rhs) TW_VISIBILITY_DEFAULT; + +TW_EXTERN_C_END diff --git a/tools/windows-replace/powerShell/windows-bootstrap.ps1 b/tools/windows-replace/powerShell/windows-bootstrap.ps1 new file mode 100644 index 00000000000..0b29e2d3d1c --- /dev/null +++ b/tools/windows-replace/powerShell/windows-bootstrap.ps1 @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# > powershell .\windows-bootstrap + +# Ported from the `bootstrap.sh` bash script +# Initializes the workspace with dependencies, then performs full build + +# Fail if any commands fails +$ErrorActionPreference = "Stop" + +#Set the position of the toolchain = $toolsPath +$toolsPath = Join-Path $PSScriptRoot "tools" + +Write-Host "#### Initializing workspace with dependencies ... ####" +& $toolsPath\windows-dependencies.ps1 + +Write-Host "#### Building and running tests ... ####" +& $toolsPath\windows-build-and-test.ps1 + + diff --git a/tools/windows-replace/powerShell/windows-build-and-test.ps1 b/tools/windows-replace/powerShell/windows-build-and-test.ps1 new file mode 100644 index 00000000000..815b73ab1ca --- /dev/null +++ b/tools/windows-replace/powerShell/windows-build-and-test.ps1 @@ -0,0 +1,25 @@ +# Build Wallet Core + +# Run after installing `dependencies` and `generating` code +# > powershell .\tools\windows-build-and-test.ps1 + +# Builds Wallet Core in static release mode, to build the console wallet +# Builds Wallet Core in dynamic release and debug mode, to build a DLL for applications + + + +# Fail if any commands fails +$ErrorActionPreference = "Stop" + +#Set the position of the toolchain = $toolsPath +$toolsPath = $PSScriptRoot + +echo "#### Generating files... ####" +& $toolsPath\windows-generate.ps1 + + +echo "#### Building... ####" +& $toolsPath\windows-build.ps1 + +& $toolsPath\windows-tests.ps1 + diff --git a/tools/windows-replace/powerShell/windows-build.ps1 b/tools/windows-replace/powerShell/windows-build.ps1 new file mode 100644 index 00000000000..da19f546275 --- /dev/null +++ b/tools/windows-replace/powerShell/windows-build.ps1 @@ -0,0 +1,54 @@ +# Load dependencies version +. $PSScriptRoot\windows-dependencies-version.ps1 + +$root = $pwd +$prefix = Join-Path $pwd "build\local" +$install = Join-Path $pwd "build\install" + + +if (Test-Path -Path $install -PathType Container) { + Remove-Item CPath $install -Recurse +} + +cd build + +if (-not(Test-Path -Path "static" -PathType Container)) { + mkdir "static" | Out-Null +} +cd static +cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_PREFIX_PATH=$prefix" "-DCMAKE_INSTALL_PREFIX=$install" "-DCMAKE_BUILD_TYPE=Release" "-DTW_STATIC_LIBRARY=ON" ../.. +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +cmake --build . --target INSTALL --config Release +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +$libInstall = Join-Path $install "lib\TrustWalletCore.lib" +Remove-Item CPath $libInstall # Replaced with the shared lib afterwards +cd .. + +if (-not(Test-Path -Path "shared" -PathType Container)) { + mkdir "shared" | Out-Null +} +cd shared +cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_PREFIX_PATH=$prefix" "-DCMAKE_INSTALL_PREFIX=$install" "-DCMAKE_BUILD_TYPE=Release" "-DCMAKE_DEBUG_POSTFIX=d" "-DTW_STATIC_LIBRARY=OFF" ../.. +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +cmake --build . --target INSTALL --config Debug +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +$pdbBuild = Join-Path $root "build\shared\Debug\TrustWalletCored.pdb" +$pdbInstall = Join-Path $install "bin\TrustWalletCored.pdb" +copy $pdbBuild $pdbInstall +cmake --build . --target INSTALL --config Release +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +$cppInclude = Join-Path $install "include\WalletCore" +Remove-Item CPath $cppInclude -Recurse # Useless from shared library +cd .. + +cd $root diff --git a/tools/windows-replace/powerShell/windows-dependencies-version.ps1 b/tools/windows-replace/powerShell/windows-dependencies-version.ps1 new file mode 100644 index 00000000000..00e6f353683 --- /dev/null +++ b/tools/windows-replace/powerShell/windows-dependencies-version.ps1 @@ -0,0 +1,14 @@ + +$gtestVersion="1.11.0" +$checkVersion="0.15.2" +$jsonVersion="3.10.2" +$boostVersion = "1.80.0" +$protobufVersion="3.19.2" +$SWIFT_PROTOBUF_VERSION="1.18.0" + + + +$cmakeGenerator = "Visual Studio 16 2019" +$cmakePlatform = "x64" +$cmakeToolset = "v142" + diff --git a/tools/windows-replace/powerShell/windows-dependencies.ps1 b/tools/windows-replace/powerShell/windows-dependencies.ps1 new file mode 100644 index 00000000000..8549e7f3e5c --- /dev/null +++ b/tools/windows-replace/powerShell/windows-dependencies.ps1 @@ -0,0 +1,158 @@ + +# Downloads and builds dependencies for Windows +# Run this PowerShell script from the repository root folder +# > powershell .\tools\windows-dependencies.ps1 + +# Ported from the `install-dependencies` bash script + +# GoogleTest +# Check +# Downloads the single-header Nlohmann JSON library +# Downloads boost library, only require the headers +# Downloads and builds Protobuf in static debug and release mode, with dynamic C runtime +# Builds the Wallet Core protobuf plugins in release mode + + +$ErrorActionPreference = "Stop" + +$root = $pwd +$prefix = Join-Path $pwd "build\local" +$include = Join-Path $prefix "include" + +# Load dependencies version +. $PSScriptRoot\windows-dependencies-version.ps1 + + + + +function download_dependencies +{ + & $PSScriptRoot\windows-download-dependencies.ps1 +} + +# GoogleTest +function build_gtest +{ + # Build debug and release libraries + $gtest_dir = Join-Path $root "build\local\src\gtest\googletest-release-$gtestVersion" + cd $gtest_dir + if (Test-Path -Path build_msvc -PathType Container) + { + Remove-Item CPath build_msvc -Recurse + } + mkdir build_msvc | Out-Null + cd build_msvc + cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_DEBUG_POSTFIX=d" "-DCMAKE_BUILD_TYPE=Release" "-Dgtest_force_shared_crt=ON" .. + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Debug + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Release + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } +} + +#check +function build_libcheck +{ + # Build debug and release libraries + $check_dir = Join-Path $root "build\local\src\check\check-$checkVersion" + cd $check_dir + if (-not(Test-Path -Path $check_dir\build_msvc -PathType Container)) + { + mkdir build_msvc | Out-Null + } + cd build_msvc + cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_DEBUG_POSTFIX=d" "-DCMAKE_BUILD_TYPE=Release" .. + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Debug + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Release + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } +} + +#protobuf +function build_protobuf +{ + # Build debug and release libraries + $protobuf_dir = Join-Path $root "build\local\src\protobuf\protobuf-$protobufVersion" + cd $protobuf_dir + if (Test-Path -Path build_msvc -PathType Container) + { + Remove-Item CPath build_msvc -Recurse + } + mkdir build_msvc | Out-Null + cd build_msvc + $protobufCMake = Get-Content ..\cmake\CMakeLists.txt # Bugfix + $protobufCMake = $protobufCMake.Replace("set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$:Debug>)", + "if (MSVC AND protobuf_MSVC_STATIC_RUNTIME)`r`nset(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded$<$:Debug>)`r`nendif()") # Bugfix + $protobufCMake | Out-File -encoding UTF8 ..\cmake\CMakeLists.txt # Bugfix + cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_BUILD_TYPE=Release" "-Dprotobuf_WITH_ZLIB=OFF" "-Dprotobuf_MSVC_STATIC_RUNTIME=OFF" "-Dprotobuf_BUILD_TESTS=OFF" "-Dprotobuf_BUILD_SHARED_LIBS=OFF" ../cmake + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Debug + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Release + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + + build_swift_plugin +} + +# Protobuf plugins +function build_swift_plugin +{ + $pluginSrc = Join-Path $root "protobuf-plugin" + cd $pluginSrc + if (Test-Path -Path build -PathType Container) + { + Remove-Item CPath build -Recurse + } + mkdir build | Out-Null + cd build + cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_INSTALL_PREFIX=$prefix" "-DCMAKE_BUILD_TYPE=Release" .. + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } + cmake --build . --target INSTALL --config Release + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } +} + + + +download_dependencies + +build_gtest +build_libcheck +build_protobuf +echo "`n`n" +echo "--------------------done...." + +cd $root diff --git a/tools/windows-replace/powerShell/windows-download-dependencies.ps1 b/tools/windows-replace/powerShell/windows-download-dependencies.ps1 new file mode 100644 index 00000000000..90010186637 --- /dev/null +++ b/tools/windows-replace/powerShell/windows-download-dependencies.ps1 @@ -0,0 +1,162 @@ + +# Downloads and builds dependencies for Windows + +#This script downloads third-party dependencies .By tools\Windows-dependencies.Ps1 calls. + + + +$ErrorActionPreference = "Stop" + + +$root = $pwd +$prefix = Join-Path $pwd "build\local" +$include = Join-Path $prefix "include" + +# Load dependencies version +. $PSScriptRoot\windows-dependencies-version.ps1 + + + +# GoogleTest +function download_gtest +{ + Write-Host "Downloading gtest..." + $gtestDir = Join-Path $prefix "src\gtest" + $gtestZip = "googletest-release-$gtestVersion.zip" + $gtestUrl = "https://github.com/google/googletest/archive/refs/tags/release-$gtestVersion.zip" + # Download and extract + if (-not(Test-Path -Path $gtestDir -PathType Container)) + { + mkdir $gtestDir | Out-Null + } + cd $gtestDir + if (-not(Test-Path -Path $gtestZip -PathType Leaf)) + { + Invoke-WebRequest -Uri $gtestUrl -OutFile $gtestZip + } + if (Test-Path -Path googletest-release-$gtestVersion -PathType Container) + { + Remove-Item CPath googletest-release-$gtestVersion -Recurse + } + Expand-Archive -LiteralPath $gtestZip -DestinationPath $gtestDir +} +# Check +function download_libcheck +{ + Write-Host "Downloading libcheck..." + $checkDir = Join-Path $prefix "src\check" + $checkZip = "check-$checkVersion.zip" + $checkUrl = "https://codeload.github.com/libcheck/check/zip/refs/tags/$checkVersion" + + # Download and extract + if (-not(Test-Path -Path $checkDir -PathType Container)) + { + mkdir $checkDir | Out-Null + } + cd $checkDir + if (-not(Test-Path -Path $checkZip -PathType Leaf)) + { + Invoke-WebRequest -Uri $checkUrl -OutFile $checkZip + } + if (Test-Path -Path check-$checkVersion -PathType Container) + { + Remove-Item CPath check-$checkVersion -Recurse + } + Expand-Archive -LiteralPath $checkZip -DestinationPath $checkDir + +} + +# Nlohmann JSON +function download_nolhmann_json +{ + + Write-Host "Downloading Nlohmann JSON..." + $jsonDir = Join-Path $prefix "include\nlohmann" + $jsonUrl = "https://github.com/nlohmann/json/releases/download/v$jsonVersion/json.hpp" + $jsonFile = Join-Path $jsonDir "json.hpp" + if (Test-Path -Path $jsonFile -PathType Leaf) { + Remove-Item CPath $jsonFile + } + if (-not(Test-Path -Path $jsonDir -PathType Container)) { + mkdir $jsonDir | Out-Null + } + Invoke-WebRequest -Uri $jsonUrl -OutFile $jsonFile +} + +# Boost +function download_and_move_include_boost +{ + Write-Host "Downloading boost..." + + $boostVersionU = $boostVersion.Replace(".", "_") + $boostDir = Join-Path $prefix "src\boost" + $boostZip = "boost_$boostVersionU.zip" + $boostUrl = "https://nchc.dl.sourceforge.net/project/boost/boost/$boostVersion/$boostZip" + + # Download and extract + if (-not(Test-Path -Path $boostDir -PathType Container)) + { + mkdir $boostDir | Out-Null + } + cd $boostDir + if (-not(Test-Path -Path $boostZip -PathType Leaf)) + { + Invoke-WebRequest -Uri $boostUrl -OutFile $boostZip + } + if (Test-Path -Path boost_$boostVersionU -PathType Container) + { + Remove-Item CPath boost_$boostVersionU -Recurse + } + + # Expand-Archive -LiteralPath $boostZip -DestinationPath $boostDir + $boostZipPath = Join-Path $boostDir $boostZip + Add-Type -Assembly "System.IO.Compression.Filesystem" + [System.IO.Compression.ZipFile]::ExtractToDirectory($boostZipPath, $boostDir) + + #move include + if (-not(Test-Path -Path $include -PathType Container)) + { + mkdir $include | Out-Null + } + $boostInclude = Join-Path $include "boost" + if (Test-Path -Path $boostInclude -PathType Container) + { + Remove-Item CPath $boostInclude -Recurse + } + $boostSrcInclude = Join-Path $boostDir "boost_$boostVersionU\boost" + move $boostSrcInclude $boostInclude + +} + +function download_protobuf +{ + Write-Host "Downloading Protobuf..." + + # Protobuf + $protobufVersion = "3.19.2" + $protobufDir = Join-Path $prefix "src\protobuf" + $protobufZip = "protobuf-cpp-$protobufVersion.zip" + $protobufUrl = "https://github.com/protocolbuffers/protobuf/releases/download/v$protobufVersion/$protobufZip" + + # Download and extract + if (-not(Test-Path -Path $protobufDir -PathType Container)) { + mkdir $protobufDir | Out-Null + } + cd $protobufDir + if (-not(Test-Path -Path $protobufZip -PathType Leaf)) { + Invoke-WebRequest -Uri $protobufUrl -OutFile $protobufZip + } + if (Test-Path -Path protobuf-$protobufVersion -PathType Container) { + Remove-Item CPath protobuf-$protobufVersion -Recurse + } + Expand-Archive -LiteralPath $protobufZip -DestinationPath $protobufDir +} + + +download_gtest +download_libcheck +download_nolhmann_json +download_protobuf +download_and_move_include_boost + +cd $root diff --git a/tools/windows-replace/powerShell/windows-doxygen_convert_comments.ps1 b/tools/windows-replace/powerShell/windows-doxygen_convert_comments.ps1 new file mode 100644 index 00000000000..487cffdc465 --- /dev/null +++ b/tools/windows-replace/powerShell/windows-doxygen_convert_comments.ps1 @@ -0,0 +1,31 @@ +#perl Download http://strawberryperl.com/releases.html + + + +$SWIFT_PARAMETER_PATTERN = 's/\\param\s+([^\s]+)/\- Parameter $1:/g' +$SWIFT_RETURN_PATTERN = 's/\\return/\- Returns:/g' +$SWIFT_NOTE_PATTERN = 's/\\note/\- Note:/g' +$SWIFT_SEE_PATTERN = 's/\\see/\- SeeAlso:/g' +$SWIFT_FOLDER_PATH = "swift\Sources\Generated\*.swift" +$SWIFT_FOLDER_PATH_BAK = "swift\Sources\Generated\*.bak" + +function process_swift_comments($path) +{ + perl '-pi.bak' -e $SWIFT_PARAMETER_PATTERN $path + perl '-pi.bak' -e $SWIFT_RETURN_PATTERN $path + perl '-pi.bak' -e $SWIFT_NOTE_PATTERN $path + perl '-pi.bak' -e $SWIFT_SEE_PATTERN $path +} + +function swift_convert +{ + echo "Processing swift convertion..." + + foreach($path in Get-ChildItem $SWIFT_FOLDER_PATH) + { + process_swift_comments($path) + } + Remove-Item $SWIFT_FOLDER_PATH_BAK + +} +swift_convert \ No newline at end of file diff --git a/tools/windows-replace/powerShell/windows-generate.ps1 b/tools/windows-replace/powerShell/windows-generate.ps1 new file mode 100644 index 00000000000..a9a3b9c6f66 --- /dev/null +++ b/tools/windows-replace/powerShell/windows-generate.ps1 @@ -0,0 +1,91 @@ + +# Generate protobuf headers +# Requires Ruby to be installed, and added into the system PATH environment variable + +# Ported from `generate-files` + +# Run after installing dependencies +# > powershell .\tools\windows-generate.ps1 + +$ErrorActionPreference = "Stop" + +#Set the position of the toolchain = $toolsPath +$toolsPath = Join-Path $pwd "tools" + + +$root = $pwd +$prefix = Join-Path $pwd "build\local" +$bin = Join-Path $prefix "bin" +$include = Join-Path $prefix "include" + +$env:Path = $bin + ";" + $env:Path + +where.exe ruby +where.exe protoc +protoc.exe --version + +# Clean +if (Test-Path -Path "swift\Sources\Generated" -PathType Container) { + Remove-Item CPath "swift\Sources\Generated" -Recurse +} +if (Test-Path -Path "jni\java\wallet\core\jni" -PathType Container) { + Remove-Item CPath "jni\java\wallet\core\jni" -Recurse +} +if (Test-Path -Path "jni\cpp\generated" -PathType Container) { + Remove-Item CPath "jni\cpp\generated" -Recurse +} + +mkdir swift\Sources\Generated\Protobuf | Out-Null +mkdir swift\Sources\Generated\Enums | Out-Null + +# Generate coins info from registry.json +ruby.exe codegen\bin\coins +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +# Generate interface code +ruby.exe codegen\bin\codegen +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +# Convert doxygen comments to appropriate format +& $toolsPath\windows-doxygen_convert_comments.ps1 + +# Generate Java, C++ and Swift Protobuf files +protoc.exe "-I=$include" -I=src/proto --cpp_out=src/proto --java_out=lite:jni/java src/proto/*.proto +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +# Generate internal message protocol Protobuf files -- not every time +protoc.exe "-I=$include" -I=src/Tron/Protobuf --cpp_out=src/Tron/Protobuf src/Tron/Protobuf/*.proto +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +protoc.exe "-I=$include" -I=src/Zilliqa/Protobuf --cpp_out=src/Zilliqa/Protobuf src/Zilliqa/Protobuf/*.proto +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +protoc.exe "-I=$include" -I=src/Cosmos/Protobuf --cpp_out=src/Cosmos/Protobuf src/Cosmos/Protobuf/*.proto +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +protoc.exe "-I=$include" -I=tests/chains/Cosmos/Protobuf --cpp_out=tests/chains/Cosmos/Protobuf tests/chains/Cosmos/Protobuf/*.proto +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +# Generate Proto interface file +protoc.exe "-I=$include" -I=src/proto "--plugin=$bin\protoc-gen-c-typedef.exe" --c-typedef_out include/TrustWalletCore src/proto/*.proto +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +protoc.exe "-I=$include" -I=src/proto "--plugin=$bin\protoc-gen-swift-typealias.exe" --swift-typealias_out swift/Sources/Generated/Protobuf src/proto/*.proto +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +echo "done..." \ No newline at end of file diff --git a/tools/windows-replace/powerShell/windows-replace-file.pl b/tools/windows-replace/powerShell/windows-replace-file.pl new file mode 100644 index 00000000000..2680505d77a --- /dev/null +++ b/tools/windows-replace/powerShell/windows-replace-file.pl @@ -0,0 +1,115 @@ +#!/usr/bin/perl +if ($#ARGV+1 != 1 ) { + + print "Enter the command: -e or -r,\n"; + print " -e : extract command is used to extract the modified source code to a tools file.\n"; + print " -r : replace command is used to replace the source code file.\n"; + exit; +} + +$op=$ARGV[0]; + +$searchPoint = "windows-replace/"; +$searchPointLength = length($searchPoint); +$searchPointLength = $searchPointLength - 1; +sub recure{ + + my $file = shift @_; + + if( -d $file ){ + + my @sub_files = <$file/*>; + + + foreach $sub_file (@sub_files ) + { + if(($sub_file ne $powerShellDir) && ($sub_file ne $READMEfile) && ($sub_file ne $dragonFile) ) + { + &recure($sub_file); + } + } + }else { + $cnt_file++; + + $windows_replaceFiles[$cnt_file] = $sub_file; + } +} + +sub replace_head +{ + my @toolsFiles = @_; + @TWFiles; + for($a = 0;$a < $#toolsFiles + 1;$a = $a + 1) + { + $toolsFilesString = join("",$toolsFiles[$a]); + my $stringPos = rindex($toolsFilesString,$searchPoint); + $stringPos += $searchPointLength; + my $filesName = substr($toolsFilesString,$stringPos + 1); + my $TWFileNameString = join( "",$TWdir ,$filesName ); + $TWFiles[$a] = $TWFileNameString; + } + return @TWFiles; + +} + +sub copyFile +{ + my($fromPath,$toPath) = @_; + use File::Copy; + for($a = 1;$a < @$fromPath;$a = $a + 1) + { +=pod + print "\n"; + print "fromPath = ",$fromPath->[$a],"\n"; + print "toPath = ",$toPath->[$a],"\n"; + print "\n"; +=cut + copy $fromPath->[$a] , $toPath->[$a] or warn 'copy failed.'; + } + +} +#path--> /wallet-core-win +use Cwd; +$TWdir = getcwd; + +$windowsToolkitName = "windows-replace"; + +#path--> tools/$windowsToolkitName +$windows_replaceDir = join( "", $TWdir,"/tools/$windowsToolkitName" ); + +#path--> tools/$windowsToolkitName/powerShell/ +$powerShellDir = join( "", $TWdir,"/tools/$windowsToolkitName/powerShell" ); + +#file--> tools/$windowsToolkitName/README.md +$READMEfile = join( "", $TWdir,"/tools/$windowsToolkitName/README.md" ); + +#file--> tools/$windowsToolkitName/windows-dragon-king.pl +$dragonFile = join( "", $TWdir,"/tools/$windowsToolkitName/windows-dragon-king.pl" ); + +#file--> tools/$windowsToolkitName/*.* +@windows_replaceFiles; +&recure($windows_replaceDir); + + +#file--> tools/windows-*.ps1 & tools/windows-*.pl +@TWFiles = replace_head(@windows_replaceFiles); + +if ($op eq "-e") +{ + copyFile(\@TWFiles,\@windows_replaceFiles); + +} +elsif($op eq "-r") +{ + copyFile(\@windows_replaceFiles,\@TWFiles); +} +else{ + + print "Enter the command: -e or -r,\n"; + print " -e : extract command is used to extract the modified source code to a tools file.\n"; + print " -r : replace command is used to replace the source code file.\n"; + exit; +} + + + diff --git a/tools/windows-replace/powerShell/windows-samples.ps1 b/tools/windows-replace/powerShell/windows-samples.ps1 new file mode 100644 index 00000000000..4ed82a3a656 --- /dev/null +++ b/tools/windows-replace/powerShell/windows-samples.ps1 @@ -0,0 +1,28 @@ + +# Build Samples + +# Load dependencies version +. $PSScriptRoot\windows-dependencies-version.ps1 + +$ErrorActionPreference = "Stop" + +$root = $pwd +$prefix = Join-Path $pwd "build\local" +$install = Join-Path $pwd "build\install" + + +cd samples\cpp +if (-not(Test-Path -Path "build" -PathType Container)) { + mkdir "build" | Out-Null +} +cd build +cmake -G $cmakeGenerator -A $cmakePlatform -T $cmakeToolset "-DCMAKE_BUILD_TYPE=Release" "-DWALLET_CORE=$root" .. +cmake --build . --config Release +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} +$dllInstall = Join-Path $install "bin\TrustWalletCore.dll" +$dllCopy = Join-Path $root "samples\cpp\build\Release\TrustWalletCore.dll" +copy $dllInstall $dllCopy + +cd $root diff --git a/tools/windows-replace/powerShell/windows-tests.ps1 b/tools/windows-replace/powerShell/windows-tests.ps1 new file mode 100644 index 00000000000..d3883de0bb2 --- /dev/null +++ b/tools/windows-replace/powerShell/windows-tests.ps1 @@ -0,0 +1,14 @@ +# Run tests + +$ErrorActionPreference = "Stop" + +.\build\static\trezor-crypto\crypto\tests\Release\TrezorCryptoTests.exe +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + +.\build\static\tests\Release\tests.exe tests +if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE +} + diff --git a/tools/windows-replace/protobuf-plugin/CMakeLists.txt b/tools/windows-replace/protobuf-plugin/CMakeLists.txt new file mode 100644 index 00000000000..2d8c3a722fb --- /dev/null +++ b/tools/windows-replace/protobuf-plugin/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.2 FATAL_ERROR) +project(TrustWalletCoreProtobufPlugin) + +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version" FORCE) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if ("$ENV{PREFIX}" STREQUAL "") + set(PREFIX "${CMAKE_SOURCE_DIR}/../build/local") +else() + set(PREFIX "$ENV{PREFIX}") +endif() + +include_directories(${PREFIX}/include) +link_directories(${PREFIX}/lib) + +find_package(Protobuf REQUIRED PATH ${PREFIX}/lib/pkgconfig) +include_directories(${Protobuf_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(protoc-gen-c-typedef c_typedef.cc ${PROTO_SRCS} ${PROTO_HDRS}) +if (WIN32) + target_link_libraries(protoc-gen-c-typedef ${Protobuf_LIBRARIES} ${Protobuf_PROTOC_LIBRARIES}) +else() + target_link_libraries(protoc-gen-c-typedef protobuf -lprotoc -pthread) +endif() + +add_executable(protoc-gen-swift-typealias swift_typealias.cc ${PROTO_SRCS} ${PROTO_HDRS}) +if (WIN32) + target_link_libraries(protoc-gen-swift-typealias ${Protobuf_LIBRARIES} ${Protobuf_PROTOC_LIBRARIES}) +else() + target_link_libraries(protoc-gen-swift-typealias protobuf -lprotoc -pthread) +endif() + +install(TARGETS protoc-gen-c-typedef protoc-gen-swift-typealias DESTINATION bin) diff --git a/tools/windows-replace/readme.md b/tools/windows-replace/readme.md new file mode 100644 index 00000000000..5bab8dbaba3 --- /dev/null +++ b/tools/windows-replace/readme.md @@ -0,0 +1,33 @@ +# windows-replace folder + +The windows-replace folder manages modified files. + +used when there are no powershell scripts in the tools directory: + +```perl .\tools\windows-replace\windows-dragon-king.pl -spouting``` + +Place Windows dependencies in the tools folder and project home directory. + + + +```perl .\tools\windows-replace\windows-dragon-king.pl -suction``` + +Reclaim the files in the tools folder and project home directory. + +#Replace the file +```perl .\tools\windows-replace-file.pl -e ``` + +Replace the modified file with the source file. + +#Build + + powershell ```.\windows-bootstrap``` + + Or, broken up in smaller steps: + + powershell ```.\tools\windows-dependencies.ps1``` + + This script builds TrustWallet and runs the tests: + + powershell ```.\tools\windows-build-and-test.ps1``` + diff --git a/tools/windows-replace/src/Aptos/MoveTypes.cpp b/tools/windows-replace/src/Aptos/MoveTypes.cpp new file mode 100644 index 00000000000..4df1a932c5f --- /dev/null +++ b/tools/windows-replace/src/Aptos/MoveTypes.cpp @@ -0,0 +1,152 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include + +namespace TW::Aptos { + +Aptos::ModuleId::ModuleId(Address accountAddress, Identifier name) noexcept + : mAccountAddress(accountAddress), mName(std::move(name)) { +} + +Data ModuleId::accessVector() const noexcept { + BCS::Serializer serializer; + serializer << static_cast(gCodeTag) << mAccountAddress << mName; + return serializer.bytes; +} + +std::string ModuleId::string() const noexcept { + std::stringstream ss; + ss << mAccountAddress.string() << "::" << mName; + return ss.str(); +} + +std::string ModuleId::shortString() const noexcept { + std::stringstream ss; + ss << "0x" << mAccountAddress.shortString() << "::" << mName; + return ss.str(); +} + +#ifdef _MSC_VER +#pragma optimize( "", off ) +#endif +Data StructTag::serialize(bool withResourceTag) const noexcept { + BCS::Serializer serializer; + if (withResourceTag) + { + serializer << gResourceTag; + } + serializer << mAccountAddress << mModule << mName << mTypeParams; + return serializer.bytes; +} +#ifdef _MSC_VER +#pragma optimize("", on) +#endif + +StructTag::StructTag(Address accountAddress, Identifier module, Identifier name, std::vector typeParams) noexcept + : mAccountAddress(accountAddress), mModule(std::move(module)), mName(std::move(name)), mTypeParams(std::move(typeParams)) { +} +std::string StructTag::string() const noexcept { + std::stringstream ss; + ss << "0x" << mAccountAddress.shortString() << "::" << mModule << "::" << mName; + if (!mTypeParams.empty()) { + ss << "<"; + ss << TypeTagToString(*mTypeParams.begin()); + std::for_each(begin(mTypeParams) + 1, end(mTypeParams), [&ss](auto&& cur) { + ss << ", " << TypeTagToString(cur); + }); + ss << ">"; + } + return ss.str(); +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, Bool) noexcept { + stream << Bool::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, U8) noexcept { + stream << U8::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, U64) noexcept { + stream << U64::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, U128) noexcept { + stream << U128::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, TAddress) noexcept { + stream << TAddress::value; + return stream; +} +BCS::Serializer& operator<<(BCS::Serializer& stream, TSigner) noexcept { + stream << TSigner::value; + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const StructTag& st) noexcept { + auto res = st.serialize(); + stream.add_bytes(begin(res), end(res)); + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const TStructTag& st) noexcept { + stream << TStructTag::value; + auto res = st.st.serialize(false); + stream.add_bytes(begin(res), end(res)); + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const Vector& t) noexcept { + stream << Vector::value; + for (auto&& cur: t.tags) { + stream << cur; + } + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const TypeTag& t) noexcept { + std::visit([&stream](auto&& arg) { stream << arg; }, t.tags); + return stream; +} + +BCS::Serializer& operator<<(BCS::Serializer& stream, const ModuleId& module) noexcept { + stream << module.address() << module.name(); + return stream; +} +std::string TypeTagToString(const TypeTag& typeTag) noexcept { + auto visit_functor = [](const TypeTag::TypeTagVariant& value) -> std::string { + if (std::holds_alternative(value)) { + return "bool"; + } else if (std::holds_alternative(value)) { + return "u8"; + } else if (std::holds_alternative(value)) { + return "u64"; + } else if (std::holds_alternative(value)) { + return "u128"; + } else if (std::holds_alternative(value)) { + return "address"; + } else if (std::holds_alternative(value)) { + return "signer"; + } else if (auto* vectorData = std::get_if(&value); vectorData != nullptr && !vectorData->tags.empty()) { + std::stringstream ss; + ss << "vector<" << TypeTagToString(*vectorData->tags.begin()) << ">"; + return ss.str(); + } else if (auto* structData = std::get_if(&value); structData) { + return structData->string(); + } else if (auto* tStructData = std::get_if(&value); tStructData) { + return tStructData->st.string(); + } else { + return ""; + } + }; + + return std::visit(visit_functor, typeTag.tags); +} + +} // namespace TW::Aptos diff --git a/tools/windows-replace/src/Base32.h b/tools/windows-replace/src/Base32.h new file mode 100644 index 00000000000..7c9f5e9cb3e --- /dev/null +++ b/tools/windows-replace/src/Base32.h @@ -0,0 +1,68 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" + +#include + +#include + +namespace TW::Base32 { + +/// Decode Base32 string, return bytes as Data +/// alphabet: Optional alphabet, if missing, default ALPHABET_RFC4648 +inline bool decode(const std::string& encoded_in, Data& decoded_out, const char* alphabet_in = nullptr) { + size_t inLen = encoded_in.size(); + // obtain output length first + size_t outLen = base32_decoded_length(inLen); + //win +#ifdef _MSC_VER + uint8_t *buf = (uint8_t *)_alloca(outLen); +#else + uint8_t buf[outLen]; +#endif + + if (alphabet_in == nullptr) { + alphabet_in = BASE32_ALPHABET_RFC4648; + } + // perform the base32 decode + uint8_t* retval = base32_decode(encoded_in.data(), inLen, buf, outLen, alphabet_in); + if (retval == nullptr) { + return false; + } + decoded_out.assign(buf, buf + outLen); + return true; +} + +/// Encode bytes in Data to Base32 string +/// alphabet: Optional alphabet, if missing, default ALPHABET_RFC4648 +inline std::string encode(const Data& val, const char* alphabet = nullptr) { + size_t inLen = val.size(); + // obtain output length first, reserve for terminator + size_t outLen = base32_encoded_length(inLen) + 1; + //win +#ifdef _MSC_VER + char *buf = (char *)_alloca(outLen); +#else + char buf[outLen]; +#endif + if (alphabet == nullptr) { + alphabet = BASE32_ALPHABET_RFC4648; + } + // perform the base32 encode + char* retval = base32_encode(val.data(), inLen, buf, outLen, alphabet); + if (retval == nullptr) { + // return empty string if failed + return std::string(); + } + // make sure there is a terminator ath the end + buf[outLen - 1] = '\0'; + return std::string(buf); +} + +} // namespace TW::Base32 diff --git a/tools/windows-replace/src/BinaryCoding.h b/tools/windows-replace/src/BinaryCoding.h new file mode 100644 index 00000000000..88c11ebab31 --- /dev/null +++ b/tools/windows-replace/src/BinaryCoding.h @@ -0,0 +1,120 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include "Data.h" + +#include +#include +#include +#include +#include + +//win +#ifdef _MSC_VER +#define _Nonnull +#endif + +namespace TW { + +/// Encodes a 16-bit little-endian value into the provided buffer. +inline void encode16LE(uint16_t val, std::vector& data) { + data.push_back(static_cast(val)); + data.push_back(static_cast(val >> 8)); +} + +/// Decodes a 16-bit little-endian value from the provided buffer. +inline uint16_t decode16LE(const uint8_t* _Nonnull src) { + return static_cast((src[0]) | ((uint16_t)(src[1]) << 8)); +} + +/// Encodes a 32-bit little-endian value into the provided buffer. +inline void encode32LE(uint32_t val, std::vector& data) { + data.push_back(static_cast(val)); + data.push_back(static_cast((val >> 8))); + data.push_back(static_cast((val >> 16))); + data.push_back(static_cast((val >> 24))); +} + +/// Decodes a 32-bit little-endian value from the provided buffer. +inline uint32_t decode32LE(const uint8_t* _Nonnull src) { + // clang-format off + return static_cast(src[0]) + | (static_cast(src[1]) << 8) + | (static_cast(src[2]) << 16) + | (static_cast(src[3]) << 24); + // clang-format on +} + +/// Encodes a 64-bit little-endian value into the provided buffer. +void encode64LE(uint64_t val, std::vector& data); + +/// Decodes a 64-bit little-endian value from the provided buffer. +uint64_t decode64LE(const uint8_t* _Nonnull src); + +/// Returns the number of bytes it would take to serialize the provided value +/// as a variable-length integer (varint). +uint8_t varIntSize(uint64_t value); + +/// Encodes a value as a variable-length integer. +/// +/// A variable-length integer (varint) is an encoding for integers up to a max +/// value of 2^64-1 that uses a variable number of bytes depending on the value +/// being encoded. It produces fewer bytes for smaller numbers as opposed to a +/// fixed-size encoding. Little endian byte order is used. +/// +/// \returns the number of bytes written. +uint8_t encodeVarInt(uint64_t size, std::vector& data); + +/// Decodes an integer as a variable-length integer. See encodeVarInt(). +/// +/// \returns a tuple with a success indicator and the decoded integer. +std::tuple decodeVarInt(const Data& in, size_t& indexInOut); + +/// Encodes a 16-bit big-endian value into the provided buffer. +inline void encode16BE(uint16_t val, std::vector& data) { + data.push_back(static_cast(val >> 8)); + data.push_back(static_cast(val)); +} + +/// Decodes a 16-bit big-endian value from the provided buffer. +inline uint16_t decode16BE(const uint8_t* _Nonnull src) { + return static_cast((src[1]) | ((uint16_t)(src[0]) << 8)); +} + +/// Encodes a 32-bit big-endian value into the provided buffer. +inline void encode32BE(uint32_t val, std::vector& data) { + data.push_back(static_cast((val >> 24))); + data.push_back(static_cast((val >> 16))); + data.push_back(static_cast((val >> 8))); + data.push_back(static_cast(val)); +} + +/// Decodes a 32-bit big-endian value from the provided buffer. +inline uint32_t decode32BE(const uint8_t* _Nonnull src) { + // clang-format off + return static_cast(src[3]) + | (static_cast(src[2]) << 8) + | (static_cast(src[1]) << 16) + | (static_cast(src[0]) << 24); + // clang-format on +} + +/// Encodes a 64-bit big-endian value into the provided buffer. +void encode64BE(uint64_t val, std::vector& data); + +/// Decodes a 64-bit big-endian value from the provided buffer. +uint64_t decode64BE(const uint8_t* _Nonnull src); + +/// Encodes an ASCII string prefixed by the length (varInt) +void encodeString(const std::string& str, std::vector& data); + +/// Decodes an ASCII string prefixed by its length (varInt) +/// \returns a tuple with a success indicator and the decoded string. +std::tuple decodeString(const Data& in, size_t& indexInOut); + +} // namespace TW diff --git a/tools/windows-replace/src/Ethereum/ABI/ParamFactory.cpp b/tools/windows-replace/src/Ethereum/ABI/ParamFactory.cpp new file mode 100644 index 00000000000..e82ce201246 --- /dev/null +++ b/tools/windows-replace/src/Ethereum/ABI/ParamFactory.cpp @@ -0,0 +1,206 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "ParamFactory.h" +#include "ParamAddress.h" +#include "HexCoding.h" + +#include +#include + +using namespace std; +using namespace boost::algorithm; +using json = nlohmann::json; + +namespace TW::Ethereum::ABI { + +static int parseBitSize(const std::string& type) { + int size = stoi(type); + if (size < 8 || size > 256 || size % 8 != 0 || + size == 8 || size == 16 || size == 32 || size == 64 || size == 256) { + throw invalid_argument("invalid bit size"); + } + return size; +} + +static std::shared_ptr makeUInt(const std::string& type) { + auto bits = parseBitSize(type); + return make_shared(bits); +} + +static std::shared_ptr makeInt(const std::string& type) { + auto bits = parseBitSize(type); + return make_shared(bits); +} + +static bool isArrayType(const std::string& type) { + return ends_with(type, "[]") && type.length() >= 3; +} + +static std::string getArrayElemType(const std::string& arrayType) { + if (isArrayType(arrayType)) { + return arrayType.substr(0, arrayType.length() - 2); + } + return ""; +} + +std::shared_ptr ParamFactory::make(const std::string& type) { + shared_ptr param; + if (isArrayType(type)) { + auto elemType = getArrayElemType(type); + auto elemParam = make(elemType); + if (!elemParam) { + return param; + } + param = make_shared(elemParam); + } else if (type == "address") { + param = make_shared(); + } else if (type == "uint8") { + param = make_shared(); + } else if (type == "uint16") { + param = make_shared(); + } else if (type == "uint32") { + param = make_shared(); + } else if (type == "uint64") { + param = make_shared(); + } else if (type == "uint256" || type == "uint") { + param = make_shared(); + } else if (type == "int8") { + param = make_shared(); + } else if (type == "int16") { + param = make_shared(); + } else if (type == "int32") { + param = make_shared(); + } else if (type == "int64") { + param = make_shared(); + } else if (type == "int256" || type == "int") { + param = make_shared(); + } else if (starts_with(type, "uint")) { + param = makeUInt(type.substr(4, type.size() - 1)); + } else if (starts_with(type, "int")) { + param = makeInt(type.substr(3, type.size() - 1)); + } else if (type == "bool") { + param = make_shared(); + } else if (type == "bytes") { + param = make_shared(); + } else if (starts_with(type, "bytes")) { + auto bits = stoi(type.substr(5, type.size() - 1)); + param = make_shared(bits); + } else if (type == "string") { + param = make_shared(); + } + return param; +} + +std::string joinArrayElems(const std::vector& strings) { + auto array = json::array(); + for (const auto& string : strings) { + // parse to prevent quotes on simple values + auto value = json::parse(string, nullptr, false); + if (value.is_discarded()) { + // fallback + value = json(string); + } + array.push_back(value); + } + return array.dump(); +} + +std::shared_ptr ParamFactory::makeNamed(const std::string& name, const std::string& type) { + auto param = make(type); + if (!param) { + return nullptr; + } + return std::make_shared(name, param); +} + +bool ParamFactory::isPrimitive(const std::string& type) { + if (starts_with(type, "address")) { + return true; + } else if (starts_with(type, "uint")) { + return true; + } else if (starts_with(type, "int")) { + return true; + } else if (starts_with(type, "bool")) { + return true; + } else if (starts_with(type, "bytes")) { + return true; + } else if (starts_with(type, "string")) { + return true; + } + return false; +} + +std::string ParamFactory::getValue(const std::shared_ptr& param, const std::string& type) { + std::string result = ""; + if (isArrayType(type)) { + auto values = getArrayValue(param, type); + result = joinArrayElems(values); + } else if (type == "address") { + auto value = dynamic_pointer_cast(param); + result = hexEncoded(value->getData()); + } else if (type == "uint8") { + //result = boost::lexical_cast((uint)dynamic_pointer_cast(param)->getVal()); + result = boost::lexical_cast((unsigned int)dynamic_pointer_cast(param)->getVal());//win + + } else if (type == "uint16") { + result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); + } else if (type == "uint32") { + result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); + } else if (type == "uint64") { + result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); + } else if (type == "uint256" || type == "uint") { + result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); + } else if (type == "int8") { + result = boost::lexical_cast((int)dynamic_pointer_cast(param)->getVal()); + } else if (type == "int16") { + result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); + } else if (type == "int32") { + result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); + } else if (type == "int64") { + result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); + } else if (type == "int256" || type == "int") { + result = boost::lexical_cast(dynamic_pointer_cast(param)->getVal()); + } else if (starts_with(type, "uint")) { + auto value = dynamic_pointer_cast(param); + result = boost::lexical_cast(value->getVal()); + } else if (starts_with(type, "int")) { + auto value = dynamic_pointer_cast(param); + result = boost::lexical_cast(value->getVal()); + } else if (type == "bool") { + auto value = dynamic_pointer_cast(param); + result = value->getVal() ? "true" : "false"; + } else if (type == "bytes") { + auto value = dynamic_pointer_cast(param); + result = hexEncoded(value->getVal()); + } else if (starts_with(type, "bytes")) { + auto value = dynamic_pointer_cast(param); + result = hexEncoded(value->getVal()); + } else if (type == "string") { + auto value = dynamic_pointer_cast(param); + result = value->getVal(); + } + return result; +} + +std::vector ParamFactory::getArrayValue(const std::shared_ptr& param, const std::string& type) { + if (!isArrayType(type)) { + return std::vector(); + } + auto array = dynamic_pointer_cast(param); + if (!array) { + return std::vector(); + } + auto elemType = getArrayElemType(type); + auto elems = array->getVal(); + std::vector values(elems.size()); + for (auto i = 0ul; i < elems.size(); ++i) { + values[i] = getValue(elems[i], elemType); + } + return values; +} + +} // namespace TW::Ethereum::ABI diff --git a/tools/windows-replace/src/Everscale/Cell.cpp b/tools/windows-replace/src/Everscale/Cell.cpp new file mode 100644 index 00000000000..8d1349937c9 --- /dev/null +++ b/tools/windows-replace/src/Everscale/Cell.cpp @@ -0,0 +1,405 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Cell.h" + +#include +#include +#include +#include + +#include "../BinaryCoding.h" + +#include +#include + +#ifdef _MSC_VER +#include +typedef SSIZE_T ssize_t; +#endif + +using namespace TW; + +namespace TW::Everscale { + +constexpr static uint32_t BOC_MAGIC = 0xb5ee9c72; + +uint16_t computeBitLen(const Data& data, bool aligned) { + auto bitLen = static_cast(data.size() * 8); + if (aligned) { + return bitLen; + } + + for (auto i = static_cast(data.size() - 1); i >= 0; --i) { + if (data[i] == 0) { + bitLen -= 8; + } else { + auto skip = 1; + uint8_t mask = 1; + while ((data[i] & mask) == 0) { + skip += 1; + mask <<= 1; + } + bitLen -= skip; + break; + } + } + return bitLen; +} + +struct Reader { + const uint8_t* _Nonnull buffer; + size_t bufferLen; + size_t offset = 0; + + explicit Reader(const uint8_t* _Nonnull buffer, size_t len) noexcept + : buffer(buffer), bufferLen(len) { + } + + void require(size_t bytes) const { + if (offset + bytes > bufferLen) { + throw std::runtime_error("unexpected eof"); + } + } + + void advance(size_t bytes) { + offset += bytes; + } + + const uint8_t* _Nonnull data() const { + return buffer + offset; + } + + size_t readNextUint(uint8_t len) { + const auto* _Nonnull p = data(); + advance(len); + + switch (len) { + case 1: + return static_cast(*p); + case 2: + return static_cast(decode16BE(p)); + case 3: + return static_cast(p[2]) | (static_cast(p[1]) << 8) | (static_cast(p[0]) << 16); + case 4: + return static_cast(decode32BE(p)); + default: + // Unreachable in valid cells + return 0; + } + } +}; + +std::shared_ptr Cell::fromBase64(const std::string& encoded) { + // `Hash::base64` trims \0 bytes from the end of the _decoded_ data, so + // raw transform is used here + using namespace boost::archive::iterators; + using It = transform_width, 8, 6>; + Data boc(It(begin(encoded)), It(end(encoded))); + return Cell::deserialize(boc.data(), boc.size()); +} + +std::shared_ptr Cell::deserialize(const uint8_t* _Nonnull data, size_t len) { + Reader reader(data, len); + + // Parse header + reader.require(6); + // 1. Magic + if (reader.readNextUint(sizeof(BOC_MAGIC)) != BOC_MAGIC) { + throw std::runtime_error("unknown magic"); + } + // 2. Flags + struct Flags { + uint8_t refSize : 3; + uint8_t : 2; // unused + bool hasCacheBits : 1; + bool hasCrc : 1; + bool indexIncluded : 1; + }; + + static_assert(sizeof(Flags) == 1, "flags must be represented as 1 byte"); + const auto flags = reinterpret_cast(reader.data())[0]; + const auto refSize = flags.refSize; + const auto offsetSize = reader.data()[1]; + reader.advance(2); + + // 3. Counters and root index + reader.require(refSize * 3 + offsetSize + refSize); + const auto cellCount = reader.readNextUint(refSize); + const auto rootCount = reader.readNextUint(refSize); + if (rootCount != 1) { + throw std::runtime_error("unsupported root count"); + } + if (rootCount > cellCount) { + throw std::runtime_error("root count is greater than cell count"); + } + const auto absent_count = reader.readNextUint(refSize); + if (absent_count > 0) { + throw std::runtime_error("absent cells are not supported"); + } + + reader.readNextUint(offsetSize); // total cell size + + const auto rootIndex = reader.readNextUint(refSize); + + // 4. Cell offsets (skip if specified) + if (flags.indexIncluded) { + reader.advance(cellCount * offsetSize); + } + + // 5. Deserialize cells + struct IntermediateCell { + uint16_t bitLen; + Data data; + std::vector references; + }; + + std::vector intermediate{}; + intermediate.reserve(cellCount); + + for (size_t i = 0; i < cellCount; ++i) { + struct Descriptor { + uint8_t refCount : 3; + bool exotic : 1; + bool storeHashes : 1; + uint8_t level : 3; + }; + + static_assert(sizeof(Descriptor) == 1, "cell descriptor must be represented as 1 byte"); + + reader.require(2); + const auto d1 = reinterpret_cast(reader.data())[0]; + if (d1.level != 0) { + throw std::runtime_error("non-zero level is not supported"); + } + if (d1.exotic) { + throw std::runtime_error("exotic cells are not supported"); + } + if (d1.refCount == 7 && d1.storeHashes) { + throw std::runtime_error("absent cells are not supported"); + } + if (d1.refCount > 4) { + throw std::runtime_error("invalid ref count"); + } + const auto d2 = reader.data()[1]; + const auto byteLen = (d2 >> 1) + (d2 & 0b1); + reader.advance(2); + + // Skip stored hashes + if (d1.storeHashes) { + reader.advance(sizeof(uint16_t) + Hash::sha256Size); + } + + reader.require(byteLen); + Data cellData(byteLen); + std::memcpy(cellData.data(), reader.data(), byteLen); + reader.advance(byteLen); + + std::vector references{}; + references.reserve(refSize * d1.refCount); + reader.require(refSize * d1.refCount); + for (size_t r = 0; r < d1.refCount; ++r) { + const auto index = reader.readNextUint(refSize); + if (index > cellCount || index <= i) { + throw std::runtime_error("invalid child index"); + } + + references.push_back(index); + } + + const auto bitLen = computeBitLen(cellData, (d2 & 0b1) == 0); + intermediate.emplace_back( + IntermediateCell{ + .bitLen = bitLen, + .data = std::move(cellData), + .references = std::move(references), + }); + } + + std::unordered_map doneCells{}; + + size_t index = cellCount; + for (auto it = intermediate.rbegin(); it != intermediate.rend(); ++it, --index) { + auto& raw = *it; + + Cell::Refs references{}; + for (size_t r = 0; r < raw.references.size(); ++r) { + const auto child = doneCells.find(raw.references[r]); + if (child == doneCells.end()) { + throw std::runtime_error("child cell not found"); + } + references[r] = child->second; + } + + auto cell = std::make_shared(raw.bitLen, std::move(raw.data), raw.references.size(), std::move(references)); + cell->finalize(); + doneCells.emplace(index - 1, cell); + } + + const auto root = doneCells.find(rootIndex); + if (root == doneCells.end()) { + throw std::runtime_error("root cell not found"); + } + return std::move(root->second); +} + +class SerializationContext { +public: + static SerializationContext build(const Cell& cell) { + SerializationContext ctx{}; + fillContext(cell, ctx); + return ctx; + } + + void encode(Data& os) const { + os.reserve(os.size() + HEADER_SIZE + cellsSize); + + const auto cellCount = static_cast(reversedCells.size()); + + // Write header + encode32BE(BOC_MAGIC, os); + os.push_back(REF_SIZE); + os.push_back(OFFSET_SIZE); + encode16BE(static_cast(cellCount), os); + encode16BE(1, os); // root count + encode16BE(0, os); // absent cell count + encode16BE(static_cast(cellsSize), os); + encode16BE(0, os); // root cell index + + // Write cells + size_t i = cellCount - 1; + while (true) { + const auto& cell = *reversedCells[i]; + + // Write cell data + const auto [d1, d2] = cell.getDescriptorBytes(); + os.push_back(d1); + os.push_back(d2); + os.insert(os.end(), cell.data.begin(), cell.data.end()); + + // Write cell references + for (const auto& child : cell.references) { + if (child == nullptr) { + break; + } + + // Map cell hash to index (which must be presented) + const auto it = indices.find(child->hash); + assert(it != indices.end()); + + encode16BE(cellCount - it->second - 1, os); + } + + if (i == 0) { + break; + } else { + --i; + } + } + } + +private: + // uint16_t will be enough for wallet transactions (e.g. 64k is the size of the whole elector) + using ref_t = uint16_t; + using offset_t = uint16_t; + + constexpr static uint8_t REF_SIZE = sizeof(ref_t); + constexpr static uint8_t OFFSET_SIZE = sizeof(offset_t); + constexpr static size_t HEADER_SIZE = + /*magic*/ sizeof(BOC_MAGIC) + + /*ref_size*/ 1 + + /*offset_size*/ 1 + + /*cell_count*/ REF_SIZE + + /*root_count*/ REF_SIZE + + /*absent_count*/ REF_SIZE + + /*data_size*/ OFFSET_SIZE + + /*root_cell_index*/ REF_SIZE; + + size_t cellsSize = 0; + ref_t index = 0; + std::map indices{}; + std::vector reversedCells{}; + + static void fillContext(const Cell& cell, SerializationContext& ctx) { + if (ctx.indices.find(cell.hash) != ctx.indices.end()) { + return; + } + + for (const auto& ref : cell.references) { + if (ref == nullptr) { + break; + } + fillContext(*ref, ctx); + } + + ctx.indices.insert(std::make_pair(cell.hash, ctx.index++)); + ctx.reversedCells.emplace_back(&cell); + ctx.cellsSize += cell.serializedSize(REF_SIZE); + } +}; + +void Cell::serialize(Data& os) const { + assert(finalized); + const auto ctx = SerializationContext::build(*this); + ctx.encode(os); +} + +void Cell::finalize() { + if (finalized) { + return; + } + + if (bitLen > Cell::MAX_BITS || refCount > Cell::MAX_REFS) { + throw std::invalid_argument("invalid cell"); + } + + // Finalize child cells and update current cell depth + // NOTE: Use before context creation to reduce stack size + for (const auto& ref : references) { + if (ref == nullptr) { + break; + } + ref->finalize(); + depth = std::max(depth, static_cast(ref->depth + 1)); + } + + // Compute cell hash + const auto dataSize = std::min(data.size(), static_cast((bitLen + 7) / 8)); + + Data normalized{}; + normalized.reserve(/* descriptor bytes */ 2 + dataSize + refCount * (sizeof(uint16_t) + Hash::sha256Size)); + + // Write descriptor bytes + const auto [d1, d2] = getDescriptorBytes(); + normalized.push_back(d1); + normalized.push_back(d2); + std::copy(data.begin(), std::next(data.begin(), static_cast(dataSize)), std::back_inserter(normalized)); + + // Write all children depths + for (const auto& ref : references) { + if (ref == nullptr) { + break; + } + encode16BE(ref->depth, normalized); + } + + // Write all children hashes + for (const auto& ref : references) { + if (ref == nullptr) { + break; + } + std::copy(ref->hash.begin(), ref->hash.end(), std::back_inserter(normalized)); + } + + // Done + const auto computedHash = Hash::sha256(normalized); + assert(computedHash.size() == Hash::sha256Size); + + std::copy(computedHash.begin(), computedHash.end(), hash.begin()); + finalized = true; +} + +} // namespace TW::Everscale diff --git a/tools/windows-replace/src/Everscale/Cell.h b/tools/windows-replace/src/Everscale/Cell.h new file mode 100644 index 00000000000..8ff98f09c28 --- /dev/null +++ b/tools/windows-replace/src/Everscale/Cell.h @@ -0,0 +1,70 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include +#include + +#include + +#include "Data.h" +#include "../Hash.h" + +//win +#ifdef _MSC_VER +#define _Nonnull +#endif +namespace TW::Everscale { + +class Cell { +public: + constexpr static uint16_t MAX_BITS = 1023; + constexpr static uint8_t MAX_REFS = 4; + + using Ref = std::shared_ptr; + using Refs = std::array; + using CellHash = std::array; + + bool finalized = false; + uint16_t bitLen = 0; + std::vector data{}; + uint8_t refCount = 0; + Refs references{}; + + uint16_t depth = 0; + CellHash hash{}; + + Cell() = default; + + Cell(uint16_t bitLen, std::vector data, uint8_t refCount, Refs references) + : bitLen(bitLen), data(std::move(data)), refCount(refCount), references(std::move(references)) {} + + // Deserialize from Base64 + static std::shared_ptr fromBase64(const std::string& encoded); + + // Deserialize from BOC representation + static std::shared_ptr deserialize(const uint8_t* _Nonnull data, size_t len); + + // Serialize to binary stream + void serialize(Data& os) const; + + // Compute cell depth and hash + void finalize(); + + [[nodiscard]] inline std::pair getDescriptorBytes() const noexcept { + const uint8_t d1 = refCount; + const uint8_t d2 = (static_cast(bitLen >> 2) & 0b11111110) | (bitLen % 8 != 0); + return std::pair{d1, d2}; + } + + [[nodiscard]] inline size_t serializedSize(uint8_t refSize) const noexcept { + return 2 + (bitLen + 7) / 8 + refCount * refSize; + } +}; + +} // namespace TW::Everscale diff --git a/tools/windows-replace/src/Everscale/CellSlice.h b/tools/windows-replace/src/Everscale/CellSlice.h new file mode 100644 index 00000000000..f7446f1482f --- /dev/null +++ b/tools/windows-replace/src/Everscale/CellSlice.h @@ -0,0 +1,36 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include +#include + +#include "Cell.h" +//win +#ifdef _MSC_VER +#define _Nonnull +#endif + +namespace TW::Everscale { + +class CellSlice { +public: + const Cell* _Nonnull cell; + uint16_t dataOffset = 0; + + explicit CellSlice(const Cell* _Nonnull cell) noexcept + : cell(cell) {} + + uint32_t getNextU32(); + Data getNextBytes(uint8_t bytes); + +private: + void require(uint16_t bits) const; +}; + +} // namespace TW::Everscale diff --git a/tools/windows-replace/src/HDWallet.cpp b/tools/windows-replace/src/HDWallet.cpp new file mode 100644 index 00000000000..068de7f20e5 --- /dev/null +++ b/tools/windows-replace/src/HDWallet.cpp @@ -0,0 +1,390 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HDWallet.h" + +#include "Base58.h" +#include "BinaryCoding.h" +#include "Bitcoin/CashAddress.h" +#include "Bitcoin/SegwitAddress.h" +#include "Coin.h" +#include "Mnemonic.h" +#include "memory/memzero_wrapper.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace TW; + +namespace { + +uint32_t fingerprint(HDNode* node, Hash::Hasher hasher); +std::string serialize(const HDNode* node, uint32_t fingerprint, uint32_t version, bool use_public, Hash::Hasher hasher); +bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher, HDNode* node); +HDNode getNode(const HDWallet& wallet, TWCurve curve, const DerivationPath& derivationPath); +HDNode getMasterNode(const HDWallet& wallet, TWCurve curve); + +const char* curveName(TWCurve curve); +} // namespace + +const int MnemonicBufLength = Mnemonic::MaxWords * (BIP39_MAX_WORD_LENGTH + 3) + 20; // some extra slack + +HDWallet::HDWallet(int strength, const std::string& passphrase) + : passphrase(passphrase) { + char buf[MnemonicBufLength]; + //win + if (!random_init()) { + throw std::runtime_error("Failed to initialize random number generator"); + } + const char* mnemonic_chars = mnemonic_generate(strength, buf, MnemonicBufLength); + if (mnemonic_chars == nullptr) { + //win + random_release(); + throw std::invalid_argument("Invalid strength"); + } + mnemonic = mnemonic_chars; + TW::memzero(buf, MnemonicBufLength); + updateSeedAndEntropy(); +} + +HDWallet::HDWallet(const std::string& mnemonic, const std::string& passphrase, const bool check) + : mnemonic(mnemonic), passphrase(passphrase) { + if (mnemonic.length() == 0 || + (check && !Mnemonic::isValid(mnemonic))) { + throw std::invalid_argument("Invalid mnemonic"); + } + //win + if (!random_init()) { + throw std::runtime_error("Failed to initialize random number generator"); + } + updateSeedAndEntropy(check); +} + +HDWallet::HDWallet(const Data& entropy, const std::string& passphrase) + : passphrase(passphrase) { + char buf[MnemonicBufLength]; + const char* mnemonic_chars = mnemonic_from_data(entropy.data(), static_cast(entropy.size()), buf, MnemonicBufLength); + if (mnemonic_chars == nullptr) { + throw std::invalid_argument("Invalid mnemonic data"); + } + mnemonic = mnemonic_chars; + TW::memzero(buf, MnemonicBufLength); + //win + if (!random_init()) { + throw std::runtime_error("Failed to initialize random number generator"); + } + updateSeedAndEntropy(); +} + +HDWallet::~HDWallet() { + random_release();//win + std::fill(seed.begin(), seed.end(), 0); + std::fill(mnemonic.begin(), mnemonic.end(), 0); + std::fill(passphrase.begin(), passphrase.end(), 0); +} + +void HDWallet::updateSeedAndEntropy(bool check) { + assert(!check || Mnemonic::isValid(mnemonic)); // precondition + + // generate seed from mnemonic + mnemonic_to_seed(mnemonic.c_str(), passphrase.c_str(), seed.data(), nullptr); + + // generate entropy bits from mnemonic + Data entropyRaw((Mnemonic::MaxWords * Mnemonic::BitsPerWord) / 8); + // entropy is truncated to fully bytes, 4 bytes for each 3 words (=33 bits) + auto entropyBytes = mnemonic_to_bits(mnemonic.c_str(), entropyRaw.data()) / 33 * 4; + // copy to truncate + entropy = data(entropyRaw.data(), entropyBytes); + assert(!check || entropy.size() > 10); +} + +PrivateKey HDWallet::getMasterKey(TWCurve curve) const { + auto node = getMasterNode(*this, curve); + auto data = Data(node.private_key, node.private_key + PrivateKey::_size); + return PrivateKey(data); +} + +PrivateKey HDWallet::getMasterKeyExtension(TWCurve curve) const { + auto node = getMasterNode(*this, curve); + auto data = Data(node.private_key_extension, node.private_key_extension + PrivateKey::_size); + return PrivateKey(data); +} + +PrivateKey HDWallet::getKey(TWCoinType coin, TWDerivation derivation) const { + const auto path = TW::derivationPath(coin, derivation); + return getKey(coin, path); +} + +DerivationPath HDWallet::cardanoStakingDerivationPath(const DerivationPath& path) { + DerivationPath stakingPath = path; + stakingPath.indices[3].value = 2; + stakingPath.indices[4].value = 0; + return stakingPath; +} + +PrivateKey HDWallet::getKey(TWCoinType coin, const DerivationPath& derivationPath) const { + const auto curve = TWCoinTypeCurve(coin); + return getKeyByCurve(curve, derivationPath); +} + +PrivateKey HDWallet::getKeyByCurve(TWCurve curve, const DerivationPath& derivationPath) const { + const auto privateKeyType = PrivateKey::getType(curve); + auto node = getNode(*this, curve, derivationPath); + switch (privateKeyType) { + case TWPrivateKeyTypeCardano: { + if (derivationPath.indices.size() < 4 || derivationPath.indices[3].value > 1) { + // invalid derivation path + return PrivateKey(Data(PrivateKey::cardanoKeySize)); + } + const DerivationPath stakingPath = cardanoStakingDerivationPath(derivationPath); + + auto pkData = Data(node.private_key, node.private_key + PrivateKey::_size); + auto extData = Data(node.private_key_extension, node.private_key_extension + PrivateKey::_size); + auto chainCode = Data(node.chain_code, node.chain_code + PrivateKey::_size); + + // repeat with staking path + const auto node2 = getNode(*this, curve, stakingPath); + auto pkData2 = Data(node2.private_key, node2.private_key + PrivateKey::_size); + auto extData2 = Data(node2.private_key_extension, node2.private_key_extension + PrivateKey::_size); + auto chainCode2 = Data(node2.chain_code, node2.chain_code + PrivateKey::_size); + + TW::memzero(&node); + return PrivateKey(pkData, extData, chainCode, pkData2, extData2, chainCode2); + } + + case TWPrivateKeyTypeDefault: + default: + // default path + auto data = Data(node.private_key, node.private_key + PrivateKey::_size); + TW::memzero(&node); + return PrivateKey(data); + } +} + +std::string HDWallet::getRootKey(TWCoinType coin, TWHDVersion version) const { + const auto curve = TWCoinTypeCurve(coin); + auto node = getMasterNode(*this, curve); + return serialize(&node, 0, version, false, base58Hasher(coin)); +} + +std::string HDWallet::deriveAddress(TWCoinType coin) const { + return deriveAddress(coin, TWDerivationDefault); +} + +std::string HDWallet::deriveAddress(TWCoinType coin, TWDerivation derivation) const { + const auto derivationPath = TW::derivationPath(coin, derivation); + return TW::deriveAddress(coin, getKey(coin, derivationPath), derivation); +} + +std::string HDWallet::getExtendedPrivateKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const { + if (version == TWHDVersionNone) { + return ""; + } + + const auto curve = TWCoinTypeCurve(coin); + const auto path = TW::derivationPath(coin, derivation); + auto derivationPath = DerivationPath({DerivationPathIndex(purpose, true), DerivationPathIndex(path.coin(), true)}); + auto node = getNode(*this, curve, derivationPath); + auto fingerprintValue = fingerprint(&node, publicKeyHasher(coin)); + hdnode_private_ckd(&node, account + 0x80000000); + return serialize(&node, fingerprintValue, version, false, base58Hasher(coin)); +} + +std::string HDWallet::getExtendedPublicKeyAccount(TWPurpose purpose, TWCoinType coin, TWDerivation derivation, TWHDVersion version, uint32_t account) const { + if (version == TWHDVersionNone) { + return ""; + } + + const auto curve = TWCoinTypeCurve(coin); + const auto path = TW::derivationPath(coin, derivation); + auto derivationPath = DerivationPath({DerivationPathIndex(purpose, true), DerivationPathIndex(path.coin(), true)}); + auto node = getNode(*this, curve, derivationPath); + auto fingerprintValue = fingerprint(&node, publicKeyHasher(coin)); + hdnode_private_ckd(&node, account + 0x80000000); + hdnode_fill_public_key(&node); + return serialize(&node, fingerprintValue, version, true, base58Hasher(coin)); +} + +std::optional HDWallet::getPublicKeyFromExtended(const std::string& extended, TWCoinType coin, const DerivationPath& path) { + const auto curve = TW::curve(coin); + const auto hasher = TW::base58Hasher(coin); + + auto node = HDNode{}; + if (!deserialize(extended, curve, hasher, &node)) { + return {}; + } + if (node.curve->params == nullptr) { + return {}; + } + hdnode_public_ckd(&node, path.change()); + hdnode_public_ckd(&node, path.address()); + hdnode_fill_public_key(&node); + + // These public key type are not applicable. Handled above, as node.curve->params is null + assert(curve != TWCurveED25519 && curve != TWCurveED25519Blake2bNano && curve != TWCurveED25519ExtendedCardano && curve != TWCurveCurve25519); + TWPublicKeyType keyType = TW::publicKeyType(coin); + if (curve == TWCurveSECP256k1) { + auto pubkey = PublicKey(Data(node.public_key, node.public_key + 33), TWPublicKeyTypeSECP256k1); + if (keyType == TWPublicKeyTypeSECP256k1Extended) { + return pubkey.extended(); + } else { + return pubkey; + } + } else if (curve == TWCurveNIST256p1) { + auto pubkey = PublicKey(Data(node.public_key, node.public_key + 33), TWPublicKeyTypeNIST256p1); + if (keyType == TWPublicKeyTypeNIST256p1Extended) { + return pubkey.extended(); + } else { + return pubkey; + } + } + return {}; +} + +std::optional HDWallet::getPrivateKeyFromExtended(const std::string& extended, TWCoinType coin, const DerivationPath& path) { + const auto curve = TW::curve(coin); + const auto hasher = TW::base58Hasher(coin); + + auto node = HDNode{}; + if (!deserialize(extended, curve, hasher, &node)) { + return {}; + } + hdnode_private_ckd(&node, path.change()); + hdnode_private_ckd(&node, path.address()); + + return PrivateKey(Data(node.private_key, node.private_key + 32)); +} + +namespace { + +uint32_t fingerprint(HDNode* node, Hash::Hasher hasher) { + hdnode_fill_public_key(node); + auto digest = Hash::hash(hasher, node->public_key, 33); + return ((uint32_t)digest[0] << 24) + (digest[1] << 16) + (digest[2] << 8) + digest[3]; +} + +std::string serialize(const HDNode* node, uint32_t fingerprint, uint32_t version, bool use_public, Hash::Hasher hasher) { + Data node_data; + node_data.reserve(78); + + encode32BE(version, node_data); + node_data.push_back(static_cast(node->depth)); + encode32BE(fingerprint, node_data); + encode32BE(node->child_num, node_data); + node_data.insert(node_data.end(), node->chain_code, node->chain_code + 32); + if (use_public) { + node_data.insert(node_data.end(), node->public_key, node->public_key + 33); + } else { + node_data.push_back(0); + node_data.insert(node_data.end(), node->private_key, node->private_key + 32); + } + + return Base58::bitcoin.encodeCheck(node_data, hasher); +} + +bool deserialize(const std::string& extended, TWCurve curve, Hash::Hasher hasher, HDNode* node) { + TW::memzero(node); + const char* curveNameStr = curveName(curve); + if (curveNameStr == nullptr || ::strlen(curveNameStr) == 0) { + return false; + } + node->curve = get_curve_by_name(curveNameStr); + assert(node->curve != nullptr); + + const auto node_data = Base58::bitcoin.decodeCheck(extended, hasher); + if (node_data.size() != 78) { + return false; + } + + uint32_t version = decode32BE(node_data.data()); + if (TWHDVersionIsPublic(static_cast(version))) { + std::copy(node_data.begin() + 45, node_data.begin() + 45 + 33, node->public_key); + } else if (TWHDVersionIsPrivate(static_cast(version))) { + if (node_data[45]) { // invalid data + return false; + } + std::copy(node_data.begin() + 46, node_data.begin() + 46 + 32, node->private_key); + } else { + return false; // invalid version + } + node->depth = node_data[4]; + node->child_num = decode32BE(node_data.data() + 9); + std::copy(node_data.begin() + 13, node_data.begin() + 13 + 32, node->chain_code); + return true; +} + +HDNode getNode(const HDWallet& wallet, TWCurve curve, const DerivationPath& derivationPath) { + const auto privateKeyType = PrivateKey::getType(curve); + auto node = getMasterNode(wallet, curve); + for (auto& index : derivationPath.indices) { + switch (privateKeyType) { + case TWPrivateKeyTypeCardano: + hdnode_private_ckd_cardano(&node, index.derivationIndex()); + break; + case TWPrivateKeyTypeDefault: + default: + hdnode_private_ckd(&node, index.derivationIndex()); + break; + } + } + return node; +} + +HDNode getMasterNode(const HDWallet& wallet, TWCurve curve) { + const auto privateKeyType = PrivateKey::getType(curve); + HDNode node; + switch (privateKeyType) { + case TWPrivateKeyTypeCardano: { + // Derives the root Cardano HDNode from a passphrase and the entropy encoded in + // a BIP-0039 mnemonic using the Icarus derivation (V2) scheme + const auto entropy = wallet.getEntropy(); + uint8_t secret[CARDANO_SECRET_LENGTH]; + secret_from_entropy_cardano_icarus((const uint8_t*)"", 0, entropy.data(), int(entropy.size()), secret, nullptr); + hdnode_from_secret_cardano(secret, &node); + TW::memzero(secret, CARDANO_SECRET_LENGTH); + break; + } + case TWPrivateKeyTypeDefault: + default: + hdnode_from_seed(wallet.getSeed().data(), HDWallet::seedSize, curveName(curve), &node); + break; + } + return node; +} + +const char* curveName(TWCurve curve) { + switch (curve) { + case TWCurveSECP256k1: + return SECP256K1_NAME; + case TWCurveED25519: + return ED25519_NAME; + case TWCurveED25519Blake2bNano: + return ED25519_BLAKE2B_NANO_NAME; + case TWCurveED25519ExtendedCardano: + return ED25519_CARDANO_NAME; + case TWCurveNIST256p1: + return NIST256P1_NAME; + case TWCurveCurve25519: + return CURVE25519_NAME; + case TWCurveNone: + default: + return ""; + } +} + +} // namespace diff --git a/tools/windows-replace/src/Keystore/EncryptionParameters.cpp b/tools/windows-replace/src/Keystore/EncryptionParameters.cpp new file mode 100644 index 00000000000..d3e08dd6ebe --- /dev/null +++ b/tools/windows-replace/src/Keystore/EncryptionParameters.cpp @@ -0,0 +1,160 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "EncryptionParameters.h" + +#include "../Hash.h" + +#include +#include +#include +#include + +using namespace TW; + +namespace TW::Keystore { + +template +static Data computeMAC(Iter begin, Iter end, const Data& key) { + auto data = Data(); + data.reserve((end - begin) + key.size()); + data.insert(data.end(), begin, end); + append(data, key); + return Hash::keccak256(data); +} + +// ----------------- +// Encoding/Decoding +// ----------------- + +namespace CodingKeys { +static const auto encrypted = "ciphertext"; +static const auto cipher = "cipher"; +static const auto cipherParams = "cipherparams"; +static const auto kdf = "kdf"; +static const auto kdfParams = "kdfparams"; +static const auto mac = "mac"; +} // namespace CodingKeys + +EncryptionParameters::EncryptionParameters(const nlohmann::json& json) { + cipher = json[CodingKeys::cipher].get(); + cipherParams = AESParameters(json[CodingKeys::cipherParams]); + + auto kdf = json[CodingKeys::kdf].get(); + if (kdf == "scrypt") { + kdfParams = ScryptParameters(json[CodingKeys::kdfParams]); + } else if (kdf == "pbkdf2") { + kdfParams = PBKDF2Parameters(json[CodingKeys::kdfParams]); + } +} + +nlohmann::json EncryptionParameters::json() const { + nlohmann::json j; + j[CodingKeys::cipher] = cipher; + j[CodingKeys::cipherParams] = cipherParams.json(); + + if (auto* scryptParams = std::get_if(&kdfParams); scryptParams) { + j[CodingKeys::kdf] = "scrypt"; + j[CodingKeys::kdfParams] = scryptParams->json(); + } else if (auto* pbkdf2Params = std::get_if(&kdfParams); pbkdf2Params) { + j[CodingKeys::kdf] = "pbkdf2"; + j[CodingKeys::kdfParams] = pbkdf2Params->json(); + } + + return j; +} + +EncryptedPayload::EncryptedPayload(const Data& password, const Data& data, const EncryptionParameters& params) + : params(std::move(params)), _mac() { + auto scryptParams = std::get(this->params.kdfParams); + auto derivedKey = Data(scryptParams.desiredKeyLength); + scrypt(reinterpret_cast(password.data()), password.size(), scryptParams.salt.data(), + scryptParams.salt.size(), scryptParams.n, scryptParams.r, scryptParams.p, derivedKey.data(), + scryptParams.desiredKeyLength); + + aes_encrypt_ctx ctx; + auto result = aes_encrypt_key128(derivedKey.data(), &ctx); + assert(result == EXIT_SUCCESS); + if (result == EXIT_SUCCESS) { + Data iv = this->params.cipherParams.iv; + encrypted = Data(data.size()); + aes_ctr_encrypt(data.data(), encrypted.data(), static_cast(data.size()), iv.data(), aes_ctr_cbuf_inc, &ctx); + + _mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); + } +} + +EncryptedPayload::~EncryptedPayload() { + std::fill(encrypted.begin(), encrypted.end(), 0); + std::fill(_mac.begin(), _mac.end(), 0); +} + +Data EncryptedPayload::decrypt(const Data& password) const { + auto derivedKey = Data(); + auto mac = Data(); + + if (auto* scryptParams = std::get_if(¶ms.kdfParams); scryptParams) { + derivedKey.resize(scryptParams->defaultDesiredKeyLength); + scrypt(password.data(), password.size(), scryptParams->salt.data(), + scryptParams->salt.size(), scryptParams->n, scryptParams->r, scryptParams->p, derivedKey.data(), + scryptParams->defaultDesiredKeyLength); + mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); + } else if (auto* pbkdf2Params = std::get_if(¶ms.kdfParams); pbkdf2Params) { + derivedKey.resize(pbkdf2Params->defaultDesiredKeyLength); + pbkdf2_hmac_sha256(password.data(), static_cast(password.size()), pbkdf2Params->salt.data(), + static_cast(pbkdf2Params->salt.size()), pbkdf2Params->iterations, derivedKey.data(), + pbkdf2Params->defaultDesiredKeyLength); + mac = computeMAC(derivedKey.end() - 16, derivedKey.end(), encrypted); + } else { + throw DecryptionError::unsupportedKDF; + } + + if (mac != _mac) { + throw DecryptionError::invalidPassword; + } + + Data decrypted(encrypted.size()); + Data iv = params.cipherParams.iv; + if (params.cipher == "aes-128-ctr") { + aes_encrypt_ctx ctx; + //win + //auto __attribute__((unused)) result = aes_encrypt_key(derivedKey.data(), 16, &ctx); + [[maybe_unused]] auto result = aes_encrypt_key(derivedKey.data(), 16, &ctx); + assert(result != EXIT_FAILURE); + + aes_ctr_decrypt(encrypted.data(), decrypted.data(), static_cast(encrypted.size()), iv.data(), + aes_ctr_cbuf_inc, &ctx); + } else if (params.cipher == "aes-128-cbc") { + aes_decrypt_ctx ctx; + //win + //auto __attribute__((unused)) result = aes_decrypt_key(derivedKey.data(), 16, &ctx); + [[maybe_unused]] auto result = aes_decrypt_key(derivedKey.data(), 16, &ctx); + assert(result != EXIT_FAILURE); + + for (auto i = 0ul; i < encrypted.size(); i += 16) { + aes_cbc_decrypt(encrypted.data() + i, decrypted.data() + i, 16, iv.data(), &ctx); + } + } else { + throw DecryptionError::unsupportedCipher; + } + + return decrypted; +} + +EncryptedPayload::EncryptedPayload(const nlohmann::json& json) { + params = EncryptionParameters(json); + encrypted = parse_hex(json[CodingKeys::encrypted].get()); + _mac = parse_hex(json[CodingKeys::mac].get()); +} + +nlohmann::json EncryptedPayload::json() const { + nlohmann::json j = params.json(); + j[CodingKeys::encrypted] = hex(encrypted); + j[CodingKeys::mac] = hex(_mac); + return j; +} + +} // namespace TW::Keystore diff --git a/tools/windows-replace/src/NULS/Address.cpp b/tools/windows-replace/src/NULS/Address.cpp new file mode 100644 index 00000000000..494131dcb1e --- /dev/null +++ b/tools/windows-replace/src/NULS/Address.cpp @@ -0,0 +1,85 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +#include "Address.h" + +#include + +#include "../Base58.h" +#include "../BinaryCoding.h" +#include "../HexCoding.h" + +using namespace TW; + +namespace TW::NULS { + +const std::string Address::prefix("NULSd"); +const std::array Address::mainnetId = {0x01, 0x00}; + +bool Address::isValid(const std::string& string) { + if (string.empty()) { + return false; + } + if (string.length() <= prefix.length()) { + return false; + } + + std::string address = string.substr(prefix.length(), string.length() - prefix.length()); + Data decoded = Base58::bitcoin.decode(address); + if (decoded.size() != size) { + return false; + } + + // Check Xor + uint8_t checkSum = 0x00; + for (int i = 0; i < 23; ++i) { + checkSum ^= decoded[i]; + } + + return decoded[23] == checkSum; +} + +Address::Address(const TW::PublicKey& publicKey) { + // Main-Net chainID + bytes[0] = mainnetId[0]; + bytes[1] = mainnetId[1]; + // Address Type + bytes[2] = addressType; + //ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, bytes.begin() + 3); + ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, &bytes[3]);//win + bytes[23] = checksum(bytes); +} + +Address::Address(const std::string& string) { + if (false == isValid(string)) { + throw std::invalid_argument("Invalid address string"); + } + std::string address = string.substr(prefix.length(), string.length() - prefix.length()); + const auto decoded = Base58::bitcoin.decode(address); + std::copy(decoded.begin(), decoded.end(), bytes.begin()); +} + +uint16_t Address::chainID() const { + return decode16LE(bytes.data()); +} + +uint8_t Address::type() const { + return bytes[2]; +} + +std::string Address::string() const { + return prefix + Base58::bitcoin.encode(&bytes[0], &bytes[0] + bytes.size());//win + //return prefix + Base58::bitcoin.encode(bytes.begin(), bytes.end()); +} + +uint8_t Address::checksum(std::array& byteArray) const { + uint8_t checkSum = 0x00; + for (int i = 0; i < 23; ++i) { + checkSum ^= byteArray[i]; + } + return checkSum; +} + +} // namespace TW::NULS diff --git a/tools/windows-replace/src/interface/TWBitcoinScript.cpp b/tools/windows-replace/src/interface/TWBitcoinScript.cpp new file mode 100644 index 00000000000..8b685973143 --- /dev/null +++ b/tools/windows-replace/src/interface/TWBitcoinScript.cpp @@ -0,0 +1,165 @@ +// Copyright © 2017-2021 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include + +#include "../Bitcoin/Script.h" +#include "../Bitcoin/SigHashType.h" + +#include + +struct TWBitcoinScript *_Nonnull TWBitcoinScriptCreate() { + auto* script = new TWBitcoinScript{}; + return script; +} + +struct TWBitcoinScript *TWBitcoinScriptCreateWithData(TWData *data) { + auto* script = new TWBitcoinScript{}; + script->impl.bytes.resize(TWDataSize(data)); + TWDataCopyBytes(data, 0, TWDataSize(data), script->impl.bytes.data()); + return script; +} + +struct TWBitcoinScript *_Nonnull TWBitcoinScriptCreateWithBytes(uint8_t *_Nonnull bytes, size_t size) { + auto* script = new TWBitcoinScript{}; + std::copy(bytes, bytes + size, std::back_inserter(script->impl.bytes)); + return script; +} + +struct TWBitcoinScript *TWBitcoinScriptCreateCopy(const struct TWBitcoinScript *script) { + auto* newScript = new TWBitcoinScript{}; + newScript->impl.bytes = script->impl.bytes; + return newScript; +} + +void TWBitcoinScriptDelete(struct TWBitcoinScript *script) { + delete script; +} + +size_t TWBitcoinScriptSize(const struct TWBitcoinScript *script) { + return script->impl.bytes.size(); +} + +TWData *TWBitcoinScriptData(const struct TWBitcoinScript *script) { + return TWDataCreateWithBytes(&script->impl.bytes[0], script->impl.bytes.size()); +} + +TWData *TWBitcoinScriptScriptHash(const struct TWBitcoinScript *_Nonnull script) { + auto result = script->impl.hash(); + return TWDataCreateWithBytes(result.data(), result.size()); +} + +bool TWBitcoinScriptIsPayToScriptHash(const struct TWBitcoinScript *script) { + return script->impl.isPayToScriptHash(); +} + +bool TWBitcoinScriptIsPayToWitnessScriptHash(const struct TWBitcoinScript *script) { + return script->impl.isPayToWitnessScriptHash(); +} + +bool TWBitcoinScriptIsPayToWitnessPublicKeyHash(const struct TWBitcoinScript *script) { + return script->impl.isPayToWitnessPublicKeyHash(); +} + +bool TWBitcoinScriptIsWitnessProgram(const struct TWBitcoinScript *script) { + return script->impl.isWitnessProgram(); +} + +bool TWBitcoinScriptEqual(const struct TWBitcoinScript *_Nonnull lhs, const struct TWBitcoinScript *_Nonnull rhs) { + return lhs->impl.bytes == rhs->impl.bytes; +} + +TWData *TWBitcoinScriptMatchPayToPubkey(const struct TWBitcoinScript *script) { + std::vector data; + if (script->impl.matchPayToPublicKey(data)) { + return TWDataCreateWithBytes(data.data(), data.size()); + } + return nullptr; +} + +TWData *TWBitcoinScriptMatchPayToPubkeyHash(const struct TWBitcoinScript *script) { + std::vector data; + if (script->impl.matchPayToPublicKeyHash(data)) { + return TWDataCreateWithBytes(data.data(), data.size()); + } + return nullptr; +} + +TWData *_Nullable TWBitcoinScriptMatchPayToScriptHash(const struct TWBitcoinScript *script) { + std::vector data; + if (script->impl.matchPayToScriptHash(data)) { + return TWDataCreateWithBytes(data.data(), data.size()); + } + return nullptr; +} + +TWData *_Nullable TWBitcoinScriptMatchPayToWitnessPublicKeyHash(const struct TWBitcoinScript *script) { + std::vector data; + if (script->impl.matchPayToWitnessPublicKeyHash(data)) { + return TWDataCreateWithBytes(data.data(), data.size()); + } + return nullptr; +} + +TWData *_Nullable TWBitcoinScriptMatchPayToWitnessScriptHash(const struct TWBitcoinScript *script) { + std::vector data; + if (script->impl.matchPayToWitnessScriptHash(data)) { + return TWDataCreateWithBytes(data.data(), data.size()); + } + return nullptr; +} + +TWData *TWBitcoinScriptEncode(const struct TWBitcoinScript *script) { + auto result = std::vector{}; + script->impl.encode(result); + return TWDataCreateWithBytes(result.data(), result.size()); +} + +struct TWBitcoinScript *TWBitcoinScriptBuildPayToPublicKey(TWData *pubkey) { + auto* v = reinterpret_cast*>(pubkey); + auto script = TW::Bitcoin::Script::buildPayToPublicKey(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; +} + +struct TWBitcoinScript *TWBitcoinScriptBuildPayToPublicKeyHash(TWData *hash) { + auto* v = reinterpret_cast*>(hash); + auto script = TW::Bitcoin::Script::buildPayToPublicKeyHash(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; +} + +struct TWBitcoinScript *TWBitcoinScriptBuildPayToScriptHash(TWData *scriptHash) { + auto* v = reinterpret_cast*>(scriptHash); + auto script = TW::Bitcoin::Script::buildPayToScriptHash(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; +} + +struct TWBitcoinScript *TWBitcoinScriptBuildPayToWitnessPubkeyHash(TWData *hash) { + auto* v = reinterpret_cast*>(hash); + auto script = TW::Bitcoin::Script::buildPayToWitnessPublicKeyHash(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; +} + +struct TWBitcoinScript *TWBitcoinScriptBuildPayToWitnessScriptHash(TWData *scriptHash) { + auto* v = reinterpret_cast*>(scriptHash); + auto script = TW::Bitcoin::Script::buildPayToWitnessScriptHash(*v); + return new TWBitcoinScript{ script };//win + //return new TWBitcoinScript{ .impl = script }; +} + +struct TWBitcoinScript *_Nonnull TWBitcoinScriptLockScriptForAddress(TWString *_Nonnull address, enum TWCoinType coin) { + auto* s = reinterpret_cast(address); + auto script = TW::Bitcoin::Script::lockScriptForAddress(*s, coin); + //return new TWBitcoinScript{ .impl = script }; + return new TWBitcoinScript{ script };//win +} + +uint32_t TWBitcoinScriptHashTypeForCoin(enum TWCoinType coinType) { + return TW::Bitcoin::hashTypeForCoin(coinType); +} diff --git a/tools/windows-replace/tests/CMakeLists.txt b/tools/windows-replace/tests/CMakeLists.txt new file mode 100644 index 00000000000..fbeed4ace8a --- /dev/null +++ b/tools/windows-replace/tests/CMakeLists.txt @@ -0,0 +1,69 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + +enable_testing() + +# Prevent overriding the parent project's compiler/linker +# settings on Windows +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + +if(WIN32) + find_package(GTest REQUIRED) +else() + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_SOURCE_DIR}/build/local/src/gtest/googletest-release-1.11.0 + ${CMAKE_CURRENT_BINARY_DIR}/googletest-build + EXCLUDE_FROM_ALL) + set(GTEST_LIBRARIES gtest) + set(GTEST_MAIN_LIBRARIES gtest_main) +endif() + +# Note: Protobuf is defined in included CMake +##find_library(Protobuf REQUIRED PATH ${CMAKE_SOURCE_DIR}/build/local/lib/pkgconfig NO_DEFAULT_PATH) +##include_directories(${Protobuf_INCLUDE_DIRS}) + +# Test executable +file(GLOB_RECURSE test_sources *.cpp **/*.cpp **/*.cc) +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") + list(FILTER test_sources EXCLUDE REGEX ".*CMakeCXXCompilerId\\.cpp$") +endif() + +add_executable(tests ${test_sources}) +target_link_libraries(tests ${GTEST_MAIN_LIBRARIES} TrezorCrypto TrustWalletCore walletconsolelib ${Protobuf_LIBRARIES} Boost::boost) +target_include_directories(tests PRIVATE ${CMAKE_SOURCE_DIR}/src) +target_include_directories(tests PRIVATE ${CMAKE_SOURCE_DIR}/tests/common) + +if(NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) + target_compile_options(tests PRIVATE "-Wall") +endif() + +if (NOT ANDROID AND TW_UNITY_BUILD) + set_target_properties(tests PROPERTIES UNITY_BUILD ON) +endif() + +set_target_properties(tests + PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON +) + +option(CODE_COVERAGE "Enable coverage reporting" OFF) +if(CODE_COVERAGE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + # Add required flags (GCC & LLVM/Clang) + target_compile_options(tests INTERFACE + -O0 # no optimization + -g # generate debug info + --coverage # sets all required flags + ) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13) + target_link_options(tests INTERFACE --coverage) + else() + target_link_libraries(tests INTERFACE --coverage) + endif() +endif() + +add_test(NAME example_test COMMAND tests) diff --git a/tools/windows-replace/tests/chains/Aptos/MoveTypesTests.cpp b/tools/windows-replace/tests/chains/Aptos/MoveTypesTests.cpp new file mode 100644 index 00000000000..45c7ec321e7 --- /dev/null +++ b/tools/windows-replace/tests/chains/Aptos/MoveTypesTests.cpp @@ -0,0 +1,60 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include + +namespace TW::Aptos::tests { + +TEST(AptosMoveTypes, ModuleId) { + ModuleId module(gAddressOne, "coin"); + ASSERT_EQ(module.address(), gAddressOne); + ASSERT_EQ(module.name(), "coin"); + ASSERT_EQ(hex(module.accessVector()), "00000000000000000000000000000000000000000000000000000000000000000104636f696e"); + ASSERT_EQ(module.string(), "0x0000000000000000000000000000000000000000000000000000000000000001::coin"); + ASSERT_EQ(module.shortString(), "0x1::coin"); +} + +/* +TEST(AptosMoveTypes, StructTag) { + auto functorTest = [](T value, const std::string expectedHex) { + TypeTag t{.tags = value}; + StructTag st(gAddressOne, "abc", "abc", std::vector{{t}}); + ASSERT_EQ(st.moduleID().name(), "abc"); + ASSERT_EQ(st.moduleID().address(), gAddressOne); + ASSERT_EQ(hex(st.serialize()), expectedHex); + }; + functorTest(Bool{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630100"); + functorTest(U8{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630101"); + functorTest(U64{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630102"); + functorTest(U128{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630103"); + functorTest(TAddress{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630104"); + functorTest(TSigner{}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630105"); + functorTest(Vector{.tags = std::vector{{TypeTag{.tags = U8{}}}}}, "0100000000000000000000000000000000000000000000000000000000000000010361626303616263010601"); + StructTag stInner(gAddressOne, "foo", "bar", std::vector{{U8{}}}); + functorTest(TStructTag{stInner}, "01000000000000000000000000000000000000000000000000000000000000000103616263036162630107000000000000000000000000000000000000000000000000000000000000000103666f6f036261720101"); +} +*/ +TEST(AptosMoveTypes, TypeTagDisplay) { + auto functorTest = [](const TypeTag &value, const std::string& expected) { + ASSERT_EQ(TypeTagToString(value), expected); + }; + functorTest(TypeTag{.tags = Bool{}}, "bool"); + functorTest(TypeTag{.tags = U8{}}, "u8"); + functorTest(TypeTag{.tags = U64{}}, "u64"); + functorTest(TypeTag{.tags = U128{}}, "u128"); + functorTest(TypeTag{.tags = TAddress{}}, "address"); + functorTest(TypeTag{.tags = TSigner{}}, "signer"); + TypeTag t{.tags = TypeTag::TypeTagVariant(Vector{.tags = {{U8{}}}})}; + functorTest(t, "vector"); + StructTag st(gAddressOne, "foo", "bar", std::vector{{U8{}}}); + TypeTag anotherT{.tags = TypeTag::TypeTagVariant(st)}; + functorTest(anotherT, "0x1::foo::bar"); + functorTest(gTransferTag, "0x1::aptos_coin::AptosCoin"); +} + +} // namespace TW::Aptos::tests diff --git a/tools/windows-replace/tests/chains/Bitcoin/MessageSignerTests.cpp b/tools/windows-replace/tests/chains/Bitcoin/MessageSignerTests.cpp new file mode 100644 index 00000000000..df617721300 --- /dev/null +++ b/tools/windows-replace/tests/chains/Bitcoin/MessageSignerTests.cpp @@ -0,0 +1,167 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Bitcoin/MessageSigner.h" +#include +#include "Bitcoin/Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "Base64.h" +#include "Coin.h" +#include "Data.h" +#include "TestUtilities.h" + +#include +#include + +#include +#include + +namespace TW::Bitcoin::MessageSignerTests { + +const auto gPrivateKey = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5")); + +TEST(BitcoinMessageSigner, VerifyMessage) { + EXPECT_TRUE(MessageSigner::verifyMessage( + "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", + "test signature", + "H+3L5IbSVcejp4S2VwLXCxLEMQAWDvKbE8lQyq0ocdvyM1aoEudkzN/S/qLI3vnNOFY6V13BXWSFrPr3OjGa5Dk=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN", + "This is an example of a signed message.", + "G39Qf0XrZHICWbz3r5gOkcgTRw3vM4leGjiR3refr/K1OezcKmmXaLn4zc8ji2rjbBUIMrIhH/jc5Z2qEEz7qVk=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "1H8X4u6CVZRTLLNbUQTKAnc5vCkqWMpwfF", + "compressed key", + "IKUI9v2xbHogJe8HKXI2M5KEhMKaW6fjNxtyEy27Mf+3/e1ht4jZoc85e4F8stPsxt4Xcg8Yr42S28O6L/Qx9fE=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + "test signature", + "ILH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X", + "another text", + "H7vrF2C+TlFiHyegAw3QLv6SK0myuEEXUOgfx0+Qio1YVDuSa6p/OHpoQVlUt3F8QJdbdZN9M1h/fYEAnEz16V0=" + )); + EXPECT_TRUE(MessageSigner::verifyMessage( + "1E4T9JZ3mq6cdgiRJEWzHqDXb9t322fE6d", + "test signature", + "HLH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo=" + )); +} + +TEST(BitcoinMessageSigner, SignAndVerify) { + const auto pubKey = gPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + EXPECT_EQ(hex(pubKey.bytes), "0399c6f51ad6f98c9c583f8e92bb7758ab2ca9a04110c0a1126ec43e5453d196c1"); + const auto address = Address(pubKey, TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); + EXPECT_EQ(address, "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X"); + + { + const auto msg = "test signature"; + const auto signature = MessageSigner::signMessage(gPrivateKey, address, msg); + EXPECT_EQ(signature, "ILH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo="); + + EXPECT_TRUE(MessageSigner::verifyMessage(address, msg, signature)); + } + { + const auto msg = "another text"; + const auto signature = MessageSigner::signMessage(gPrivateKey, address, msg); + EXPECT_EQ(signature, "H7vrF2C+TlFiHyegAw3QLv6SK0myuEEXUOgfx0+Qio1YVDuSa6p/OHpoQVlUt3F8QJdbdZN9M1h/fYEAnEz16V0="); + + EXPECT_TRUE(MessageSigner::verifyMessage(address, msg, signature)); + } + + // uncompressed + const auto pubKeyUncomp = gPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1Extended); + const auto keyHash = pubKeyUncomp.hash(Data{TW::p2pkhPrefix(TWCoinTypeBitcoin)}, Hash::HasherSha256ripemd); + const auto addressUncomp = Address(keyHash).string(); + EXPECT_EQ(addressUncomp, "1E4T9JZ3mq6cdgiRJEWzHqDXb9t322fE6d"); + { + const auto msg = "test signature"; + const auto signature = MessageSigner::signMessage(gPrivateKey, addressUncomp, msg, false); + EXPECT_EQ(signature, "HLH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo="); + + EXPECT_TRUE(MessageSigner::verifyMessage(addressUncomp, msg, signature)); + } +} + +TEST(BitcoinMessageSigner, SignNegative) { + const auto address = Address(gPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1), TW::p2pkhPrefix(TWCoinTypeBitcoin)).string(); + EXPECT_EQ(address, "19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X"); + const auto msg = "test signature"; + // Use invalid address + EXPECT_EXCEPTION(MessageSigner::signMessage(gPrivateKey, "__THIS_IS_NOT_A_VALID_ADDRESS__", msg), "Address is not valid (legacy) address"); + // Use invalid address, not legacy + EXPECT_EXCEPTION(MessageSigner::signMessage(gPrivateKey, "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8", msg), "Address is not valid (legacy) address"); + // Use valid, but not matching key + EXPECT_EXCEPTION(MessageSigner::signMessage(gPrivateKey, "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", msg), "Address does not match key"); +} + +TEST(BitcoinMessageSigner, VerifyNegative) { + const auto sig = parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae439"); + // Baseline positive + EXPECT_TRUE(MessageSigner::verifyMessage( + "1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr", + "test signature", + sig + )); + + // Provide non-matching address + EXPECT_FALSE(MessageSigner::verifyMessage( + "1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN", + "test signature", + sig + )); + // Signature too short + EXPECT_EXCEPTION(MessageSigner::verifyMessage( + "1HZwkjkeaoZfTSaJxDw6aKkxp45agDiEzN", + "test signature", + parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae4") + ), "signature too short"); + // Invalid address + EXPECT_EXCEPTION(MessageSigner::verifyMessage( + "__THIS_IS_NOT_A_VALID_ADDRESS__", + "test signature", + parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae4") + ), "Input address invalid"); + EXPECT_EXCEPTION(MessageSigner::verifyMessage( + "bc1qpjult34k9spjfym8hss2jrwjgf0xjf40ze0pp8", + "test signature", + parse_hex("1fedcbe486d255c7a3a784b65702d70b12c43100160ef29b13c950caad2871dbf23356a812e764ccdfd2fea2c8def9cd38563a575dc15d6485acfaf73a319ae4") + ), "Input address invalid"); +} + +TEST(BitcoinMessageSigner, MessageToHash) { + EXPECT_EQ(hex(MessageSigner::messageToHash("Hello, world!")), "02d6c0643e40b0db549cbbd7eb47dcab71a59d7017199ebde6b272f28fbbf95f"); + EXPECT_EQ(hex(MessageSigner::messageToHash("test signature")), "8e81cc5bca9862d8b7f22be1f7cb762b49121cf4e1611c27906a041f9a9eb21f"); +} + +TEST(TWBitcoinMessageSigner, VerifyMessage) { + EXPECT_TRUE(TWBitcoinMessageSignerVerifyMessage( + STRING("1B8Qea79tsxmn4dTiKKRVvsJpHwL2fMQnr").get(), + STRING("test signature").get(), + STRING("H+3L5IbSVcejp4S2VwLXCxLEMQAWDvKbE8lQyq0ocdvyM1aoEudkzN/S/qLI3vnNOFY6V13BXWSFrPr3OjGa5Dk=").get() + )); +} + +TEST(TWBitcoinMessageSigner, SignAndVerify) { + const auto privKeyData = "afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"; + const auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA(privKeyData).get())); + const auto address = STRING("19cAJn4Ms8jodBBGtroBNNpCZiHAWGAq7X"); + const auto message = STRING("test signature"); + + const auto signature = WRAPS(TWBitcoinMessageSignerSignMessage(privateKey.get(), address.get(), message.get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(signature.get())), "ILH5K7JQLaRGaKGXXH5mYM6FIIy9IWyY4JUPI+PHYY4WaupxUbg+zy0bhBCrDuehy9x4WidwjkRR1GSLnWvOXBo="); + + EXPECT_TRUE(TWBitcoinMessageSignerVerifyMessage(address.get(), message.get(), signature.get())); +} + +} // namespace TW::Bitcoin diff --git a/tools/windows-replace/tests/chains/Bitcoin/TestUtilities.h b/tools/windows-replace/tests/chains/Bitcoin/TestUtilities.h new file mode 100644 index 00000000000..78c0d89cb39 --- /dev/null +++ b/tools/windows-replace/tests/chains/Bitcoin/TestUtilities.h @@ -0,0 +1,88 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#pragma once + +#include +#include + +#include +#include +#include + +#include + +#define WRAP(type, x) std::shared_ptr(x, type##Delete) +#define WRAPD(x) std::shared_ptr(x, TWDataDelete) +#define WRAPS(x) std::shared_ptr(x, TWStringDelete) +#define STRING(x) std::shared_ptr(TWStringCreateWithUTF8Bytes(x), TWStringDelete) +#define DATA(x) std::shared_ptr(TWDataCreateWithHexString(STRING(x).get()), TWDataDelete) + +inline void assertStringsEqual(const std::shared_ptr& string, const char* expected) { + ASSERT_STREQ(TWStringUTF8Bytes(string.get()), expected); +} + +inline void assertHexEqual(const std::shared_ptr& data, const char* expected) { + auto hex = WRAPS(TWStringCreateWithHexData(data.get())); + assertStringsEqual(hex, expected); +} + + +inline void assertJSONEqual(const nlohmann::json& lhs, const nlohmann::json& rhs) { + ASSERT_EQ(lhs, rhs); +} + +inline void assertJSONEqual(const std::string& lhs, const char* expected) { + auto lhsJson = nlohmann::json::parse(lhs); + auto rhsJson = nlohmann::json::parse(std::string(expected)); + return assertJSONEqual(lhsJson, rhsJson); +} + +inline std::vector* dataFromTWData(TWData* data) { + return const_cast*>(reinterpret_cast*>(data)); +} + +/// Return a writable temp dir which can be used to create files during testing +std::string getTestTempDir(void); + +#define ANY_SIGN(input, coin) \ + {\ + auto inputData = input.SerializeAsString();\ + auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData.data(), inputData.size()));\ + auto outputTWData = WRAPD(TWAnySignerSign(inputTWData.get(), coin));\ + output.ParseFromArray(TWDataBytes(outputTWData.get()), static_cast(TWDataSize(outputTWData.get())));\ + } +#define ANY_PLAN(input, output, coin) \ + {\ + auto inputData = input.SerializeAsString();\ + auto inputTWData = WRAPD(TWDataCreateWithBytes((const uint8_t *)inputData.data(), inputData.size()));\ + auto outputTWData = WRAPD(TWAnySignerPlan(inputTWData.get(), coin));\ + output.ParseFromArray(TWDataBytes(outputTWData.get()), static_cast(TWDataSize(outputTWData.get())));\ + } +#define DUMP_PROTO(input) \ + { \ + std::string json; \ + google::protobuf::util::MessageToJsonString(input, &json); \ + std::cout<<"dump proto: "< + +#include "Cbor.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "uint256.h" +#include "TestUtilities.h" +#include + +#include +#include + +#ifdef _MSC_VER + typedef unsigned int uint; +#endif + +using namespace TW; +using namespace std; + +namespace TW::Cardano::SigningTests { + +const auto privateKeyTest1 = "089b68e458861be0c44bf9f7967f05cc91e51ede86dc679448a3566990b7785bd48c330875b1e0d03caaed0e67cecc42075dce1c7a13b1c49240508848ac82f603391c68824881ae3fc23a56a1a75ada3b96382db502e37564e84a5413cfaf1290dbd508e5ec71afaea98da2df1533c22ef02a26bb87b31907d0b2738fb7785b38d53aa68fc01230784c9209b2b2a2faf28491b3b1f1d221e63e704bbd0403c4154425dfbb01a2c5c042da411703603f89af89e57faae2946e2a5c18b1c5ca0e"; +const auto ownAddress1 = "addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23"; +const auto sundaeTokenPolicy = "9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77"; + +TEST(CardanoSigning, SelectInputs) { + const auto inputs = std::vector({ + TxInput{{parse_hex("0001"), 0}, "ad01", 700, {}}, + TxInput{{parse_hex("0002"), 1}, "ad02", 900, {}}, + TxInput{{parse_hex("0003"), 2}, "ad03", 300, {}}, + TxInput{{parse_hex("0004"), 3}, "ad04", 600, {}}, + }); + + { // 2 + const auto s1 = Signer::selectInputsWithTokens(inputs, 1500, {}); + ASSERT_EQ(s1.size(), 2ul); + EXPECT_EQ(s1[0].amount, 900ul); + EXPECT_EQ(s1[1].amount, 700ul); + } + { // all + const auto s1 = Signer::selectInputsWithTokens(inputs, 10000, {}); + ASSERT_EQ(s1.size(), 4ul); + EXPECT_EQ(s1[0].amount, 900ul); + EXPECT_EQ(s1[1].amount, 700ul); + EXPECT_EQ(s1[2].amount, 600ul); + EXPECT_EQ(s1[3].amount, 300ul); + } + { // 3 + const auto s1 = Signer::selectInputsWithTokens(inputs, 2000, {}); + ASSERT_EQ(s1.size(), 3ul); + } + { // 1 + const auto s1 = Signer::selectInputsWithTokens(inputs, 500, {}); + ASSERT_EQ(s1.size(), 1ul); + } + { // at least 0 is returned + const auto s1 = Signer::selectInputsWithTokens(inputs, 0, {}); + ASSERT_EQ(s1.size(), 1ul); + } +} + +Proto::SigningInput createSampleInput(uint64_t amount, int utxoCount = 10, + const std::string& alternateToAddress = "", bool omitPrivateKey = false) { + const std::string toAddress = (alternateToAddress.length() > 0) ? alternateToAddress : "addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5"; + + Proto::SigningInput input; + if (utxoCount >= 1) { + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(1); + utxo1->set_address(ownAddress1); + utxo1->set_amount(1500000); + } + if (utxoCount >= 2) { + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0"); + utxo2->mutable_out_point()->set_tx_hash(txHash2.data(), txHash2.size()); + utxo2->mutable_out_point()->set_output_index(0); + utxo2->set_address(ownAddress1); + utxo2->set_amount(6500000); + } + + if (!omitPrivateKey) { + const auto privateKeyData = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + } + input.mutable_transfer_message()->set_to_address(toAddress); + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(amount); + input.mutable_transfer_message()->set_use_max_amount(false); + input.set_ttl(53333333); + return input; +} + +TEST(CardanoSigning, Plan) { + auto input = createSampleInput(7000000); + + { + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, 7000000ul); + EXPECT_EQ(plan.fee, 170196ul); + EXPECT_EQ(plan.change, 829804ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + EXPECT_EQ(plan.error, Common::Proto::OK); + } + { // very small target amount + input.mutable_transfer_message()->set_amount(1); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.utxos.size(), 1ul); + EXPECT_EQ(plan.availableAmount, 6500000ul); + EXPECT_EQ(plan.amount, 1ul); + EXPECT_EQ(plan.fee, 168435ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // small target amount + input.mutable_transfer_message()->set_amount(2000000); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.utxos.size(), 1ul); + EXPECT_EQ(plan.availableAmount, 6500000ul); + EXPECT_EQ(plan.amount, 2000000ul); + EXPECT_EQ(plan.fee, 168611ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // small target amount requested, but max amount + input.mutable_transfer_message()->set_amount(2000000); + input.mutable_transfer_message()->set_use_max_amount(true); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, 7832667ul); + EXPECT_EQ(plan.fee, 167333ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } +} + +TEST(CardanoSigning, PlanForceFee) { + auto requestedAmount = 6500000ul; + auto availableAmount = 8000000ul; + auto input = createSampleInput(requestedAmount); + + { + auto fee = 170147ul; + input.mutable_transfer_message()->set_force_fee(fee); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, requestedAmount); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, availableAmount - requestedAmount - fee); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + EXPECT_EQ(plan.error, Common::Proto::OK); + } + { // tiny fee + auto fee = 100ul; + input.mutable_transfer_message()->set_force_fee(fee); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, requestedAmount); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, availableAmount - requestedAmount - fee); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // large fee + auto fee = 1200000ul; + input.mutable_transfer_message()->set_force_fee(fee); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, requestedAmount); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, availableAmount - requestedAmount - fee); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // very large fee, larger than possible, truncated + auto fee = 3000000ul; + input.mutable_transfer_message()->set_force_fee(fee); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, requestedAmount); + EXPECT_EQ(plan.fee, 1500000ul); + EXPECT_EQ(plan.change, 0ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } + { // force fee and max amount: fee is used, amount is max, change 0 + auto fee = 160000ul; + input.mutable_transfer_message()->set_force_fee(fee); + input.mutable_transfer_message()->set_use_max_amount(true); + auto signer = Signer(input); + const auto plan = signer.doPlan(); + EXPECT_EQ(plan.availableAmount, availableAmount); + EXPECT_EQ(plan.amount, 7840000ul); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, 0ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + } +} + +TEST(CardanoSigning, PlanMissingPrivateKey) { + auto input = createSampleInput(7000000, 10, "", true); + + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, 7000000ul); + EXPECT_EQ(plan.fee, 170196ul); + EXPECT_EQ(plan.change, 829804ul); + EXPECT_EQ(plan.amount + plan.change + plan.fee, plan.availableAmount); + EXPECT_EQ(plan.error, Common::Proto::OK); +} + +TEST(CardanoSigning, SignTransfer1) { + const auto input = createSampleInput(7000000); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a006acfc082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a000ca96c021a000298d4031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058407cf591599852b5f5e007fdc241062405c47e519266c0d884b0767c1d4f5eacce00db035998e53ed10ca4ba5ce4aac8693798089717ce6cf4415f345cc764200ef6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "9b5b15e133cd73ccaa85307d2986aebc846505118a2eb4e6111e6b4b67d1f389"); + + { + const auto decode = Cbor::Decode(encoded); + ASSERT_TRUE(decode.isValid()); + EXPECT_EQ(decode.dumpToString(), "[{0: [[h\"554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0\", 0], [h\"f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767\", 1]], 1: [[h\"01558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd5\", 7000000], [h\"01df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b\", 829804]], 2: 170196, 3: 53333333}, {0: [[h\"6d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df290\", h\"7cf591599852b5f5e007fdc241062405c47e519266c0d884b0767c1d4f5eacce00db035998e53ed10ca4ba5ce4aac8693798089717ce6cf4415f345cc764200e\"]]}, null]"); + EXPECT_EQ(decode.getArrayElements().size(), 3ul); + } +} + +TEST(CardanoSigning, PlanAndSignTransfer1) { + uint amount = 6000000; + auto input = createSampleInput(amount); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, amount); + EXPECT_EQ(plan.fee, 170196ul); + EXPECT_EQ(plan.change, 8000000 - amount - 170196); + ASSERT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.utxos[0].amount, 6500000ul); + EXPECT_EQ(plan.utxos[1].amount, 1500000ul); + + // perform sign with default plan + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a005b8d8082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a001bebac021a000298d4031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058404abc749ffaffcf2f87970e4f1983c5e44b352ee1515b60017fc65e581d42b3a6ed146d5eb35d04a770460b0541a25afd5aedfd027fdaded82686f43454196a0cf6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "3852f809245d7000ad0c5ccb1357e5d333b0dd25158924581e4c7049ec68c564"); + } + + // set different plan, with one input only + input.mutable_plan()->set_amount(amount); + input.mutable_plan()->set_available_amount(6500000); + input.mutable_plan()->set_fee(165489); + input.mutable_plan()->set_change(17191988); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40081825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a005b8d8082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a01065434021a00028671031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058408311a058035d75545a47b844fea401aa9c23e99fe7bc8136b554396eef135d4cd93062c5df38e613185c21bb1c98b881d1e0fd1024d3539b163c8e14d1a6e40df6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "e319c0bfc99cdb79d64f00b7e8fb8bfbf29fa70554c84f101e92b7dfed172448"); +} + +TEST(CardanoSigning, PlanAndSignMaxAmount) { + auto input = createSampleInput(7000000); + input.mutable_transfer_message()->set_use_max_amount(true); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 8000000ul); + EXPECT_EQ(plan.amount, 8000000 - 167333ul); + EXPECT_EQ(plan.fee, 167333ul); + EXPECT_EQ(plan.change, 0ul); + ASSERT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.utxos[0].amount, 1500000ul); + EXPECT_EQ(plan.utxos[1].amount, 6500000ul); + } + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000018182583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a0077845b021a00028da5031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058403e64473e08adc863953c0e9f820b658dda0b8a423d6172fdccff73fcd5559956c9df8ed93ff67405331d368a0c11fd18c69781046384946582e1555e9e8ec70bf6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "ca0f1e12f20c95011da7d686d206a1eb98df94accd74c4df4ef403c5ce836057"); +} + +TEST(CardanoSigning, SignNegative) { + { // plan with error + auto input = createSampleInput(7000000); + const auto error = Common::Proto::Error_invalid_memo; + input.mutable_plan()->set_error(error); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), error); + } + { // zero requested amount + auto input = createSampleInput(0); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), Common::Proto::Error_zero_amount_requested); + } + { // no utxo + auto input = createSampleInput(7000000, 0); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), Common::Proto::Error_missing_input_utxos); + } + { // low balance + auto input = createSampleInput(7000000000); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), Common::Proto::Error_low_balance); + } + { // missing private key + auto input = createSampleInput(7000000, 10, "", true); + auto signer = Signer(input); + const auto output = signer.sign(); + EXPECT_EQ(output.error(), Common::Proto::Error_missing_private_key); + } +} + +TEST(CardanoSigning, SignTransfer_0db1ea) { + const auto amount = 1100000ul; + + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("81b935447bb994567f041d181b628a0afbcd747d0199c9ff4cd895686bbee8c6"); + utxo1->mutable_out_point()->set_tx_hash(std::string(txHash1.begin(), txHash1.end())); + utxo1->mutable_out_point()->set_output_index(0); + utxo1->set_address(ownAddress1); + utxo1->set_amount(1000000); + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("3a9068a273cc2af59b45593b78973841d972d01802abe992c55dbeecdffc561b"); + utxo2->mutable_out_point()->set_tx_hash(std::string(txHash2.begin(), txHash2.end())); + utxo2->mutable_out_point()->set_output_index(0); + utxo2->set_address(ownAddress1); + utxo2->set_amount(1800000); + + const auto privateKeyData1 = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData1.data(), privateKeyData1.size()); + input.mutable_transfer_message()->set_to_address("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(amount); + auto fee = 170147ul; + input.mutable_transfer_message()->set_use_max_amount(false); + input.mutable_transfer_message()->set_force_fee(fee); // use force fee feature here + input.set_ttl(54675589); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 2800000ul); + EXPECT_EQ(plan.amount, amount); + EXPECT_EQ(plan.fee, fee); + EXPECT_EQ(plan.change, 2800000ul - amount - fee); + EXPECT_EQ(plan.utxos.size(), 2ul); + } + + // set plan with specific fee, to match the real transaction + input.mutable_plan()->set_amount(amount); + input.mutable_plan()->set_available_amount(2800000); + input.mutable_plan()->set_fee(fee); + input.mutable_plan()->set_change(2800000 - amount - fee); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + *(input.mutable_plan()->add_utxos()) = input.utxos(1); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + // https://cardanoscan.io/transaction/0db1ea8c5c5828bbd027fcef3da02a63b86899db670ad7bb0630cefbe35944fa + // curl -d '{"txHash":"0db1ea..44fa","txBody":"83a400..06f6"}' -H "Content-Type: application/json" https:///api/txs/submit + EXPECT_EQ(output.error(), Common::Proto::OK); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a4008282582081b935447bb994567f041d181b628a0afbcd747d0199c9ff4cd895686bbee8c6008258203a9068a273cc2af59b45593b78973841d972d01802abe992c55dbeecdffc561b000182825839018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a34681a0010c8e082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a001757fd021a000298a3031a03424885a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058406300b52aaff1e26067a3e0a48ae26f4f068765f46f934fabeab872c1d25535fc94893ec72feacd787f0174fbabd8933727d9a2b319b406e7a855843b0c051806f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "0db1ea8c5c5828bbd027fcef3da02a63b86899db670ad7bb0630cefbe35944fa"); +} + +TEST(CardanoSigning, SignTransferFromLegacy) { + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(1); + utxo1->set_address("Ae2tdPwUPEZMRgecV9jV2e9RdbrmnWu7YgRie4de16xLdkWhy6q7ypmRhgn"); + utxo1->set_amount(1500000); + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af0"); + utxo2->mutable_out_point()->set_tx_hash(txHash2.data(), txHash2.size()); + utxo2->mutable_out_point()->set_output_index(0); + utxo2->set_address("Ae2tdPwUPEZMRgecV9jV2e9RdbrmnWu7YgRie4de16xLdkWhy6q7ypmRhgn"); + utxo2->set_amount(6500000); + + const auto privateKeyData = parse_hex("c031e942f6bf2b2864700e7da20964ee6bb6d716345ce2e24d8c00e6500b574411111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); + { + const auto privKey = PrivateKey(privateKeyData); + const auto pubKey = privKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + const auto addr = AddressV2(pubKey); + EXPECT_EQ(addr.string(), "Ae2tdPwUPEZMRgecV9jV2e9RdbrmnWu7YgRie4de16xLdkWhy6q7ypmRhgn"); + } + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + input.mutable_transfer_message()->set_to_address("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5"); + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(7000000); + input.mutable_transfer_message()->set_use_max_amount(false); + input.set_ttl(53333333); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_address); + EXPECT_EQ(hex(output.encoded()), ""); +} + +TEST(CardanoSigning, SignTransferToLegacy) { + const auto toAddressLegacy = "DdzFFzCqrhssmYoG5Eca1bKZFdGS8d6iag1mU4wbLeYcSPVvBNF2wRG8yhjzQqErbg63N6KJA4DHqha113tjKDpGEwS5x1dT2KfLSbSJ"; + EXPECT_FALSE(AddressV3::isValid(toAddressLegacy)); // not V3 + EXPECT_TRUE(AddressV3::isValidLegacy(toAddressLegacy)); + + const auto input = createSampleInput(7000000, 10, toAddressLegacy); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + EXPECT_EQ(hex(output.encoded()), "83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282584c82d818584283581c6aebd89cf88271c3ee76339930d8956b03f018b2f4871522f88eb8f9a101581e581c692a37dae3bc63dfc3e1463f12011f26655ab1d1e0f4ed4b8fc63708001ad8a9555b1a006acfc082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a000ca627021a00029c19031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840db9becdc733f4c08c0e7abc29b5cc6469f9339d32f565df8bf77455439ae1f949facc9b831754e74d3fbb42e99647eedd6c28de1461d18c315485f5d24b5b90af6"); + EXPECT_EQ(hex(data(output.tx_id())), "f9b713e9987ec1377ac223f50d63c7a5e155915302de43f40d7b2627accabf69"); +} + +TEST(CardanoSigning, SignTransferToInvalid) { + const auto input = createSampleInput(7000000, 10, "__INVALID_ADDRESS__"); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_address); + EXPECT_EQ(hex(output.encoded()), ""); +} + +TEST(CardanoSigning, SignTransferToken) { + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(1); + utxo1->set_address(ownAddress1); + utxo1->set_amount(8051373); + // some token, to be preserved + auto* token3 = utxo1->add_token_amount(); + token3->set_policy_id(sundaeTokenPolicy); + token3->set_asset_name("CUBY"); + const auto tokenAmount3 = store(uint256_t(3000000)); + token3->set_amount(tokenAmount3.data(), tokenAmount3.size()); + + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e767"); + utxo2->mutable_out_point()->set_tx_hash(txHash2.data(), txHash2.size()); + utxo2->mutable_out_point()->set_output_index(2); + utxo2->set_address(ownAddress1); + utxo2->set_amount(2000000); + // some SUNDAE token, to be transferred + auto* token1 = utxo2->add_token_amount(); + token1->set_policy_id(sundaeTokenPolicy); + token1->set_asset_name("SUNDAE"); + const auto tokenAmount1 = store(uint256_t(80996569)); + token1->set_amount(tokenAmount1.data(), tokenAmount1.size()); + // some other token, to be preserved + auto* token2 = utxo2->add_token_amount(); + token2->set_policy_id(sundaeTokenPolicy); + token2->set_asset_name("CUBY"); + const auto tokenAmount2 = store(uint256_t(2000000)); + token2->set_amount(tokenAmount2.data(), tokenAmount2.size()); + + const auto privateKeyData = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + input.mutable_transfer_message()->set_to_address("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5"); + input.mutable_transfer_message()->set_change_address("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); + input.mutable_transfer_message()->set_amount(1500000); + auto* toToken = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + toToken->set_policy_id(sundaeTokenPolicy); + toToken->set_asset_name("SUNDAE"); + const auto toTokenAmount = store(uint256_t(20000000)); + toToken->set_amount(toTokenAmount.data(), toTokenAmount.size()); + input.mutable_transfer_message()->set_use_max_amount(false); + input.set_ttl(53333333); + + { // check min ADA amount, set it + const auto bundleProtoData = data(input.transfer_message().token_amount().SerializeAsString()); + const auto minAdaAmount = TWCardanoMinAdaAmount(&bundleProtoData); + EXPECT_EQ(minAdaAmount, 1444443ul); + input.mutable_transfer_message()->set_amount(minAdaAmount); + } + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 10051373ul); + EXPECT_EQ(plan.amount, 1444443ul); + EXPECT_EQ(plan.fee, 174601ul); + EXPECT_EQ(plan.change, 8432329ul); + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableTokens.size(), 2ul); + EXPECT_EQ(plan.availableTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_CUBY"), 5000000); + EXPECT_EQ(plan.availableTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 80996569); + EXPECT_EQ(plan.outputTokens.size(), 1ul); + EXPECT_EQ(plan.outputTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_CUBY"), 0); + EXPECT_EQ(plan.outputTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 20000000); + EXPECT_EQ(plan.changeTokens.size(), 2ul); + EXPECT_EQ(plan.changeTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_CUBY"), 5000000); + EXPECT_EQ(plan.changeTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 60996569); + } + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::OK); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76702018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd5821a00160a5ba1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a01312d00825839018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468821a0080aac9a1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a244435542591a004c4b404653554e4441451a03a2bbd9021a0002aa09031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840d90dcfbd190cbe59c42094e59eeb49b3de9d80a85b786cc311f932c5c9302d1c8c6c577b22aa70ff7955c139c700ea918f8cb425c3ba43a27980e1d238e4e908f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "201c537693b005b64a0f0528e366ec67a84be0119ed4363b547f141f2a7770c2"); + + { + // also test proto toProto / fromProto + const Proto::TransactionPlan planProto = Signer::plan(input); + const auto plan2 = TransactionPlan::fromProto(planProto); + EXPECT_EQ(plan2.amount, 1444443ul); + EXPECT_EQ(plan2.change, 8432329ul); + } +} + +TEST(CardanoSigning, SignTransferToken_1dd248) { + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("f2d2b11c8c07c5c646f5b5af20fddf2f0a174743c6a1b13cca27e28a6ca34710"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(0); + utxo1->set_address(ownAddress1); + utxo1->set_amount(1500000); + // some token + auto* token3 = utxo1->add_token_amount(); + token3->set_policy_id(sundaeTokenPolicy); + token3->set_asset_name("SUNDAE"); + const auto tokenAmount3 = store(uint256_t(20000000)); + token3->set_amount(tokenAmount3.data(), tokenAmount3.size()); + + auto* utxo2 = input.add_utxos(); + const auto txHash2 = parse_hex("6975fcf7bbca745c85f50777f956219868fd9cad14ba496fed1371252e8df60f"); + utxo2->mutable_out_point()->set_tx_hash(txHash2.data(), txHash2.size()); + utxo2->mutable_out_point()->set_output_index(0); + utxo2->set_address(ownAddress1); + utxo2->set_amount(10258890); + + const auto privateKeyData = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + input.mutable_transfer_message()->set_to_address("addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); // Test + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(1600000); + auto* toToken = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + toToken->set_policy_id(sundaeTokenPolicy); + toToken->set_asset_name("SUNDAE"); + const auto toTokenAmount = store(uint256_t(11000000)); + toToken->set_amount(toTokenAmount.data(), toTokenAmount.size()); + input.mutable_transfer_message()->set_use_max_amount(false); + input.set_ttl(61232158); + + { // check min ADA amount + const auto bundleProtoData = data(input.transfer_message().token_amount().SerializeAsString()); + EXPECT_EQ(TWCardanoMinAdaAmount(&bundleProtoData), 1444443ul); + EXPECT_GT(input.transfer_message().amount(), TWCardanoMinAdaAmount(&bundleProtoData)); + } + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 11758890ul); + EXPECT_EQ(plan.amount, 11758890 - 9984729 - 174161ul); + EXPECT_EQ(plan.fee, 174161ul); + EXPECT_EQ(plan.change, 9984729ul); + EXPECT_EQ(plan.utxos.size(), 2ul); + EXPECT_EQ(plan.availableTokens.size(), 1ul); + EXPECT_EQ(plan.availableTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 20000000); + EXPECT_EQ(plan.outputTokens.size(), 1ul); + EXPECT_EQ(plan.outputTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 11000000); + EXPECT_EQ(plan.changeTokens.size(), 1ul); + EXPECT_EQ(plan.changeTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 9000000); + } + + // set plan with specific fee, to match the real transaction + input.mutable_plan()->set_available_amount(11758890); + input.mutable_plan()->set_amount(1600000); + input.mutable_plan()->set_fee(174102); + input.mutable_plan()->set_change(9984788); + *(input.mutable_plan()->add_available_tokens()) = input.utxos(0).token_amount(0); + *(input.mutable_plan()->add_output_tokens()) = input.utxos(0).token_amount(0); + input.mutable_plan()->mutable_output_tokens(0)->set_amount(toTokenAmount.data(), toTokenAmount.size()); + *(input.mutable_plan()->add_change_tokens()) = input.utxos(0).token_amount(0); + const auto changeTokenAmount = store(uint256_t(9000000)); + input.mutable_plan()->mutable_change_tokens(0)->set_amount(changeTokenAmount.data(), changeTokenAmount.size()); + *(input.mutable_plan()->add_utxos()) = input.utxos(1); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + // https://cardanoscan.io/transaction/1dd24872d93d3b5091b98e19b9f920cd0c4369e4c5ca178e898152c52f00c162 + // curl -d '{"txHash":"1dd248..c162","txBody":"83a400..08f6"}' -H "Content-Type: application/json" https:///api/txs/submit + EXPECT_EQ(output.error(), Common::Proto::OK); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a400828258206975fcf7bbca745c85f50777f956219868fd9cad14ba496fed1371252e8df60f00825820f2d2b11c8c07c5c646f5b5af20fddf2f0a174743c6a1b13cca27e28a6ca34710000182825839018d98bea0414243dc84070f96265577e7e6cf702d62e871016885034ecc64bf258b8e330cf0cdd9fdb03e10b4e4ac08f5da1fdec6222a3468821a00186a00a1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a00a7d8c082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b821a00985b14a1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a00895440021a0002a816031a03a6541ea100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840c8cdee32bfd584f55cf334b4ec6f734635144736d48f882e647a7a6283f230bc5a67d4dd66a9e523e0c29c812ed1e3589febbcf96547a1fc6d061a7ccfb81308f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "1dd24872d93d3b5091b98e19b9f920cd0c4369e4c5ca178e898152c52f00c162"); +} + +TEST(CardanoSigning, SignTransferTokenMaxAmount_620b71) { + Proto::SigningInput input; + auto* utxo1 = input.add_utxos(); + const auto txHash1 = parse_hex("46964521ad00d9b3f3d41f77c07e1b3093848048dbdf2d95cf900e15cdac0d7f"); + utxo1->mutable_out_point()->set_tx_hash(txHash1.data(), txHash1.size()); + utxo1->mutable_out_point()->set_output_index(0); + utxo1->set_address(ownAddress1); + utxo1->set_amount(2170871); + // some token + auto* token1 = utxo1->add_token_amount(); + token1->set_policy_id(sundaeTokenPolicy); + token1->set_asset_name("SUNDAE"); + const auto tokenAmount1 = store(uint256_t(20000000)); + token1->set_amount(tokenAmount1.data(), tokenAmount1.size()); + + const auto privateKeyData = parse_hex(privateKeyTest1); + input.add_private_key(privateKeyData.data(), privateKeyData.size()); + input.mutable_transfer_message()->set_to_address("addr1q92cmkgzv9h4e5q7mnrzsuxtgayvg4qr7y3gyx97ukmz3dfx7r9fu73vqn25377ke6r0xk97zw07dqr9y5myxlgadl2s0dgke5"); + input.mutable_transfer_message()->set_change_address(ownAddress1); + input.mutable_transfer_message()->set_amount(666); // doesn't matter, max is used + auto* toToken = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + toToken->set_policy_id(sundaeTokenPolicy); + toToken->set_asset_name("SUNDAE"); + const auto toTokenAmount = store(uint256_t(666)); // doesn't matter, max is used + input.mutable_transfer_message()->set_use_max_amount(true); + input.set_ttl(61085916); + + { + // run plan and check result + auto signer = Signer(input); + const auto plan = signer.doPlan(); + + EXPECT_EQ(plan.availableAmount, 2170871ul); + EXPECT_EQ(plan.amount, 2170871 - 167730ul); + EXPECT_EQ(plan.fee, 167730ul); + EXPECT_EQ(plan.change, 0ul); + EXPECT_EQ(plan.utxos.size(), 1ul); + EXPECT_EQ(plan.availableTokens.size(), 1ul); + EXPECT_EQ(plan.availableTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 20000000); + EXPECT_EQ(plan.outputTokens.size(), 1ul); + EXPECT_EQ(plan.outputTokens.getAmount("9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77_SUNDAE"), 20000000); + EXPECT_EQ(plan.changeTokens.size(), 0ul); + } + + // set plan with specific fee, to match the real transaction + input.mutable_plan()->set_available_amount(2170871); + input.mutable_plan()->set_amount(1998526); + input.mutable_plan()->set_fee(172345); + input.mutable_plan()->set_change(0); + *(input.mutable_plan()->add_available_tokens()) = input.utxos(0).token_amount(0); + *(input.mutable_plan()->add_output_tokens()) = input.utxos(0).token_amount(0); + *(input.mutable_plan()->add_utxos()) = input.utxos(0); + input.mutable_plan()->set_error(Common::Proto::OK); + + auto signer = Signer(input); + const auto output = signer.sign(); + + // https://cardanoscan.io/transaction/620b719338efb419b0e1417bfbe01fc94a62d5669a4b8cbbf4e32ecc1ca3b872 + // curl -d '{"txHash":"620b71..b872","txBody":"83a400..08f6"}' -H "Content-Type: application/json" https:///api/txs/submit + EXPECT_EQ(output.error(), Common::Proto::OK); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a4008182582046964521ad00d9b3f3d41f77c07e1b3093848048dbdf2d95cf900e15cdac0d7f00018182583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd5821a001e7ebea1581c9a9693a9a37912a5097918f97918d15240c92ab729a0b7c4aa144d77a14653554e4441451a01312d00021a0002a139031a03a418dca100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df2905840e1d1565cd747b20b0f10a92f068f3d5faebdee92b4b4a4b96ce14736d975e17d1446f7f51e64997a0bb38e0151dc738468161d574d6cfcd8040e4455ff46bc08f6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "620b719338efb419b0e1417bfbe01fc94a62d5669a4b8cbbf4e32ecc1ca3b872"); +} + +TEST(CardanoSigning, SignTransferTwoTokens) { + auto input = createSampleInput(7000000); + input.mutable_transfer_message()->set_amount(1500000); + auto* token1 = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + token1->set_policy_id(sundaeTokenPolicy); + token1->set_asset_name("SUNDAE"); + const auto tokenAmount1 = store(uint256_t(40000000)); + token1->set_amount(tokenAmount1.data(), tokenAmount1.size()); + auto* token2 = input.mutable_transfer_message()->mutable_token_amount()->add_token(); + token2->set_policy_id(sundaeTokenPolicy); + token2->set_asset_name("CUBY"); + const auto tokenAmount2 = store(uint256_t(2000000)); + token2->set_amount(tokenAmount2.data(), tokenAmount2.size()); + + auto signer = Signer(input); + const auto output = signer.sign(); + + EXPECT_EQ(output.error(), Common::Proto::Error_invalid_requested_token_amount); + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(output.encoded()), ""); +} + +TEST(CardanoSigning, SignMessageWithKey) { + // test case from cardano-crypto.js + + const auto privateKey = PrivateKey(parse_hex( + "d809b1b4b4c74734037f76aace501730a3fe2fca30b5102df99ad3f7c0103e48" + "d54cde47e9041b31f3e6873d700d83f7a937bea746dadfa2c5b0a6a92502356c" + "69272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000" + "1111111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111" + "1111111111111111111111111111111111111111111111111111111111111111")); + + const auto publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519Cardano); + EXPECT_EQ(hex(publicKey.bytes), + "e6f04522f875c1563682ca876ddb04c2e2e3ae718e3ff9f11c03dd9f9dccf698" + "69272d81c376382b8a87c21370a7ae9618df8da708d1a9490939ec54ebe43000" + "857eed804ff087b97f87848f6493e87257a8c5203cb9f422f6e7a7d8a4d299f3" + "1111111111111111111111111111111111111111111111111111111111111111"); + + const auto sampleMessageStr = "Hello world"; + const auto sampleMessage = data(sampleMessageStr); + + const auto signature = privateKey.sign(sampleMessage, TWCurveED25519ExtendedCardano); + + const auto sampleRightSignature = "1096ddcfb2ad21a4c0d861ef3fabe18841e8de88105b0d8e36430d7992c588634ead4100c32b2800b31b65e014d54a8238bdda63118d829bf0bcf1b631e86f0e"; + EXPECT_EQ(hex(signature), sampleRightSignature); +} + +TEST(CardanoSigning, AnySignTransfer1) { + const auto input = createSampleInput(7000000); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeCardano); + + EXPECT_EQ(output.error(), Common::Proto::OK); + + const auto encoded = data(output.encoded()); + EXPECT_EQ(hex(encoded), "83a40082825820554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af000825820f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76701018282583901558dd902616f5cd01edcc62870cb4748c45403f1228218bee5b628b526f0ca9e7a2c04d548fbd6ce86f358be139fe680652536437d1d6fd51a006acfc082583901df58ee97ce7a46cd8bdeec4e5f3a03297eb197825ed5681191110804df22424b6880b39e4bac8c58de9fe6d23d79aaf44756389d827aa09b1a000ca96c021a000298d4031a032dcd55a100818258206d8a0b425bd2ec9692af39b1c0cf0e51caa07a603550e22f54091e872c7df29058407cf591599852b5f5e007fdc241062405c47e519266c0d884b0767c1d4f5eacce00db035998e53ed10ca4ba5ce4aac8693798089717ce6cf4415f345cc764200ef6"); + const auto txid = data(output.tx_id()); + EXPECT_EQ(hex(txid), "9b5b15e133cd73ccaa85307d2986aebc846505118a2eb4e6111e6b4b67d1f389"); +} + +TEST(CardanoSigning, AnyPlan1) { + const auto input = createSampleInput(7000000); + + Proto::TransactionPlan plan; + ANY_PLAN(input, plan, TWCoinTypeCardano); + + EXPECT_EQ(plan.error(), Common::Proto::OK); + EXPECT_EQ(plan.amount(), 7000000ul); + EXPECT_EQ(plan.available_amount(), 8000000ul); + EXPECT_EQ(plan.fee(), 170196ul); + EXPECT_EQ(plan.change(), 829804ul); + ASSERT_EQ(plan.utxos_size(), 2); + EXPECT_EQ(plan.utxos(0).amount(), 6500000ul); + EXPECT_EQ(plan.utxos(1).amount(), 1500000ul); + + EXPECT_EQ(hex(plan.SerializeAsString()), "0880a4e80310c09fab0318d4b10a20ecd2324292010a220a20554f2fd942a23d06835d26bbd78f0106fa94c8a551114a0bef81927f66467af01267616464723171383034336d356865656179646e76746d6d6b7975686536717635686176766873663064323671336a7967737370786c796670796b3679716b77307968747976747230666c656b6a3834753634617a38326375666d716e36357a6473796c7a6b323318a0dd8c034293010a240a20f074134aabbfb13b8aec7cf5465b1e5a862bde5cb88532cc7e64619179b3e76710011267616464723171383034336d356865656179646e76746d6d6b7975686536717635686176766873663064323671336a7967737370786c796670796b3679716b77307968747976747230666c656b6a3834753634617a38326375666d716e36357a6473796c7a6b323318e0c65b"); + + { + // also test fromProto + const auto plan2 = TransactionPlan::fromProto(plan); + EXPECT_EQ(plan2.amount, plan.amount()); + EXPECT_EQ(plan2.change, plan.change()); + } +} + +} // namespace TW::Cardano::tests \ No newline at end of file diff --git a/tools/windows-replace/tests/chains/Cardano/TWCardanoAddressTests.cpp b/tools/windows-replace/tests/chains/Cardano/TWCardanoAddressTests.cpp new file mode 100644 index 00000000000..f610ffd1bba --- /dev/null +++ b/tools/windows-replace/tests/chains/Cardano/TWCardanoAddressTests.cpp @@ -0,0 +1,74 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include +#include +#include +#include +#include +#include +#include +#include +#include "TestUtilities.h" +#include "PrivateKey.h" + +#include + +TEST(TWCardano, AddressFromPublicKey) { + auto privateKey = WRAP(TWPrivateKey, TWPrivateKeyCreateWithData(DATA( + "b0884d248cb301edd1b34cf626ba6d880bb3ae8fd91b4696446999dc4f0b5744309941d56938e943980d11643c535e046653ca6f498c014b88f2ad9fd6e71effbf36a8fa9f5e11eb7a852c41e185e3969d518e66e6893c81d3fc7227009952d4" + "639aadd8b6499ae39b78018b79255fbd8f585cbda9cbb9e907a72af86afb7a05d41a57c2dec9a6a19d6bf3b1fa784f334f3a0048d25ccb7b78a7b44066f9ba7bed7f28be986cbe06819165f2ee41b403678a098961013cf4a2f3e9ea61fb6c1a" + ).get())); + ASSERT_NE(nullptr, privateKey.get()); + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519Cardano(privateKey.get())); + ASSERT_NE(nullptr, publicKey.get()); + ASSERT_EQ(128ul, publicKey.get()->impl.bytes.size()); + auto address = WRAP(TWAnyAddress, TWAnyAddressCreateWithPublicKey(publicKey.get(), TWCoinTypeCardano)); + auto addressString = WRAPS(TWAnyAddressDescription(address.get())); + assertStringsEqual(addressString, "addr1qx4z6twzknkkux0hhp0kq6hvdfutczp56g56y5em8r8mgvxalp7nkkk25vuspleke2zltaetmlwrfxv7t049cq9jmwjswmfw6t"); + + auto address2 = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(STRING("addr1qx4z6twzknkkux0hhp0kq6hvdfutczp56g56y5em8r8mgvxalp7nkkk25vuspleke2zltaetmlwrfxv7t049cq9jmwjswmfw6t").get(), TWCoinTypeCardano)); + ASSERT_NE(nullptr, address2.get()); + auto address2String = WRAPS(TWAnyAddressDescription(address2.get())); + assertStringsEqual(address2String, "addr1qx4z6twzknkkux0hhp0kq6hvdfutczp56g56y5em8r8mgvxalp7nkkk25vuspleke2zltaetmlwrfxv7t049cq9jmwjswmfw6t"); + + ASSERT_TRUE(TWAnyAddressEqual(address.get(), address2.get())); +} + +TEST(TWCardano, AddressFromWallet) { + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic( + STRING("cost dash dress stove morning robust group affair stomach vacant route volume yellow salute laugh").get(), + STRING("").get() + )); + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeCardano)); + auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); + EXPECT_EQ(TWDataSize(privateKeyData.get()), 192ul); + + auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeyEd25519Cardano(privateKey.get())); + auto publicKeyData = WRAPD(TWPublicKeyData(publicKey.get())); + EXPECT_EQ(TWDataSize(publicKeyData.get()), 128ul); + assertHexEqual(publicKeyData, "fafa7eb4146220db67156a03a5f7a79c666df83eb31abbfbe77c85e06d40da3110f3245ddf9132ecef98c670272ef39c03a232107733d4a1d28cb53318df26faf4b8d5201961e68f2e177ba594101f513ee70fe70a41324e8ea8eb787ffda6f4bf2eea84515a4e16c4ff06c92381822d910b5cbf9e9c144e1fb76a6291af7276"); + + auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeCardano, privateKey.get())); + assertStringsEqual(address, "addr1qxxe304qg9py8hyyqu8evfj4wln7dnms943wsugpdzzsxnkvvjljtzuwxvx0pnwelkcruy95ujkq3aw6rl0vvg32x35qc92xkq"); +} + +/* +TEST(TWCardano, GetStakingKey) { + { + auto stakingAddress = WRAPS(TWCardanoGetStakingAddress(STRING("addr1q8043m5heeaydnvtmmkyuhe6qv5havvhsf0d26q3jygsspxlyfpyk6yqkw0yhtyvtr0flekj84u64az82cufmqn65zdsylzk23").get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(stakingAddress.get())), "stake1u80jysjtdzqt88jt4jx93h5lumfr67d273r4vwyasfa2pxcwxllmx"); + } + { // negative case: cannot get staking address from non-base address + auto stakingAddress = WRAPS(TWCardanoGetStakingAddress(STRING("stake1u95zuevxqjvpdh83r08ywq9xal6nxl48fgm0wvngyenvs4qh0hqf9").get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(stakingAddress.get())), ""); + } + { // negative case: cannot get staking address from invalid address, should not throw + auto stakingAddress = WRAPS(TWCardanoGetStakingAddress(STRING("__THIS_IS_NOT_A_VALID_CARDANO_ADDRESS__").get())); + EXPECT_EQ(std::string(TWStringUTF8Bytes(stakingAddress.get())), ""); + } +} +*/ diff --git a/tools/windows-replace/tests/chains/Polkadot/SignerTests.cpp b/tools/windows-replace/tests/chains/Polkadot/SignerTests.cpp new file mode 100644 index 00000000000..59d51714c23 --- /dev/null +++ b/tools/windows-replace/tests/chains/Polkadot/SignerTests.cpp @@ -0,0 +1,340 @@ +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "Polkadot/Signer.h" +#include "Polkadot/Extrinsic.h" +#include "Polkadot/Address.h" +#include "Polkadot/SS58Address.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "proto/Polkadot.pb.h" +#include "uint256.h" + +#include +#include + + +namespace TW::Polkadot::tests { + auto privateKey = PrivateKey(parse_hex("0xabf8e5bdbe30c65656c0a3cbd181ff8a56294a69dfedd27982aace4a76909115")); + auto privateKeyIOS = PrivateKey(parse_hex("37932b086586a6675e66e562fe68bd3eeea4177d066619c602fe3efc290ada62")); + auto privateKeyThrow2 = PrivateKey(parse_hex("70a794d4f1019c3ce002f33062f45029c4f930a56b3d20ec477f7668c6bbc37f")); + auto privateKeyPolkadot = PrivateKey(parse_hex("298fcced2b497ed48367261d8340f647b3fca2d9415d57c2e3c5ef90482a2266")); + auto addressThrow2 = "14Ztd3KJDaB9xyJtRkREtSZDdhLSbm7UUKt8Z7AwSv7q85G2"; + auto toPublicKey = PublicKey(parse_hex("0x8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"), TWPublicKeyTypeED25519); + auto genesisHash = parse_hex("91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3"); + auto controller1 = "14xKzzU1ZYDnzFj7FgdtDAYSMJNARjDc2gNw4XAFDgr4uXgp"; + +TEST(PolkadotSigner, SignTransfer_9fd062) { + auto toAddress = Address("13ZLCqJNPsRZYEbwjtZZFpWt9GyFzg5WahXCVWKpWdUJqrQ5"); + + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + auto blockHash = parse_hex("0x5d2143bb808626d63ad7e1cda70fa8697059d670a992e82cd440fbb95ea40351"); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(3); + input.set_spec_version(26); + { + PublicKey publicKey = privateKeyThrow2.getPublicKey(TWPublicKeyTypeED25519); + Address address = Address(publicKey); + EXPECT_EQ(address.string(), addressThrow2); + } + input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(5); + + // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true + auto era = input.mutable_era(); + era->set_block_number(3541050); + era->set_period(64); + + auto balanceCall = input.mutable_balance_call(); + auto transfer = balanceCall->mutable_transfer(); + auto value = store(uint256_t(2000000000)); // 0.2 + transfer->set_to_address(toAddress.string()); + transfer->set_value(value.data(), value.size()); + + auto extrinsic = Extrinsic(input); + auto preimage = extrinsic.encodePayload(); + EXPECT_EQ(hex(preimage), "05007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0300943577a5030c001a0000000500000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c35d2143bb808626d63ad7e1cda70fa8697059d670a992e82cd440fbb95ea40351"); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/0x9fd06208a6023e489147d8d93f0182b0cb7e45a40165247319b87278e08362d8 + EXPECT_EQ(hex(output.encoded()), "3502849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f7830073e59cef381aedf56d7af076bafff9857ffc1e3bd7d1d7484176ff5b58b73f1211a518e1ed1fd2ea201bd31869c0798bba4ffe753998c409d098b65d25dff801a5030c0005007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0300943577"); +} + +TEST(PolkadotSigner, SignTransferDOT) { + + auto blockHash = parse_hex("0x343a3f4258fd92f5ca6ca5abdf473d86a78b0bcd0dc09c568ca594245cc8c642"); + auto toAddress = SS58Address(toPublicKey, TWSS58AddressTypePolkadot); + + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + + input.set_nonce(0); + input.set_spec_version(17); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(3); + + auto& era = *input.mutable_era(); + era.set_block_number(927699); + era.set_period(8); + + auto balanceCall = input.mutable_balance_call(); + auto& transfer = *balanceCall->mutable_transfer(); + auto value = store(uint256_t(12345)); + transfer.set_to_address(toAddress.string()); + transfer.set_value(value.data(), value.size()); + + auto extrinsic = Extrinsic(input); + auto preimage = extrinsic.encodePayload(); + auto output = Signer::sign(input); + + ASSERT_EQ(hex(preimage), "05008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c032000000110000000300000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3343a3f4258fd92f5ca6ca5abdf473d86a78b0bcd0dc09c568ca594245cc8c642"); + ASSERT_EQ(hex(output.encoded()), "29028488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee003d91a06263956d8ce3ce5c55455baefff299d9cb2bb3f76866b6828ee4083770b6c03b05d7b6eb510ac78d047002c1fe5c6ee4b37c9c5a8b09ea07677f12e50d3200000005008eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48e5c0"); +} + +TEST(PolkadotSigner, SignTransfer_72dd5b) { + + auto blockHash = parse_hex("7d5fa17b70251d0806f26156b1b698dfd09e040642fa092595ce0a78e9e84fcd"); + + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + + input.set_nonce(1); + input.set_spec_version(28); + input.set_private_key(privateKeyIOS.bytes.data(), privateKeyIOS.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(6); + + auto& era = *input.mutable_era(); + era.set_block_number(3910736); + era.set_period(64); + + auto balanceCall = input.mutable_balance_call(); + auto& transfer = *balanceCall->mutable_transfer(); + auto value = store(uint256_t(10000000000)); + transfer.set_to_address("13ZLCqJNPsRZYEbwjtZZFpWt9GyFzg5WahXCVWKpWdUJqrQ5"); + transfer.set_value(value.data(), value.size()); + + auto extrinsic = Extrinsic(input); + auto preimage = extrinsic.encodePayload(); + auto output = Signer::sign(input); + + ASSERT_EQ(hex(preimage), "0500007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b5402050104001c0000000600000091b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c37d5fa17b70251d0806f26156b1b698dfd09e040642fa092595ce0a78e9e84fcd"); + ASSERT_EQ(hex(output.encoded()), "410284008d96660f14babe708b5e61853c9f5929bc90dd9874485bf4d6dc32d3e6f22eaa0038ec4973ab9773dfcbf170b8d27d36d89b85c3145e038d68914de83cf1f7aca24af64c55ec51ba9f45c5a4d74a9917dee380e9171108921c3e5546e05be15206050104000500007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b5402"); +} + +TEST(PolkadotSigner, SignBond_8da66d) { + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + auto blockHash = parse_hex("0xf1eee612825f29abd3299b486e401299df2faa55b7ce1e34bf2243bd591905fc"); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(0); + input.set_spec_version(26); + { + PublicKey publicKey = privateKeyThrow2.getPublicKey(TWPublicKeyTypeED25519); + Address address = Address(publicKey); + EXPECT_EQ(address.string(), addressThrow2); + } + input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(5); + + // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true + auto era = input.mutable_era(); + era->set_block_number(3540912); + era->set_period(64); + + auto stakingCall = input.mutable_staking_call(); + auto bond = stakingCall->mutable_bond(); + auto value = store(uint256_t(11000000000)); // 1.1 + bond->set_controller(addressThrow2); // myself + bond->set_value(value.data(), value.size()); + bond->set_reward_destination(Proto::RewardDestination::STASH); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/0x8da66d3fe0f592cff714ec107289370365117a1abdb72a19ac91181fdcf62bba + ASSERT_EQ(hex(output.encoded()), "3d02849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f783009025843bc49c1c4fbc99dbbd290c92f9879665d55b02f110abfb4800f0e7630877d2cffd853deae7466c22fbc8616a609e1b92615bb365ea8adccba5ef7624050503000007009dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f7830700aea68f0201"); +} + +TEST(PolkadotSigner, SignBondAndNominate_4955314_2) { + + auto key = parse_hex("7f44b19b391a8015ca4c7d94097b3695867a448d1391e7f3243f06987bdb6858"); + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(genesisHash.data(), genesisHash.size()); + input.set_nonce(4); + input.set_spec_version(30); + input.set_private_key(key.data(), key.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(7); + + auto stakingCall = input.mutable_staking_call(); + auto bondnom = stakingCall->mutable_bond_and_nominate(); + auto value = store(uint256_t(10000000000)); // 1 DOT + bondnom->set_controller("13ZLCqJNPsRZYEbwjtZZFpWt9GyFzg5WahXCVWKpWdUJqrQ5"); + bondnom->set_value(value.data(), value.size()); + bondnom->set_reward_destination(Proto::RewardDestination::STASH); + bondnom->add_nominators("1zugcavYA9yCuYwiEYeMHNJm9gXznYjNfXQjZsZukF1Mpow"); + bondnom->add_nominators("15oKi7HoBQbwwdQc47k71q4sJJWnu5opn1pqoGx4NAEYZSHs"); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/4955314-2 + ASSERT_EQ(hex(output.encoded()), "6103840036092fac541e0e5feda19e537c679b487566d7101141c203ac8322c27e5f076a00a8b1f859d788f11a958e98b731358f89cf3fdd41a667ea992522e8d4f46915f4c03a1896f2ac54bdc5f16e2ce8a2a3bf233d02aad8192332afd2113ed6688e0d0010001a02080700007120f76076bcb0efdf94c7219e116899d0163ea61cb428183d71324eb33b2bce0700e40b540201070508002c2a55b5ffdca266bd0207df97565b03255f70783ca1a349be5ed9f44589c36000d44533a4d21fd9d6f5d57c8cd05c61a6f23f9131cec8ae386b6b437db399ec3d"); +} + +TEST(PolkadotSigner, SignNominate_452522) { + auto input = Proto::SigningInput(); + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + auto blockHash = parse_hex("0x211787d016e39007ac054547737a10542620013e73648b3134541d536cb44e2c"); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(1); + input.set_spec_version(26); + input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(5); + + // era: for blockhash and block number, use curl -H "Content-Type: application/json" -H "Accept: text/plain" https:///transaction/material?noMeta=true + auto era = input.mutable_era(); + era->set_block_number(3540945); + era->set_period(64); + + auto stakingCall = input.mutable_staking_call(); + auto nominate = stakingCall->mutable_nominate(); + + nominate->add_nominators(controller1); + nominate->add_nominators("1REAJ1k691g5Eqqg9gL7vvZCBG7FCCZ8zgQkZWd4va5ESih"); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/0x4525224b7d8f3e58de3a54a9fbfd071401c2b737f314c972a2bb087a0ff508a6 + ASSERT_EQ(hex(output.encoded()), "a502849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f78300d73ff0dc456704743f70173a56e6c13e88a6e1dddb38a23552a066e44fb64e2c9d8a5e9a76afb9489b8540365f668bddd34b7d9c8dbdc4600e6316080e55a30315010400070508aee72821ca00e62304e4f0d858122a65b87c8df4f0eae224ae064b951d39f610127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a"); +} + +TEST(PolkadotSigner, SignNominate2) { + auto blockHash = parse_hex("d22a6b2e3e61325050718bd04a14da9efca1f41c9f0a525c375d36106e25af68"); + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(0); + input.set_spec_version(17); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(3); + + auto stakingCall = input.mutable_staking_call(); + auto& nominate = *stakingCall->mutable_nominate(); + // payload size larger than 256, will be hashed + nominate.add_nominators("1zugcabYjgfQdMLC3cAzQ8tJZMo45tMnGpivpAzpxB4CZyK"); + nominate.add_nominators("1REAJ1k691g5Eqqg9gL7vvZCBG7FCCZ8zgQkZWd4va5ESih"); + nominate.add_nominators("1WG3jyNqniQMRZGQUc7QD2kVLT8hkRPGMSqAb5XYQM1UDxN"); + nominate.add_nominators("16QFrtU6kDdBjxY8qEKz5EEfuDkHxqG8pix3wSGKQzRcuWHo"); + nominate.add_nominators("14ShUZUYUR35RBZW6uVVt1zXDxmSQddkeDdXf1JkMA6P721N"); + nominate.add_nominators("15MUBwP6dyVw5CXF9PjSSv7SdXQuDSwjX86v1kBodCSWVR7c"); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.encoded()), "a1048488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee00135bbc68b67fffadaf7e98b6402c4fc60382765f543225083a024b0e0ff8071d4ec4ddd67a65828113cc76f3208765608be010d2fcfdcd47e8fe342872704c000000000705182c2a55b5a116a4c88aff57e8f2b70ba72dda72dda4b78630e16ad0ca69006f18127a30e486492921e58f2564b36ab1ca21ff630672f0e76920edd601f8f2b89a1650c532ed1a8641e8922aa24ade0ff411d03edd9ed1c6b7fe42f1a801cee37ceee9d5d071a418b51c02b456d5f5cefd6231041ad59b0e8379c59c11ba4a2439984e16482c99cfad1436111e321a86d87d0fac203bf64538f888e45d793b5413c08d5de7a5d97bea2c7ddf516d0635bddc43f326ae2f80e2595b49d4a08c4619"); +} + +TEST(PolkadotSigner, SignChill) { + auto blockHash = parse_hex("1d4a1ecc8b1c37bf0ba5d3e0bf14ec5402fbb035eeaf6d8042c07ca5f8c57429"); + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(0); + input.set_spec_version(17); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(3); + + auto stakingCall = input.mutable_staking_call(); + //auto __attribute__((unused)) &chill = *stakingCall->mutable_chill(); + [[maybe_unused]] auto &chill = *stakingCall->mutable_chill(); //win + auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.encoded()), "9d018488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee0088b5e1cd93ba74b82e329f95e1b22660385970182172b2ae280801fdd1ee5652cf7bf319e5e176ccc299dd8eb1e7fccb0ea7717efaf4aacd7640789dd09c1e070000000706"); +} + +TEST(PolkadotSigner, SignWithdraw) { + auto blockHash = parse_hex("7b4d1d1e2573eabcc90a3e96058eb0d8d21d7a0b636e8030d152d9179a345dda"); + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(0); + input.set_spec_version(17); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(3); + + auto stakingCall = input.mutable_staking_call(); + auto& withdraw = *stakingCall->mutable_withdraw_unbonded(); + withdraw.set_slashing_spans(10); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.encoded()), "ad018488dc3417d5058ec4b4503e0c12ea1a0a89be200fe98922423d4334014fa6b0ee002e49bf0dec9bef01dd3bd25419e2147dc983613d0860108f889f9ff2d062c5e3267e309e2dbc35dd2fc2b877b57d86a5f12cbeb8217485be32be3c34d2507d0e00000007030a000000"); +} + +TEST(PolkadotSigner, SignUnbond_070957) { + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + auto blockHash = parse_hex("0x53040c71c6061bd256346b81fcb3545c13b5c34c7cd0c2c25f00aa6e564b16d5"); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(2); + input.set_spec_version(26); + input.set_private_key(privateKeyThrow2.bytes.data(), privateKeyThrow2.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(5); + + auto era = input.mutable_era(); + era->set_block_number(3540983); + era->set_period(64); + + auto stakingCall = input.mutable_staking_call(); + auto unbond = stakingCall->mutable_unbond(); + auto value = store(uint256_t(4000000000)); + unbond->set_value(value.data(), value.size()); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/0x070957ab697adbe11f7d72a1314d0a81d272a747d2e6880818073317125f980a + ASSERT_EQ(hex(output.encoded()), "b501849dca538b7a925b8ea979cc546464a3c5f81d2398a3a272f6f93bdf4803f2f783003a762d9dc3f2aba8922c4babf7e6622ca1d74da17ab3f152d8f29b0ffee53c7e5e150915912a9dfd98ef115d272e096543eef9f513207dd606eea97d023a64087503080007020300286bee"); +} + +TEST(PolkadotSigner, SignChillAndUnbond) { + auto blockHash = parse_hex("0x35ba668bb19453e8da6334cadcef2a27c8d4141bfc8b49e78e853c3d73e1ecd0"); + auto input = Proto::SigningInput(); + + input.set_genesis_hash(genesisHash.data(), genesisHash.size()); + input.set_block_hash(blockHash.data(), blockHash.size()); + input.set_nonce(6); + input.set_spec_version(9200); + input.set_private_key(privateKeyPolkadot.bytes.data(), privateKeyPolkadot.bytes.size()); + input.set_network(Proto::Network::POLKADOT); + input.set_transaction_version(12); + + auto era = input.mutable_era(); + era->set_block_number(10541373); + era->set_period(64); + + auto stakingCall = input.mutable_staking_call(); + auto chillBond = stakingCall->mutable_chill_and_unbond(); + auto value = store(uint256_t(100500000000)); // 10.05 DOT + chillBond->set_value(value.data(), value.size()); + + auto output = Signer::sign(input); + // https://polkadot.subscan.io/extrinsic/10541383-2 + ASSERT_EQ(hex(output.encoded()), "d10184008361bd08ddca5fda28b5e2aa84dc2621de566e23e089e555a42194c3eaf2da7900c891ba102db672e378945d74cf7f399226a76b43cab502436971599255451597fc2599902e4b62c7ce85ecc3f653c693fef3232be620984b5bb5bcecbbd7b209d50318001a02080706070207004d446617"); +} + +} // namespace TW::Polkadot::tests diff --git a/tools/windows-replace/tests/chains/Zilliqa/SignerTests.cpp b/tools/windows-replace/tests/chains/Zilliqa/SignerTests.cpp new file mode 100644 index 00000000000..18c192e0572 --- /dev/null +++ b/tools/windows-replace/tests/chains/Zilliqa/SignerTests.cpp @@ -0,0 +1,113 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "Zilliqa/Address.h" +#include "Zilliqa/Signer.h" +#include "proto/Zilliqa.pb.h" +#include "uint256.h" + +#include + +namespace TW::Zilliqa::tests { + +TEST(ZilliqaSigner, PreImage) { + auto privateKey = PrivateKey(parse_hex("0E891B9DFF485000C7D1DC22ECF3A583CC50328684321D61947A86E57CF6C638")); + auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + ASSERT_EQ(hex(pubKey.bytes), "034ae47910d58b9bde819c3cffa8de4441955508db00aa2540db8e6bf6e99abc1b"); + + auto amount = uint256_t(15000000000000); + auto gasPrice = uint256_t(1000000000); + auto amountData = store(amount); + auto gasData = store(gasPrice); + auto toAddress = Address(parse_hex("0x9Ca91EB535Fb92Fda5094110FDaEB752eDb9B039")); + + auto input = Proto::SigningInput(); + auto& tx = *input.mutable_transaction(); + auto& transfer = *tx.mutable_transfer(); + transfer.set_amount(amountData.data(), amountData.size()); + + input.set_version(65537); + input.set_nonce(4); + input.set_to(toAddress.string()); + input.set_gas_price(gasData.data(), gasData.size()); + input.set_gas_limit(uint64_t(1)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + Address address; + auto preImage = Signer::getPreImage(input, address); + auto signature = Signer::sign(input).signature(); + + ASSERT_EQ(hex(preImage.begin(), preImage.end()), "0881800410041a149ca91eb535fb92fda5094110fdaeb752edb9b03922230a21034ae47910d58b9bde819c3cffa8de4441955508db00aa2540db8e6bf6e99abc1b2a120a10000000000000000000000da475abf00032120a100000000000000000000000003b9aca003801"); + + ASSERT_TRUE(pubKey.verifyZilliqa(Data(signature.begin(), signature.end()), preImage)); +} + +TEST(ZilliqaSigner, Signing) { + auto privateKey = PrivateKey(parse_hex("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748")); + auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + + // 1 ZIL + auto amount = uint256_t(1000000000000); + auto gasPrice = uint256_t(1000000000); + auto amountData = store(amount); + auto gasData = store(gasPrice); + auto toAddress = Address(parse_hex("0x7FCcaCf066a5F26Ee3AFfc2ED1FA9810Deaa632C")); + + auto input = Proto::SigningInput(); + auto& tx = *input.mutable_transaction(); + auto& transfer = *tx.mutable_transfer(); + transfer.set_amount(amountData.data(), amountData.size()); + + input.set_version(65537); + input.set_nonce(2); + input.set_to(toAddress.string()); + input.set_gas_price(gasData.data(), gasData.size()); + input.set_gas_limit(uint64_t(1)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto output = Signer::sign(input); + + ASSERT_EQ(hex(output.signature().begin(), output.signature().end()), "001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268"); + ASSERT_EQ(output.json(), R"({"amount":"1000000000000","code":"","data":"","gasLimit":"1","gasPrice":"1000000000","nonce":2,"pubKey":"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c","signature":"001fa4df08c11a4a79e96e69399ee48eeecc78231a78b0355a8ca783c77c139436e37934fecc2252ed8dac00e235e22d18410461fb896685c4270642738ed268","toAddr":"7FCcaCf066a5F26Ee3AFfc2ED1FA9810Deaa632C","version":65537})"); +} + +TEST(ZilliqaSigner, SigningData) { + // https://viewblock.io/zilliqa/tx/0x6228b3d7e69fc3481b84fd00e892cec359a41654f58948ff7b1b932396b00ad9 + auto privateKey = PrivateKey(parse_hex("0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748")); + auto pubKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); + + // 10 ZIL + auto amount = uint256_t(10000000000000); + auto gasPrice = uint256_t(2000000000); + auto amountData = store(amount); + auto gasData = store(gasPrice); + + std::string json = "{\"_tag\":\"DelegateStake\",\"params\":[{\"type\":\"ByStr20\",\"value\":\"0x122219cCeAb410901e96c3A0e55E46231480341b\",\"vname\":\"ssnaddr\"}]}"; + auto jsonData = Data(json.begin(), json.end()); + + auto input = Proto::SigningInput(); + auto& tx = *input.mutable_transaction(); + auto& raw = *tx.mutable_raw_transaction(); + raw.set_amount(amountData.data(), amountData.size()); + raw.set_data(jsonData.data(), jsonData.size()); + + input.set_version(65537); + input.set_nonce(56); + input.set_to("zil1g029nmzsf36r99vupp4s43lhs40fsscx3jjpuy"); + input.set_gas_price(gasData.data(), gasData.size()); + input.set_gas_limit(uint64_t(5000)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + auto output = Signer::sign(input); + //ASSERT_EQ(output.json(), R"({"amount":"10000000000000","code":"","data":"{\"_tag\":\"DelegateStake\",\"params\":[{\"type\":\"ByStr20\",\"value\":\"0x122219cCeAb410901e96c3A0e55E46231480341b\",\"vname\":\"ssnaddr\"}]}","gasLimit":"5000","gasPrice":"2000000000","nonce":56,"pubKey":"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c","signature":"437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d","toAddr":"43D459eC504C7432959c086B0ac7F7855E984306","version":65537})"); + ASSERT_EQ(output.json(), "{\"amount\":\"10000000000000\",\"code\":\"\",\"data\":\"{\\\"_tag\\\":\\\"DelegateStake\\\",\\\"params\\\":[{\\\"type\\\":\\\"ByStr20\\\",\\\"value\\\":\\\"0x122219cCeAb410901e96c3A0e55E46231480341b\\\",\\\"vname\\\":\\\"ssnaddr\\\"}]}\",\"gasLimit\":\"5000\",\"gasPrice\":\"2000000000\",\"nonce\":56,\"pubKey\":\"03fb30b196ce3e976593ecc2da220dca9cdea8c84d2373770042a930b892ac0f5c\",\"signature\":\"437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d\",\"toAddr\":\"43D459eC504C7432959c086B0ac7F7855E984306\",\"version\":65537}");//win + + ASSERT_EQ(hex(output.signature().begin(), output.signature().end()), "437fb5c3ce2c6b01f9d490f670539fae4533c82a21fa7edfe6b23df70d732937e8c578c8d6ed24be9150f5126f7b7c977a467af8947ef92a720908a761a6eb0d"); +} + +} // namespace TW::Zilliqa::tests \ No newline at end of file diff --git a/tools/windows-replace/tests/common/BCSTests.cpp b/tools/windows-replace/tests/common/BCSTests.cpp new file mode 100644 index 00000000000..fee7c0805df --- /dev/null +++ b/tools/windows-replace/tests/common/BCSTests.cpp @@ -0,0 +1,165 @@ +// Copyright © 2017-2022 Trust Wallet. +// Created by Clément Doumergue +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +#include "BCS.h" +#include "HexCoding.h" + +#include + +namespace TW::BCS::tests { + +TEST(BCS, Integral) { + Serializer os; + os << uint32_t(0xAABBCCDD); + ASSERT_EQ(os.bytes, parse_hex("0xDDCCBBAA")); + + os.clear(); + os << int32_t(-305419896); + ASSERT_EQ(os.bytes, parse_hex("0x88A9CBED")); +} + +TEST(BCS, ULEB128) { + Serializer os; + os << uleb128{0x00000001}; + ASSERT_EQ(os.bytes, parse_hex("0x01")); + + os.clear(); + os << uleb128{0x00000080}; + ASSERT_EQ(os.bytes, parse_hex("0x8001")); + + os.clear(); + os << uleb128{0x00004000}; + ASSERT_EQ(os.bytes, parse_hex("0x808001")); + + os.clear(); + os << uleb128{0x00200000}; + ASSERT_EQ(os.bytes, parse_hex("0x80808001")); + + os.clear(); + os << uleb128{0x10000000}; + ASSERT_EQ(os.bytes, parse_hex("0x8080808001")); + + os.clear(); + os << uleb128{0x0000250F}; + ASSERT_EQ(os.bytes, parse_hex("0x8F4A")); +} + +TEST(BCS, String) { + Serializer os; + os << std::string_view("abcd"); + ASSERT_EQ(os.bytes, parse_hex("0x0461626364")); + + os.clear(); + os << std::string_view(""); + ASSERT_EQ(os.bytes, parse_hex("0x00")); +} + +TEST(BCS, Optional) { + Serializer os; + os << std::optional{0xBBCCDD}; + ASSERT_EQ(os.bytes, parse_hex("0x01DDCCBB00")); + + os.clear(); + os << std::optional{}; + ASSERT_EQ(os.bytes, parse_hex("0x00")); + + os.clear(); + os << std::nullopt; + ASSERT_EQ(os.bytes, parse_hex("0x00")); +} + +TEST(BCS, Tuple) { + Serializer os; + os << std::tuple{uint16_t(1), 'a'}; + ASSERT_EQ(os.bytes, parse_hex("0x010061")); + + os.clear(); + os << std::tuple{std::optional{123}, std::string_view("abcd"), uint8_t(0x0E)}; + ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); + + os.clear(); + os << std::tuple{}; + ASSERT_EQ(os.bytes, (Data{})); +} + +TEST(BCS, Pair) { + Serializer os; + os << std::pair{uint16_t(1), 'a'}; + ASSERT_EQ(os.bytes, parse_hex("0x010061")); + + os.clear(); + os << std::pair{std::optional{123}, std::string_view("abcd")}; + ASSERT_EQ(os.bytes, parse_hex("0x017b0000000461626364")); +} +/* +struct my_struct { + std::optional first; + std::string_view second; + uint8_t third; +}; + +TEST(BCS, Struct) { + Serializer os; + os << my_struct{{123}, "abcd", 0x0E}; + ASSERT_EQ(os.bytes, parse_hex("0x017b00000004616263640e")); +} +*/ +TEST(BCS, Variant) { + using V = std::variant; + + Serializer os; + os << V{uint32_t(1)}; + ASSERT_EQ(os.bytes, parse_hex("0x0001000000")); + + os.clear(); + os << V{char('a')}; + ASSERT_EQ(os.bytes, parse_hex("0x0161")); + + os.clear(); + os << V{true}; + ASSERT_EQ(os.bytes, parse_hex("0x0201")); +} + +TEST(BCS, Map) { + Serializer os; + os << std::map{{'a', 0}, {'b', 1}, {'c', 2}}; + ASSERT_EQ(os.bytes, parse_hex("0x03610062016302")); +} + +class my_number { +private: + int value; + +public: + explicit my_number(int value) noexcept + : value(value) { + } + + [[nodiscard]] auto get_value() const { + return value; + } +}; + +Serializer& operator<<(Serializer& stream, my_number n) noexcept { + return stream << n.get_value(); +} + +static_assert(CustomSerializable, "my_number does not model the CustomSerializable concept"); + +TEST(BCS, Custom) { + Serializer os; + os << my_number{0xBBCCDD}; + ASSERT_EQ(os.bytes, parse_hex("0xDDCCBB00")); +} + +TEST(BCS, Vector) { + Serializer os; + os << std::vector{1}; + ASSERT_EQ(os.bytes, parse_hex("0101")); +} + +} diff --git a/tools/windows-replace/tests/main.cpp b/tools/windows-replace/tests/main.cpp new file mode 100644 index 00000000000..136462e0cce --- /dev/null +++ b/tools/windows-replace/tests/main.cpp @@ -0,0 +1,61 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +#include + +#ifdef WIN32 +// Copyright © 2017-2020 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +#include +std::string TESTS_ROOT; + +int main(int argc, char **argv) { + if (argc < 2) { + std::cerr << "Please specify the tests root folder." << std::endl; + exit(1); + } + + TESTS_ROOT = argv[1]; + struct stat s; + if (stat(TESTS_ROOT.c_str(), &s) != 0 || (s.st_mode & S_IFDIR) == 0) { + std::cerr << "Please specify the tests root folder. '" << TESTS_ROOT << "' is not a valid directory." << std::endl; + exit(1); + } + std::cout<<"TESTS_ROOT: "< +#include + + +std::string TESTS_ROOT; + +int main(int argc, char** argv) { + + // current path + auto path = std::filesystem::current_path(); + // executable path + path.append(argv[0]); + // normalize + path = std::filesystem::canonical(path); + std::cout<<"normaliz path: "< + $ + PRIVATE + src +) + +install( + TARGETS TrezorCrypto + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/tools/windows-replace/trezor-crypto/crypto/base32.c b/tools/windows-replace/trezor-crypto/crypto/base32.c new file mode 100644 index 00000000000..d1cb294c77f --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/base32.c @@ -0,0 +1,245 @@ +/** + * Copyright (c) 2017 Saleem Rashid + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, E1PRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include + +const char *BASE32_ALPHABET_RFC4648 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ23456789"; + +static inline void base32_5to8(const uint8_t *in, uint8_t length, uint8_t *out); +static inline bool base32_8to5(const uint8_t *in, uint8_t length, uint8_t *out, + const char *alphabet); +static inline void base32_8to5_raw(const uint8_t *in, uint8_t length, + uint8_t *out); + +static inline int base32_encode_character(uint8_t decoded, + const char *alphabet); +static inline int base32_decode_character(char encoded, const char *alphabet); + +char *base32_encode(const uint8_t *in, size_t inlen, char *out, size_t outlen, + const char *alphabet) { + size_t length = base32_encoded_length(inlen); + if (outlen <= length) { + return NULL; + } + + base32_encode_unsafe(in, inlen, (uint8_t *)out); + + for (size_t i = 0; i < length; i++) { + int ret = base32_encode_character(out[i], alphabet); + + if (ret == -1) { + return false; + } else { + out[i] = ret; + } + } + + out[length] = '\0'; + return &out[length]; +} + +uint8_t *base32_decode(const char *in, size_t inlen, uint8_t *out, + size_t outlen, const char *alphabet) { + size_t length = base32_decoded_length(inlen); + if (outlen < length) { + return NULL; + } + + if (!base32_decode_unsafe((uint8_t *)in, inlen, (uint8_t *)out, alphabet)) { + return NULL; + } + + return &out[length]; +} + +void base32_encode_unsafe(const uint8_t *in, size_t inlen, uint8_t *out) { + uint8_t remainder = inlen % 5; + size_t limit = inlen - remainder; + + size_t i = 0, j = 0; + for (i = 0, j = 0; i < limit; i += 5, j += 8) { + base32_5to8(&in[i], 5, &out[j]); + } + + if (remainder) base32_5to8(&in[i], remainder, &out[j]); +} + +bool base32_decode_unsafe(const uint8_t *in, size_t inlen, uint8_t *out, + const char *alphabet) { + uint8_t remainder = inlen % 8; + size_t limit = inlen - remainder; + + size_t i = 0, j = 0; + for (i = 0, j = 0; i < limit; i += 8, j += 5) { + if (!base32_8to5(&in[i], 8, &out[j], alphabet)) { + return false; + } + } + + if (remainder && !base32_8to5(&in[i], remainder, &out[j], alphabet)) { + return false; + } + + return true; +} + +size_t base32_encoded_length(size_t inlen) { + uint8_t remainder = inlen % 5; + + return (inlen / 5) * 8 + (remainder * 8 + 4) / 5; +} + +size_t base32_decoded_length(size_t inlen) { + uint8_t remainder = inlen % 8; + + return (inlen / 8) * 5 + (remainder * 5) / 8; +} + +void base32_5to8(const uint8_t *in, uint8_t length, uint8_t *out) { + if (length >= 1) { + out[0] = (in[0] >> 3); + out[1] = (in[0] & 7) << 2; + } + + if (length >= 2) { + out[1] |= (in[1] >> 6); + out[2] = (in[1] >> 1) & 31; + out[3] = (in[1] & 1) << 4; + } + + if (length >= 3) { + out[3] |= (in[2] >> 4); + out[4] = (in[2] & 15) << 1; + } + + if (length >= 4) { + out[4] |= (in[3] >> 7); + out[5] = (in[3] >> 2) & 31; + out[6] = (in[3] & 3) << 3; + } + + if (length >= 5) { + out[6] |= (in[4] >> 5); + out[7] = (in[4] & 31); + } +} + +bool base32_8to5(const uint8_t *in, uint8_t length, uint8_t *out, + const char *alphabet) { + if (length == 1 || length == 3 || length == 6 || length > 8) { + return false; + } + + if (alphabet) { + #ifdef _MSC_VER + uint8_t *decoded = _alloca(length); + #else + uint8_t decoded[length]; + #endif + memset(decoded, 0, length); //win memset(decoded, 0, sizeof(decoded)); + + for (size_t i = 0; i < length; i++) { + int ret = base32_decode_character(in[i], alphabet); + + if (ret == -1) { + return false; + } else { + decoded[i] = ret; + } + } + + base32_8to5_raw(decoded, length, out); + } else { + base32_8to5_raw(in, length, out); + } + + return true; +} + +void base32_8to5_raw(const uint8_t *in, uint8_t length, uint8_t *out) { + if (length >= 2) { + out[0] = (in[0] << 3); + out[0] |= (in[1] >> 2); + } + + if (length >= 4) { + out[1] = (in[1] & 3) << 6; + out[1] |= (in[2] << 1); + out[1] |= (in[3] >> 4); + } + + if (length >= 5) { + out[2] = (in[3] & 15) << 4; + out[2] |= (in[4] >> 1); + } + + if (length >= 7) { + out[3] = (in[4] & 1) << 7; + out[3] |= (in[5] << 2); + out[3] |= (in[6] >> 3); + } + + if (length >= 8) { + out[4] = (in[6] & 7) << 5; + out[4] |= (in[7] & 31); + } +} + +int base32_encode_character(uint8_t decoded, const char *alphabet) { + if (decoded >> 5) { + return -1; + } + + if (alphabet == BASE32_ALPHABET_RFC4648) { + if (decoded < 26) { + return 'A' + decoded; + } else { + return '2' - 26 + decoded; + } + } + + return alphabet[decoded]; +} + +int base32_decode_character(char encoded, const char *alphabet) { + if (alphabet == BASE32_ALPHABET_RFC4648) { + if (encoded >= 'A' && encoded <= 'Z') { + return encoded - 'A'; + } else if (encoded >= 'a' && encoded <= 'z') { + return encoded - 'a'; + } else if (encoded >= '2' && encoded <= '7') { + return encoded - '2' + 26; + } else { + return -1; + } + } + + const char *occurrence = strchr(alphabet, encoded); + + if (occurrence) { + return occurrence - alphabet; + } else { + return -1; + } +} diff --git a/tools/windows-replace/trezor-crypto/crypto/base58.c b/tools/windows-replace/trezor-crypto/crypto/base58.c new file mode 100644 index 00000000000..4c870cd50e5 --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/base58.c @@ -0,0 +1,294 @@ +/** + * Copyright (c) 2012-2014 Luke Dashjr + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include + +const char b58digits_ordered[] = + "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; +const int8_t b58digits_map[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, + 8, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, 12, 13, 14, 15, 16, -1, 17, 18, + 19, 20, 21, -1, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, -1, -1, -1, -1, + -1, -1, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, -1, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, -1, -1, -1, -1, -1, +}; + +typedef uint64_t b58_maxint_t; +typedef uint32_t b58_almostmaxint_t; +#define b58_almostmaxint_bits (sizeof(b58_almostmaxint_t) * 8) +const b58_almostmaxint_t b58_almostmaxint_mask = + ((((b58_maxint_t)1) << b58_almostmaxint_bits) - 1); + +// Decodes a null-terminated Base58 string `b58` to binary and writes the result +// at the end of the buffer `bin` of size `*binszp`. On success `*binszp` is set +// to the number of valid bytes at the end of the buffer. +bool b58tobin(void *bin, size_t *binszp, const char *b58) { + size_t binsz = *binszp; + + if (binsz == 0) { + return false; + } + + const unsigned char *b58u = (const unsigned char *)b58; + unsigned char *binu = bin; + size_t outisz = + (binsz + sizeof(b58_almostmaxint_t) - 1) / sizeof(b58_almostmaxint_t); + //win + #ifdef _MSC_VER + b58_almostmaxint_t *outi = _alloca(sizeof(b58_almostmaxint_t) * outisz); +#else + b58_almostmaxint_t outi[outisz]; +#endif +//win + b58_maxint_t t = 0; + b58_almostmaxint_t c = 0; + size_t i = 0, j = 0; + uint8_t bytesleft = binsz % sizeof(b58_almostmaxint_t); + b58_almostmaxint_t zeromask = + bytesleft ? (b58_almostmaxint_mask << (bytesleft * 8)) : 0; + unsigned zerocount = 0; + + size_t b58sz = strlen(b58); + + memzero(outi, sizeof(b58_almostmaxint_t) * outisz);//win + + // Leading zeros, just count + for (i = 0; i < b58sz && b58u[i] == '1'; ++i) ++zerocount; + + for (; i < b58sz; ++i) { + if (b58u[i] & 0x80) + // High-bit set on invalid digit + return false; + if (b58digits_map[b58u[i]] == -1) + // Invalid base58 digit + return false; + c = (unsigned)b58digits_map[b58u[i]]; + for (j = outisz; j--;) { + t = ((b58_maxint_t)outi[j]) * 58 + c; + c = t >> b58_almostmaxint_bits; + outi[j] = t & b58_almostmaxint_mask; + } + if (c) + // Output number too big (carry to the next int32) + return false; + if (outi[0] & zeromask) + // Output number too big (last int32 filled too far) + return false; + } + + j = 0; + if (bytesleft) { + for (i = bytesleft; i > 0; --i) { + *(binu++) = (outi[0] >> (8 * (i - 1))) & 0xff; + } + ++j; + } + + for (; j < outisz; ++j) { + for (i = sizeof(*outi); i > 0; --i) { + *(binu++) = (outi[j] >> (8 * (i - 1))) & 0xff; + } + } + + // locate the most significant byte + binu = bin; + for (i = 0; i < binsz; ++i) { + if (binu[i]) break; + } + + // prepend the correct number of null-bytes + if (zerocount > i) { + /* result too large */ + return false; + } + *binszp = binsz - i + zerocount; + + return true; +} + +int b58check(const void *bin, size_t binsz, HasherType hasher_type, + const char *base58str) { + unsigned char buf[32] = {0}; + const uint8_t *binc = bin; + unsigned i = 0; + if (binsz < 4) return -4; + hasher_Raw(hasher_type, bin, binsz - 4, buf); + if (memcmp(&binc[binsz - 4], buf, 4)) return -1; + + // Check number of zeros is correct AFTER verifying checksum (to avoid + // possibility of accessing base58str beyond the end) + for (i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i) { + } // Just finding the end of zeros, nothing to do in loop + if (binc[i] == '\0' || base58str[i] == '1') return -3; + + return binc[0]; +} + +bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { + const uint8_t *bin = data; + int carry = 0; + size_t i = 0, j = 0, high = 0, zcount = 0; + size_t size = 0; + + while (zcount < binsz && !bin[zcount]) ++zcount; + + size = (binsz - zcount) * 138 / 100 + 1; + #ifdef _MSC_VER + uint8_t *buf = _alloca(size); +#else + uint8_t buf[size]; +#endif + memzero(buf, size); + + + for (i = zcount, high = size - 1; i < binsz; ++i, high = j) { + for (carry = bin[i], j = size - 1; (j > high) || carry; --j) { + carry += 256 * buf[j]; + buf[j] = carry % 58; + carry /= 58; + if (!j) { + // Otherwise j wraps to maxint which is > high + break; + } + } + } + + for (j = 0; j < size && !buf[j]; ++j) + ; + + if (*b58sz <= zcount + size - j) { + *b58sz = zcount + size - j + 1; + return false; + } + + if (zcount) memset(b58, '1', zcount); + for (i = zcount; j < size; ++i, ++j) b58[i] = b58digits_ordered[buf[j]]; + b58[i] = '\0'; + *b58sz = i + 1; + + return true; +} + +int base58_encode_check(const uint8_t *data, int datalen, + HasherType hasher_type, char *str, int strsize) { + if (datalen > 128) { + return 0; + } + +#ifdef _MSC_VER + uint8_t *buf = _alloca((size_t)datalen + 32); +#else + uint8_t buf[datalen + 32]; +#endif + memset(buf, 0, (size_t)datalen + 32);//win memset(buf, 0, (size_t)datalen + 32); + uint8_t *hash = buf + datalen; + memcpy(buf, data, datalen); + hasher_Raw(hasher_type, data, datalen, hash); + size_t res = strsize; + bool success = b58enc(str, &res, buf, datalen + 4); + memzero(buf, (size_t)datalen + 32); //win memzero(buf, (size_t)datalen + 32); + return success ? res : 0; +} + +int base58_decode_check(const char *str, HasherType hasher_type, uint8_t *data, + int datalen) { + if (datalen > 128) { + return 0; + } + #ifdef _MSC_VER + uint8_t *d = _alloca((size_t)datalen + 4); +#else + uint8_t d[datalen + 4]; + #endif + memset(d, 0, (size_t)datalen + 4); //win memset(d, 0, sizeof(d)); + size_t res = datalen + 4; + if (b58tobin(d, &res, str) != true) { + return 0; + } + uint8_t *nd = d + datalen + 4 - res; + if (b58check(nd, res, hasher_type, str) < 0) { + return 0; + } + memcpy(data, nd, res - 4); + return res - 4; +} + +#if USE_GRAPHENE +int b58gphcheck(const void *bin, size_t binsz, const char *base58str) { + unsigned char buf[32] = {0}; + const uint8_t *binc = bin; + unsigned i = 0; + if (binsz < 4) return -4; + ripemd160(bin, binsz - 4, buf); // No double SHA256, but a single RIPEMD160 + if (memcmp(&binc[binsz - 4], buf, 4)) return -1; + + // Check number of zeros is correct AFTER verifying checksum (to avoid + // possibility of accessing base58str beyond the end) + for (i = 0; binc[i] == '\0' && base58str[i] == '1'; ++i) { + } // Just finding the end of zeros, nothing to do in loop + if (binc[i] == '\0' || base58str[i] == '1') return -3; + + return binc[0]; +} + +int base58gph_encode_check(const uint8_t *data, int datalen, char *str, + int strsize) { + if (datalen > 128) { + return 0; + } + uint8_t buf[datalen + 32]; + memset(buf, 0, sizeof(buf)); + uint8_t *hash = buf + datalen; + memcpy(buf, data, datalen); + ripemd160(data, datalen, hash); // No double SHA256, but a single RIPEMD160 + size_t res = strsize; + bool success = b58enc(str, &res, buf, datalen + 4); + memzero(buf, sizeof(buf)); + return success ? res : 0; +} + +int base58gph_decode_check(const char *str, uint8_t *data, int datalen) { + if (datalen > 128) { + return 0; + } + uint8_t d[datalen + 4]; + memset(d, 0, sizeof(d)); + size_t res = datalen + 4; + if (b58tobin(d, &res, str) != true) { + return 0; + } + uint8_t *nd = d + datalen + 4 - res; + if (b58gphcheck(nd, res, str) < 0) { + return 0; + } + memcpy(data, nd, res - 4); + return res - 4; +} +#endif diff --git a/tools/windows-replace/trezor-crypto/crypto/bignum.c b/tools/windows-replace/trezor-crypto/crypto/bignum.c new file mode 100644 index 00000000000..be739c611e5 --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/bignum.c @@ -0,0 +1,1841 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * Copyright (c) 2015 Jochen Hoenicke + * Copyright (c) 2016 Alex Beregszaszi + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include +#include +#include +#include + +#include +#include + +/* + This library implements 256-bit numbers arithmetic. + + An unsigned 256-bit number is represented by a bignum256 structure, that is an + array of nine 32-bit values called limbs. Limbs are digits of the number in + the base 2**29 representation in the little endian order. This means that + bignum256 x; + represents the value + sum([x[i] * 2**(29*i) for i in range(9)). + + A limb of a bignum256 is *normalized* iff it's less than 2**29. + A bignum256 is *normalized* iff every its limb is normalized. + A number is *fully reduced modulo p* iff it is less than p. + A number is *partly reduced modulo p* iff is is less than 2*p. + The number p is usually a prime number such that 2^256 - 2^224 <= p <= 2^256. + + All functions except bn_fast_mod expect that all their bignum256 inputs are + normalized. (The function bn_fast_mod allows the input number to have the + most significant limb unnormalized). All bignum256 outputs of all functions + are guaranteed to be normalized. + + A number can be partly reduced with bn_fast_mod, a partly reduced number can + be fully reduced with bn_mod. + + A function has *constant control flow with regard to its argument* iff the + order in which instructions of the function are executed doesn't depend on the + value of the argument. + A function has *constant memory access flow with regard to its argument* iff + the memory addresses that are acessed and the order in which they are accessed + don't depend on the value of the argument. + A function *has contant control (memory access) flow* iff it has constant + control (memory access) flow with regard to all its arguments. + + The following function has contant control flow with regard to its arugment + n, however is doesn't have constant memory access flow with regard to it: + void (int n, int *a) } + a[0] = 0; + a[n] = 0; // memory address reveals the value of n + } + + Unless stated otherwise all functions are supposed to have both constant + control flow and constant memory access flow. + */ + +#define BN_MAX_DECIMAL_DIGITS \ + 79 // floor(log(2**(LIMBS * BITS_PER_LIMB), 10)) + 1 + +// out_number = (bignum256) in_number +// Assumes in_number is a raw bigendian 256-bit number +// Guarantees out_number is normalized +void bn_read_be(const uint8_t *in_number, bignum256 *out_number) { + uint32_t temp = 0; + + for (int i = 0; i < BN_LIMBS - 1; i++) { + uint32_t limb = read_be(in_number + (BN_LIMBS - 2 - i) * 4); + + temp |= limb << (BN_EXTRA_BITS * i); + out_number->val[i] = temp & BN_LIMB_MASK; + + temp = limb >> (32 - BN_EXTRA_BITS * (i + 1)); + } + + out_number->val[BN_LIMBS - 1] = temp; +} + +// out_number = (256BE) in_number +// Assumes in_number < 2**256 +// Guarantess out_number is a raw bigendian 256-bit number +void bn_write_be(const bignum256 *in_number, uint8_t *out_number) { + uint32_t temp = in_number->val[BN_LIMBS - 1]; + for (int i = BN_LIMBS - 2; i >= 0; i--) { + uint32_t limb = in_number->val[i]; + + temp = (temp << (BN_BITS_PER_LIMB - BN_EXTRA_BITS * i)) | + (limb >> (BN_EXTRA_BITS * i)); + write_be(out_number + (BN_LIMBS - 2 - i) * 4, temp); + + temp = limb; + } +} + +// out_number = (bignum256) in_number +// Assumes in_number is a raw little endian 256-bit number +// Guarantees out_number is normalized +void bn_read_le(const uint8_t *in_number, bignum256 *out_number) { + uint32_t temp = 0; + for (int i = 0; i < BN_LIMBS - 1; i++) { + uint32_t limb = read_le(in_number + i * 4); + + temp |= limb << (BN_EXTRA_BITS * i); + out_number->val[i] = temp & BN_LIMB_MASK; + temp = limb >> (32 - BN_EXTRA_BITS * (i + 1)); + } + + out_number->val[BN_LIMBS - 1] = temp; +} + +// out_number = (256LE) in_number +// Assumes in_number < 2**256 +// Guarantess out_number is a raw little endian 256-bit number +void bn_write_le(const bignum256 *in_number, uint8_t *out_number) { + uint32_t temp = in_number->val[BN_LIMBS - 1]; + + for (int i = BN_LIMBS - 2; i >= 0; i--) { + uint32_t limb = in_number->val[i]; + temp = (temp << (BN_BITS_PER_LIMB - BN_EXTRA_BITS * i)) | + (limb >> (BN_EXTRA_BITS * i)); + write_le(out_number + i * 4, temp); + temp = limb; + } +} + +// out_number = (bignum256) in_number +// Guarantees out_number is normalized +void bn_read_uint32(uint32_t in_number, bignum256 *out_number) { + out_number->val[0] = in_number & BN_LIMB_MASK; + out_number->val[1] = in_number >> BN_BITS_PER_LIMB; + for (uint32_t i = 2; i < BN_LIMBS; i++) out_number->val[i] = 0; +} + +// out_number = (bignum256) in_number +// Guarantees out_number is normalized +void bn_read_uint64(uint64_t in_number, bignum256 *out_number) { + out_number->val[0] = in_number & BN_LIMB_MASK; + out_number->val[1] = (in_number >>= BN_BITS_PER_LIMB) & BN_LIMB_MASK; + out_number->val[2] = in_number >> BN_BITS_PER_LIMB; + for (uint32_t i = 3; i < BN_LIMBS; i++) out_number->val[i] = 0; +} +//win +#ifdef _MSC_VER +#include +uint32_t __forceinline bn_clz(uint32_t value) +{ + unsigned long leading_zero = 0; + if (_BitScanReverse(&leading_zero, value)) + return 31 - leading_zero; + else + return 32; +} +#else +#define bn_clz __builtin_clz +#endif + +// Returns the bitsize of x +// Assumes x is normalized +// The function doesn't have neither constant control flow nor constant memory +// access flow +int bn_bitcount(const bignum256 *x) { + for (int i = BN_LIMBS - 1; i >= 0; i--) { + uint32_t limb = x->val[i]; + if (limb != 0) { + // __builtin_clz returns the number of leading zero bits starting at the + // most significant bit position + return i * BN_BITS_PER_LIMB + (32 - bn_clz(limb)); //win return i * BN_BITS_PER_LIMB + (32 - __builtin_clz(limb)); + } + } + return 0; +} + +// Returns the number of decimal digits of x; if x is 0, returns 1 +// Assumes x is normalized +// The function doesn't have neither constant control flow nor constant memory +// access flow +unsigned int bn_digitcount(const bignum256 *x) { + bignum256 val = {0}; + bn_copy(x, &val); + + unsigned int digits = 1; + for (unsigned int i = 0; i < BN_MAX_DECIMAL_DIGITS; i += 3) { + uint32_t limb = 0; + + bn_divmod1000(&val, &limb); + + if (limb >= 100) { + digits = i + 3; + } else if (limb >= 10) { + digits = i + 2; + } else if (limb >= 1) { + digits = i + 1; + } + } + + memzero(&val, sizeof(val)); + + return digits; +} + +// x = 0 +// Guarantees x is normalized +void bn_zero(bignum256 *x) { + for (int i = 0; i < BN_LIMBS; i++) { + x->val[i] = 0; + } +} + +// x = 1 +// Guarantees x is normalized +void bn_one(bignum256 *x) { + x->val[0] = 1; + for (int i = 1; i < BN_LIMBS; i++) { + x->val[i] = 0; + } +} + +// Returns x == 0 +// Assumes x is normalized +int bn_is_zero(const bignum256 *x) { + uint32_t result = 0; + for (int i = 0; i < BN_LIMBS; i++) { + result |= x->val[i]; + } + return !result; +} + +// Returns x == 1 +// Assumes x is normalized +int bn_is_one(const bignum256 *x) { + uint32_t result = x->val[0] ^ 1; + for (int i = 1; i < BN_LIMBS; i++) { + result |= x->val[i]; + } + return !result; +} + +// Returns x < y +// Assumes x, y are normalized +int bn_is_less(const bignum256 *x, const bignum256 *y) { + uint32_t res1 = 0; + uint32_t res2 = 0; + for (int i = BN_LIMBS - 1; i >= 0; i--) { + res1 = (res1 << 1) | (x->val[i] < y->val[i]); + res2 = (res2 << 1) | (x->val[i] > y->val[i]); + } + return res1 > res2; +} + +// Returns x == y +// Assumes x, y are normalized +int bn_is_equal(const bignum256 *x, const bignum256 *y) { + uint32_t result = 0; + for (int i = 0; i < BN_LIMBS; i++) { + result |= x->val[i] ^ y->val[i]; + } + return !result; +} + +// res = cond if truecase else falsecase +// Assumes cond is either 0 or 1 +// Works properly even if &res == &truecase or &res == &falsecase or +// &truecase == &falsecase or &res == &truecase == &falsecase +void bn_cmov(bignum256 *res, volatile uint32_t cond, const bignum256 *truecase, + const bignum256 *falsecase) { + assert((int)(cond == 1) | (cond == 0)); + + uint32_t tmask = -cond; // tmask = 0xFFFFFFFF if cond else 0x00000000 + uint32_t fmask = ~tmask; // fmask = 0x00000000 if cond else 0xFFFFFFFF + + for (int i = 0; i < BN_LIMBS; i++) { + res->val[i] = (truecase->val[i] & tmask) | (falsecase->val[i] & fmask); + } +} + +// x = -x % prime if cond else x, +// Explicitly x = (3 * prime - x if x > prime else 2 * prime - x) if cond else +// else (x if x > prime else x + prime) +// Assumes x is normalized and partly reduced +// Assumes cond is either 1 or 0 +// Guarantees x is normalized +// Assumes prime is normalized and +// 0 < prime < 2**260 == 2**(BITS_PER_LIMB * LIMBS - 1) +void bn_cnegate(volatile uint32_t cond, bignum256 *x, const bignum256 *prime) { + assert((int)(cond == 1) | (cond == 0)); + + uint32_t tmask = -cond; // tmask = 0xFFFFFFFF if cond else 0x00000000 + uint32_t fmask = ~tmask; // fmask = 0x00000000 if cond else 0xFFFFFFFF + + bn_mod(x, prime); + // x < prime + + uint32_t acc1 = 1; + uint32_t acc2 = 0; + + for (int i = 0; i < BN_LIMBS; i++) { + acc1 += (BN_BASE - 1) + 2 * prime->val[i] - x->val[i]; + // acc1 neither overflows 32 bits nor underflows 0 + // Proof: + // acc1 + (BASE - 1) + 2 * prime[i] - x[i] + // >= (BASE - 1) - x >= (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) + // == 0 + // acc1 + (BASE - 1) + 2 * prime[i] - x[i] + // <= acc1 + (BASE - 1) + 2 * prime[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + 2 * (2**BITS_PER_LIMB - 1) + + // (2**BITS_PER_LIMB - 1) + // == 7 + 3 * 2**29 < 2**32 + + acc2 += prime->val[i] + x->val[i]; + // acc2 doesn't overflow 32 bits + // Proof: + // acc2 + prime[i] + x[i] + // <= 2**(32 - BITS_PER_LIMB) - 1 + 2 * (2**BITS_PER_LIMB - 1) + // == 2**(32 - BITS_PER_LIMB) + 2**(BITS_PER_LIMB + 1) - 2 + // == 2**30 + 5 < 2**32 + + // x = acc1 & LIMB_MASK if cond else acc2 & LIMB_MASK + x->val[i] = ((acc1 & tmask) | (acc2 & fmask)) & BN_LIMB_MASK; + + acc1 >>= BN_BITS_PER_LIMB; + // acc1 <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + // acc1 == 2**(BITS_PER_LIMB * (i + 1)) + 2 * prime[:i + 1] - x[:i + 1] + // >> BITS_PER_LIMB * (i + 1) + + acc2 >>= BN_BITS_PER_LIMB; + // acc2 <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + // acc2 == prime[:i + 1] + x[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc1 == 1); // assert prime <= 2**260 + // assert(acc2 == 0); + + // clang-format off + // acc1 == 1 + // Proof: + // acc1 == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime[:LIMBS] - x[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime - x >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2 * prime >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2 * (2**(BITS_PER_LIMB * LIMBS - 1) - 1) >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2**(BITS_PER_LIMB * LIMBS) - 2 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc1 == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime[:LIMBS] - x[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + 2 * prime - x >> BITS_PER_LIMB * LIMBS + // >= 2**(BITS_PER_LIMB * LIMBS) + 0 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc2 == 0 + // Proof: + // acc2 == prime[:LIMBS] + x[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == prime + x >> BITS_PER_LIMB * LIMBS + // <= 2 * prime - 1 >> BITS_PER_LIMB * LIMBS + // <= 2 * (2**(BITS_PER_LIMB * LIMBS - 1) - 1) - 1 >> 261 + // == 2**(BITS_PER_LIMB * LIMBS) - 3 >> BITS_PER_LIMB * LIMBS + // == 0 + // clang-format on +} + +// x <<= 1 +// Assumes x is normalized, x < 2**260 == 2**(LIMBS*BITS_PER_LIMB - 1) +// Guarantees x is normalized +void bn_lshift(bignum256 *x) { + for (int i = BN_LIMBS - 1; i > 0; i--) { + x->val[i] = ((x->val[i] << 1) & BN_LIMB_MASK) | + (x->val[i - 1] >> (BN_BITS_PER_LIMB - 1)); + } + x->val[0] = (x->val[0] << 1) & BN_LIMB_MASK; +} + +// x >>= 1, i.e. x = floor(x/2) +// Assumes x is normalized +// Guarantees x is normalized +// If x is partly reduced (fully reduced) modulo prime, +// guarantess x will be partly reduced (fully reduced) modulo prime +void bn_rshift(bignum256 *x) { + for (int i = 0; i < BN_LIMBS - 1; i++) { + x->val[i] = + (x->val[i] >> 1) | ((x->val[i + 1] & 1) << (BN_BITS_PER_LIMB - 1)); + } + x->val[BN_LIMBS - 1] >>= 1; +} + +// Sets i-th least significant bit (counting from zero) +// Assumes x is normalized and 0 <= i < 261 == LIMBS*BITS_PER_LIMB +// Guarantees x is normalized +// The function has constant control flow but not constant memory access flow +// with regard to i +void bn_setbit(bignum256 *x, uint16_t i) { + assert(i < BN_LIMBS * BN_BITS_PER_LIMB); + x->val[i / BN_BITS_PER_LIMB] |= (1u << (i % BN_BITS_PER_LIMB)); +} + +// clears i-th least significant bit (counting from zero) +// Assumes x is normalized and 0 <= i < 261 == LIMBS*BITS_PER_LIMB +// Guarantees x is normalized +// The function has constant control flow but not constant memory access flow +// with regard to i +void bn_clearbit(bignum256 *x, uint16_t i) { + assert(i < BN_LIMBS * BN_BITS_PER_LIMB); + x->val[i / BN_BITS_PER_LIMB] &= ~(1u << (i % BN_BITS_PER_LIMB)); +} + +// returns i-th least significant bit (counting from zero) +// Assumes x is normalized and 0 <= i < 261 == LIMBS*BITS_PER_LIMB +// The function has constant control flow but not constant memory access flow +// with regard to i +uint32_t bn_testbit(const bignum256 *x, uint16_t i) { + assert(i < BN_LIMBS * BN_BITS_PER_LIMB); + return (x->val[i / BN_BITS_PER_LIMB] >> (i % BN_BITS_PER_LIMB)) & 1; +} + +// res = x ^ y +// Assumes x, y are normalized +// Guarantees res is normalized +// Works properly even if &res == &x or &res == &y or &res == &x == &y +void bn_xor(bignum256 *res, const bignum256 *x, const bignum256 *y) { + for (int i = 0; i < BN_LIMBS; i++) { + res->val[i] = x->val[i] ^ y->val[i]; + } +} + +// x = x / 2 % prime +// Explicitly x = x / 2 if is_even(x) else (x + prime) / 2 +// Assumes x is normalized, x + prime < 261 == LIMBS * BITS_PER_LIMB +// Guarantees x is normalized +// If x is partly reduced (fully reduced) modulo prime, +// guarantess x will be partly reduced (fully reduced) modulo prime +// Assumes prime is an odd number and normalized +void bn_mult_half(bignum256 *x, const bignum256 *prime) { + // x = x / 2 if is_even(x) else (x + prime) / 2 + + uint32_t x_is_odd_mask = + -(x->val[0] & 1); // x_is_odd_mask = 0xFFFFFFFF if is_odd(x) else 0 + + uint32_t acc = (x->val[0] + (prime->val[0] & x_is_odd_mask)) >> 1; + // acc < 2**BITS_PER_LIMB + // Proof: + // acc == x[0] + prime[0] & x_is_odd_mask >> 1 + // <= (2**(BITS_PER_LIMB) - 1) + (2**(BITS_PER_LIMB) - 1) >> 1 + // == 2**(BITS_PER_LIMB + 1) - 2 >> 1 + // < 2**(BITS_PER_LIMB) + + for (int i = 0; i < BN_LIMBS - 1; i++) { + uint32_t temp = (x->val[i + 1] + (prime->val[i + 1] & x_is_odd_mask)); + // temp < 2**(BITS_PER_LIMB + 1) + // Proof: + // temp == x[i + 1] + val[i + 1] & x_is_odd_mask + // <= (2**(BITS_PER_LIMB) - 1) + (2**(BITS_PER_LIMB) - 1) + // < 2**(BITS_PER_LIMB + 1) + + acc += (temp & 1) << (BN_BITS_PER_LIMB - 1); + // acc doesn't overflow 32 bits + // Proof: + // acc + (temp & 1 << BITS_PER_LIMB - 1) + // <= 2**(BITS_PER_LIMB + 1) + 2**(BITS_PER_LIMB - 1) + // <= 2**30 + 2**28 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + acc += temp >> 1; + // acc < 2**(BITS_PER_LIMB + 1) + // Proof: + // acc + (temp >> 1) + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**(BITS_PER_LIMB + 1) - 1 >> 1) + // == 7 + 2**(BITS_PER_LIMB) - 1 < 2**(BITS_PER_LIMB + 1) + + // acc == x[:i+2]+(prime[:i+2] & x_is_odd_mask) >> BITS_PER_LIMB * (i+1) + } + x->val[BN_LIMBS - 1] = acc; + + // assert(acc >> BITS_PER_LIMB == 0); + // acc >> BITS_PER_LIMB == 0 + // Proof: + // acc + // == x[:LIMBS] + (prime[:LIMBS] & x_is_odd_mask) >> BITS_PER_LIMB*LIMBS + // == x + (prime & x_is_odd_mask) >> BITS_PER_LIMB * LIMBS + // <= x + prime >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // == 0 +} + +// x = x * k % prime +// Assumes x is normalized, 0 <= k <= 8 = 2**(32 - BITS_PER_LIMB) +// Assumes prime is normalized and 2^256 - 2^224 <= prime <= 2^256 +// Guarantees x is normalized and partly reduced modulo prime +void bn_mult_k(bignum256 *x, uint8_t k, const bignum256 *prime) { + assert(k <= 8); + + for (int i = 0; i < BN_LIMBS; i++) { + x->val[i] = k * x->val[i]; + // x[i] doesn't overflow 32 bits + // k * x[i] <= 2**(32 - BITS_PER_LIMB) * (2**BITS_PER_LIMB - 1) + // < 2**(32 - BITS_PER_LIMB) * 2**BITS_PER_LIMB == 2**32 + } + + bn_fast_mod(x, prime); +} + +// Reduces partly reduced x modulo prime +// Explicitly x = x if x < prime else x - prime +// Assumes x is partly reduced modulo prime +// Guarantees x is fully reduced modulo prime +// Assumes prime is nonzero and normalized +void bn_mod(bignum256 *x, const bignum256 *prime) { + uint32_t x_less_prime = bn_is_less(x, prime); + + bignum256 temp = {0}; + bn_subtract(x, prime, &temp); + bn_cmov(x, x_less_prime, x, &temp); + + memzero(&temp, sizeof(temp)); +} + +// Auxiliary function for bn_multiply +// res = k * x +// Assumes k and x are normalized +// Guarantees res is normalized 18 digit little endian number in base 2**29 +void bn_multiply_long(const bignum256 *k, const bignum256 *x, + uint32_t res[2 * BN_LIMBS]) { + // Uses long multiplication in base 2**29, see + // https://en.wikipedia.org/wiki/Multiplication_algorithm#Long_multiplication + + uint64_t acc = 0; + + // compute lower half + for (int i = 0; i < BN_LIMBS; i++) { + for (int j = 0; j <= i; j++) { + acc += k->val[j] * (uint64_t)x->val[i - j]; + // acc doesn't overflow 64 bits + // Proof: + // acc <= acc + sum([k[j] * x[i-j] for j in range(i)]) + // <= (2**(64 - BITS_PER_LIMB) - 1) + + // LIMBS * (2**BITS_PER_LIMB - 1) * (2**BITS_PER_LIMB - 1) + // == (2**35 - 1) + 9 * (2**29 - 1) * (2**29 - 1) + // <= 2**35 + 9 * 2**58 < 2**64 + } + + res[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 2**35 - 1 == 2**(64 - BITS_PER_LIMB) - 1 + } + + // compute upper half + for (int i = BN_LIMBS; i < 2 * BN_LIMBS - 1; i++) { + for (int j = i - BN_LIMBS + 1; j < BN_LIMBS; j++) { + acc += k->val[j] * (uint64_t)x->val[i - j]; + // acc doesn't overflow 64 bits + // Proof: + // acc <= acc + sum([k[j] * x[i-j] for j in range(i)]) + // <= (2**(64 - BITS_PER_LIMB) - 1) + // LIMBS * (2**BITS_PER_LIMB - 1) * (2**BITS_PER_LIMB - 1) + // == (2**35 - 1) + 9 * (2**29 - 1) * (2**29 - 1) + // <= 2**35 + 9 * 2**58 < 2**64 + } + + res[i] = acc & (BN_BASE - 1); + acc >>= BN_BITS_PER_LIMB; + // acc < 2**35 == 2**(64 - BITS_PER_LIMB) + } + + res[2 * BN_LIMBS - 1] = acc; +} + +// Auxiliary function for bn_multiply +// Assumes 0 <= d <= 8 == LIMBS - 1 +// Assumes res is normalized and res < 2**(256 + 29*d + 31) +// Guarantess res in normalized and res < 2 * prime * 2**(29*d) +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_multiply_reduce_step(uint32_t res[2 * BN_LIMBS], const bignum256 *prime, + uint32_t d) { + // clang-format off + // Computes res = res - (res // 2**(256 + BITS_PER_LIMB * d)) * prime * 2**(BITS_PER_LIMB * d) + + // res - (res // 2**(256 + BITS_PER_LIMB * d)) * prime * 2**(BITS_PER_LIMB * d) < 2 * prime * 2**(BITS_PER_LIMB * d) + // Proof: + // res - res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * prime + // == res - res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * (2**256 - (2**256 - prime)) + // == res - res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * 2**256 + res // (2**(256 + BITS_PER_LIMB * d)) * 2**(BITS_PER_LIMB * d) * (2**256 - prime) + // == (res % 2**(256 + BITS_PER_LIMB * d)) + res // (2**256 + BITS_PER_LIMB * d) * 2**(BITS_PER_LIMB * d) * (2**256 - prime) + // <= (2**(256 + 29*d + 31) % 2**(256 + 29*d)) + (2**(256 + 29*d + 31) - 1) / (2**256 + 29*d) * 2**(29*d) * (2**256 - prime) + // <= 2**(256 + 29*d) + 2**(256 + 29*d + 31) / (2**256 + 29*d) * 2**(29*d) * (2**256 - prime) + // == 2**(256 + 29*d) + 2**31 * 2**(29*d) * (2**256 - prime) + // == 2**(29*d) * (2**256 + 2**31 * (2*256 - prime)) + // <= 2**(29*d) * (2**256 + 2**31 * 2*224) + // <= 2**(29*d) * (2**256 + 2**255) + // <= 2**(29*d) * 2 * (2**256 - 2**224) + // <= 2 * prime * 2**(29*d) + // clang-format on + + uint32_t coef = + (res[d + BN_LIMBS - 1] >> (256 - (BN_LIMBS - 1) * BN_BITS_PER_LIMB)) + + (res[d + BN_LIMBS] << ((BN_LIMBS * BN_BITS_PER_LIMB) - 256)); + + // coef == res // 2**(256 + BITS_PER_LIMB * d) + + // coef < 2**31 + // Proof: + // coef == res // 2**(256 + BITS_PER_LIMB * d) + // < 2**(256 + 29 * d + 31) // 2**(256 + 29 * d) + // == 2**31 + + const int shift = 31; + uint64_t acc = 1ull << shift; + + for (int i = 0; i < BN_LIMBS; i++) { + acc += (((uint64_t)(BN_BASE - 1)) << shift) + res[d + i] - + prime->val[i] * (uint64_t)coef; + // acc neither overflow 64 bits nor underflow zero + // Proof: + // acc + ((BASE - 1) << shift) + res[d + i] - prime[i] * coef + // >= ((BASE - 1) << shift) - prime[i] * coef + // == 2**shift * (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) * + // (2**31 - 1) + // == (2**shift - 2**31 + 1) * (2**BITS_PER_LIMB - 1) + // == (2**31 - 2**31 + 1) * (2**29 - 1) + // == 2**29 - 1 > 0 + // acc + ((BASE - 1) << shift) + res[d + i] - prime[i] * coef + // <= acc + ((BASE - 1) << shift) + res[d+i] + // <= (2**(64 - BITS_PER_LIMB) - 1) + 2**shift * (2**BITS_PER_LIMB - 1) + // + (2*BITS_PER_LIMB - 1) + // == (2**(64 - BITS_PER_LIMB) - 1) + (2**shift + 1) * + // (2**BITS_PER_LIMB - 1) + // == (2**35 - 1) + (2**31 + 1) * (2**29 - 1) + // <= 2**35 + 2**60 + 2**29 < 2**64 + + res[d + i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 2**(64 - BITS_PER_LIMB) - 1 == 2**35 - 1 + + // acc == (1 << BITS_PER_LIMB * (i + 1) + shift) + res[d : d + i + 1] + // - coef * prime[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // acc += (((uint64_t)(BASE - 1)) << shift) + res[d + LIMBS]; + // acc >>= BITS_PER_LIMB; + // assert(acc <= 1ul << shift); + + // clang-format off + // acc == 1 << shift + // Proof: + // acc + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + res[d : d + LIMBS + 1] - coef * prime[:LIMBS] >> BITS_PER_LIMB * (LIMBS + 1) + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + res[d : d + LIMBS + 1] - coef * prime >> BITS_PER_LIMB * (LIMBS + 1) + + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (res[d : d + LIMBS + 1] - coef * prime) >> BITS_PER_LIMB * (LIMBS + 1) + // <= (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (res[:d] + BASE**d * res[d : d + LIMBS + 1] - BASE**d * coef * prime)//BASE**d >> BITS_PER_LIMB * (LIMBS + 1) + // <= (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (res - BASE**d * coef * prime) // BASE**d >> BITS_PER_LIMB * (LIMBS + 1) + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + (2 * prime * BASE**d) // BASE**d >> BITS_PER_LIMB * (LIMBS + 1) + // <= (1 << 321) + 2 * 2**256 >> 290 + // == 1 << 31 == 1 << shift + + // == (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + res[d : d + LIMBS + 1] - coef * prime[:LIMBS + 1] >> BITS_PER_LIMB * (LIMBS + 1) + // >= (1 << BITS_PER_LIMB * (LIMBS + 1) + shift) + 0 >> BITS_PER_LIMB * (LIMBS + 1) + // == 1 << shift + // clang-format on + + res[d + BN_LIMBS] = 0; +} + +// Auxiliary function for bn_multiply +// Partly reduces res and stores both in x and res +// Assumes res in normalized and res < 2**519 +// Guarantees x is normalized and partly reduced modulo prime +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_multiply_reduce(bignum256 *x, uint32_t res[2 * BN_LIMBS], + const bignum256 *prime) { + for (int i = BN_LIMBS - 1; i >= 0; i--) { + // res < 2**(256 + 29*i + 31) + // Proof: + // if i == LIMBS - 1: + // res < 2**519 + // == 2**(256 + 29 * 8 + 31) + // == 2**(256 + 29 * (LIMBS - 1) + 31) + // else: + // res < 2 * prime * 2**(29 * (i + 1)) + // <= 2**256 * 2**(29*i + 29) < 2**(256 + 29*i + 31) + bn_multiply_reduce_step(res, prime, i); + } + + for (int i = 0; i < BN_LIMBS; i++) { + x->val[i] = res[i]; + } +} + +// x = k * x % prime +// Assumes k, x are normalized, k * x < 2**519 +// Guarantees x is normalized and partly reduced modulo prime +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_multiply(const bignum256 *k, bignum256 *x, const bignum256 *prime) { + uint32_t res[2 * BN_LIMBS] = {0}; + + bn_multiply_long(k, x, res); + bn_multiply_reduce(x, res, prime); + + memzero(res, sizeof(res)); +} + +// Partly reduces x modulo prime +// Assumes limbs of x except the last (the most significant) one are normalized +// Assumes prime is normalized and 2^256 - 2^224 <= prime <= 2^256 +// Guarantees x is normalized and partly reduced modulo prime +void bn_fast_mod(bignum256 *x, const bignum256 *prime) { + // Computes x = x - (x // 2**256) * prime + + // x < 2**((LIMBS - 1) * BITS_PER_LIMB + 32) == 2**264 + + // x - (x // 2**256) * prime < 2 * prime + // Proof: + // x - (x // 2**256) * prime + // == x - (x // 2**256) * (2**256 - (2**256 - prime)) + // == x - ((x // 2**256) * 2**256) + (x // 2**256) * (2**256 - prime) + // == (x % prime) + (x // 2**256) * (2**256 - prime) + // <= prime - 1 + (2**264 // 2**256) * (2**256 - prime) + // <= 2**256 + 2**8 * 2**224 == 2**256 + 2**232 + // < 2 * (2**256 - 2**224) + // <= 2 * prime + + // x - (x // 2**256 - 1) * prime < 2 * prime + // Proof: + // x - (x // 2**256) * prime + prime + // == x - (x // 2**256) * (2**256 - (2**256 - prime)) + prime + // == x - ((x//2**256) * 2**256) + (x//2**256) * (2**256 - prime) + prime + // == (x % prime) + (x // 2**256) * (2**256 - prime) + prime + // <= 2 * prime - 1 + (2**264 // 2**256) * (2**256 - prime) + // <= 2 * prime + 2**8 * 2**224 == 2**256 + 2**232 + 2**256 - 2**224 + // < 2 * (2**256 - 2**224) + // <= 2 * prime + + uint32_t coef = + x->val[BN_LIMBS - 1] >> (256 - ((BN_LIMBS - 1) * BN_BITS_PER_LIMB)); + + // clang-format off + // coef == x // 2**256 + // 0 <= coef < 2**((LIMBS - 1) * BITS_PER_LIMB + 32 - 256) == 256 + // Proof: + //* Let x[[a : b] be the number consisting of a-th to (b-1)-th bit of the number x. + // x[LIMBS - 1] >> (256 - ((LIMBS - 1) * BITS_PER_LIMB)) + // == x[[(LIMBS - 1) * BITS_PER_LIMB : (LIMBS - 1) * BITS_PER_LIMB + 32]] >> (256 - ((LIMBS - 1) * BITS_PER_LIMB)) + // == x[[256 - ((LIMBS - 1) * BITS_PER_LIMB) + (LIMBS - 1) * BITS_PER_LIMB : (LIMBS - 1) * BITS_PER_LIMB + 32]] + // == x[[256 : (LIMBS - 1) * BITS_PER_LIMB + 32]] + // == x[[256 : 264]] == x // 2**256 + // clang-format on + + const int shift = 8; + uint64_t acc = 1ull << shift; + + for (int i = 0; i < BN_LIMBS; i++) { + acc += (((uint64_t)(BN_BASE - 1)) << shift) + x->val[i] - + prime->val[i] * (uint64_t)coef; + // acc neither overflows 64 bits nor underflows 0 + // Proof: + // acc + (BASE - 1 << shift) + x[i] - prime[i] * coef + // >= (BASE - 1 << shift) - prime[i] * coef + // >= 2**shift * (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) * 255 + // == (2**shift - 255) * (2**BITS_PER_LIMB - 1) + // == (2**8 - 255) * (2**29 - 1) == 2**29 - 1 >= 0 + // acc + (BASE - 1 << shift) + x[i] - prime[i] * coef + // <= acc + ((BASE - 1) << shift) + x[i] + // <= (2**(64 - BITS_PER_LIMB) - 1) + 2**shift * (2**BITS_PER_LIMB - 1) + // + (2**32 - 1) + // == (2**35 - 1) + 2**8 * (2**29 - 1) + 2**32 + // < 2**35 + 2**37 + 2**32 < 2**64 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 2**(64 - BITS_PER_LIMB) - 1 == 2**35 - 1 + + // acc == (1 << BITS_PER_LIMB * (i + 1) + shift) + x[:i + 1] + // - coef * prime[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 1 << shift); + + // clang-format off + // acc == 1 << shift + // Proof: + // acc + // == (1 << BITS_PER_LIMB * LIMBS + shift) + x[:LIMBS] - coef * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == (1 << BITS_PER_LIMB * LIMBS + shift) + (x - coef * prime) >> BITS_PER_LIMB * LIMBS + // <= (1 << BITS_PER_LIMB * LIMBS + shift) + (2 * prime) >> BITS_PER_LIMB * LIMBS + // <= (1 << BITS_PER_LIMB * LIMBS + shift) + 2 * 2**256 >> BITS_PER_LIMB * LIMBS + // <= 2**269 + 2**257 >> 2**261 + // <= 1 << 8 == 1 << shift + + // acc + // == (1 << BITS_PER_LIMB * LIMBS + shift) + x[:LIMBS] - coef * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // >= (1 << BITS_PER_LIMB * LIMBS + shift) + 0 >> BITS_PER_LIMB * LIMBS + // == (1 << BITS_PER_LIMB * LIMBS + shift) + 0 >> BITS_PER_LIMB * LIMBS + // <= 1 << 8 == 1 << shift + // clang-format on +} + +// res = x**e % prime +// Assumes both x and e are normalized, x < 2**259 +// Guarantees res is normalized and partly reduced modulo prime +// Works properly even if &x == &res +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to e +void bn_power_mod(const bignum256 *x, const bignum256 *e, + const bignum256 *prime, bignum256 *res) { + // Uses iterative right-to-left exponentiation by squaring, see + // https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method + + bignum256 acc = {0}; + bn_copy(x, &acc); + + bn_one(res); + for (int i = 0; i < BN_LIMBS; i++) { + uint32_t limb = e->val[i]; + + for (int j = 0; j < BN_BITS_PER_LIMB; j++) { + // Break if the following bits of the last limb are zero + if (i == BN_LIMBS - 1 && limb == 0) break; + + if (limb & 1) + // acc * res < 2**519 + // Proof: + // acc * res <= max(2**259 - 1, 2 * prime) * (2 * prime) + // == max(2**259 - 1, 2**257) * 2**257 < 2**259 * 2**257 + // == 2**516 < 2**519 + bn_multiply(&acc, res, prime); + + limb >>= 1; + // acc * acc < 2**519 + // Proof: + // acc * acc <= max(2**259 - 1, 2 * prime)**2 + // <= (2**259)**2 == 2**518 < 2**519 + bn_multiply(&acc, &acc, prime); + } + // acc == x**(e[:i + 1]) % prime + } + + memzero(&acc, sizeof(acc)); +} + +// x = sqrt(x) % prime +// Explicitly x = x**((prime+1)/4) % prime +// The other root is -sqrt(x) +// Assumes x is normalized, x < 2**259 and quadratic residuum mod prime +// Assumes prime is a prime number, prime % 4 == 3, it is normalized and +// 2**256 - 2**224 <= prime <= 2**256 +// Guarantees x is normalized and fully reduced modulo prime +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime +void bn_sqrt(bignum256 *x, const bignum256 *prime) { + // Uses the Lagrange formula for the primes of the special form, see + // http://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus + // If prime % 4 == 3, then sqrt(x) % prime == x**((prime+1)//4) % prime + + assert(prime->val[BN_LIMBS - 1] % 4 == 3); + + // e = (prime + 1) // 4 + bignum256 e = {0}; + bn_copy(prime, &e); + bn_addi(&e, 1); + bn_rshift(&e); + bn_rshift(&e); + + bn_power_mod(x, &e, prime, x); + bn_mod(x, prime); + + memzero(&e, sizeof(e)); +} + +// a = 1/a % 2**n +// Assumes a is odd, 1 <= n <= 32 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to n +uint32_t inverse_mod_power_two(uint32_t a, uint32_t n) { + // Uses "Explicit Quadratic Modular inverse modulo 2" from section 3.3 of "On + // Newton-Raphson iteration for multiplicative inverses modulo prime powers" + // by Jean-Guillaume Dumas, see + // https://arxiv.org/pdf/1209.6626.pdf + + // 1/a % 2**n + // = (2-a) * product([1 + (a-1)**(2**i) for i in range(1, floor(log2(n)))]) + + uint32_t acc = 2 - a; + uint32_t f = a - 1; + + // mask = (1 << n) - 1 + uint32_t mask = n == 32 ? 0xFFFFFFFF : (1u << n) - 1; + + for (uint32_t i = 1; i < n; i <<= 1) { + f = (f * f) & mask; + acc = (acc * (1 + f)) & mask; + } + + return acc; +} + +// x = (x / 2**BITS_PER_LIMB) % prime +// Assumes both x and prime are normalized +// Assumes prime is an odd number and normalized +// Guarantees x is normalized +// If x is partly reduced (fully reduced) modulo prime, +// guarantess x will be partly reduced (fully reduced) modulo prime +void bn_divide_base(bignum256 *x, const bignum256 *prime) { + // Uses an explicit formula for the modular inverse of power of two + // (x / 2**n) % prime == (x + ((-x / prime) % 2**n) * prime) // 2**n + // Proof: + // (x + ((-x / prime) % 2**n) * prime) % 2**n + // == (x - x / prime * prime) % 2**n + // == 0 + // (x + ((-1 / prime) % 2**n) * prime) % prime + // == x + // if x < prime: + // (x + ((-x / prime) % 2**n) * prime) // 2**n + // <= ((prime - 1) + (2**n - 1) * prime) / 2**n + // == (2**n * prime - 1) / 2**n == prime - 1 / 2**n < prime + // if x < 2 * prime: + // (x + ((-x / prime) % 2**n) * prime) // 2**n + // <= ((2 * prime - 1) + (2**n - 1) * prime) / 2**n + // == (2**n * prime + prime - 1) / 2**n + // == prime + (prime - 1) / 2**n < 2 * prime + + // m = (-x / prime) % 2**BITS_PER_LIMB + uint32_t m = (x->val[0] * (BN_BASE - inverse_mod_power_two( + prime->val[0], BN_BITS_PER_LIMB))) & + BN_LIMB_MASK; + // m < 2**BITS_PER_LIMB + + uint64_t acc = x->val[0] + (uint64_t)m * prime->val[0]; + acc >>= BN_BITS_PER_LIMB; + + for (int i = 1; i < BN_LIMBS; i++) { + acc = acc + x->val[i] + (uint64_t)m * prime->val[i]; + // acc does not overflow 64 bits + // acc == acc + x + m * prime + // <= 2**(64 - BITS_PER_LIMB) + 2**(BITS_PER_LIMB) + // 2**(BITS_PER_LIMB) * 2**(BITS_PER_LIMB) + // <= 2**(2 * BITS_PER_LIMB) + 2**(64 - BITS_PER_LIMB) + + // 2**(BITS_PER_LIMB) + // <= 2**58 + 2**35 + 2**29 < 2**64 + + x->val[i - 1] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc < 2**35 == 2**(64 - BITS_PER_LIMB) + + // acc == x[:i + 1] + m * prime[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + x->val[BN_LIMBS - 1] = acc; + + assert(acc >> BN_BITS_PER_LIMB == 0); + + // clang-format off + // acc >> BITS_PER_LIMB == 0 + // Proof: + // acc >> BITS_PER_LIMB + // == (x[:LIMB] + m * prime[:LIMB] >> BITS_PER_LIMB * LIMBS) >> BITS_PER_LIMB * (LIMBS + 1) + // == x + m * prime >> BITS_PER_LIMB * (LIMBS + 1) + // <= (2**(BITS_PER_LIMB * LIMBS) - 1) + (2**BITS_PER_LIMB - 1) * (2**(BITS_PER_LIMB * LIMBS) - 1) >> BITS_PER_LIMB * (LIMBS + 1) + // == 2**(BITS_PER_LIMB * LIMBS) - 1 + 2**(BITS_PER_LIMB * (LIMBS + 1)) - 2**(BITS_PER_LIMB * LIMBS) - 2**BITS_PER_LIMB + 1 >> BITS_PER_LIMB * (LIMBS + 1) + // == 2**(BITS_PER_LIMB * (LIMBS + 1)) - 2**BITS_PER_LIMB >> BITS_PER_LIMB * (LIMBS + 1) + // == 0 + // clang-format on +} + +// x = 1/x % prime if x != 0 else 0 +// Assumes x is normalized +// Assumes prime is a prime number +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime +void bn_inverse_slow(bignum256 *x, const bignum256 *prime) { + // Uses formula 1/x % prime == x**(prime - 2) % prime + // See https://en.wikipedia.org/wiki/Fermat%27s_little_theorem + + bn_fast_mod(x, prime); + + // e = prime - 2 + bignum256 e = {0}; + bn_read_uint32(2, &e); + bn_subtract(prime, &e, &e); + + bn_power_mod(x, &e, prime, x); + bn_mod(x, prime); + + memzero(&e, sizeof(e)); +} + +#if false +// x = 1/x % prime if x != 0 else 0 +// Assumes x is is_normalized +// Assumes GCD(x, prime) = 1 +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is odd, normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to prime and x +void bn_inverse_fast(bignum256 *x, const bignum256 *prime) { + // "The Almost Montgomery Inverse" from the section 3 of "Constant Time + // Modular Inversion" by Joppe W. Bos + // See http://www.joppebos.com/files/CTInversion.pdf + + /* + u = prime + v = x & prime + s = 1 + r = 0 + + k = 0 + while v != 1: + k += 1 + if is_even(u): + u = u // 2 + s = 2 * s + elif is_even(v): + v = v // 2 + r = 2 * r + elif v < u: + u = (u - v) // 2 + r = r + s + s = 2 * s + else: + v = (v - u) // 2 + s = r + s + r = 2 * r + + s = (s / 2**k) % prime + return s + */ + + if (bn_is_zero(x)) return; + + bn_fast_mod(x, prime); + bn_mod(x, prime); + + bignum256 u = {0}, v = {0}, r = {0}, s = {0}; + bn_copy(prime, &u); + bn_copy(x, &v); + bn_one(&s); + bn_zero(&r); + + int k = 0; + while (!bn_is_one(&v)) { + if ((u.val[0] & 1) == 0) { + bn_rshift(&u); + bn_lshift(&s); + } else if ((v.val[0] & 1) == 0) { + bn_rshift(&v); + bn_lshift(&r); + } else if (bn_is_less(&v, &u)) { + bn_subtract(&u, &v, &u); + bn_rshift(&u); + bn_add(&r, &s); + bn_lshift(&s); + } else { + bn_subtract(&v, &u, &v); + bn_rshift(&v); + bn_add(&s, &r); + bn_lshift(&r); + } + k += 1; + assert(!bn_is_zero(&v)); // assert GCD(x, prime) == 1 + } + + // s = s / 2**(k // BITS_PER_LIMB * BITS_PER_LIMB) + for (int i = 0; i < k / BITS_PER_LIMB; i++) { + bn_divide_base(&s, prime); + } + + // s = s / 2**(k % BITS_PER_LIMB) + for (int i = 0; i < k % BN_BITS_PER_LIMB; i++) { + bn_mult_half(&s, prime); + } + + bn_copy(&s, x); + + memzero(&u, sizeof(u)); + memzero(&v, sizeof(v)); + memzero(&r, sizeof(r)); + memzero(&s, sizeof(s)); +} +#endif + +// x = 1/x % prime if x != 0 else 0 +// Assumes x is is_normalized +// Assumes GCD(x, prime) = 1 +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is odd, normalized, 2**256 - 2**224 <= prime <= 2**256 +// The function has constant control flow but not constant memory access flow +// with regard to prime and x +void bn_inverse_fast(bignum256 *x, const bignum256 *prime) { + // Custom constant time version of "The Almost Montgomery Inverse" from the + // section 3 of "Constant Time Modular Inversion" by Joppe W. Bos + // See http://www.joppebos.com/files/CTInversion.pdf + + /* + u = prime + v = x % prime + s = 1 + r = 0 + + k = 0 + while v != 1: + k += 1 + if is_even(u): # b1 + u = u // 2 + s = 2 * s + elif is_even(v): # b2 + v = v // 2 + r = 2 * r + elif v < u: # b3 + u = (u - v) // 2 + r = r + s + s = 2 * s + else: # b4 + v = (v - u) // 2 + s = r + s + r = 2 * r + + s = (s / 2**k) % prime + return s + */ + + bn_fast_mod(x, prime); + bn_mod(x, prime); + + bignum256 u = {0}, v = {0}, r = {0}, s = {0}; + bn_copy(prime, &u); + bn_copy(x, &v); + bn_one(&s); + bn_zero(&r); + + bignum256 zero = {0}; + bn_zero(&zero); + + int k = 0; + + int finished = 0, u_even = 0, v_even = 0, v_less_u = 0, b1 = 0, b2 = 0, + b3 = 0, b4 = 0; + finished = 0; + + for (int i = 0; i < 2 * BN_LIMBS * BN_BITS_PER_LIMB; i++) { + finished = finished | -bn_is_one(&v); + u_even = -bn_is_even(&u); + v_even = -bn_is_even(&v); + v_less_u = -bn_is_less(&v, &u); + + b1 = ~finished & u_even; + b2 = ~finished & ~b1 & v_even; + b3 = ~finished & ~b1 & ~b2 & v_less_u; + b4 = ~finished & ~b1 & ~b2 & ~b3; + +// The ternary operator for pointers with constant control flow +// BN_INVERSE_FAST_TERNARY(c, t, f) = t if c else f +// Very nasty hack, sorry for that +#define BN_INVERSE_FAST_TERNARY(c, t, f) \ + ((void *)(((c) & (uintptr_t)(t)) | (~(c) & (uintptr_t)(f)))) + + bn_subtract(BN_INVERSE_FAST_TERNARY(b3, &u, &v), + BN_INVERSE_FAST_TERNARY( + b3 | b4, BN_INVERSE_FAST_TERNARY(b3, &v, &u), &zero), + BN_INVERSE_FAST_TERNARY(b3, &u, &v)); + + bn_add(BN_INVERSE_FAST_TERNARY(b3, &r, &s), + BN_INVERSE_FAST_TERNARY(b3 | b4, BN_INVERSE_FAST_TERNARY(b3, &s, &r), + &zero)); + bn_rshift(BN_INVERSE_FAST_TERNARY(b1 | b3, &u, &v)); + bn_lshift(BN_INVERSE_FAST_TERNARY(b1 | b3, &s, &r)); + + k = k - ~finished; + } + + // s = s / 2**(k // BITS_PER_LIMB * BITS_PER_LIMB) + for (int i = 0; i < 2 * BN_LIMBS; i++) { + // s = s / 2**BITS_PER_LIMB % prime if i < k // BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_divide_base(&r, prime); + bn_cmov(&s, i < k / BN_BITS_PER_LIMB, &r, &s); + } + + // s = s / 2**(k % BITS_PER_LIMB) + for (int i = 0; i < BN_BITS_PER_LIMB; i++) { + // s = s / 2 % prime if i < k % BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_mult_half(&r, prime); + bn_cmov(&s, i < k % BN_BITS_PER_LIMB, &r, &s); + } + + bn_cmov(x, bn_is_zero(x), x, &s); + + memzero(&u, sizeof(u)); + memzero(&v, sizeof(v)); + memzero(&r, sizeof(s)); + memzero(&s, sizeof(s)); +} + +#if false +// x = 1/x % prime if x != 0 else 0 +// Assumes x is is_normalized +// Assumes GCD(x, prime) = 1 +// Guarantees x is normalized and fully reduced modulo prime +// Assumes prime is odd, normalized, 2**256 - 2**224 <= prime <= 2**256 +void bn_inverse_fast(bignum256 *x, const bignum256 *prime) { + // Custom constant time version of "The Almost Montgomery Inverse" from the + // section 3 of "Constant Time Modular Inversion" by Joppe W. Bos + // See http://www.joppebos.com/files/CTInversion.pdf + + /* + u = prime + v = x % prime + s = 1 + r = 0 + + k = 0 + while v != 1: + k += 1 + if is_even(u): # b1 + u = u // 2 + s = 2 * s + elif is_even(v): # b2 + v = v // 2 + r = 2 * r + elif v < u: # b3 + u = (u - v) // 2 + r = r + s + s = 2 * s + else: # b4 + v = (v - u) // 2 + s = r + s + r = 2 * r + + s = (s / 2**k) % prime + return s + */ + + bn_fast_mod(x, prime); + bn_mod(x, prime); + + bignum256 u = {0}, v = {0}, r = {0}, s = {0}; + bn_copy(prime, &u); + bn_copy(x, &v); + bn_one(&s); + bn_zero(&r); + + bignum256 zero = {0}; + bn_zero(&zero); + + int k = 0; + + uint32_t finished = 0, u_even = 0, v_even = 0, v_less_u = 0, b1 = 0, b2 = 0, + b3 = 0, b4 = 0; + finished = 0; + + bignum256 u_half = {0}, v_half = {0}, u_minus_v_half = {0}, v_minus_u_half = {0}, r_plus_s = {0}, r_twice = {0}, s_twice = {0}; + for (int i = 0; i < 2 * BN_LIMBS * BN_BITS_PER_LIMB; i++) { + finished = finished | bn_is_one(&v); + u_even = bn_is_even(&u); + v_even = bn_is_even(&v); + v_less_u = bn_is_less(&v, &u); + + b1 = (finished ^ 1) & u_even; + b2 = (finished ^ 1) & (b1 ^ 1) & v_even; + b3 = (finished ^ 1) & (b1 ^ 1) & (b2 ^ 1) & v_less_u; + b4 = (finished ^ 1) & (b1 ^ 1) & (b2 ^ 1) & (b3 ^ 1); + + // u_half = u // 2 + bn_copy(&u, &u_half); + bn_rshift(&u_half); + + // v_half = v // 2 + bn_copy(&v, &v_half); + bn_rshift(&v_half); + + // u_minus_v_half = (u - v) // 2 + bn_subtract(&u, &v, &u_minus_v_half); + bn_rshift(&u_minus_v_half); + + // v_minus_u_half = (v - u) // 2 + bn_subtract(&v, &u, &v_minus_u_half); + bn_rshift(&v_minus_u_half); + + // r_plus_s = r + s + bn_copy(&r, &r_plus_s); + bn_add(&r_plus_s, &s); + + // r_twice = 2 * r + bn_copy(&r, &r_twice); + bn_lshift(&r_twice); + + // s_twice = 2 * s + bn_copy(&s, &s_twice); + bn_lshift(&s_twice); + + bn_cmov(&u, b1, &u_half, &u); + bn_cmov(&u, b3, &u_minus_v_half, &u); + + bn_cmov(&v, b2, &v_half, &v); + bn_cmov(&v, b4, &v_minus_u_half, &v); + + bn_cmov(&r, b2 | b4, &r_twice, &r); + bn_cmov(&r, b3, &r_plus_s, &r); + + bn_cmov(&s, b1 | b3, &s_twice, &s); + bn_cmov(&s, b4, &r_plus_s, &s); + + k = k + (finished ^ 1); + } + + // s = s / 2**(k // BITS_PER_LIMB * BITS_PER_LIMB) + for (int i = 0; i < 2 * BN_LIMBS; i++) { + // s = s / 2**BITS_PER_LIMB % prime if i < k // BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_divide_base(&r, prime); + bn_cmov(&s, i < k / BITS_PER_LIMB, &r, &s); + } + + // s = s / 2**(k % BITS_PER_LIMB) + for (int i = 0; i < BN_BITS_PER_LIMB; i++) { + // s = s / 2 % prime if i < k % BITS_PER_LIMB else s + bn_copy(&s, &r); + bn_mult_half(&r, prime); + bn_cmov(&s, i < k % BN_BITS_PER_LIMB, &r, &s); + } + + bn_cmov(x, bn_is_zero(x), x, &s); + + memzero(&u, sizeof(u)); + memzero(&v, sizeof(v)); + memzero(&r, sizeof(r)); + memzero(&s, sizeof(s)); + memzero(&u_half, sizeof(u_half)); + memzero(&v_half, sizeof(v_half)); + memzero(&u_minus_v_half, sizeof(u_minus_v_half)); + memzero(&v_minus_u_half, sizeof(v_minus_u_half)); + memzero(&r_twice, sizeof(r_twice)); + memzero(&s_twice, sizeof(s_twice)); + memzero(&r_plus_s, sizeof(r_plus_s)); +} +#endif + +// Normalizes x +// Assumes x < 2**261 == 2**(LIMBS * BITS_PER_LIMB) +// Guarantees x is normalized +void bn_normalize(bignum256 *x) { + uint32_t acc = 0; + + for (int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // acc + x[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + // == 7 + 2**29 - 1 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= (BN_BITS_PER_LIMB); + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + } +} + +// x = x + y +// Assumes x, y are normalized, x + y < 2**(LIMBS*BITS_PER_LIMB) == 2**261 +// Guarantees x is normalized +// Works properly even if &x == &y +void bn_add(bignum256 *x, const bignum256 *y) { + uint32_t acc = 0; + for (int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i] + y->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // acc + x[i] + y[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + 2 * (2**BITS_PER_LIMB - 1) + // == (2**(32 - BITS_PER_LIMB) - 1) + 2**(BITS_PER_LIMB + 1) - 2 + // == 7 + 2**30 - 2 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == x[:i + 1] + y[:i + 1] >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 0); // assert x + y < 2**261 + // acc == 0 + // Proof: + // acc == x[:LIMBS] + y[:LIMBS] >> LIMBS * BITS_PER_LIMB + // == x + y >> LIMBS * BITS_PER_LIMB + // <= 2**(LIMBS * BITS_PER_LIMB) - 1 >> LIMBS * BITS_PER_LIMB == 0 +} + +// x = x + y % prime +// Assumes x, y are normalized +// Guarantees x is normalized and partly reduced modulo prime +// Assumes prime is normalized and 2^256 - 2^224 <= prime <= 2^256 +void bn_addmod(bignum256 *x, const bignum256 *y, const bignum256 *prime) { + for (int i = 0; i < BN_LIMBS; i++) { + x->val[i] += y->val[i]; + // x[i] doesn't overflow 32 bits + // Proof: + // x[i] + y[i] + // <= 2 * (2**BITS_PER_LIMB - 1) + // == 2**30 - 2 < 2**32 + } + + bn_fast_mod(x, prime); +} + +// x = x + y +// Assumes x is normalized +// Assumes y <= 2**32 - 2**29 == 2**32 - 2**BITS_PER_LIMB and +// x + y < 2**261 == 2**(LIMBS * BITS_PER_LIMB) +// Guarantees x is normalized +void bn_addi(bignum256 *x, uint32_t y) { + // assert(y <= 3758096384); // assert y <= 2**32 - 2**29 + uint32_t acc = y; + + for (int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // if i == 0: + // acc + x[i] == y + x[0] + // <= (2**32 - 2**BITS_PER_LIMB) + (2**BITS_PER_LIMB - 1) + // == 2**32 - 1 < 2**32 + // else: + // acc + x[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + // == 7 + 2**29 - 1 < 2**32 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= (BN_BITS_PER_LIMB); + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == x[:i + 1] + y >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 0); // assert x + y < 2**261 + // acc == 0 + // Proof: + // acc == x[:LIMBS] + y << LIMBS * BITS_PER_LIMB + // == x + y << LIMBS * BITS_PER_LIMB + // <= 2**(LIMBS + BITS_PER_LIMB) - 1 << LIMBS * BITS_PER_LIMB + // == 0 +} + +// x = x - y % prime +// Explicitly x = x + prime - y +// Assumes x, y are normalized +// Assumes y < prime[0], x + prime - y < 2**261 == 2**(LIMBS * BITS_PER_LIMB) +// Guarantees x is normalized +// If x is fully reduced modulo prime, +// guarantess x will be partly reduced modulo prime +// Assumes prime is nonzero and normalized +void bn_subi(bignum256 *x, uint32_t y, const bignum256 *prime) { + assert(y < prime->val[0]); + + // x = x + prime - y + + uint32_t acc = -y; + for (int i = 0; i < BN_LIMBS; i++) { + acc += x->val[i] + prime->val[i]; + // acc neither overflows 32 bits nor underflows 0 + // Proof: + // acc + x[i] + prime[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + 2 * (2**BITS_PER_LIMB - 1) + // <= 7 + 2**30 - 2 < 2**32 + // acc + x[i] + prime[i] + // >= -y + prime[0] >= 0 + + x->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == x[:i + 1] + prime[:i + 1] - y >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 0); // assert x + prime - y < 2**261 + // acc == 0 + // Proof: + // acc == x[:LIMBS] + prime[:LIMBS] - y >> BITS_PER_LIMB * LIMBS + // == x + prime - y >> BITS_PER_LIMB * LIMBS + // <= 2**(LIMBS * BITS_PER_LIMB) - 1 >> BITS_PER_LIMB * LIMBS == 0 +} + +// res = x - y % prime +// Explicitly res = x + (2 * prime - y) +// Assumes x, y are normalized, y is partly reduced +// Assumes x + 2 * prime - y < 2**261 == 2**(BITS_PER_LIMB * LIMBS) +// Guarantees res is normalized +// Assumes prime is nonzero and normalized +void bn_subtractmod(const bignum256 *x, const bignum256 *y, bignum256 *res, + const bignum256 *prime) { + // res = x + (2 * prime - y) + + uint32_t acc = 1; + + for (int i = 0; i < BN_LIMBS; i++) { + acc += (BN_BASE - 1) + x->val[i] + 2 * prime->val[i] - y->val[i]; + // acc neither overflows 32 bits nor underflows 0 + // Proof: + // acc + (BASE - 1) + x[i] + 2 * prime[i] - y[i] + // >= (BASE - 1) - y[i] + // == (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) == 0 + // acc + (BASE - 1) + x[i] + 2 * prime[i] - y[i] + // <= acc + (BASE - 1) + x[i] + 2 * prime[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + + // (2**BITS_PER_LIMB - 1) + 2 * (2**BITS_PER_LIMB - 1) + // <= (2**(32 - BITS_PER_LIMB) - 1) + 4 * (2**BITS_PER_LIMB - 1) + // == 7 + 4 * 2**29 - 4 == 2**31 + 3 < 2**32 + + res->val[i] = acc & (BN_BASE - 1); + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == 2**(BITS_PER_LIMB * (i + 1)) + x[:i+1] - y[:i+1] + 2*prime[:i+1] + // >> BITS_PER_LIMB * (i+1) + } + + // assert(acc == 1); // assert x + 2 * prime - y < 2**261 + + // clang-format off + // acc == 1 + // Proof: + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] + 2 * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y + 2 * prime >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x + (2 * prime - y) >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // <= 2 * 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] + 2 * prime[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y + 2 * prime >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x + (2 * prime - y) >> BITS_PER_LIMB * LIMBS + // >= 2**(BITS_PER_LIMB * LIMBS) + 0 + 1 >> BITS_PER_LIMB * LIMBS + // == 1 + // clang-format on +} + +// res = x - y +// Assumes x, y are normalized and x >= y +// Guarantees res is normalized +// Works properly even if &x == &y or &x == &res or &y == &res or +// &x == &y == &res +void bn_subtract(const bignum256 *x, const bignum256 *y, bignum256 *res) { + uint32_t acc = 1; + for (int i = 0; i < BN_LIMBS; i++) { + acc += (BN_BASE - 1) + x->val[i] - y->val[i]; + // acc neither overflows 32 bits nor underflows 0 + // Proof: + // acc + (BASE - 1) + x[i] - y[i] + // >= (BASE - 1) - y == (2**BITS_PER_LIMB - 1) - (2**BITS_PER_LIMB - 1) + // == 0 + // acc + (BASE - 1) + x[i] - y[i] + // <= acc + (BASE - 1) + x[i] + // <= (2**(32 - BITS_PER_LIMB) - 1) + (2**BITS_PER_LIMB - 1) + + // (2**BITS_PER_LIMB - 1) + // == 7 + 2 * 2**29 < 2 **32 + + res->val[i] = acc & BN_LIMB_MASK; + acc >>= BN_BITS_PER_LIMB; + // acc <= 7 == 2**(32 - BITS_PER_LIMB) - 1 + + // acc == 2**(BITS_PER_LIMB * (i + 1)) + x[:i + 1] - y[:i + 1] + // >> BITS_PER_LIMB * (i + 1) + } + + // assert(acc == 1); // assert x >= y + + // clang-format off + // acc == 1 + // Proof: + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x >> BITS_PER_LIMB * LIMBS + // <= 2**(BITS_PER_LIMB * LIMBS) + 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // <= 2 * 2**(BITS_PER_LIMB * LIMBS) - 1 >> BITS_PER_LIMB * LIMBS + // == 1 + + // acc == 2**(BITS_PER_LIMB * LIMBS) + x[:LIMBS] - y[:LIMBS] >> BITS_PER_LIMB * LIMBS + // == 2**(BITS_PER_LIMB * LIMBS) + x - y >> BITS_PER_LIMB * LIMBS + // >= 2**(BITS_PER_LIMB * LIMBS) >> BITS_PER_LIMB * LIMBS + // == 1 +} + +// q = x // d, r = x % d +// Assumes x is normalized, 1 <= d <= 61304 +// Guarantees q is normalized +void bn_long_division(bignum256 *x, uint32_t d, bignum256 *q, uint32_t *r) { + assert(1 <= d && d < 61304); + + uint32_t acc = 0; + + *r = x->val[BN_LIMBS - 1] % d; + q->val[BN_LIMBS - 1] = x->val[BN_LIMBS - 1] / d; + + for (int i = BN_LIMBS - 2; i >= 0; i--) { + acc = *r * (BN_BASE % d) + x->val[i]; + // acc doesn't overflow 32 bits + // Proof: + // r * (BASE % d) + x[i] + // <= (d - 1) * (d - 1) + (2**BITS_PER_LIMB - 1) + // == d**2 - 2*d + 2**BITS_PER_LIMB + // == 61304**2 - 2 * 61304 + 2**29 + // == 3758057808 + 2**29 < 2**32 + + q->val[i] = *r * (BN_BASE / d) + (acc / d); + // q[i] doesn't overflow 32 bits + // Proof: + // r * (BASE // d) + (acc // d) + // <= (d - 1) * (2**BITS_PER_LIMB / d) + + // ((d**2 - 2*d + 2**BITS_PER_LIMB) / d) + // <= (d - 1) * (2**BITS_PER_LIMB / d) + (d - 2 + 2**BITS_PER_LIMB / d) + // == (d - 1 + 1) * (2**BITS_PER_LIMB / d) + d - 2 + // == 2**BITS_PER_LIMB + d - 2 <= 2**29 + 61304 < 2**32 + + // q[i] == (r * BASE + x[i]) // d + // Proof: + // q[i] == r * (BASE // d) + (acc // d) + // == r * (BASE // d) + (r * (BASE % d) + x[i]) // d + // == (r * d * (BASE // d) + r * (BASE % d) + x[i]) // d + // == (r * (d * (BASE // d) + (BASE % d)) + x[i]) // d + // == (r * BASE + x[i]) // d + + // q[i] < 2**BITS_PER_LIMB + // Proof: + // q[i] == (r * BASE + x[i]) // d + // <= ((d - 1) * 2**BITS_PER_LIMB + (2**BITS_PER_LIMB - 1)) / d + // == (d * 2**BITS_PER_LIMB - 1) / d == 2**BITS_PER_LIMB - 1 / d + // < 2**BITS_PER_LIMB + + *r = acc % d; + // r == (r * BASE + x[i]) % d + // Proof: + // r == acc % d == (r * (BASE % d) + x[i]) % d + // == (r * BASE + x[i]) % d + + // x[:i] == q[:i] * d + r + } +} + +// x = x // 58, r = x % 58 +// Assumes x is normalized +// Guarantees x is normalized +void bn_divmod58(bignum256 *x, uint32_t *r) { bn_long_division(x, 58, x, r); } + +// x = x // 1000, r = x % 1000 +// Assumes x is normalized +// Guarantees x is normalized +void bn_divmod1000(bignum256 *x, uint32_t *r) { + bn_long_division(x, 1000, x, r); +} + +// x = x // 10, r = x % 10 +// Assumes x is normalized +// Guarantees x is normalized +void bn_divmod10(bignum256 *x, uint32_t *r) { bn_long_division(x, 10, x, r); } + +// Formats amount +// Assumes amount is normalized +// Assumes prefix and suffix are null-terminated strings +// Assumes output is an array of length output_length +// The function doesn't have neither constant control flow nor constant memory +// access flow with regard to any its argument +size_t bn_format(const bignum256 *amount, const char *prefix, const char *suffix, unsigned int decimals, int exponent, bool trailing, char *output, size_t output_length) { + +/* + Python prototype of the function: + + def format(amount, prefix, suffix, decimals, exponent, trailing): + if exponent >= 0: + amount *= 10 ** exponent + else: + amount //= 10 ** (-exponent) + + d = pow(10, decimals) + + if decimals: + output = "%d.%0*d" % (amount // d, decimals, amount % d) + if not trailing: + output = output.rstrip("0").rstrip(".") + else: + output = "%d" % (amount // d) + + return prefix + output + suffix +*/ + +// Auxiliary macro for bn_format +// If enough space adds one character to output starting from the end +#define BN_FORMAT_ADD_OUTPUT_CHAR(c) \ + { \ + --position; \ + if (output <= position && position < output + output_length) { \ + *position = (c); \ + } else { \ + memset(output, '\0', output_length); \ + return 0; \ + } \ + } + + bignum256 temp = {0}; + bn_copy(amount, &temp); + uint32_t digit = 0; + + char *position = output + output_length; + + // Add string ending character + BN_FORMAT_ADD_OUTPUT_CHAR('\0'); + + // Add suffix + size_t suffix_length = suffix ? strlen(suffix) : 0; + for (int i = suffix_length - 1; i >= 0; --i) + BN_FORMAT_ADD_OUTPUT_CHAR(suffix[i]) + + // amount //= 10**exponent + for (; exponent < 0; ++exponent) { + // if temp == 0, there is no need to divide it by 10 anymore + if (bn_is_zero(&temp)) { + exponent = 0; + break; + } + bn_divmod10(&temp, &digit); + } + + // exponent >= 0 && decimals >= 0 + + bool fractional_part = false; // is fractional-part of amount present + + { // Add fractional-part digits of amount + // Add trailing zeroes + unsigned int trailing_zeros = decimals < (unsigned int) exponent ? decimals : (unsigned int) exponent; + // When casting a negative int to unsigned int, UINT_MAX is added to the int before + // Since exponent >= 0, the value remains unchanged + decimals -= trailing_zeros; + exponent -= trailing_zeros; + + if (trailing && trailing_zeros) { + fractional_part = true; + for (; trailing_zeros > 0; --trailing_zeros) + BN_FORMAT_ADD_OUTPUT_CHAR('0') + } + + // exponent == 0 || decimals == 0 + + // Add significant digits and leading zeroes + for (; decimals > 0; --decimals) { + bn_divmod10(&temp, &digit); + + if (fractional_part || digit || trailing) { + fractional_part = true; + BN_FORMAT_ADD_OUTPUT_CHAR('0' + digit) + } + else if (bn_is_zero(&temp)) { + // We break since the remaining digits are zeroes and fractional_part == trailing == false + decimals = 0; + break; + } + } + // decimals == 0 + } + + if (fractional_part) { + BN_FORMAT_ADD_OUTPUT_CHAR('.') + } + + { // Add integer-part digits of amount + // Add trailing zeroes + if (!bn_is_zero(&temp)) { + for (; exponent > 0; --exponent) { + BN_FORMAT_ADD_OUTPUT_CHAR('0') + } + } + // decimals == 0 && exponent == 0 + + // Add significant digits + do { + bn_divmod10(&temp, &digit); + BN_FORMAT_ADD_OUTPUT_CHAR('0' + digit) + } while (!bn_is_zero(&temp)); + } + + // Add prefix + size_t prefix_length = prefix ? strlen(prefix) : 0; + for (int i = prefix_length - 1; i >= 0; --i) + BN_FORMAT_ADD_OUTPUT_CHAR(prefix[i]) + + // Move formatted amount to the start of output + int length = output - position + output_length; + memmove(output, position, length); + return length - 1; +} + +#if USE_BN_PRINT +// Prints x in hexadecimal +// Assumes x is normalized and x < 2**256 +void bn_print(const bignum256 *x) { + printf("%06x", x->val[8]); + printf("%08x", ((x->val[7] << 3) | (x->val[6] >> 26))); + printf("%07x", ((x->val[6] << 2) | (x->val[5] >> 27)) & 0x0FFFFFFF); + printf("%07x", ((x->val[5] << 1) | (x->val[4] >> 28)) & 0x0FFFFFFF); + printf("%07x", x->val[4] & 0x0FFFFFFF); + printf("%08x", ((x->val[3] << 3) | (x->val[2] >> 26))); + printf("%07x", ((x->val[2] << 2) | (x->val[1] >> 27)) & 0x0FFFFFFF); + printf("%07x", ((x->val[1] << 1) | (x->val[0] >> 28)) & 0x0FFFFFFF); + printf("%07x", x->val[0] & 0x0FFFFFFF); +} + +// Prints comma separated list of limbs of x +void bn_print_raw(const bignum256 *x) { + for (int i = 0; i < BN_LIMBS - 1; i++) { + printf("0x%08x, ", x->val[i]); + } + printf("0x%08x", x->val[BN_LIMBS - 1]); +} +#endif + +#if USE_INVERSE_FAST +void bn_inverse(bignum256 *x, const bignum256 *prime) { + bn_inverse_fast(x, prime); +} +#else +void bn_inverse(bignum256 *x, const bignum256 *prime) { + bn_inverse_slow(x, prime); +} +#endif diff --git a/tools/windows-replace/trezor-crypto/crypto/blake2b.c b/tools/windows-replace/trezor-crypto/crypto/blake2b.c new file mode 100644 index 00000000000..c630ed0f8cf --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/blake2b.c @@ -0,0 +1,345 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif +typedef struct blake2b_param__ +{ + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ +} //win __attribute__((packed)) blake2b_param; + +#ifndef _MSC_VER +__attribute__((packed)) +#endif +blake2b_param; +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +const uint64_t blake2b_IV[8] = +{ + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; + +const uint8_t blake2b_sigma[12][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } +}; + + +static void blake2b_set_lastnode( blake2b_state *S ) +{ + S->f[1] = (uint64_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2b_is_lastblock( const blake2b_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2b_set_lastblock( blake2b_state *S ) +{ + if( S->last_node ) blake2b_set_lastnode( S ); + + S->f[0] = (uint64_t)-1; +} + +static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + +static void blake2b_init0( blake2b_state *S ) +{ + size_t i = 0; + memzero( S, sizeof( blake2b_state ) ); + + for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; +} + +/* init xors IV with input parameter block */ +int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) +{ + const uint8_t *p = ( const uint8_t * )( P ); + size_t i = 0; + + blake2b_init0( S ); + + /* IV XOR ParamBlock */ + for( i = 0; i < 8; ++i ) + S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); + + S->outlen = P->digest_length; + return 0; +} + + +/* Sequential blake2b initialization */ +int blake2b_Init( blake2b_state *S, size_t outlen ) +{ + blake2b_param P[1] = {0}; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memzero( P->reserved, sizeof( P->reserved ) ); + memzero( P->salt, sizeof( P->salt ) ); + memzero( P->personal, sizeof( P->personal ) ); + return blake2b_init_param( S, P ); +} + +int blake2b_InitPersonal( blake2b_state *S, size_t outlen, const void *personal, size_t personal_len) +{ + blake2b_param P[1] = {0}; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + if ( ( !personal ) || ( personal_len != BLAKE2B_PERSONALBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memzero( P->reserved, sizeof( P->reserved ) ); + memzero( P->salt, sizeof( P->salt ) ); + memcpy( P->personal, personal, BLAKE2B_PERSONALBYTES ); + return blake2b_init_param( S, P ); +} + +int blake2b_InitKey( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2b_param P[1] = {0}; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memzero( P->reserved, sizeof( P->reserved ) ); + memzero( P->salt, sizeof( P->salt ) ); + memzero( P->personal, sizeof( P->personal ) ); + + if( blake2b_init_param( S, P ) < 0 ) return -1; + + { + uint8_t block[BLAKE2B_BLOCKBYTES] = {0}; + memzero( block, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2b_Update( S, block, BLAKE2B_BLOCKBYTES ); + memzero( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2*i+0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2*i+1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while(0) + +static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) +{ + uint64_t m[16] = {0}; + uint64_t v[16] = {0}; + size_t i = 0; + + for( i = 0; i < 16; ++i ) { + m[i] = load64( block + i * sizeof( m[i] ) ); + } + + for( i = 0; i < 8; ++i ) { + v[i] = S->h[i]; + } + + v[ 8] = blake2b_IV[0]; + v[ 9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + ROUND( 10 ); + ROUND( 11 ); + + for( i = 0; i < 8; ++i ) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2b_Update( blake2b_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); + blake2b_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress( S, in ); + in += BLAKE2B_BLOCKBYTES; + inlen -= BLAKE2B_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + +int blake2b_Final( blake2b_state *S, void *out, size_t outlen ) +{ + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + size_t i = 0; + + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2b_is_lastblock( S ) ) + return -1; + + blake2b_increment_counter( S, S->buflen ); + blake2b_set_lastblock( S ); + memzero( S->buf + S->buflen, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ + blake2b_compress( S, S->buf ); + + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, S->outlen ); + memzero(buffer, sizeof(buffer)); + return 0; +} + +int blake2b(const uint8_t *msg, uint32_t msg_len, void *out, size_t outlen) +{ + BLAKE2B_CTX ctx; + if (0 != blake2b_Init(&ctx, outlen)) return -1; + if (0 != blake2b_Update(&ctx, msg, msg_len)) return -1; + if (0 != blake2b_Final(&ctx, out, outlen)) return -1; + return 0; +} + +// [wallet-core] +int blake2b_Personal(const uint8_t *msg, uint32_t msg_len, const void *personal, size_t personal_len, void *out, size_t outlen) +{ + BLAKE2B_CTX ctx; + if (0 != blake2b_InitPersonal(&ctx, outlen, personal, personal_len)) return -1; + if (0 != blake2b_Update(&ctx, msg, msg_len)) return -1; + if (0 != blake2b_Final(&ctx, out, outlen)) return -1; + return 0; +} + +int blake2b_Key(const uint8_t *msg, uint32_t msg_len, const void *key, size_t keylen, void *out, size_t outlen) +{ + BLAKE2B_CTX ctx; + if (0 != blake2b_InitKey(&ctx, outlen, key, keylen)) return -1; + if (0 != blake2b_Update(&ctx, msg, msg_len)) return -1; + if (0 != blake2b_Final(&ctx, out, outlen)) return -1; + return 0; +} diff --git a/tools/windows-replace/trezor-crypto/crypto/blake2s.c b/tools/windows-replace/trezor-crypto/crypto/blake2s.c new file mode 100644 index 00000000000..6de68990302 --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/blake2s.c @@ -0,0 +1,329 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include + +#include +#include +#include + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif + +typedef struct blake2s_param__ +{ + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint16_t xof_length; /* 14 */ + uint8_t node_depth; /* 15 */ + uint8_t inner_length; /* 16 */ + /* uint8_t reserved[0]; */ + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ +} // win __attribute__((packed)) blake2s_param; +#ifndef _MSC_VER +__attribute__((packed)) +#endif +blake2s_param; +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +const uint32_t blake2s_IV[8] = +{ + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +const uint8_t blake2s_sigma[10][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + +static void blake2s_set_lastnode( blake2s_state *S ) +{ + S->f[1] = (uint32_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2s_is_lastblock( const blake2s_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2s_set_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_set_lastnode( S ); + + S->f[0] = (uint32_t)-1; +} + +static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + +static void blake2s_init0( blake2s_state *S ) +{ + size_t i = 0; + memzero( S, sizeof( blake2s_state ) ); + + for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i]; +} + +/* init2 xors IV with input parameter block */ +int blake2s_init_param( blake2s_state *S, const blake2s_param *P ) +{ + const unsigned char *p = ( const unsigned char * )( P ); + size_t i = 0; + + blake2s_init0( S ); + + /* IV XOR ParamBlock */ + for( i = 0; i < 8; ++i ) + S->h[i] ^= load32( &p[i * 4] ); + + S->outlen = P->digest_length; + return 0; +} + + +/* Sequential blake2s initialization */ +int blake2s_Init( blake2s_state *S, size_t outlen ) +{ + blake2s_param P[1] = {0}; + + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + /* memzero(P->reserved, sizeof(P->reserved) ); */ + memzero( P->salt, sizeof( P->salt ) ); + memzero( P->personal, sizeof( P->personal ) ); + return blake2s_init_param( S, P ); +} + +int blake2s_InitPersonal( blake2s_state *S, size_t outlen, const void *personal, size_t personal_len) +{ + blake2s_param P[1] = {0}; + + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + if ( ( !personal ) || ( personal_len != BLAKE2S_PERSONALBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + /* memzero(P->reserved, sizeof(P->reserved) ); */ + memzero( P->salt, sizeof( P->salt ) ); + memcpy( P->personal, personal, BLAKE2S_PERSONALBYTES ); + return blake2s_init_param( S, P ); +} + + +int blake2s_InitKey( blake2s_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2s_param P[1] = {0}; + + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + /* memzero(P->reserved, sizeof(P->reserved) ); */ + memzero( P->salt, sizeof( P->salt ) ); + memzero( P->personal, sizeof( P->personal ) ); + + if( blake2s_init_param( S, P ) < 0 ) return -1; + + { + uint8_t block[BLAKE2S_BLOCKBYTES] = {0}; + memzero( block, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2s_Update( S, block, BLAKE2S_BLOCKBYTES ); + memzero( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2s_sigma[r][2*i+0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2*i+1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while(0) + +static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] ) +{ + uint32_t m[16] = {0}; + uint32_t v[16] = {0}; + size_t i = 0; + + for( i = 0; i < 16; ++i ) { + m[i] = load32( in + i * sizeof( m[i] ) ); + } + + for( i = 0; i < 8; ++i ) { + v[i] = S->h[i]; + } + + v[ 8] = blake2s_IV[0]; + v[ 9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + + for( i = 0; i < 8; ++i ) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2s_Update( blake2s_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2S_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2S_BLOCKBYTES) { + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress( S, in ); + in += BLAKE2S_BLOCKBYTES; + inlen -= BLAKE2S_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + +int blake2s_Final( blake2s_state *S, void *out, size_t outlen ) +{ + uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; + size_t i = 0; + + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2s_is_lastblock( S ) ) + return -1; + + blake2s_increment_counter( S, ( uint32_t )S->buflen ); + blake2s_set_lastblock( S ); + memzero( S->buf + S->buflen, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ + blake2s_compress( S, S->buf ); + + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store32( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, outlen ); + memzero(buffer, sizeof(buffer)); + return 0; +} + +int blake2s(const uint8_t *msg, uint32_t msg_len, void *out, size_t outlen) +{ + BLAKE2S_CTX ctx; + if (0 != blake2s_Init(&ctx, outlen)) return -1; + if (0 != blake2s_Update(&ctx, msg, msg_len)) return -1; + if (0 != blake2s_Final(&ctx, out, outlen)) return -1; + return 0; +} + +int blake2s_Key(const uint8_t *msg, uint32_t msg_len, const void *key, size_t keylen, void *out, size_t outlen) +{ + BLAKE2S_CTX ctx; + if (0 != blake2s_InitKey(&ctx, outlen, key, keylen)) return -1; + if (0 != blake2s_Update(&ctx, msg, msg_len)) return -1; + if (0 != blake2s_Final(&ctx, out, outlen)) return -1; + return 0; +} diff --git a/tools/windows-replace/trezor-crypto/crypto/cardano.c b/tools/windows-replace/trezor-crypto/crypto/cardano.c new file mode 100644 index 00000000000..650b66ad513 --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/cardano.c @@ -0,0 +1,328 @@ +/** + * Copyright (c) 2013-2021 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if USE_CARDANO + +#define CARDANO_MAX_NODE_DEPTH 1048576 + +const curve_info ed25519_cardano_info = { + .bip32_name = ED25519_CARDANO_NAME, + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +static void scalar_multiply8(const uint8_t *src, int bytes, uint8_t *dst) { + uint8_t prev_acc = 0; + for (int i = 0; i < bytes; i++) { + dst[i] = (src[i] << 3) + (prev_acc & 0x7); + prev_acc = src[i] >> 5; + } + dst[bytes] = src[bytes - 1] >> 5; +} + +static void scalar_add_256bits(const uint8_t *src1, const uint8_t *src2, + uint8_t *dst) { + uint16_t r = 0; + for (int i = 0; i < 32; i++) { + r = r + (uint16_t)src1[i] + (uint16_t)src2[i]; + dst[i] = r & 0xff; + r >>= 8; + } +} + +static void cardano_ed25519_tweak_bits(uint8_t private_key[32]) { + private_key[0] &= 0xf8; + private_key[31] &= 0x1f; + private_key[31] |= 0x40; +} + +int hdnode_private_ckd_cardano(HDNode *inout, uint32_t index) { + if (inout->curve != &ed25519_cardano_info) { + return 0; + } + + if (inout->depth >= CARDANO_MAX_NODE_DEPTH) { + return 0; + } + + // checks for hardened/non-hardened derivation, keysize 32 means we are + // dealing with public key and thus non-h, keysize 64 is for private key + int keysize = 32; + if (index & 0x80000000) { + keysize = 64; + } + + CONFIDENTIAL uint8_t data[1 + 64 + 4]; + CONFIDENTIAL uint8_t z[32 + 32]; + CONFIDENTIAL uint8_t priv_key[64]; + CONFIDENTIAL uint8_t res_key[64]; + + write_le(data + keysize + 1, index); + + memcpy(priv_key, inout->private_key, 32); + memcpy(priv_key + 32, inout->private_key_extension, 32); + + if (keysize == 64) { // private derivation + data[0] = 0; + memcpy(data + 1, inout->private_key, 32); + memcpy(data + 1 + 32, inout->private_key_extension, 32); + } else { // public derivation + if (hdnode_fill_public_key(inout) != 0) { + return 0; + } + data[0] = 2; + memcpy(data + 1, inout->public_key + 1, 32); + } + + CONFIDENTIAL HMAC_SHA512_CTX ctx; + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, 1 + keysize + 4); + hmac_sha512_Final(&ctx, z); + + CONFIDENTIAL uint8_t zl8[32]; + memzero(zl8, 32); + + /* get 8 * Zl */ + scalar_multiply8(z, 28, zl8); + /* Kl = 8*Zl + parent(K)l */ + scalar_add_256bits(zl8, priv_key, res_key); + + /* Kr = Zr + parent(K)r */ + scalar_add_256bits(z + 32, priv_key + 32, res_key + 32); + + memcpy(inout->private_key, res_key, 32); + memcpy(inout->private_key_extension, res_key + 32, 32); + + if (keysize == 64) { + data[0] = 1; + } else { + data[0] = 3; + } + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, 1 + keysize + 4); + hmac_sha512_Final(&ctx, z); + + memcpy(inout->chain_code, z + 32, 32); + inout->depth++; + inout->child_num = index; + memzero(inout->public_key, sizeof(inout->public_key)); + + // making sure to wipe our memory + memzero(z, sizeof(z)); + memzero(data, sizeof(data)); + memzero(priv_key, sizeof(priv_key)); + memzero(res_key, sizeof(res_key)); + return 1; +} + +int hdnode_from_secret_cardano(const uint8_t secret[CARDANO_SECRET_LENGTH], + HDNode *out) { + memzero(out, sizeof(HDNode)); + out->depth = 0; + out->child_num = 0; + out->curve = &ed25519_cardano_info; + memcpy(out->private_key, secret, 32); + memcpy(out->private_key_extension, secret + 32, 32); + memcpy(out->chain_code, secret + 64, 32); + + cardano_ed25519_tweak_bits(out->private_key); + + out->public_key[0] = 0; + if (hdnode_fill_public_key(out) != 0) { + return 0; + } + + return 1; +} + +// Derives the root Cardano secret from a master secret, aka seed, as defined in +// SLIP-0023. +int secret_from_seed_cardano_slip23(const uint8_t *seed, int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]) { + CONFIDENTIAL uint8_t I[SHA512_DIGEST_LENGTH]; + CONFIDENTIAL HMAC_SHA512_CTX ctx; + + hmac_sha512_Init(&ctx, (const uint8_t *)ED25519_CARDANO_NAME, + strlen(ED25519_CARDANO_NAME)); + hmac_sha512_Update(&ctx, seed, seed_len); + hmac_sha512_Final(&ctx, I); + + sha512_Raw(I, 32, secret_out); + + memcpy(secret_out + SHA512_DIGEST_LENGTH, I + 32, 32); + cardano_ed25519_tweak_bits(secret_out); + + memzero(I, sizeof(I)); + memzero(&ctx, sizeof(ctx)); + return 1; +} + +// Derives the root Cardano secret from a BIP-32 master secret via the Ledger +// derivation: +// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Ledger.md +int secret_from_seed_cardano_ledger(const uint8_t *seed, int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]) { + CONFIDENTIAL uint8_t chain_code[SHA256_DIGEST_LENGTH]; + CONFIDENTIAL uint8_t root_key[SHA512_DIGEST_LENGTH]; + CONFIDENTIAL HMAC_SHA256_CTX ctx; + CONFIDENTIAL HMAC_SHA512_CTX sctx; + + const uint8_t *intermediate_result = seed; + int intermediate_result_len = seed_len; + do { + // STEP 1: derive a master secret like in BIP-32/SLIP-10 + hmac_sha512_Init(&sctx, (const uint8_t *)ED25519_SEED_NAME, + strlen(ED25519_SEED_NAME)); + hmac_sha512_Update(&sctx, intermediate_result, intermediate_result_len); + hmac_sha512_Final(&sctx, root_key); + + // STEP 2: check that the resulting key does not have a particular bit set, + // otherwise iterate like in SLIP-10 + intermediate_result = root_key; + intermediate_result_len = sizeof(root_key); + } while (root_key[31] & 0x20); + + // STEP 3: calculate the chain code as a HMAC-SHA256 of "\x01" + seed, + // key is "ed25519 seed" + hmac_sha256_Init(&ctx, (const unsigned char *)ED25519_SEED_NAME, + strlen(ED25519_SEED_NAME)); + hmac_sha256_Update(&ctx, (const unsigned char *)"\x01", 1); + hmac_sha256_Update(&ctx, seed, seed_len); + hmac_sha256_Final(&ctx, chain_code); + + //win +#ifdef _MSC_VER +static_assert( + SHA512_DIGEST_LENGTH + SHA256_DIGEST_LENGTH == CARDANO_SECRET_LENGTH, + "Invalid configuration of Cardano secret size"); + memcpy(secret_out, root_key, SHA512_DIGEST_LENGTH); + memcpy(secret_out + SHA512_DIGEST_LENGTH, chain_code, SHA256_DIGEST_LENGTH); +#else +// STEP 4: extract information into output + _Static_assert( + SHA512_DIGEST_LENGTH + SHA256_DIGEST_LENGTH == CARDANO_SECRET_LENGTH, + "Invalid configuration of Cardano secret size"); + memcpy(secret_out, root_key, SHA512_DIGEST_LENGTH); + memcpy(secret_out + SHA512_DIGEST_LENGTH, chain_code, SHA256_DIGEST_LENGTH); +#endif +#define CARDANO_ICARUS_ROUNDS_PER_STEP \ + (CARDANO_ICARUS_PBKDF2_ROUNDS / CARDANO_ICARUS_STEPS) + + + + + // STEP 5: tweak bits of the private key + cardano_ed25519_tweak_bits(secret_out); + + memzero(&ctx, sizeof(ctx)); + memzero(&sctx, sizeof(sctx)); + memzero(root_key, sizeof(root_key)); + memzero(chain_code, sizeof(chain_code)); + return 1; +} + +#define CARDANO_ICARUS_STEPS 32 +//win +#ifdef _MSC_VER +static_assert( + CARDANO_ICARUS_PBKDF2_ROUNDS % CARDANO_ICARUS_STEPS == 0, + "CARDANO_ICARUS_STEPS does not divide CARDANO_ICARUS_PBKDF2_ROUNDS"); +#else +_Static_assert( + CARDANO_ICARUS_PBKDF2_ROUNDS % CARDANO_ICARUS_STEPS == 0, + "CARDANO_ICARUS_STEPS does not divide CARDANO_ICARUS_PBKDF2_ROUNDS"); +#endif +#define CARDANO_ICARUS_ROUNDS_PER_STEP \ + (CARDANO_ICARUS_PBKDF2_ROUNDS / CARDANO_ICARUS_STEPS) + +// Derives the root Cardano HDNode from a passphrase and the entropy encoded in +// a BIP-0039 mnemonic using the Icarus derivation scheme, aka V2 derivation +// scheme: +// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Icarus.md +int secret_from_entropy_cardano_icarus( + const uint8_t *pass, int pass_len, const uint8_t *entropy, int entropy_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH], + void (*progress_callback)(uint32_t, uint32_t)) { + CONFIDENTIAL PBKDF2_HMAC_SHA512_CTX pctx; + CONFIDENTIAL uint8_t digest[SHA512_DIGEST_LENGTH]; + uint32_t progress = 0; + + // PASS 1: first 64 bytes + pbkdf2_hmac_sha512_Init(&pctx, pass, pass_len, entropy, entropy_len, 1); + if (progress_callback) { + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + for (int i = 0; i < CARDANO_ICARUS_STEPS; i++) { + pbkdf2_hmac_sha512_Update(&pctx, CARDANO_ICARUS_ROUNDS_PER_STEP); + if (progress_callback) { + progress += CARDANO_ICARUS_ROUNDS_PER_STEP; + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + } + pbkdf2_hmac_sha512_Final(&pctx, digest); + + memcpy(secret_out, digest, SHA512_DIGEST_LENGTH); + + // PASS 2: remaining 32 bytes + pbkdf2_hmac_sha512_Init(&pctx, pass, pass_len, entropy, entropy_len, 2); + if (progress_callback) { + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + for (int i = 0; i < CARDANO_ICARUS_STEPS; i++) { + pbkdf2_hmac_sha512_Update(&pctx, CARDANO_ICARUS_ROUNDS_PER_STEP); + if (progress_callback) { + progress += CARDANO_ICARUS_ROUNDS_PER_STEP; + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + } + pbkdf2_hmac_sha512_Final(&pctx, digest); + + memcpy(secret_out + SHA512_DIGEST_LENGTH, digest, + CARDANO_SECRET_LENGTH - SHA512_DIGEST_LENGTH); + + cardano_ed25519_tweak_bits(secret_out); + + memzero(&pctx, sizeof(pctx)); + memzero(digest, sizeof(digest)); + return 1; +} + +#endif // USE_CARDANO \ No newline at end of file diff --git a/tools/windows-replace/trezor-crypto/crypto/monero/base58.c b/tools/windows-replace/trezor-crypto/crypto/monero/base58.c new file mode 100644 index 00000000000..c5a6f1ccd2b --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/monero/base58.c @@ -0,0 +1,255 @@ +// Copyright (c) 2014-2018, The Monero Project +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are +// permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list +// of conditions and the following disclaimer in the documentation and/or other +// materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors may be +// used to endorse or promote products derived from this software without specific +// prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF +// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers + +#include +#include +#include +#include +#include +#include "int-util.h" +#include + +const size_t alphabet_size = 58; // sizeof(b58digits_ordered) - 1; +const size_t encoded_block_sizes[] = {0, 2, 3, 5, 6, 7, 9, 10, 11}; +const size_t full_block_size = sizeof(encoded_block_sizes) / sizeof(encoded_block_sizes[0]) - 1; +const size_t full_encoded_block_size = 11; // encoded_block_sizes[full_block_size]; +const size_t addr_checksum_size = 4; +const int decoded_block_sizes[] = {0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7, 8}; +#define reverse_alphabet(letter) ((int8_t) b58digits_map[(int)letter]) + + +uint64_t uint_8be_to_64(const uint8_t* data, size_t size) +{ + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t res = 0; + switch (9 - size) + { + case 1: res |= *data++; /* FALLTHRU */ + case 2: res <<= 8; res |= *data++; /* FALLTHRU */ + case 3: res <<= 8; res |= *data++; /* FALLTHRU */ + case 4: res <<= 8; res |= *data++; /* FALLTHRU */ + case 5: res <<= 8; res |= *data++; /* FALLTHRU */ + case 6: res <<= 8; res |= *data++; /* FALLTHRU */ + case 7: res <<= 8; res |= *data++; /* FALLTHRU */ + case 8: res <<= 8; res |= *data; break; + default: assert(false); + } + + return res; +} + +void uint_64_to_8be(uint64_t num, size_t size, uint8_t* data) +{ + assert(1 <= size && size <= sizeof(uint64_t)); + + uint64_t num_be = SWAP64(num); + memcpy(data, (uint8_t*)(&num_be) + sizeof(uint64_t) - size, size); +} + +void encode_block(const char* block, size_t size, char* res) +{ + assert(1 <= size && size <= full_block_size); + + uint64_t num = uint_8be_to_64((uint8_t*)(block), size); + int i = ((int)(encoded_block_sizes[size])) - 1; + while (0 <= i) + { + uint64_t remainder = num % alphabet_size; + num /= alphabet_size; + res[i] = b58digits_ordered[remainder]; + --i; + } +} + +bool decode_block(const char* block, size_t size, char* res) +{ + assert(1 <= size && size <= full_encoded_block_size); + + int res_size = decoded_block_sizes[size]; + if (res_size <= 0) + return false; // Invalid block size + + uint64_t res_num = 0; + uint64_t order = 1; + for (size_t i = size - 1; i < size; --i) + { + if (block[i] & 0x80) + return false; // Invalid symbol + int digit = reverse_alphabet(block[i]); + if (digit < 0) + return false; // Invalid symbol + + uint64_t product_hi = 0; + uint64_t tmp = res_num + mul128(order, (uint64_t) digit, &product_hi); + if (tmp < res_num || 0 != product_hi) + return false; // Overflow + + res_num = tmp; + order *= alphabet_size; // Never overflows, 58^10 < 2^64 + } + + if ((size_t)res_size < full_block_size && (UINT64_C(1) << (8 * res_size)) <= res_num) + return false; // Overflow + + uint_64_to_8be(res_num, res_size, (uint8_t*)(res)); + + return true; +} + + +bool xmr_base58_encode(char *b58, size_t *b58sz, const void *data, size_t binsz) +{ + if (binsz==0) + return true; + + const char * data_bin = data; + size_t full_block_count = binsz / full_block_size; + size_t last_block_size = binsz % full_block_size; + size_t res_size = full_block_count * full_encoded_block_size + encoded_block_sizes[last_block_size]; + + if (b58sz){ + if (res_size >= *b58sz){ + return false; + } + *b58sz = res_size; + } + + for (size_t i = 0; i < full_block_count; ++i) + { + encode_block(data_bin + i * full_block_size, full_block_size, b58 + i * full_encoded_block_size); + } + + if (0 < last_block_size) + { + encode_block(data_bin + full_block_count * full_block_size, last_block_size, b58 + full_block_count * full_encoded_block_size); + } + + return true; +} + +bool xmr_base58_decode(const char *b58, size_t b58sz, void *data, size_t *binsz) +{ + if (b58sz == 0) { + *binsz = 0; + return true; + } + + size_t full_block_count = b58sz / full_encoded_block_size; + size_t last_block_size = b58sz % full_encoded_block_size; + int last_block_decoded_size = decoded_block_sizes[last_block_size]; + if (last_block_decoded_size < 0) { + *binsz = 0; + return false; // Invalid enc length + } + + size_t data_size = full_block_count * full_block_size + last_block_decoded_size; + if (*binsz < data_size){ + *binsz = 0; + return false; + } + + char * data_bin = data; + for (size_t i = 0; i < full_block_count; ++i) + { + if (!decode_block(b58 + i * full_encoded_block_size, full_encoded_block_size, data_bin + i * full_block_size)) + return false; + } + + if (0 < last_block_size) + { + if (!decode_block(b58 + full_block_count * full_encoded_block_size, last_block_size, + data_bin + full_block_count * full_block_size)) + return false; + } + + return true; +} + +int xmr_base58_addr_encode_check(uint64_t tag, const uint8_t *data, size_t binsz, char *b58, size_t b58sz) +{ + if (binsz > 128 || tag > 127) { // tag varint + return false; + } + + size_t b58size = b58sz; +#ifdef _MSC_VER + uint8_t *buf = _alloca((binsz + 1) + HASHER_DIGEST_LENGTH); +#else + uint8_t buf[(binsz + 1) + HASHER_DIGEST_LENGTH]; +#endif + memset(buf, 0, sizeof((binsz + 1) + HASHER_DIGEST_LENGTH)); // win memset(buf, 0, sizeof(buf)); + uint8_t *hash = buf + binsz + 1; + buf[0] = (uint8_t) tag; + memcpy(buf + 1, data, binsz); + hasher_Raw(HASHER_SHA3K, buf, binsz + 1, hash); + + bool r = xmr_base58_encode(b58, &b58size, buf, binsz + 1 + addr_checksum_size); + return (int) (!r ? 0 : b58size); +} + +int xmr_base58_addr_decode_check(const char *addr, size_t sz, uint64_t *tag, void *data, size_t datalen) +{ + size_t buflen = 1 + 64 + addr_checksum_size; + #ifdef _MSC_VER + uint8_t *buf = _alloca(buflen); + #else + uint8_t buf[buflen]; + #endif + memset(buf, 0, buflen); //win memset(buf, 0, sizeof(buf)); + + uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; + + if (!xmr_base58_decode(addr, sz, buf, &buflen)){ + return 0; + } + + size_t res_size = buflen - addr_checksum_size - 1; + if (datalen < res_size){ + return 0; + } + + if (buflen <= addr_checksum_size+1) { + return 0; + } + + hasher_Raw(HASHER_SHA3K, buf, buflen - addr_checksum_size, hash); + if (memcmp(hash, buf + buflen - addr_checksum_size, addr_checksum_size) != 0){ + return 0; + } + + *tag = buf[0]; + if (*tag > 127){ + return false; // varint + } + + memcpy(data, buf+1, res_size); + return (int) res_size; +} diff --git a/tools/windows-replace/trezor-crypto/crypto/rand.c b/tools/windows-replace/trezor-crypto/crypto/rand.c new file mode 100644 index 00000000000..caff5a4242c --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/rand.c @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include + +#include +#include + +#ifdef WIN32 + +#include +#include + +static volatile LONG random_refc = 0; +static volatile BOOL random_lock = 0; +static volatile PVOID random_provider = NULL; + +// [wallet-core] +void *random_init() { + BCRYPT_ALG_HANDLE prov; + while (InterlockedCompareExchange(&random_lock, 1, 0)) + { + SwitchToThread(); + } + LONG inc = InterlockedIncrement(&random_refc); + if (inc == 1) + { + // Create new provider + if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&prov, BCRYPT_RNG_ALGORITHM, NULL, 0))) { + prov = NULL; + } + (void)InterlockedExchangePointer(&random_provider, prov); + } + else + { + // Get existing provider + prov = InterlockedCompareExchangePointer(&random_provider, 0, 0); + } + if (!prov) + { + InterlockedDecrement(&random_refc); + } + (void)InterlockedExchange(&random_lock, 0); + return prov; +} + +// [wallet-core] +void random_release() { + while (InterlockedCompareExchange(&random_lock, 1, 0)) + { + SwitchToThread(); + } + LONG dec = InterlockedDecrement(&random_refc); + if (dec == 0) + { + BCryptCloseAlgorithmProvider(random_provider, 0); + random_provider = NULL; + } + (void)InterlockedExchange(&random_lock, 0); +} + +// [wallet-core] +uint32_t random32() { + BCRYPT_ALG_HANDLE prov; + uint32_t res; + if (!(prov = random_init())) { + return 0; + } + if (!BCRYPT_SUCCESS(BCryptGenRandom(prov, (PUCHAR)(&res), sizeof(res), 0))) { + res = 0; + } + random_release(); + return res; +} + +void random_buffer(uint8_t *buf, size_t len) { + BCRYPT_ALG_HANDLE prov; + if (!(prov = random_init())) { + return; + } + for (size_t i = 0; i < len;) + { + size_t l = min(0x80000000UL, len); + (void)BCryptGenRandom(prov, (PUCHAR)(buf + i), (ULONG)l, 0); + i += l; + } + random_release(); +} + +#else +#include +#include + + // [wallet-core] +void *random_init() { + static int dummy; + return &dummy; // return a valid pointer +} + +// [wallet-core] +void random_release() { + // no-op +} + +// [wallet-core] +uint32_t __attribute__((weak)) random32(void) { + int randomData = open("/dev/urandom", O_RDONLY); + if (randomData < 0) { + return 0; + } + + uint32_t result; + if (read(randomData, &result, sizeof(result)) < 0) { + return 0; + } + + close(randomData); + + return result; +} + +void __attribute__((weak)) random_buffer(uint8_t *buf, size_t len) { + int randomData = open("/dev/urandom", O_RDONLY); + if (randomData < 0) { + return; + } + if (read(randomData, buf, len) < 0) { + return; + } + close(randomData); +} +#endif \ No newline at end of file diff --git a/tools/windows-replace/trezor-crypto/crypto/sha3.c b/tools/windows-replace/trezor-crypto/crypto/sha3.c new file mode 100644 index 00000000000..0033186fe59 --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/sha3.c @@ -0,0 +1,398 @@ +/* sha3.c - an implementation of Secure Hash Algorithm 3 (Keccak). + * based on the + * The Keccak SHA-3 submission. Submission to NIST (Round 3), 2011 + * by Guido Bertoni, Joan Daemen, Michaël Peeters and Gilles Van Assche + * + * Copyright: 2013 Aleksey Kravchenko + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk! + */ + +#include +#include + +#include +#include + +#define I64(x) x##LL +#define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) +#define le2me_64(x) (x) +//#define IS_ALIGNED_64(p) (0 == (7 & ((long)(p)))) // [wallet-core] pointer/numerical type, for MacOS SDK 12.3 +#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) //win +#define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) + +/* constants */ +#define NumberOfRounds 24 + +/* SHA3 (Keccak) constants for 24 rounds */ +uint64_t keccak_round_constants[NumberOfRounds] = { + I64(0x0000000000000001), I64(0x0000000000008082), I64(0x800000000000808A), I64(0x8000000080008000), + I64(0x000000000000808B), I64(0x0000000080000001), I64(0x8000000080008081), I64(0x8000000000008009), + I64(0x000000000000008A), I64(0x0000000000000088), I64(0x0000000080008009), I64(0x000000008000000A), + I64(0x000000008000808B), I64(0x800000000000008B), I64(0x8000000000008089), I64(0x8000000000008003), + I64(0x8000000000008002), I64(0x8000000000000080), I64(0x000000000000800A), I64(0x800000008000000A), + I64(0x8000000080008081), I64(0x8000000000008080), I64(0x0000000080000001), I64(0x8000000080008008) +}; + +/* Initializing a sha3 context for given number of output bits */ +static void keccak_Init(SHA3_CTX *ctx, unsigned bits) +{ + /* NB: The Keccak capacity parameter = bits * 2 */ + unsigned rate = 1600 - bits * 2; + + memzero(ctx, sizeof(SHA3_CTX)); + ctx->block_size = rate / 8; + assert(rate <= 1600 && (rate % 64) == 0); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_224_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 224); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_256_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 256); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_384_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 384); +} + +/** + * Initialize context before calculating hash. + * + * @param ctx context to initialize + */ +void sha3_512_Init(SHA3_CTX *ctx) +{ + keccak_Init(ctx, 512); +} + +/* Keccak theta() transformation */ +static void keccak_theta(uint64_t *A) +{ + unsigned int x = 0; + uint64_t C[5] = {0}, D[5] = {0}; + + for (x = 0; x < 5; x++) { + C[x] = A[x] ^ A[x + 5] ^ A[x + 10] ^ A[x + 15] ^ A[x + 20]; + } + D[0] = ROTL64(C[1], 1) ^ C[4]; + D[1] = ROTL64(C[2], 1) ^ C[0]; + D[2] = ROTL64(C[3], 1) ^ C[1]; + D[3] = ROTL64(C[4], 1) ^ C[2]; + D[4] = ROTL64(C[0], 1) ^ C[3]; + + for (x = 0; x < 5; x++) { + A[x] ^= D[x]; + A[x + 5] ^= D[x]; + A[x + 10] ^= D[x]; + A[x + 15] ^= D[x]; + A[x + 20] ^= D[x]; + } +} + +/* Keccak pi() transformation */ +static void keccak_pi(uint64_t *A) +{ + uint64_t A1 = 0; + A1 = A[1]; + A[ 1] = A[ 6]; + A[ 6] = A[ 9]; + A[ 9] = A[22]; + A[22] = A[14]; + A[14] = A[20]; + A[20] = A[ 2]; + A[ 2] = A[12]; + A[12] = A[13]; + A[13] = A[19]; + A[19] = A[23]; + A[23] = A[15]; + A[15] = A[ 4]; + A[ 4] = A[24]; + A[24] = A[21]; + A[21] = A[ 8]; + A[ 8] = A[16]; + A[16] = A[ 5]; + A[ 5] = A[ 3]; + A[ 3] = A[18]; + A[18] = A[17]; + A[17] = A[11]; + A[11] = A[ 7]; + A[ 7] = A[10]; + A[10] = A1; + /* note: A[ 0] is left as is */ +} + +/* Keccak chi() transformation */ +static void keccak_chi(uint64_t *A) +{ + int i = 0; + for (i = 0; i < 25; i += 5) { + uint64_t A0 = A[0 + i], A1 = A[1 + i]; + A[0 + i] ^= ~A1 & A[2 + i]; + A[1 + i] ^= ~A[2 + i] & A[3 + i]; + A[2 + i] ^= ~A[3 + i] & A[4 + i]; + A[3 + i] ^= ~A[4 + i] & A0; + A[4 + i] ^= ~A0 & A1; + } +} + +static void sha3_permutation(uint64_t *state) +{ + int round = 0; + for (round = 0; round < NumberOfRounds; round++) + { + keccak_theta(state); + + /* apply Keccak rho() transformation */ + state[ 1] = ROTL64(state[ 1], 1); + state[ 2] = ROTL64(state[ 2], 62); + state[ 3] = ROTL64(state[ 3], 28); + state[ 4] = ROTL64(state[ 4], 27); + state[ 5] = ROTL64(state[ 5], 36); + state[ 6] = ROTL64(state[ 6], 44); + state[ 7] = ROTL64(state[ 7], 6); + state[ 8] = ROTL64(state[ 8], 55); + state[ 9] = ROTL64(state[ 9], 20); + state[10] = ROTL64(state[10], 3); + state[11] = ROTL64(state[11], 10); + state[12] = ROTL64(state[12], 43); + state[13] = ROTL64(state[13], 25); + state[14] = ROTL64(state[14], 39); + state[15] = ROTL64(state[15], 41); + state[16] = ROTL64(state[16], 45); + state[17] = ROTL64(state[17], 15); + state[18] = ROTL64(state[18], 21); + state[19] = ROTL64(state[19], 8); + state[20] = ROTL64(state[20], 18); + state[21] = ROTL64(state[21], 2); + state[22] = ROTL64(state[22], 61); + state[23] = ROTL64(state[23], 56); + state[24] = ROTL64(state[24], 14); + + keccak_pi(state); + keccak_chi(state); + + /* apply iota(state, round) */ + *state ^= keccak_round_constants[round]; + } +} + +/** + * The core transformation. Process the specified block of data. + * + * @param hash the algorithm state + * @param block the message block to process + * @param block_size the size of the processed block in bytes + */ +static void sha3_process_block(uint64_t hash[25], const uint64_t *block, size_t block_size) +{ + /* expanded loop */ + hash[ 0] ^= le2me_64(block[ 0]); + hash[ 1] ^= le2me_64(block[ 1]); + hash[ 2] ^= le2me_64(block[ 2]); + hash[ 3] ^= le2me_64(block[ 3]); + hash[ 4] ^= le2me_64(block[ 4]); + hash[ 5] ^= le2me_64(block[ 5]); + hash[ 6] ^= le2me_64(block[ 6]); + hash[ 7] ^= le2me_64(block[ 7]); + hash[ 8] ^= le2me_64(block[ 8]); + /* if not sha3-512 */ + if (block_size > 72) { + hash[ 9] ^= le2me_64(block[ 9]); + hash[10] ^= le2me_64(block[10]); + hash[11] ^= le2me_64(block[11]); + hash[12] ^= le2me_64(block[12]); + /* if not sha3-384 */ + if (block_size > 104) { + hash[13] ^= le2me_64(block[13]); + hash[14] ^= le2me_64(block[14]); + hash[15] ^= le2me_64(block[15]); + hash[16] ^= le2me_64(block[16]); + /* if not sha3-256 */ + if (block_size > 136) { + hash[17] ^= le2me_64(block[17]); +#ifdef FULL_SHA3_FAMILY_SUPPORT + /* if not sha3-224 */ + if (block_size > 144) { + hash[18] ^= le2me_64(block[18]); + hash[19] ^= le2me_64(block[19]); + hash[20] ^= le2me_64(block[20]); + hash[21] ^= le2me_64(block[21]); + hash[22] ^= le2me_64(block[22]); + hash[23] ^= le2me_64(block[23]); + hash[24] ^= le2me_64(block[24]); + } +#endif + } + } + } + /* make a permutation of the hash */ + sha3_permutation(hash); +} + +#define SHA3_FINALIZED 0x80000000 + +/** + * Calculate message hash. + * Can be called repeatedly with chunks of the message to be hashed. + * + * @param ctx the algorithm context containing current hashing state + * @param msg message chunk + * @param size length of the message chunk + */ +void sha3_Update(SHA3_CTX *ctx, const unsigned char *msg, size_t size) +{ + size_t idx = (size_t)ctx->rest; + size_t block_size = (size_t)ctx->block_size; + + if (ctx->rest & SHA3_FINALIZED) return; /* too late for additional input */ + ctx->rest = (unsigned)((ctx->rest + size) % block_size); + + /* fill partial block */ + if (idx) { + size_t left = block_size - idx; + memcpy((char*)ctx->message + idx, msg, (size < left ? size : left)); + if (size < left) return; + + /* process partial block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + msg += left; + size -= left; + } + while (size >= block_size) { + uint64_t *aligned_message_block = NULL; + if (IS_ALIGNED_64(msg)) { + /* the most common case is processing of an already aligned message + without copying it */ + aligned_message_block = (uint64_t*)(void*)msg; + } else { + memcpy(ctx->message, msg, block_size); + aligned_message_block = ctx->message; + } + + sha3_process_block(ctx->hash, aligned_message_block, block_size); + msg += block_size; + size -= block_size; + } + if (size) { + memcpy(ctx->message, msg, size); /* save leftovers */ + } +} + +/** + * Store calculated hash into the given array. + * + * @param ctx the algorithm context containing current hashing state + * @param result calculated hash in binary form + */ +void sha3_Final(SHA3_CTX *ctx, unsigned char* result) +{ + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if (!(ctx->rest & SHA3_FINALIZED)) + { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x06; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if (result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +#if USE_KECCAK +/** +* Store calculated hash into the given array. +* +* @param ctx the algorithm context containing current hashing state +* @param result calculated hash in binary form +*/ +void keccak_Final(SHA3_CTX *ctx, unsigned char* result) +{ + size_t digest_length = 100 - ctx->block_size / 2; + const size_t block_size = ctx->block_size; + + if (!(ctx->rest & SHA3_FINALIZED)) + { + /* clear the rest of the data queue */ + memzero((char*)ctx->message + ctx->rest, block_size - ctx->rest); + ((char*)ctx->message)[ctx->rest] |= 0x01; + ((char*)ctx->message)[block_size - 1] |= 0x80; + + /* process final block */ + sha3_process_block(ctx->hash, ctx->message, block_size); + ctx->rest = SHA3_FINALIZED; /* mark context as finalized */ + } + + assert(block_size > digest_length); + if (result) me64_to_le_str(result, ctx->hash, digest_length); + memzero(ctx, sizeof(SHA3_CTX)); +} + +void keccak_256(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + keccak_256_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} + +void keccak_512(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + keccak_512_Init(&ctx); + keccak_Update(&ctx, data, len); + keccak_Final(&ctx, digest); +} +#endif /* USE_KECCAK */ + +void sha3_256(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + sha3_256_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} + +void sha3_512(const unsigned char* data, size_t len, unsigned char* digest) +{ + SHA3_CTX ctx = {0}; + sha3_512_Init(&ctx); + sha3_Update(&ctx, data, len); + sha3_Final(&ctx, digest); +} diff --git a/tools/windows-replace/trezor-crypto/crypto/shamir.c b/tools/windows-replace/trezor-crypto/crypto/shamir.c new file mode 100644 index 00000000000..5aea89bec6b --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/shamir.c @@ -0,0 +1,347 @@ +/* + * Implementation of the hazardous parts of the SSS library + * + * Copyright (c) 2017 Daan Sprenkels + * Copyright (c) 2019 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * This code contains the actual Shamir secret sharing functionality. The + * implementation of this code is based on the idea that the user likes to + * generate/combine 32 shares (in GF(2^8)) at the same time, because a 256 bit + * key will be exactly 32 bytes. Therefore we bitslice all the input and + * unbitslice the output right before returning. + * + * This bitslice approach optimizes natively on all architectures that are 32 + * bit or more. Care is taken to use not too many registers, to ensure that no + * values have to be leaked to the stack. + * + * All functions in this module are implemented constant time and constant + * lookup operations, as all proper crypto code must be. + */ + +#include +#include +#include + +static void bitslice(uint32_t r[8], const uint8_t *x, size_t len) { + size_t bit_idx = 0, arr_idx = 0; + uint32_t cur = 0; + + memset(r, 0, sizeof(uint32_t[8])); + for (arr_idx = 0; arr_idx < len; arr_idx++) { + cur = (uint32_t)x[arr_idx]; + for (bit_idx = 0; bit_idx < 8; bit_idx++) { + r[bit_idx] |= ((cur >> bit_idx) & 1) << arr_idx; + } + } +} + +static void unbitslice(uint8_t *r, const uint32_t x[8], size_t len) { + size_t bit_idx = 0, arr_idx = 0; + uint32_t cur = 0; + + memset(r, 0, sizeof(uint8_t) * len); + for (bit_idx = 0; bit_idx < 8; bit_idx++) { + cur = (uint32_t)x[bit_idx]; + for (arr_idx = 0; arr_idx < len; arr_idx++) { + r[arr_idx] |= ((cur >> arr_idx) & 1) << bit_idx; + } + } +} + +static void bitslice_setall(uint32_t r[8], const uint8_t x) { + size_t idx = 0; + for (idx = 0; idx < 8; idx++) { + r[idx] = -((x >> idx) & 1); + } +} + +/* + * Add (XOR) `r` with `x` and store the result in `r`. + */ +static void gf256_add(uint32_t r[8], const uint32_t x[8]) { + size_t idx = 0; + for (idx = 0; idx < 8; idx++) r[idx] ^= x[idx]; +} + +/* + * Safely multiply two bitsliced polynomials in GF(2^8) reduced by + * x^8 + x^4 + x^3 + x + 1. `r` and `a` may overlap, but overlapping of `r` + * and `b` will produce an incorrect result! If you need to square a polynomial + * use `gf256_square` instead. + */ +static void gf256_mul(uint32_t r[8], const uint32_t a[8], const uint32_t b[8]) { + /* This function implements Russian Peasant multiplication on two + * bitsliced polynomials. + * + * I personally think that these kinds of long lists of operations + * are often a bit ugly. A double for loop would be nicer and would + * take up a lot less lines of code. + * However, some compilers seem to fail in optimizing these kinds of + * loops. So we will just have to do this by hand. + */ + uint32_t a2[8] = {0}; + memcpy(a2, a, sizeof(uint32_t[8])); + + r[0] = a2[0] & b[0]; /* add (assignment, because r is 0) */ + r[1] = a2[1] & b[0]; + r[2] = a2[2] & b[0]; + r[3] = a2[3] & b[0]; + r[4] = a2[4] & b[0]; + r[5] = a2[5] & b[0]; + r[6] = a2[6] & b[0]; + r[7] = a2[7] & b[0]; + a2[0] ^= a2[7]; /* reduce */ + a2[2] ^= a2[7]; + a2[3] ^= a2[7]; + + r[0] ^= a2[7] & b[1]; /* add */ + r[1] ^= a2[0] & b[1]; + r[2] ^= a2[1] & b[1]; + r[3] ^= a2[2] & b[1]; + r[4] ^= a2[3] & b[1]; + r[5] ^= a2[4] & b[1]; + r[6] ^= a2[5] & b[1]; + r[7] ^= a2[6] & b[1]; + a2[7] ^= a2[6]; /* reduce */ + a2[1] ^= a2[6]; + a2[2] ^= a2[6]; + + r[0] ^= a2[6] & b[2]; /* add */ + r[1] ^= a2[7] & b[2]; + r[2] ^= a2[0] & b[2]; + r[3] ^= a2[1] & b[2]; + r[4] ^= a2[2] & b[2]; + r[5] ^= a2[3] & b[2]; + r[6] ^= a2[4] & b[2]; + r[7] ^= a2[5] & b[2]; + a2[6] ^= a2[5]; /* reduce */ + a2[0] ^= a2[5]; + a2[1] ^= a2[5]; + + r[0] ^= a2[5] & b[3]; /* add */ + r[1] ^= a2[6] & b[3]; + r[2] ^= a2[7] & b[3]; + r[3] ^= a2[0] & b[3]; + r[4] ^= a2[1] & b[3]; + r[5] ^= a2[2] & b[3]; + r[6] ^= a2[3] & b[3]; + r[7] ^= a2[4] & b[3]; + a2[5] ^= a2[4]; /* reduce */ + a2[7] ^= a2[4]; + a2[0] ^= a2[4]; + + r[0] ^= a2[4] & b[4]; /* add */ + r[1] ^= a2[5] & b[4]; + r[2] ^= a2[6] & b[4]; + r[3] ^= a2[7] & b[4]; + r[4] ^= a2[0] & b[4]; + r[5] ^= a2[1] & b[4]; + r[6] ^= a2[2] & b[4]; + r[7] ^= a2[3] & b[4]; + a2[4] ^= a2[3]; /* reduce */ + a2[6] ^= a2[3]; + a2[7] ^= a2[3]; + + r[0] ^= a2[3] & b[5]; /* add */ + r[1] ^= a2[4] & b[5]; + r[2] ^= a2[5] & b[5]; + r[3] ^= a2[6] & b[5]; + r[4] ^= a2[7] & b[5]; + r[5] ^= a2[0] & b[5]; + r[6] ^= a2[1] & b[5]; + r[7] ^= a2[2] & b[5]; + a2[3] ^= a2[2]; /* reduce */ + a2[5] ^= a2[2]; + a2[6] ^= a2[2]; + + r[0] ^= a2[2] & b[6]; /* add */ + r[1] ^= a2[3] & b[6]; + r[2] ^= a2[4] & b[6]; + r[3] ^= a2[5] & b[6]; + r[4] ^= a2[6] & b[6]; + r[5] ^= a2[7] & b[6]; + r[6] ^= a2[0] & b[6]; + r[7] ^= a2[1] & b[6]; + a2[2] ^= a2[1]; /* reduce */ + a2[4] ^= a2[1]; + a2[5] ^= a2[1]; + + r[0] ^= a2[1] & b[7]; /* add */ + r[1] ^= a2[2] & b[7]; + r[2] ^= a2[3] & b[7]; + r[3] ^= a2[4] & b[7]; + r[4] ^= a2[5] & b[7]; + r[5] ^= a2[6] & b[7]; + r[6] ^= a2[7] & b[7]; + r[7] ^= a2[0] & b[7]; + + memzero(a2, sizeof(a2)); +} + +/* + * Square `x` in GF(2^8) and write the result to `r`. `r` and `x` may overlap. + */ +static void gf256_square(uint32_t r[8], const uint32_t x[8]) { + uint32_t r8 = 0, r10 = 0, r12 = 0, r14 = 0; + /* Use the Freshman's Dream rule to square the polynomial + * Assignments are done from 7 downto 0, because this allows the user + * to execute this function in-place (e.g. `gf256_square(r, r);`). + */ + r14 = x[7]; + r12 = x[6]; + r10 = x[5]; + r8 = x[4]; + r[6] = x[3]; + r[4] = x[2]; + r[2] = x[1]; + r[0] = x[0]; + + /* Reduce with x^8 + x^4 + x^3 + x + 1 until order is less than 8 */ + r[7] = r14; /* r[7] was 0 */ + r[6] ^= r14; + r10 ^= r14; + /* Skip, because r13 is always 0 */ + r[4] ^= r12; + r[5] = r12; /* r[5] was 0 */ + r[7] ^= r12; + r8 ^= r12; + /* Skip, because r11 is always 0 */ + r[2] ^= r10; + r[3] = r10; /* r[3] was 0 */ + r[5] ^= r10; + r[6] ^= r10; + r[1] = r14; /* r[1] was 0 */ + r[2] ^= r14; /* Substitute r9 by r14 because they will always be equal*/ + r[4] ^= r14; + r[5] ^= r14; + r[0] ^= r8; + r[1] ^= r8; + r[3] ^= r8; + r[4] ^= r8; +} + +/* + * Invert `x` in GF(2^8) and write the result to `r` + */ +static void gf256_inv(uint32_t r[8], uint32_t x[8]) { + uint32_t y[8] = {0}, z[8] = {0}; + + gf256_square(y, x); // y = x^2 + gf256_square(y, y); // y = x^4 + gf256_square(r, y); // r = x^8 + gf256_mul(z, r, x); // z = x^9 + gf256_square(r, r); // r = x^16 + gf256_mul(r, r, z); // r = x^25 + gf256_square(r, r); // r = x^50 + gf256_square(z, r); // z = x^100 + gf256_square(z, z); // z = x^200 + gf256_mul(r, r, z); // r = x^250 + gf256_mul(r, r, y); // r = x^254 + + memzero(y, sizeof(y)); + memzero(z, sizeof(z)); +} + +bool shamir_interpolate(uint8_t *result, uint8_t result_index, + const uint8_t *share_indices, + const uint8_t **share_values, uint8_t share_count, + size_t len) { + size_t i = 0, j = 0; + uint32_t x[8] = {0}; + #ifdef _MSC_VER + uint32_t (*xs)[8] = _alloca(sizeof(uint32_t) * share_count * 8); + memset(xs, 0, sizeof(uint32_t) * share_count * 8); + uint32_t (*ys)[8] = _alloca(sizeof(uint32_t) * share_count * 8); + memset(ys, 0, sizeof(uint32_t) * share_count * 8); +#else + uint32_t xs[share_count][8]; + memset(xs, 0, sizeof(xs)); + uint32_t ys[share_count][8]; + memset(ys, 0, sizeof(ys)); + #endif + uint32_t num[8] = {~0}; /* num is the numerator (=1) */ + uint32_t denom[8] = {0}; + uint32_t tmp[8] = {0}; + uint32_t secret[8] = {0}; + bool ret = true; + + if (len > SHAMIR_MAX_LEN) return false; + + /* Collect the x and y values */ + for (i = 0; i < share_count; i++) { + bitslice_setall(xs[i], share_indices[i]); + bitslice(ys[i], share_values[i], len); + } + bitslice_setall(x, result_index); + + for (i = 0; i < share_count; i++) { + memcpy(tmp, x, sizeof(uint32_t[8])); + gf256_add(tmp, xs[i]); + gf256_mul(num, num, tmp); + } + + /* Use Lagrange basis polynomials to calculate the secret coefficient */ + for (i = 0; i < share_count; i++) { + /* The code below assumes that none of the share_indices are equal to + * result_index. We need to treat that as a special case. */ + if (share_indices[i] != result_index) { + memcpy(denom, x, sizeof(denom)); + gf256_add(denom, xs[i]); + } else { + bitslice_setall(denom, 1); + gf256_add(secret, ys[i]); + } + for (j = 0; j < share_count; j++) { + if (i == j) continue; + memcpy(tmp, xs[i], sizeof(uint32_t[8])); + gf256_add(tmp, xs[j]); + gf256_mul(denom, denom, tmp); + } + if ((denom[0] | denom[1] | denom[2] | denom[3] | denom[4] | denom[5] | + denom[6] | denom[7]) == 0) { + /* The share_indices are not unique. */ + ret = false; + break; + } + gf256_inv(tmp, denom); /* inverted denominator */ + gf256_mul(tmp, tmp, num); /* basis polynomial */ + gf256_mul(tmp, tmp, ys[i]); /* scaled coefficient */ + gf256_add(secret, tmp); + } + + if (ret == true) { + unbitslice(result, secret, len); + } + + memzero(x, sizeof(x)); +#ifdef _MSC_VER + memzero(xs, sizeof(uint32_t) * share_count * 8); + memzero(ys, sizeof(uint32_t) * share_count * 8); +#else + memzero(xs, sizeof(xs)); + memzero(ys, sizeof(ys)); +#endif + memzero(num, sizeof(num)); + memzero(denom, sizeof(denom)); + memzero(tmp, sizeof(tmp)); + memzero(secret, sizeof(secret)); + return ret; +} diff --git a/tools/windows-replace/trezor-crypto/crypto/tests/CMakeLists.txt b/tools/windows-replace/trezor-crypto/crypto/tests/CMakeLists.txt new file mode 100644 index 00000000000..4960fbdd0ee --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/tests/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + +enable_testing() + +if(WIN32) + find_library(CHECK_LIB_RELEASE check PATH ${CMAKE_SOURCE_DIR}/build/local/lib) + find_library(CHECK_LIB_DEBUG checkd PATH ${CMAKE_SOURCE_DIR}/build/local/lib) + set (CHECK_LIBRARIES optimized ${CHECK_LIB_RELEASE} debug ${CHECK_LIB_DEBUG}) +else() + find_library(check PATH ${CMAKE_SOURCE_DIR}/build/local/lib/pkgconfig NO_DEFAULT_PATH) + set (CHECK_LIBRARIES check) +endif() + +# Test executable +add_executable(TrezorCryptoTests test_check.c) +target_link_libraries(TrezorCryptoTests TrezorCrypto ${CHECK_LIBRARIES}) + +target_link_directories(TrezorCryptoTests PRIVATE ${PREFIX}/lib) +target_include_directories(TrezorCryptoTests PRIVATE ${CMAKE_SOURCE_DIR}/src ${PREFIX}/include) + +add_test(NAME test_check COMMAND TrezorCryptoTests) diff --git a/tools/windows-replace/trezor-crypto/crypto/tests/test_check.c b/tools/windows-replace/trezor-crypto/crypto/tests/test_check.c new file mode 100644 index 00000000000..097689f21ed --- /dev/null +++ b/tools/windows-replace/trezor-crypto/crypto/tests/test_check.c @@ -0,0 +1,9472 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef VALGRIND +#include +#include +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if USE_MONERO // [wallet-core] +#include "../monero/monero.h" +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef VALGRIND +/* + * This is a clever trick to make Valgrind's Memcheck verify code + * is constant-time with respect to secret data. + */ + +/* Call after secret data is written, before first use */ +#define MARK_SECRET_DATA(addr, len) VALGRIND_MAKE_MEM_UNDEFINED(addr, len) +/* Call before secret data is freed or to mark non-secret data (public keys or + * signatures) */ +#define UNMARK_SECRET_DATA(addr, len) VALGRIND_MAKE_MEM_DEFINED(addr, len) +#else +#define MARK_SECRET_DATA(addr, len) +#define UNMARK_SECRET_DATA(addr, len) +#endif + +#define FROMHEX_MAXLEN 512 + +#define VERSION_PUBLIC 0x0488b21e +#define VERSION_PRIVATE 0x0488ade4 + +#define DECRED_VERSION_PUBLIC 0x02fda926 +#define DECRED_VERSION_PRIVATE 0x02fda4e8 + +const uint8_t *fromhex(const char *str) { + static uint8_t buf[FROMHEX_MAXLEN]; + size_t len = strlen(str) / 2; + if (len > FROMHEX_MAXLEN) len = FROMHEX_MAXLEN; + for (size_t i = 0; i < len; i++) { + uint8_t c = 0; + if (str[i * 2] >= '0' && str[i * 2] <= '9') c += (str[i * 2] - '0') << 4; + if ((str[i * 2] & ~0x20) >= 'A' && (str[i * 2] & ~0x20) <= 'F') + c += (10 + (str[i * 2] & ~0x20) - 'A') << 4; + if (str[i * 2 + 1] >= '0' && str[i * 2 + 1] <= '9') + c += (str[i * 2 + 1] - '0'); + if ((str[i * 2 + 1] & ~0x20) >= 'A' && (str[i * 2 + 1] & ~0x20) <= 'F') + c += (10 + (str[i * 2 + 1] & ~0x20) - 'A'); + buf[i] = c; + } + return buf; +} + +void nem_private_key(const char *reversed_hex, ed25519_secret_key private_key) { + const uint8_t *reversed_key = fromhex(reversed_hex); + for (size_t j = 0; j < sizeof(ed25519_secret_key); j++) { + private_key[j] = reversed_key[sizeof(ed25519_secret_key) - j - 1]; + } +} + +START_TEST(test_bignum_read_be) { + bignum256 a; + uint8_t input[32]; + + memcpy( + input, + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd5"), + 32); + + bn_read_be(input, &a); + + bignum256 b = {{0x086d8bd5, 0x1018f82f, 0x11a8bb07, 0x0bc3f7af, 0x0437cd3b, + 0x14087f0a, 0x15498fe5, 0x10b161bb, 0xc55ece}}; + + for (int i = 0; i < 9; i++) { + ck_assert_uint_eq(a.val[i], b.val[i]); + } +} +END_TEST + +START_TEST(test_bignum_write_be) { + bignum256 a = {{0x086d8bd5, 0x1018f82f, 0x11a8bb07, 0x0bc3f7af, 0x0437cd3b, + 0x14087f0a, 0x15498fe5, 0x10b161bb, 0xc55ece}}; + uint8_t tmp[32]; + + bn_write_be(&a, tmp); + + ck_assert_mem_eq( + tmp, + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd5"), + 32); +} +END_TEST + +START_TEST(test_bignum_is_equal) { + bignum256 a = {{0x086d8bd5, 0x1018f82f, 0x11a8bb07, 0x0bc3f7af, 0x0437cd3b, + 0x14087f0a, 0x15498fe5, 0x10b161bb, 0xc55ece}}; + bignum256 b = {{0x086d8bd5, 0x1018f82f, 0x11a8bb07, 0x0bc3f7af, 0x0437cd3b, + 0x14087f0a, 0x15498fe5, 0x10b161bb, 0xc55ece}}; + bignum256 c = {{ + 0, + }}; + + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + ck_assert_int_eq(bn_is_equal(&c, &c), 1); + ck_assert_int_eq(bn_is_equal(&a, &c), 0); +} +END_TEST + +START_TEST(test_bignum_zero) { + bignum256 a; + bignum256 b; + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + bn_zero(&b); + + ck_assert_int_eq(bn_is_equal(&a, &b), 1); +} +END_TEST + +START_TEST(test_bignum_is_zero) { + bignum256 a; + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + ck_assert_int_eq(bn_is_zero(&a), 1); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000001"), + &a); + ck_assert_int_eq(bn_is_zero(&a), 0); + + bn_read_be( + fromhex( + "1000000000000000000000000000000000000000000000000000000000000000"), + &a); + ck_assert_int_eq(bn_is_zero(&a), 0); + + bn_read_be( + fromhex( + "f000000000000000000000000000000000000000000000000000000000000000"), + &a); + ck_assert_int_eq(bn_is_zero(&a), 0); +} +END_TEST + +START_TEST(test_bignum_one) { + bignum256 a; + bignum256 b; + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000001"), + &a); + bn_one(&b); + + ck_assert_int_eq(bn_is_equal(&a, &b), 1); +} +END_TEST + +START_TEST(test_bignum_read_le) { + bignum256 a; + bignum256 b; + + bn_read_be( + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd5"), + &a); + bn_read_le( + fromhex( + "d58b6de8051f031eeca2c6d7fbe1b5d37c4314fe1068f96352dd0d8b85ce5ec5"), + &b); + + ck_assert_int_eq(bn_is_equal(&a, &b), 1); +} +END_TEST + +START_TEST(test_bignum_write_le) { + bignum256 a; + bignum256 b; + uint8_t tmp[32]; + + bn_read_be( + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd5"), + &a); + bn_write_le(&a, tmp); + + bn_read_le(tmp, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + + bn_read_be( + fromhex( + "d58b6de8051f031eeca2c6d7fbe1b5d37c4314fe1068f96352dd0d8b85ce5ec5"), + &a); + bn_read_be(tmp, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); +} +END_TEST + +START_TEST(test_bignum_read_uint32) { + bignum256 a; + bignum256 b; + + // lowest 30 bits set + bn_read_be( + fromhex( + "000000000000000000000000000000000000000000000000000000003fffffff"), + &a); + bn_read_uint32(0x3fffffff, &b); + + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + + // bit 31 set + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000040000000"), + &a); + bn_read_uint32(0x40000000, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); +} +END_TEST + +START_TEST(test_bignum_read_uint64) { + bignum256 a; + bignum256 b; + + // lowest 30 bits set + bn_read_be( + fromhex( + "000000000000000000000000000000000000000000000000000000003fffffff"), + &a); + bn_read_uint64(0x3fffffff, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + + // bit 31 set + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000040000000"), + &a); + bn_read_uint64(0x40000000, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + + // bit 33 set + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000100000000"), + &a); + bn_read_uint64(0x100000000LL, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + + // bit 61 set + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000002000000000000000"), + &a); + bn_read_uint64(0x2000000000000000LL, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + + // all 64 bits set + bn_read_be( + fromhex( + "000000000000000000000000000000000000000000000000ffffffffffffffff"), + &a); + bn_read_uint64(0xffffffffffffffffLL, &b); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); +} +END_TEST + +START_TEST(test_bignum_write_uint32) { + bignum256 a; + + // lowest 29 bits set + bn_read_be( + fromhex( + "000000000000000000000000000000000000000000000000000000001fffffff"), + &a); + ck_assert_uint_eq(bn_write_uint32(&a), 0x1fffffff); + + // lowest 30 bits set + bn_read_be( + fromhex( + "000000000000000000000000000000000000000000000000000000003fffffff"), + &a); + ck_assert_uint_eq(bn_write_uint32(&a), 0x3fffffff); + + // bit 31 set + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000040000000"), + &a); + ck_assert_uint_eq(bn_write_uint32(&a), 0x40000000); +} +END_TEST + +START_TEST(test_bignum_write_uint64) { + bignum256 a; + + // lowest 30 bits set + bn_read_be( + fromhex( + "000000000000000000000000000000000000000000000000000000003fffffff"), + &a); + ck_assert_uint_eq(bn_write_uint64(&a), 0x3fffffff); + + // bit 31 set + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000040000000"), + &a); + ck_assert_uint_eq(bn_write_uint64(&a), 0x40000000); + + // bit 33 set + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000100000000"), + &a); + ck_assert_uint_eq(bn_write_uint64(&a), 0x100000000LL); + + // bit 61 set + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000002000000000000000"), + &a); + ck_assert_uint_eq(bn_write_uint64(&a), 0x2000000000000000LL); + + // all 64 bits set + bn_read_be( + fromhex( + "000000000000000000000000000000000000000000000000ffffffffffffffff"), + &a); + ck_assert_uint_eq(bn_write_uint64(&a), 0xffffffffffffffffLL); +} +END_TEST + +START_TEST(test_bignum_copy) { + bignum256 a; + bignum256 b; + + bn_read_be( + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd5"), + &a); + bn_copy(&a, &b); + + ck_assert_int_eq(bn_is_equal(&a, &b), 1); +} +END_TEST + +START_TEST(test_bignum_is_even) { + bignum256 a; + + bn_read_be( + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd5"), + &a); + ck_assert_int_eq(bn_is_even(&a), 0); + + bn_read_be( + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd2"), + &a); + ck_assert_int_eq(bn_is_even(&a), 1); + + bn_read_be( + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd0"), + &a); + ck_assert_int_eq(bn_is_even(&a), 1); +} +END_TEST + +START_TEST(test_bignum_is_odd) { + bignum256 a; + + bn_read_be( + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd5"), + &a); + ck_assert_int_eq(bn_is_odd(&a), 1); + + bn_read_be( + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd2"), + &a); + ck_assert_int_eq(bn_is_odd(&a), 0); + + bn_read_be( + fromhex( + "c55ece858b0ddd5263f96810fe14437cd3b5e1fbd7c6a2ec1e031f05e86d8bd0"), + &a); + ck_assert_int_eq(bn_is_odd(&a), 0); +} +END_TEST + +START_TEST(test_bignum_is_less) { + bignum256 a; + bignum256 b; + + bn_read_uint32(0x1234, &a); + bn_read_uint32(0x8765, &b); + + ck_assert_int_eq(bn_is_less(&a, &b), 1); + ck_assert_int_eq(bn_is_less(&b, &a), 0); + + bn_zero(&a); + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &b); + + ck_assert_int_eq(bn_is_less(&a, &b), 1); + ck_assert_int_eq(bn_is_less(&b, &a), 0); +} +END_TEST + +START_TEST(test_bignum_bitcount) { + bignum256 a, b; + + bn_zero(&a); + ck_assert_int_eq(bn_bitcount(&a), 0); + + bn_one(&a); + ck_assert_int_eq(bn_bitcount(&a), 1); + + // test for 10000 and 11111 when i=5 + for (int i = 2; i <= 256; i++) { + bn_one(&a); + bn_one(&b); + for (int j = 2; j <= i; j++) { + bn_lshift(&a); + bn_lshift(&b); + bn_addi(&b, 1); + } + ck_assert_int_eq(bn_bitcount(&a), i); + ck_assert_int_eq(bn_bitcount(&b), i); + } + + bn_read_uint32(0x3fffffff, &a); + ck_assert_int_eq(bn_bitcount(&a), 30); + + bn_read_uint32(0xffffffff, &a); + ck_assert_int_eq(bn_bitcount(&a), 32); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + ck_assert_int_eq(bn_bitcount(&a), 256); +} +END_TEST + +START_TEST(test_bignum_digitcount) { + bignum256 a; + + bn_zero(&a); + ck_assert_int_eq(bn_digitcount(&a), 1); + + // test for (10^i) and (10^i) - 1 + uint64_t m = 1; + for (int i = 0; i <= 19; i++, m *= 10) { + bn_read_uint64(m, &a); + ck_assert_int_eq(bn_digitcount(&a), i + 1); + + uint64_t n = m - 1; + bn_read_uint64(n, &a); + ck_assert_int_eq(bn_digitcount(&a), n == 0 ? 1 : i); + } + + bn_read_uint32(0x3fffffff, &a); + ck_assert_int_eq(bn_digitcount(&a), 10); + + bn_read_uint32(0xffffffff, &a); + ck_assert_int_eq(bn_digitcount(&a), 10); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + ck_assert_int_eq(bn_digitcount(&a), 78); +} +END_TEST + +START_TEST(test_bignum_format_uint64) { + char buf[128], str[128]; + size_t r; + // test for (10^i) and (10^i) - 1 + uint64_t m = 1; + for (int i = 0; i <= 19; i++, m *= 10) { + sprintf(str, "%" PRIu64, m); + r = bn_format_uint64(m, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, strlen(str)); + ck_assert_str_eq(buf, str); + + uint64_t n = m - 1; + sprintf(str, "%" PRIu64, n); + r = bn_format_uint64(n, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, strlen(str)); + ck_assert_str_eq(buf, str); + } +} +END_TEST + +START_TEST(test_bignum_format) { + bignum256 a; + char buf[128]; + size_t r; + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "0"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + r = bn_format(&a, NULL, NULL, 20, 0, true, buf, sizeof(buf)); + ck_assert_uint_eq(r, 22); + ck_assert_str_eq(buf, "0.00000000000000000000"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + r = bn_format(&a, NULL, NULL, 0, 5, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "0"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + r = bn_format(&a, NULL, NULL, 0, -5, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "0"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + r = bn_format(&a, "", "", 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "0"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + r = bn_format(&a, NULL, "SFFX", 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1 + 4); + ck_assert_str_eq(buf, "0SFFX"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + r = bn_format(&a, "PRFX", NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 4 + 1); + ck_assert_str_eq(buf, "PRFX0"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + r = bn_format(&a, "PRFX", "SFFX", 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 4 + 1 + 4); + ck_assert_str_eq(buf, "PRFX0SFFX"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + &a); + r = bn_format(&a, NULL, NULL, 18, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "0"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000001"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "1"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000001"), + &a); + r = bn_format(&a, NULL, NULL, 6, 6, true, buf, sizeof(buf)); + ck_assert_uint_eq(r, 8); + ck_assert_str_eq(buf, "1.000000"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000002"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "2"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000005"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "5"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000009"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "9"); + + bn_read_be( + fromhex( + "000000000000000000000000000000000000000000000000000000000000000a"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 2); + ck_assert_str_eq(buf, "10"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000014"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 2); + ck_assert_str_eq(buf, "20"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000032"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 2); + ck_assert_str_eq(buf, "50"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000063"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 2); + ck_assert_str_eq(buf, "99"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000000064"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 3); + ck_assert_str_eq(buf, "100"); + + bn_read_be( + fromhex( + "00000000000000000000000000000000000000000000000000000000000000c8"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 3); + ck_assert_str_eq(buf, "200"); + + bn_read_be( + fromhex( + "00000000000000000000000000000000000000000000000000000000000001f4"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 3); + ck_assert_str_eq(buf, "500"); + + bn_read_be( + fromhex( + "00000000000000000000000000000000000000000000000000000000000003e7"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 3); + ck_assert_str_eq(buf, "999"); + + bn_read_be( + fromhex( + "00000000000000000000000000000000000000000000000000000000000003e8"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 4); + ck_assert_str_eq(buf, "1000"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000000000000989680"), + &a); + r = bn_format(&a, NULL, NULL, 7, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 1); + ck_assert_str_eq(buf, "1"); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 78); + ck_assert_str_eq(buf, + "11579208923731619542357098500868790785326998466564056403945" + "7584007913129639935"); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + r = bn_format(&a, NULL, NULL, 1, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 79); + ck_assert_str_eq(buf, + "11579208923731619542357098500868790785326998466564056403945" + "758400791312963993.5"); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + r = bn_format(&a, NULL, NULL, 2, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 79); + ck_assert_str_eq(buf, + "11579208923731619542357098500868790785326998466564056403945" + "75840079131296399.35"); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + r = bn_format(&a, NULL, NULL, 8, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 79); + ck_assert_str_eq(buf, + "11579208923731619542357098500868790785326998466564056403945" + "75840079131.29639935"); + + bn_read_be( + fromhex( + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe3bbb00"), + &a); + r = bn_format(&a, NULL, NULL, 8, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 70); + ck_assert_str_eq(buf, + "11579208923731619542357098500868790785326998466564056403945" + "75840079131"); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + r = bn_format(&a, NULL, NULL, 18, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 79); + ck_assert_str_eq(buf, + "11579208923731619542357098500868790785326998466564056403945" + "7.584007913129639935"); + + bn_read_be( + fromhex( + "fffffffffffffffffffffffffffffffffffffffffffffffff7e52fe5afe40000"), + &a); + r = bn_format(&a, NULL, NULL, 18, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 60); + ck_assert_str_eq( + buf, "115792089237316195423570985008687907853269984665640564039457"); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + r = bn_format(&a, NULL, NULL, 78, 0, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 80); + ck_assert_str_eq(buf, + "0." + "11579208923731619542357098500868790785326998466564056403945" + "7584007913129639935"); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + r = bn_format(&a, NULL, NULL, 0, 10, false, buf, sizeof(buf)); + ck_assert_uint_eq(r, 88); + ck_assert_str_eq(buf, + "11579208923731619542357098500868790785326998466564056403945" + "75840079131296399350000000000"); + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + r = bn_format(&a, "quite a long prefix", "even longer suffix", 60, 0, false, + buf, sizeof(buf)); + ck_assert_uint_eq(r, 116); + ck_assert_str_eq(buf, + "quite a long " + "prefix115792089237316195." + "42357098500868790785326998466564056403945758400791312963993" + "5even longer suffix"); + + bn_read_be( + fromhex( + "0000000000000000000000000000000000000000000000000123456789abcdef"), + &a); + memset(buf, 'a', sizeof(buf)); + r = bn_format(&a, "prefix", "suffix", 10, 0, false, buf, 31); + ck_assert_str_eq(buf, "prefix8198552.9216486895suffix"); + ck_assert_uint_eq(r, 30); + + memset(buf, 'a', sizeof(buf)); + r = bn_format(&a, "prefix", "suffix", 10, 0, false, buf, 30); + ck_assert_uint_eq(r, 0); + ck_assert_str_eq(buf, ""); +} +END_TEST + +START_TEST(test_bignum_sqrt) { + uint32_t quadratic_residua[] = { + 1, 2, 4, 8, 9, 11, 15, 16, 17, 18, 19, 21, 22, 25, 29, + 30, 31, 32, 34, 35, 36, 38, 39, 42, 43, 44, 47, 49, 50, 58, + 59, 60, 61, 62, 64, 65, 67, 68, 69, 70, 71, 72, 76, 78, 81, + 83, 84, 86, 88, 91, 94, 98, 99, 100, 103, 107, 111, 115, 116, 118, + 120, 121, 122, 123, 124, 127, 128, 130, 131, 134, 135, 136, 137, 138, 139, + 140, 142, 144, 149, 152, 153, 156, 159, 161, 162, 165, 166, 167, 168, 169, + 171, 172, 176, 181, 182, 185, 187, 188, 189, 191, 193, 196, 197, 198, 200, + 205, 206, 209, 214, 219, 222, 223, 225, 229, 230, 231, 232, 233, 236, 237, + 239, 240, 242, 244, 246, 248, 254, 255, 256, 259, 260, 261, 262, 265, 267, + 268, 269, 270, 272, 274, 275, 276, 277, 278, 279, 280, 281, 284, 285, 287, + 288, 289, 291, 293, 298, 299, 303, 304, 306, 311, 312, 315, 318, 319, 322, + 323, 324, 327, 330, 331, 332, 334, 336, 337, 338, 339, 341, 342, 344, 349, + 351, 352, 353, 357, 359, 361, 362, 364, 365, 370, 371, 373, 374, 375, 376, + 378, 379, 382, 383, 385, 386, 387, 389, 392, 394, 395, 396, 399, 400, 409, + 410, 412, 418, 421, 423, 425, 428, 429, 431, 435, 438, 439, 441, 443, 444, + 445, 446, 450, 453, 458, 460, 461, 462, 463, 464, 465, 466, 467, 471, 472, + 473, 474, 475, 478, 479, 480, 481, 484, 485, 487, 488, 489, 492, 493, 496, + 503, 505, 508, 510, 511, 512, 517, 518, 519, 520, 521, 522, 523, 524, 525, + 527, 529, 530, 531, 533, 534, 536, 537, 538, 539, 540, 541, 544, 545, 547, + 548, 549, 550, 551, 552, 553, 554, 556, 557, 558, 560, 562, 563, 565, 568, + 570, 571, 574, 576, 578, 582, 585, 586, 587, 589, 595, 596, 597, 598, 599, + 603, 606, 607, 608, 609, 612, 613, 619, 621, 622, 623, 624, 625, 630, 633, + 636, 638, 639, 644, 645, 646, 648, 649, 651, 653, 654, 660, 662, 663, 664, + 665, 668, 671, 672, 673, 674, 676, 678, 679, 681, 682, 684, 688, 689, 698, + 702, 704, 705, 706, 707, 714, 715, 718, 722, 723, 724, 725, 728, 729, 730, + 731, 733, 735, 737, 740, 741, 742, 746, 747, 748, 750, 751, 752, 753, 755, + 756, 758, 759, 761, 763, 764, 766, 769, 770, 771, 772, 774, 775, 778, 781, + 784, 785, 788, 789, 790, 791, 792, 797, 798, 799, 800, 813, 815, 817, 818, + 819, 820, 823, 824, 833, 836, 841, 842, 846, 849, 850, 851, 856, 857, 858, + 862, 865, 870, 875, 876, 878, 882, 885, 886, 887, 888, 890, 891, 892, 893, + 895, 899, 900, 903, 906, 907, 911, 913, 915, 916, 919, 920, 921, 922, 924, + 926, 927, 928, 930, 931, 932, 934, 937, 939, 942, 943, 944, 946, 948, 949, + 950, 951, 953, 956, 958, 960, 961, 962, 963, 968, 970, 971, 974, 975, 976, + 977, 978, 984, 986, 987, 992, 995, 999}; + + bignum256 a, b; + + bn_zero(&a); + b = a; + bn_sqrt(&b, &secp256k1.prime); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + + bn_one(&a); + b = a; + bn_sqrt(&b, &secp256k1.prime); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + + // test some quadratic residua + for (size_t i = 0; i < sizeof(quadratic_residua) / sizeof(*quadratic_residua); + i++) { + bn_read_uint32(quadratic_residua[i], &a); + b = a; + bn_sqrt(&b, &secp256k1.prime); + bn_multiply(&b, &b, &secp256k1.prime); + bn_mod(&b, &secp256k1.prime); + ck_assert_int_eq(bn_is_equal(&a, &b), 1); + } +} +END_TEST + +// https://tools.ietf.org/html/rfc4648#section-10 +START_TEST(test_base32_rfc4648) { + static const struct { + const char *decoded; + const char *encoded; + const char *encoded_lowercase; + } tests[] = { + {"", "", ""}, + {"f", "MY", "my"}, + {"fo", "MZXQ", "mzxq"}, + {"foo", "MZXW6", "mzxw6"}, + {"foob", "MZXW6YQ", "mzxw6yq"}, + {"fooba", "MZXW6YTB", "mzxw6ytb"}, + {"foobar", "MZXW6YTBOI", "mzxw6ytboi"}, + }; + + char buffer[64]; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + const char *in = tests[i].decoded; + const char *out = tests[i].encoded; + const char *out_lowercase = tests[i].encoded_lowercase; + + size_t inlen = strlen(in); + size_t outlen = strlen(out); + + ck_assert_uint_eq(outlen, base32_encoded_length(inlen)); + ck_assert_uint_eq(inlen, base32_decoded_length(outlen)); + + ck_assert(base32_encode((uint8_t *)in, inlen, buffer, sizeof(buffer), + BASE32_ALPHABET_RFC4648) != NULL); + ck_assert_str_eq(buffer, out); + + char *ret = (char *)base32_decode(out, outlen, (uint8_t *)buffer, + sizeof(buffer), BASE32_ALPHABET_RFC4648); + ck_assert(ret != NULL); + *ret = '\0'; + ck_assert_str_eq(buffer, in); + + ret = (char *)base32_decode(out_lowercase, outlen, (uint8_t *)buffer, + sizeof(buffer), BASE32_ALPHABET_RFC4648); + ck_assert(ret != NULL); + *ret = '\0'; + ck_assert_str_eq(buffer, in); + } +} +END_TEST + +// from +// https://github.com/bitcoin/bitcoin/blob/master/src/test/data/base58_keys_valid.json +START_TEST(test_base58) { + static const char *base58_vector[] = { + "0065a16059864a2fdbc7c99a4723a8395bc6f188eb", + "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", + "0574f209f6ea907e2ea48f74fae05782ae8a665257", + "3CMNFxN1oHBc4R1EpboAL5yzHGgE611Xou", + "6f53c0307d6851aa0ce7825ba883c6bd9ad242b486", + "mo9ncXisMeAoXwqcV5EWuyncbmCcQN4rVs", + "c46349a418fc4578d10a372b54b45c280cc8c4382f", + "2N2JD6wb56AfK4tfmM6PwdVmoYk2dCKf4Br", + "80eddbdc1168f1daeadbd3e44c1e3f8f5a284c2029f78ad26af98583a499de5b19", + "5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr", + "8055c9bccb9ed68446d1b75273bbce89d7fe013a8acd1625514420fb2aca1a21c401", + "Kz6UJmQACJmLtaQj5A3JAge4kVTNQ8gbvXuwbmCj7bsaabudb3RD", + "ef36cb93b9ab1bdabf7fb9f2c04f1b9cc879933530ae7842398eef5a63a56800c2", + "9213qJab2HNEpMpYNBa7wHGFKKbkDn24jpANDs2huN3yi4J11ko", + "efb9f4892c9e8282028fea1d2667c4dc5213564d41fc5783896a0d843fc15089f301", + "cTpB4YiyKiBcPxnefsDpbnDxFDffjqJob8wGCEDXxgQ7zQoMXJdH", + "006d23156cbbdcc82a5a47eee4c2c7c583c18b6bf4", + "1Ax4gZtb7gAit2TivwejZHYtNNLT18PUXJ", + "05fcc5460dd6e2487c7d75b1963625da0e8f4c5975", + "3QjYXhTkvuj8qPaXHTTWb5wjXhdsLAAWVy", + "6ff1d470f9b02370fdec2e6b708b08ac431bf7a5f7", + "n3ZddxzLvAY9o7184TB4c6FJasAybsw4HZ", + "c4c579342c2c4c9220205e2cdc285617040c924a0a", + "2NBFNJTktNa7GZusGbDbGKRZTxdK9VVez3n", + "80a326b95ebae30164217d7a7f57d72ab2b54e3be64928a19da0210b9568d4015e", + "5K494XZwps2bGyeL71pWid4noiSNA2cfCibrvRWqcHSptoFn7rc", + "807d998b45c219a1e38e99e7cbd312ef67f77a455a9b50c730c27f02c6f730dfb401", + "L1RrrnXkcKut5DEMwtDthjwRcTTwED36thyL1DebVrKuwvohjMNi", + "efd6bca256b5abc5602ec2e1c121a08b0da2556587430bcf7e1898af2224885203", + "93DVKyFYwSN6wEo3E2fCrFPUp17FtrtNi2Lf7n4G3garFb16CRj", + "efa81ca4e8f90181ec4b61b6a7eb998af17b2cb04de8a03b504b9e34c4c61db7d901", + "cTDVKtMGVYWTHCb1AFjmVbEbWjvKpKqKgMaR3QJxToMSQAhmCeTN", + "007987ccaa53d02c8873487ef919677cd3db7a6912", + "1C5bSj1iEGUgSTbziymG7Cn18ENQuT36vv", + "0563bcc565f9e68ee0189dd5cc67f1b0e5f02f45cb", + "3AnNxabYGoTxYiTEZwFEnerUoeFXK2Zoks", + "6fef66444b5b17f14e8fae6e7e19b045a78c54fd79", + "n3LnJXCqbPjghuVs8ph9CYsAe4Sh4j97wk", + "c4c3e55fceceaa4391ed2a9677f4a4d34eacd021a0", + "2NB72XtkjpnATMggui83aEtPawyyKvnbX2o", + "80e75d936d56377f432f404aabb406601f892fd49da90eb6ac558a733c93b47252", + "5KaBW9vNtWNhc3ZEDyNCiXLPdVPHCikRxSBWwV9NrpLLa4LsXi9", + "808248bd0375f2f75d7e274ae544fb920f51784480866b102384190b1addfbaa5c01", + "L1axzbSyynNYA8mCAhzxkipKkfHtAXYF4YQnhSKcLV8YXA874fgT", + "ef44c4f6a096eac5238291a94cc24c01e3b19b8d8cef72874a079e00a242237a52", + "927CnUkUbasYtDwYwVn2j8GdTuACNnKkjZ1rpZd2yBB1CLcnXpo", + "efd1de707020a9059d6d3abaf85e17967c6555151143db13dbb06db78df0f15c6901", + "cUcfCMRjiQf85YMzzQEk9d1s5A4K7xL5SmBCLrezqXFuTVefyhY7", + "00adc1cc2081a27206fae25792f28bbc55b831549d", + "1Gqk4Tv79P91Cc1STQtU3s1W6277M2CVWu", + "05188f91a931947eddd7432d6e614387e32b244709", + "33vt8ViH5jsr115AGkW6cEmEz9MpvJSwDk", + "6f1694f5bc1a7295b600f40018a618a6ea48eeb498", + "mhaMcBxNh5cqXm4aTQ6EcVbKtfL6LGyK2H", + "c43b9b3fd7a50d4f08d1a5b0f62f644fa7115ae2f3", + "2MxgPqX1iThW3oZVk9KoFcE5M4JpiETssVN", + "80091035445ef105fa1bb125eccfb1882f3fe69592265956ade751fd095033d8d0", + "5HtH6GdcwCJA4ggWEL1B3jzBBUB8HPiBi9SBc5h9i4Wk4PSeApR", + "80ab2b4bcdfc91d34dee0ae2a8c6b6668dadaeb3a88b9859743156f462325187af01", + "L2xSYmMeVo3Zek3ZTsv9xUrXVAmrWxJ8Ua4cw8pkfbQhcEFhkXT8", + "efb4204389cef18bbe2b353623cbf93e8678fbc92a475b664ae98ed594e6cf0856", + "92xFEve1Z9N8Z641KQQS7ByCSb8kGjsDzw6fAmjHN1LZGKQXyMq", + "efe7b230133f1b5489843260236b06edca25f66adb1be455fbd38d4010d48faeef01", + "cVM65tdYu1YK37tNoAyGoJTR13VBYFva1vg9FLuPAsJijGvG6NEA", + "00c4c1b72491ede1eedaca00618407ee0b772cad0d", + "1JwMWBVLtiqtscbaRHai4pqHokhFCbtoB4", + "05f6fe69bcb548a829cce4c57bf6fff8af3a5981f9", + "3QCzvfL4ZRvmJFiWWBVwxfdaNBT8EtxB5y", + "6f261f83568a098a8638844bd7aeca039d5f2352c0", + "mizXiucXRCsEriQCHUkCqef9ph9qtPbZZ6", + "c4e930e1834a4d234702773951d627cce82fbb5d2e", + "2NEWDzHWwY5ZZp8CQWbB7ouNMLqCia6YRda", + "80d1fab7ab7385ad26872237f1eb9789aa25cc986bacc695e07ac571d6cdac8bc0", + "5KQmDryMNDcisTzRp3zEq9e4awRmJrEVU1j5vFRTKpRNYPqYrMg", + "80b0bbede33ef254e8376aceb1510253fc3550efd0fcf84dcd0c9998b288f166b301", + "L39Fy7AC2Hhj95gh3Yb2AU5YHh1mQSAHgpNixvm27poizcJyLtUi", + "ef037f4192c630f399d9271e26c575269b1d15be553ea1a7217f0cb8513cef41cb", + "91cTVUcgydqyZLgaANpf1fvL55FH53QMm4BsnCADVNYuWuqdVys", + "ef6251e205e8ad508bab5596bee086ef16cd4b239e0cc0c5d7c4e6035441e7d5de01", + "cQspfSzsgLeiJGB2u8vrAiWpCU4MxUT6JseWo2SjXy4Qbzn2fwDw", + "005eadaf9bb7121f0f192561a5a62f5e5f54210292", + "19dcawoKcZdQz365WpXWMhX6QCUpR9SY4r", + "053f210e7277c899c3a155cc1c90f4106cbddeec6e", + "37Sp6Rv3y4kVd1nQ1JV5pfqXccHNyZm1x3", + "6fc8a3c2a09a298592c3e180f02487cd91ba3400b5", + "myoqcgYiehufrsnnkqdqbp69dddVDMopJu", + "c499b31df7c9068d1481b596578ddbb4d3bd90baeb", + "2N7FuwuUuoTBrDFdrAZ9KxBmtqMLxce9i1C", + "80c7666842503db6dc6ea061f092cfb9c388448629a6fe868d068c42a488b478ae", + "5KL6zEaMtPRXZKo1bbMq7JDjjo1bJuQcsgL33je3oY8uSJCR5b4", + "8007f0803fc5399e773555ab1e8939907e9badacc17ca129e67a2f5f2ff84351dd01", + "KwV9KAfwbwt51veZWNscRTeZs9CKpojyu1MsPnaKTF5kz69H1UN2", + "efea577acfb5d1d14d3b7b195c321566f12f87d2b77ea3a53f68df7ebf8604a801", + "93N87D6uxSBzwXvpokpzg8FFmfQPmvX4xHoWQe3pLdYpbiwT5YV", + "ef0b3b34f0958d8a268193a9814da92c3e8b58b4a4378a542863e34ac289cd830c01", + "cMxXusSihaX58wpJ3tNuuUcZEQGt6DKJ1wEpxys88FFaQCYjku9h", + "001ed467017f043e91ed4c44b4e8dd674db211c4e6", + "13p1ijLwsnrcuyqcTvJXkq2ASdXqcnEBLE", + "055ece0cadddc415b1980f001785947120acdb36fc", + "3ALJH9Y951VCGcVZYAdpA3KchoP9McEj1G", + 0, + 0, + }; + const char **raw = base58_vector; + const char **str = base58_vector + 1; + uint8_t rawn[34]; + char strn[53]; + int r; + while (*raw && *str) { + int len = strlen(*raw) / 2; + + memcpy(rawn, fromhex(*raw), len); + r = base58_encode_check(rawn, len, HASHER_SHA2D, strn, sizeof(strn)); + ck_assert_int_eq((size_t)r, strlen(*str) + 1); + ck_assert_str_eq(strn, *str); + + r = base58_decode_check(strn, HASHER_SHA2D, rawn, len); + ck_assert_int_eq(r, len); + ck_assert_mem_eq(rawn, fromhex(*raw), len); + + raw += 2; + str += 2; + } +} +END_TEST + +#if USE_GRAPHENE + +// Graphene Base85CheckEncoding +START_TEST(test_base58gph) { + static const char *base58_vector[] = { + "02e649f63f8e8121345fd7f47d0d185a3ccaa843115cd2e9392dcd9b82263bc680", + "6dumtt9swxCqwdPZBGXh9YmHoEjFFnNfwHaTqRbQTghGAY2gRz", + "021c7359cd885c0e319924d97e3980206ad64387aff54908241125b3a88b55ca16", + "5725vivYpuFWbeyTifZ5KevnHyqXCi5hwHbNU9cYz1FHbFXCxX", + "02f561e0b57a552df3fa1df2d87a906b7a9fc33a83d5d15fa68a644ecb0806b49a", + "6kZKHSuxqAwdCYsMvwTcipoTsNE2jmEUNBQufGYywpniBKXWZK", + "03e7595c3e6b58f907bee951dc29796f3757307e700ecf3d09307a0cc4a564eba3", + "8b82mpnH8YX1E9RHnU2a2YgLTZ8ooevEGP9N15c1yFqhoBvJur", + 0, + 0, + }; + const char **raw = base58_vector; + const char **str = base58_vector + 1; + uint8_t rawn[34]; + char strn[53]; + int r; + while (*raw && *str) { + int len = strlen(*raw) / 2; + + memcpy(rawn, fromhex(*raw), len); + r = base58gph_encode_check(rawn, len, strn, sizeof(strn)); + ck_assert_int_eq((size_t)r, strlen(*str) + 1); + ck_assert_str_eq(strn, *str); + + r = base58gph_decode_check(strn, rawn, len); + ck_assert_int_eq(r, len); + ck_assert_mem_eq(rawn, fromhex(*raw), len); + + raw += 2; + str += 2; + } +} +END_TEST + +#endif + +START_TEST(test_bignum_divmod) { + uint32_t r; + int i; + + bignum256 a; + uint32_t ar[] = {15, 14, 55, 29, 44, 24, 53, 49, 18, 55, 2, 28, 5, 4, 12, + 43, 18, 37, 28, 14, 30, 46, 12, 11, 17, 10, 10, 13, 24, 45, + 4, 33, 44, 42, 2, 46, 34, 43, 45, 28, 21, 18, 13, 17}; + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &a); + + i = 0; + while (!bn_is_zero(&a) && i < 44) { + bn_divmod58(&a, &r); + ck_assert_uint_eq(r, ar[i]); + i++; + } + ck_assert_int_eq(i, 44); + + bignum256 b; + uint32_t br[] = {935, 639, 129, 913, 7, 584, 457, 39, 564, + 640, 665, 984, 269, 853, 907, 687, 8, 985, + 570, 423, 195, 316, 237, 89, 792, 115}; + + bn_read_be( + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + &b); + i = 0; + while (!bn_is_zero(&b) && i < 26) { + bn_divmod1000(&b, &r); + ck_assert_uint_eq(r, br[i]); + i++; + } + ck_assert_int_eq(i, 26); +} +END_TEST + +// test vector 1 from +// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-1 +START_TEST(test_bip32_vector_1) { + HDNode node, node2, node3; + uint32_t fingerprint; + char str[XPUB_MAXLEN]; + int r; + + // init m + hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, + SECP256K1_NAME, &node); + + // [Chain m] + fingerprint = 0; + ck_assert_uint_eq(fingerprint, 0x00000000); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqji" + "ChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2" + "gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd_prime(&node, 0); + ck_assert_uint_eq(fingerprint, 0x3442193e); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4" + "cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP" + "6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd(&node, 1); + ck_assert_uint_eq(fingerprint, 0x5c1bd648); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSx" + "qu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFH" + "KkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1/2'] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd_prime(&node, 2); + ck_assert_uint_eq(fingerprint, 0xbef5a2f9); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptW" + "mT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgq" + "FJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1/2'/2] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd(&node, 2); + ck_assert_uint_eq(fingerprint, 0xee7ab90c); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Ty" + "h8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJ" + "AyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1/2'/2/1000000000] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd(&node, 1000000000); + ck_assert_uint_eq(fingerprint, 0xd880d7d8); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8" + "kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNT" + "EcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); +} +END_TEST + +// test vector 2 from +// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-2 +START_TEST(test_bip32_vector_2) { + HDNode node, node2, node3; + uint32_t fingerprint; + char str[XPUB_MAXLEN]; + int r; + + // init m + hdnode_from_seed( + fromhex( + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c" + "999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), + 64, SECP256K1_NAME, &node); + + // [Chain m] + fingerprint = 0; + ck_assert_uint_eq(fingerprint, 0x00000000); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGds" + "o3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSC" + "Gu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0xbd16bee5); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT" + "3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGm" + "XUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0/2147483647'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 2147483647); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x5a61ff8e); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYE" + "eEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ" + "85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0/2147483647'/1] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node, 1); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0xd8ab4937); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd" + "25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5Ew" + "VvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0/2147483647'/1/2147483646'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 2147483646); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x78412e3a); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz" + "7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJ" + "bZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0/2147483647'/1/2147483646'/2] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node, 2); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x31a507b8); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"), + 33); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw" + "7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLF" + "bdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // init m + hdnode_from_seed( + fromhex( + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c" + "999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), + 64, SECP256K1_NAME, &node); + + // test public derivation + // [Chain m/0] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_public_ckd(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0xbd16bee5); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"), + 33); +} +END_TEST + +// test vector 3 from +// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-3 +START_TEST(test_bip32_vector_3) { + HDNode node, node2, node3; + uint32_t fingerprint; + char str[XPUB_MAXLEN]; + int r; + + // init m + hdnode_from_seed( + fromhex( + "4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45" + "d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be"), + 64, SECP256K1_NAME, &node); + + // [Chain m] + fingerprint = 0; + ck_assert_uint_eq(fingerprint, 0x00000000); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7" + "KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhR" + "oP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu" + "2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrAD" + "WgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); +} +END_TEST + +// test vector 4 from +// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vector-4 +START_TEST(test_bip32_vector_4) { + HDNode node, node2, node3; + uint32_t fingerprint; + char str[XPUB_MAXLEN]; + int r; + + // init m + hdnode_from_seed( + fromhex( + "3ddd5602285899a946114506157c7997e5444528f3003f6134712147db19b678"), + 32, SECP256K1_NAME, &node); + + // [Chain m] + fingerprint = 0; + ck_assert_int_eq(fingerprint, 0x00000000); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9s21ZrQH143K48vGoLGRPxgo2JNkJ3J3fqkirQC2zVdk5Dgd5w14S7f" + "RDyHH4dWNHUgkvsvNDCkvAwcSHNAQwhwgNMgZhLtQC63zxwhQmRv"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub661MyMwAqRbcGczjuMoRm6dXaLDEhW1u34gKenbeYqAix21mdUKJyuy" + "u5F1rzYGVxyL6tmgBUAEPrEz92mBXjByMRiJdba9wpnN37RLLAXa"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9vB7xEWwNp9kh1wQRfCCQMnZUEG21LpbR9NPCNN1dwhiZkjjeGRnaAL" + "mPXCX7SgjFTiCTT6bXes17boXtjq3xLpcDjzEuGLQBM5ohqkao9G"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub69AUMk3qDBi3uW1sXgjCmVjJ2G6WQoYSnNHyzkmdCHEhSZ4tBok37xf" + "FEqHd2AddP56Tqp4o56AePAgCjYdvpW2PU2jbUPFKsav5ut6Ch1m"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 1); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJ" + "eHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d" + "88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); +} +END_TEST + +START_TEST(test_bip32_compare) { + HDNode node1, node2, node3; + int i, r; + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &node1); + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + for (i = 0; i < 100; i++) { + memcpy(&node3, &node1, sizeof(HDNode)); + ck_assert_int_eq(hdnode_fill_public_key(&node3), 0); + r = hdnode_private_ckd(&node1, i); + ck_assert_int_eq(r, 1); + r = hdnode_public_ckd(&node2, i); + ck_assert_int_eq(r, 1); + r = hdnode_public_ckd(&node3, i); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(node1.depth, node2.depth); + ck_assert_uint_eq(node1.depth, node3.depth); + ck_assert_uint_eq(node1.child_num, node2.child_num); + ck_assert_uint_eq(node1.child_num, node3.child_num); + ck_assert_mem_eq(node1.chain_code, node2.chain_code, 32); + ck_assert_mem_eq(node1.chain_code, node3.chain_code, 32); + ck_assert_mem_eq( + node2.private_key, + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + 32); + ck_assert_mem_eq( + node3.private_key, + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node1), 0); + ck_assert_mem_eq(node1.public_key, node2.public_key, 33); + ck_assert_mem_eq(node1.public_key, node3.public_key, 33); + } +} +END_TEST + +START_TEST(test_bip32_optimized) { + HDNode root; + hdnode_from_seed((uint8_t *)"NothingToSeeHere", 16, SECP256K1_NAME, &root); + ck_assert_int_eq(hdnode_fill_public_key(&root), 0); + + curve_point pub; + ecdsa_read_pubkey(&secp256k1, root.public_key, &pub); + + HDNode node; + char addr1[MAX_ADDR_SIZE], addr2[MAX_ADDR_SIZE]; + + for (int i = 0; i < 40; i++) { + // unoptimized + memcpy(&node, &root, sizeof(HDNode)); + hdnode_public_ckd(&node, i); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ecdsa_get_address(node.public_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, + addr1, sizeof(addr1)); + // optimized + hdnode_public_ckd_address_optimized(&pub, root.chain_code, i, 0, + HASHER_SHA2_RIPEMD, HASHER_SHA2D, addr2, + sizeof(addr2), 0); + // check + ck_assert_str_eq(addr1, addr2); + } +} +END_TEST + +#if USE_BIP32_CACHE + +START_TEST(test_bip32_cache_1) { + HDNode node1, node2; + int i, r; + + // test 1 .. 8 + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &node1); + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &node2); + + uint32_t ii[] = {0x80000001, 0x80000002, 0x80000003, 0x80000004, + 0x80000005, 0x80000006, 0x80000007, 0x80000008}; + + for (i = 0; i < 8; i++) { + r = hdnode_private_ckd(&node1, ii[i]); + ck_assert_int_eq(r, 1); + } + r = hdnode_private_ckd_cached(&node2, ii, 8, NULL); + ck_assert_int_eq(r, 1); + ck_assert_mem_eq(&node1, &node2, sizeof(HDNode)); + + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &node1); + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &node2); + + // test 1 .. 7, 20 + ii[7] = 20; + for (i = 0; i < 8; i++) { + r = hdnode_private_ckd(&node1, ii[i]); + ck_assert_int_eq(r, 1); + } + r = hdnode_private_ckd_cached(&node2, ii, 8, NULL); + ck_assert_int_eq(r, 1); + ck_assert_mem_eq(&node1, &node2, sizeof(HDNode)); + + // test different root node + hdnode_from_seed( + fromhex( + "000000002ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &node1); + hdnode_from_seed( + fromhex( + "000000002ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &node2); + + for (i = 0; i < 8; i++) { + r = hdnode_private_ckd(&node1, ii[i]); + ck_assert_int_eq(r, 1); + } + r = hdnode_private_ckd_cached(&node2, ii, 8, NULL); + ck_assert_int_eq(r, 1); + ck_assert_mem_eq(&node1, &node2, sizeof(HDNode)); +} +END_TEST + +START_TEST(test_bip32_cache_2) { + HDNode nodea[9], nodeb[9]; + int i, j, r; + + for (j = 0; j < 9; j++) { + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d627" + "88f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &(nodea[j])); + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d627" + "88f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, SECP256K1_NAME, &(nodeb[j])); + } + + uint32_t ii[] = {0x80000001, 0x80000002, 0x80000003, 0x80000004, + 0x80000005, 0x80000006, 0x80000007, 0x80000008}; + for (j = 0; j < 9; j++) { + // non cached + for (i = 1; i <= j; i++) { + r = hdnode_private_ckd(&(nodea[j]), ii[i - 1]); + ck_assert_int_eq(r, 1); + } + // cached + r = hdnode_private_ckd_cached(&(nodeb[j]), ii, j, NULL); + ck_assert_int_eq(r, 1); + } + + ck_assert_mem_eq(&(nodea[0]), &(nodeb[0]), sizeof(HDNode)); + ck_assert_mem_eq(&(nodea[1]), &(nodeb[1]), sizeof(HDNode)); + ck_assert_mem_eq(&(nodea[2]), &(nodeb[2]), sizeof(HDNode)); + ck_assert_mem_eq(&(nodea[3]), &(nodeb[3]), sizeof(HDNode)); + ck_assert_mem_eq(&(nodea[4]), &(nodeb[4]), sizeof(HDNode)); + ck_assert_mem_eq(&(nodea[5]), &(nodeb[5]), sizeof(HDNode)); + ck_assert_mem_eq(&(nodea[6]), &(nodeb[6]), sizeof(HDNode)); + ck_assert_mem_eq(&(nodea[7]), &(nodeb[7]), sizeof(HDNode)); + ck_assert_mem_eq(&(nodea[8]), &(nodeb[8]), sizeof(HDNode)); +} +END_TEST +#endif + +START_TEST(test_bip32_nist_seed) { + HDNode node; + + // init m + hdnode_from_seed( + fromhex( + "a7305bc8df8d0951f0cb224c0e95d7707cbdf2c6ce7e8d481fec69c7ff5e9446"), + 32, NIST256P1_NAME, &node); + + // [Chain m] + ck_assert_mem_eq( + node.private_key, + fromhex( + "3b8c18469a4634517d6d0b65448f8e6c62091b45540a1743c5846be55d47d88f"), + 32); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "7762f9729fed06121fd13f326884c82f59aa95c57ac492ce8c9654e60efd130c"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0383619fadcde31063d8c5cb00dbfe1713f3e6fa169d8541a798752a1c1ca0cb20"), + 33); + + // init m + hdnode_from_seed( + fromhex( + "aa305bc8df8d0951f0cb29ad4568d7707cbdf2c6ce7e8d481fec69c7ff5e9446"), + 32, NIST256P1_NAME, &node); + + // [Chain m] + ck_assert_mem_eq( + node.chain_code, + fromhex( + "a81d21f36f987fa0be3b065301bfb6aa9deefbf3dfef6744c37b9a4abc3c68f1"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "0e49dc46ce1d8c29d9b80a05e40f5d0cd68cbf02ae98572186f5343be18084bf"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03aaa4c89acd9a98935330773d3dae55122f3591bac4a40942681768de8df6ba63"), + 33); +} +END_TEST + +START_TEST(test_bip32_nist_vector_1) { + HDNode node; + uint32_t fingerprint; + + // init m + hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, + NIST256P1_NAME, &node); + + // [Chain m] + fingerprint = 0; + ck_assert_uint_eq(fingerprint, 0x00000000); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "beeb672fe4621673f722f38529c07392fecaa61015c80c34f29ce8b41b3cb6ea"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "612091aaa12e22dd2abef664f8a01a82cae99ad7441b7ef8110424915c268bc2"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0266874dc6ade47b3ecd096745ca09bcd29638dd52c2c12117b11ed3e458cfa9e8"), + 33); + + // [Chain m/0'] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd_prime(&node, 0); + ck_assert_uint_eq(fingerprint, 0xbe6105b5); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "3460cea53e6a6bb5fb391eeef3237ffd8724bf0a40e94943c98b83825342ee11"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "6939694369114c67917a182c59ddb8cafc3004e63ca5d3b84403ba8613debc0c"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0384610f5ecffe8fda089363a41f56a5c7ffc1d81b59a612d0d649b2d22355590c"), + 33); + + // [Chain m/0'/1] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd(&node, 1); + ck_assert_uint_eq(fingerprint, 0x9b02312f); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "4187afff1aafa8445010097fb99d23aee9f599450c7bd140b6826ac22ba21d0c"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "284e9d38d07d21e4e281b645089a94f4cf5a5a81369acf151a1c3a57f18b2129"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03526c63f8d0b4bbbf9c80df553fe66742df4676b241dabefdef67733e070f6844"), + 33); + + // [Chain m/0'/1/2'] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd_prime(&node, 2); + ck_assert_uint_eq(fingerprint, 0xb98005c1); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "98c7514f562e64e74170cc3cf304ee1ce54d6b6da4f880f313e8204c2a185318"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "694596e8a54f252c960eb771a3c41e7e32496d03b954aeb90f61635b8e092aa7"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0359cf160040778a4b14c5f4d7b76e327ccc8c4a6086dd9451b7482b5a4972dda0"), + 33); + + // [Chain m/0'/1/2'/2] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd(&node, 2); + ck_assert_uint_eq(fingerprint, 0x0e9f3274); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "ba96f776a5c3907d7fd48bde5620ee374d4acfd540378476019eab70790c63a0"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "5996c37fd3dd2679039b23ed6f70b506c6b56b3cb5e424681fb0fa64caf82aaa"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "029f871f4cb9e1c97f9f4de9ccd0d4a2f2a171110c61178f84430062230833ff20"), + 33); + + // [Chain m/0'/1/2'/2/1000000000] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd(&node, 1000000000); + ck_assert_uint_eq(fingerprint, 0x8b2b5c4b); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "b9b7b82d326bb9cb5b5b121066feea4eb93d5241103c9e7a18aad40f1dde8059"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "21c4f269ef0a5fd1badf47eeacebeeaa3de22eb8e5b0adcd0f27dd99d34d0119"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02216cd26d31147f72427a453c443ed2cde8a1e53c9cc44e5ddf739725413fe3f4"), + 33); +} +END_TEST + +START_TEST(test_bip32_nist_vector_2) { + HDNode node; + uint32_t fingerprint; + int r; + + // init m + hdnode_from_seed( + fromhex( + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c" + "999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), + 64, NIST256P1_NAME, &node); + + // [Chain m] + fingerprint = 0; + ck_assert_uint_eq(fingerprint, 0x00000000); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "96cd4465a9644e31528eda3592aa35eb39a9527769ce1855beafc1b81055e75d"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "eaa31c2e46ca2962227cf21d73a7ef0ce8b31c756897521eb6c7b39796633357"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02c9e16154474b3ed5b38218bb0463e008f89ee03e62d22fdcc8014beab25b48fa"), + 33); + + // [Chain m/0] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x607f628f); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "84e9c258bb8557a40e0d041115b376dd55eda99c0042ce29e81ebe4efed9b86a"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "d7d065f63a62624888500cdb4f88b6d59c2927fee9e6d0cdff9cad555884df6e"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "039b6df4bece7b6c81e2adfeea4bcf5c8c8a6e40ea7ffa3cf6e8494c61a1fc82cc"), + 33); + + // [Chain m/0/2147483647'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 2147483647); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x946d2a54); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "f235b2bc5c04606ca9c30027a84f353acf4e4683edbd11f635d0dcc1cd106ea6"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "96d2ec9316746a75e7793684ed01e3d51194d81a42a3276858a5b7376d4b94b9"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02f89c5deb1cae4fedc9905f98ae6cbf6cbab120d8cb85d5bd9a91a72f4c068c76"), + 33); + + // [Chain m/0/2147483647'/1] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node, 1); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x218182d8); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "7c0b833106235e452eba79d2bdd58d4086e663bc8cc55e9773d2b5eeda313f3b"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "974f9096ea6873a915910e82b29d7c338542ccde39d2064d1cc228f371542bbc"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03abe0ad54c97c1d654c1852dfdc32d6d3e487e75fa16f0fd6304b9ceae4220c64"), + 33); + + // [Chain m/0/2147483647'/1/2147483646'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 2147483646); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x931223e4); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "5794e616eadaf33413aa309318a26ee0fd5163b70466de7a4512fd4b1a5c9e6a"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "da29649bbfaff095cd43819eda9a7be74236539a29094cd8336b07ed8d4eff63"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03cb8cb067d248691808cd6b5a5a06b48e34ebac4d965cba33e6dc46fe13d9b933"), + 33); + + // [Chain m/0/2147483647'/1/2147483646'/2] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node, 2); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x956c4629); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "3bfb29ee8ac4484f09db09c2079b520ea5616df7820f071a20320366fbe226a7"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "bb0a77ba01cc31d77205d51d08bd313b979a71ef4de9b062f8958297e746bd67"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "020ee02e18967237cf62672983b253ee62fa4dd431f8243bfeccdf39dbe181387f"), + 33); + + // init m + hdnode_from_seed( + fromhex( + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c" + "999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), + 64, NIST256P1_NAME, &node); + + // test public derivation + // [Chain m/0] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_public_ckd(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x607f628f); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "84e9c258bb8557a40e0d041115b376dd55eda99c0042ce29e81ebe4efed9b86a"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + 32); + ck_assert_mem_eq( + node.public_key, + fromhex( + "039b6df4bece7b6c81e2adfeea4bcf5c8c8a6e40ea7ffa3cf6e8494c61a1fc82cc"), + 33); +} +END_TEST + +START_TEST(test_bip32_nist_compare) { + HDNode node1, node2, node3; + int i, r; + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, NIST256P1_NAME, &node1); + hdnode_from_seed( + fromhex( + "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" + "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), + 64, NIST256P1_NAME, &node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + for (i = 0; i < 100; i++) { + memcpy(&node3, &node1, sizeof(HDNode)); + ck_assert_int_eq(hdnode_fill_public_key(&node3), 0); + r = hdnode_private_ckd(&node1, i); + ck_assert_int_eq(r, 1); + r = hdnode_public_ckd(&node2, i); + ck_assert_int_eq(r, 1); + r = hdnode_public_ckd(&node3, i); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(node1.depth, node2.depth); + ck_assert_uint_eq(node1.depth, node3.depth); + ck_assert_uint_eq(node1.child_num, node2.child_num); + ck_assert_uint_eq(node1.child_num, node3.child_num); + ck_assert_mem_eq(node1.chain_code, node2.chain_code, 32); + ck_assert_mem_eq(node1.chain_code, node3.chain_code, 32); + ck_assert_mem_eq( + node2.private_key, + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + 32); + ck_assert_mem_eq( + node3.private_key, + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node1), 0); + ck_assert_mem_eq(node1.public_key, node2.public_key, 33); + ck_assert_mem_eq(node1.public_key, node3.public_key, 33); + } +} +END_TEST + +START_TEST(test_bip32_nist_repeat) { + HDNode node, node2; + uint32_t fingerprint; + int r; + + // init m + hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, + NIST256P1_NAME, &node); + + // [Chain m/28578'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 28578); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0xbe6105b5); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "e94c8ebe30c2250a14713212f6449b20f3329105ea15b652ca5bdfc68f6c65c2"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "06f0db126f023755d0b8d86d4591718a5210dd8d024e3e14b6159d63f53aa669"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02519b5554a4872e8c9c1c847115363051ec43e93400e030ba3c36b52a3e70a5b7"), + 33); + + memcpy(&node2, &node, sizeof(HDNode)); + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node2, 33941); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x3e2b7bc6); + ck_assert_mem_eq( + node2.chain_code, + fromhex( + "9e87fe95031f14736774cd82f25fd885065cb7c358c1edf813c72af535e83071"), + 32); + ck_assert_mem_eq( + node2.private_key, + fromhex( + "092154eed4af83e078ff9b84322015aefe5769e31270f62c3f66c33888335f3a"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq( + node2.public_key, + fromhex( + "0235bfee614c0d5b2cae260000bb1d0d84b270099ad790022c1ae0b2e782efe120"), + 33); + + memcpy(&node2, &node, sizeof(HDNode)); + memzero(&node2.private_key, 32); + r = hdnode_public_ckd(&node2, 33941); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x3e2b7bc6); + ck_assert_mem_eq( + node2.chain_code, + fromhex( + "9e87fe95031f14736774cd82f25fd885065cb7c358c1edf813c72af535e83071"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq( + node2.public_key, + fromhex( + "0235bfee614c0d5b2cae260000bb1d0d84b270099ad790022c1ae0b2e782efe120"), + 33); +} +END_TEST + +// test vector 1 from https://en.bitcoin.it/wiki/BIP_0032_TestVectors +START_TEST(test_bip32_ed25519_vector_1) { + HDNode node; + + // init m + hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, + ED25519_NAME, &node); + + // [Chain m] + ck_assert_mem_eq( + node.chain_code, + fromhex( + "90046a93de5380a72b5e45010748567d5ea02bbf6522f979e05c0d8d8ca9fffb"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e7"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "01a4b2856bfec510abab89753fac1ac0e1112364e7d250545963f135f2a33188ed"), + 33); + + // [Chain m/0'] + hdnode_private_ckd_prime(&node, 0); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "8b59aa11380b624e81507a27fedda59fea6d0b779a778918a2fd3590e16e9c69"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "68e0fe46dfb67e368c75379acec591dad19df3cde26e63b93a8e704f1dade7a3"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "018c8a13df77a28f3445213a0f432fde644acaa215fc72dcdf300d5efaa85d350c"), + 33); + + // [Chain m/0'/1'] + hdnode_private_ckd_prime(&node, 1); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "a320425f77d1b5c2505a6b1b27382b37368ee640e3557c315416801243552f14"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "b1d0bad404bf35da785a64ca1ac54b2617211d2777696fbffaf208f746ae84f2"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "011932a5270f335bed617d5b935c80aedb1a35bd9fc1e31acafd5372c30f5c1187"), + 33); + + // [Chain m/0'/1'/2'] + hdnode_private_ckd_prime(&node, 2); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "2e69929e00b5ab250f49c3fb1c12f252de4fed2c1db88387094a0f8c4c9ccd6c"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "92a5b23c0b8a99e37d07df3fb9966917f5d06e02ddbd909c7e184371463e9fc9"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "01ae98736566d30ed0e9d2f4486a64bc95740d89c7db33f52121f8ea8f76ff0fc1"), + 33); + + // [Chain m/0'/1'/2'/2'] + hdnode_private_ckd_prime(&node, 2); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "8f6d87f93d750e0efccda017d662a1b31a266e4a6f5993b15f5c1f07f74dd5cc"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "30d1dc7e5fc04c31219ab25a27ae00b50f6fd66622f6e9c913253d6511d1e662"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "018abae2d66361c879b900d204ad2cc4984fa2aa344dd7ddc46007329ac76c429c"), + 33); + + // [Chain m/0'/1'/2'/2'/1000000000'] + hdnode_private_ckd_prime(&node, 1000000000); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "68789923a0cac2cd5a29172a475fe9e0fb14cd6adb5ad98a3fa70333e7afa230"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "8f94d394a8e8fd6b1bc2f3f49f5c47e385281d5c17e65324b0f62483e37e8793"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "013c24da049451555d51a7014a37337aa4e12d41e485abccfa46b47dfb2af54b7a"), + 33); +} +END_TEST + +// test vector 2 from https://en.bitcoin.it/wiki/BIP_0032_TestVectors +START_TEST(test_bip32_ed25519_vector_2) { + HDNode node; + int r; + + // init m + hdnode_from_seed( + fromhex( + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c" + "999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), + 64, ED25519_NAME, &node); + + // [Chain m] + ck_assert_mem_eq( + node.chain_code, + fromhex( + "ef70a74db9c3a5af931b5fe73ed8e1a53464133654fd55e7a66f8570b8e33c3b"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "171cb88b1b3c1db25add599712e36245d75bc65a1a5c9e18d76f9f2b1eab4012"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "018fe9693f8fa62a4305a140b9764c5ee01e455963744fe18204b4fb948249308a"), + 33); + + // [Chain m/0'] + r = hdnode_private_ckd_prime(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "0b78a3226f915c082bf118f83618a618ab6dec793752624cbeb622acb562862d"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "1559eb2bbec5790b0c65d8693e4d0875b1747f4970ae8b650486ed7470845635"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0186fab68dcb57aa196c77c5f264f215a112c22a912c10d123b0d03c3c28ef1037"), + 33); + + // [Chain m/0'/2147483647'] + r = hdnode_private_ckd_prime(&node, 2147483647); + ck_assert_int_eq(r, 1); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "138f0b2551bcafeca6ff2aa88ba8ed0ed8de070841f0c4ef0165df8181eaad7f"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "ea4f5bfe8694d8bb74b7b59404632fd5968b774ed545e810de9c32a4fb4192f4"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "015ba3b9ac6e90e83effcd25ac4e58a1365a9e35a3d3ae5eb07b9e4d90bcf7506d"), + 33); + + // [Chain m/0'/2147483647'/1'] + r = hdnode_private_ckd_prime(&node, 1); + ck_assert_int_eq(r, 1); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "73bd9fff1cfbde33a1b846c27085f711c0fe2d66fd32e139d3ebc28e5a4a6b90"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "3757c7577170179c7868353ada796c839135b3d30554bbb74a4b1e4a5a58505c"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "012e66aa57069c86cc18249aecf5cb5a9cebbfd6fadeab056254763874a9352b45"), + 33); + + // [Chain m/0'/2147483647'/1'/2147483646'] + r = hdnode_private_ckd_prime(&node, 2147483646); + ck_assert_int_eq(r, 1); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "0902fe8a29f9140480a00ef244bd183e8a13288e4412d8389d140aac1794825a"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "5837736c89570de861ebc173b1086da4f505d4adb387c6a1b1342d5e4ac9ec72"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "01e33c0f7d81d843c572275f287498e8d408654fdf0d1e065b84e2e6f157aab09b"), + 33); + + // [Chain m/0'/2147483647'/1'/2147483646'/2'] + r = hdnode_private_ckd_prime(&node, 2); + ck_assert_int_eq(r, 1); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "5d70af781f3a37b829f0d060924d5e960bdc02e85423494afc0b1a41bbe196d4"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "551d333177df541ad876a60ea71f00447931c0a9da16f227c11ea080d7391b8d"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0147150c75db263559a70d5778bf36abbab30fb061ad69f69ece61a72b0cfa4fc0"), + 33); +} +END_TEST + +// test vector 1 from +// https://github.com/decred/dcrd/blob/master/hdkeychain/extendedkey_test.go +START_TEST(test_bip32_decred_vector_1) { + HDNode node, node2, node3; + uint32_t fingerprint; + char str[XPUB_MAXLEN]; + int r; + + // init m + hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, + SECP256K1_NAME, &node); + + // secp256k1_decred_info.bip32_name != "Bitcoin seed" so we cannot use it in + // hdnode_from_seed + node.curve = &secp256k1_decred_info; + + // [Chain m] + fingerprint = 0; + ck_assert_uint_eq(fingerprint, 0x00000000); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3hCznBesA6jBtmoyVFPfyMSZ1qYZ3WdjdebquvkEfmRfxC9VFEFi2YD" + "aJqHnx7uGe75eGSa3Mn3oHK11hBW7KZUrPxwbCPBmuCi1nwm182s"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZ9169KDAEUnyoBhjjmT2VaEodr6pUTDoqCEAeqgbfr2JfkB88BbK77j" + "bTYbcYXb2FVz7DKBdW4P618yd51MwF8DjKVopSbS7Lkgi6bowX5w"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd_prime(&node, 0); + ck_assert_uint_eq(fingerprint, 0xbc495588); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3kUQDBztdyjKuwnaL3hfKYpT7W6X2huYH5d61YSWFBebSYwEBHAXJkC" + "pQ7rvMAxPzKqxVCGLvBqWvGxXjAyMJsV1XwKkfnQCM9KctC8k8bk"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZCGVaKZBiMo7pMgLaZm1qmchjWenTeVcUdFQkTNsFGFEA6xs4EW8PKi" + "qYqP7HBAitt9Hw16VQkQ1tjsZQSHNWFc6bEK6bLqrbco24FzBTY4"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd(&node, 1); + ck_assert_uint_eq(fingerprint, 0xc67bc2ef); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "2a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c19"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3nRtCZ5VAoHW4RUwQgRafSNRPUDFrmsgyY71A5eoZceVfuyL9SbZe2r" + "cbwDW2UwpkEniE4urffgbypegscNchPajWzy9QS4cRxF8QYXsZtq"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZEDyZgdnFBMHxqNhfCUwBfAg1UmXHiTmB5jKtzbAZhF8PTzy2PwAicN" + "dkg1CmW6TARxQeUbgC7nAQenJts4YoG3KMiqcjsjgeMvwLc43w6C"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1/2'] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd_prime(&node, 2); + ck_assert_uint_eq(fingerprint, 0xe7072187); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "04466b9cc8e161e966409ca52986c584f07e9dc81f735db683c3ff6ec7b1503f"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "0357bfe1e341d01c69fe5654309956cbea516822fba8a601743a012a7896ee8dc2"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3pYtkZK168vgrU38gXkUSjHQ2LGpEUzQ9fXrR8fGUR59YviSnm6U82X" + "jQYhpJEUPnVcC9bguJBQU5xVM4VFcDHu9BgScGPA6mQMH4bn5Cth"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZGLz7gsJAWzUksvtw3opxx5eeLq5fRaUMDABA3bdUVfnGUk5fiS5Cc3" + "kZGTjWtYr3jrEavQQnAF6jv2WCpZtFX4uFgifXqev6ED1TM9rTCB"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1/2'/2] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd(&node, 2); + ck_assert_uint_eq(fingerprint, 0xbcbbc1c4); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "cfb71883f01676f587d023cc53a35bc7f88f724b1f8c2892ac1275ac822a3edd"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02e8445082a72f29b75ca48748a914df60622a609cacfce8ed0e35804560741d29"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3r7zqYFjT3NiNzdnwGxGpYh6S1TJCp1zA6mSEGaqLBJFnCB94cRMp7Y" + "YLR49aTZHZ7ya1CXwQJ6rodKeU9NgQTxkPSK7pzgZRgjYkQ7rgJh"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZHv6Cfp2XRSWHQXZBo1dLmVM421Zdkc4MePkyBXCLFttVkCmwZkxth4" + "ZV9PzkFP3DtD5xcVq2CPSYpJMWMaoxu1ixz4GNZFVcE2xnHP6chJ"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1/2'/2/1000000000] + fingerprint = hdnode_fingerprint(&node); + hdnode_private_ckd(&node, 1000000000); + ck_assert_uint_eq(fingerprint, 0xe58b52e4); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "c783e67b921d2beb8f6b389cc646d7263b4145701dadd2161548a8b078e65e9e"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "022a471424da5e657499d1ff51cb43c47481a03b1e77f951fe64cec9f5a48f7011"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3tJXnTDSb3uE6Euo6WvvhFKfBMNfxuJt5smqyPoHEoomoBMQyhYoQSK" + "JAHWtWxmuqdUVb8q9J2NaTkF6rYm6XDrSotkJ55bM21fffa7VV97"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZL6d9amjfRy1zeoZM2zHDU7uoMvwPqtxHRQAiJjeEtQQWjP3retQV1q" + "KJyzUd6ZJNgbJGXjtc5pdoBcTTYTLoxQzvV9JJCzCjB2eCWpRf8T"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); +} +END_TEST + +// test vector 2 from +// https://github.com/decred/dcrd/blob/master/hdkeychain/extendedkey_test.go +START_TEST(test_bip32_decred_vector_2) { + HDNode node, node2, node3; + uint32_t fingerprint; + char str[XPUB_MAXLEN]; + int r; + + // init m + hdnode_from_seed( + fromhex( + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c" + "999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), + 64, SECP256K1_NAME, &node); + + // secp256k1_decred_info.bip32_name != "Bitcoin seed" so we cannot use it in + // hdnode_from_seed + node.curve = &secp256k1_decred_info; + + // [Chain m] + fingerprint = 0; + ck_assert_uint_eq(fingerprint, 0x00000000); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "60499f801b896d83179a4374aeb7822aaeaceaa0db1f85ee3e904c4defbd9689"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03cbcaa9c98c877a26977d00825c956a238e8dddfbd322cce4f74b0b5bd6ace4a7"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3hCznBesA6jBtPKJbQTxRZAKG2gyj8tZKEPaCsV4e9YYFBAgRP2eTSP" + "Aeu4r8dTMt9q51j2Vdt5zNqj7jbtovvocrP1qLj6WUTLF9xYQt4y"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZ9169KDAEUnynoD4qvXJwmxZt3FFA5UdWn1twnRReE9AxjCKJLNFY1u" + "BoegbFmwzA4Du7yqnu8tLivhrCCH6P3DgBS1HH5vmf8MpNXvvYT9"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x2524c9d3); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "f0909affaa7ee7abe5dd4e100598d4dc53cd709d5a5c2cac40e7412f232f7c9c"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02fc9e5af0ac8d9b3cecfe2a888e2117ba3d089d8585886c9c826b6b22a98d12ea"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3jMy45BuuDETfxi59P8NTSjHPrNVq4wPRfLgRd57923L2hosj5NUEqi" + "LYQ4i7fJtUpiXZLr2wUeToJY2Tm5sCpAJdajEHDmieVJiPQNXwu9"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZBA4RCkCybJFaNbqPuBiyfXY1rvmG1XTdCy1AY1U96dxkFqWc2i5KRE" + "Mh7NYPpy7ZPMhdpFMAesex3JdFDfX4J5FEW3HjSacqEYPfwb9Cj7"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0/2147483647'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 2147483647); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x6035c6ad); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "be17a268474a6bb9c61e1d720cf6215e2a88c5406c4aee7b38547f585c9a37d9"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03c01e7425647bdefa82b12d9bad5e3e6865bee0502694b94ca58b666abc0a5c3b"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3mgHPRgK838mLK6T1p6WeBoJoJtXA1pGTHjqFuyHekcM7UTuER8fGwe" + "RRsoLqSuHa98uskVPnJnfWZEBUC1AVmXnSCPDvUFKydXNnnPHTuQ"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZDUNkZEcCRCZEizDGL9sAQbZRKSnaxQLeqN9zpueeqCyq2VY7NUGMXA" + "SacsK96S8XzNjq3YgFgwLtj8MJBToW6To9U5zxuazEyh89bjR1xA"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0/2147483647'/1] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node, 1); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x36fc7080); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "f366f48f1ea9f2d1d3fe958c95ca84ea18e4c4ddb9366c336c927eb246fb38cb"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "03a7d1d856deb74c508e05031f9895dab54626251b3806e16b4bd12e781a7df5b9"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3oFqwZZ9bJcUmhAeJyyshvrTWtrAsHfcRYQbEzNiiH5nGvM6wVTDn6w" + "oQEz92b2EHTYZBtLi82jKEnxSouA3cVaW8YWBsw5c3f4mwAhA3d2"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZF3wJh7SfggGg74QZW3EE9ei8uQSJEFgd62uyuK5iMgQzUNjpSnprgT" + "pYz3d6Q3fXXtEEXQqpzWcP4LUVuXFsgA8JKt1Hot5kyUk4pPRhDz"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0/2147483647'/1/2147483646'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 2147483646); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x45309b4c); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "637807030d55d01f9a0cb3a7839515d796bd07706386a6eddf06cc29a65a0e29"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "02d2b36900396c9282fa14628566582f206a5dd0bcc8d5e892611806cafb0301f0"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3qF3177i87wMirg6sraDvqty8yZg6THpXFPSXuM5AShBiiUQbq8FhSZ" + "DGkYmBNR3RKfBrxzkKDBpsRFJfTnQfLsvpPPqRnakat6hHQA43X9"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZH38NEg1CW19dGZs8NdaT4hDkz7wXPstio1mGpHSAXHpSGW3UnTrn25" + "ERT1Mp8ae5GMoQHMbgQiPrChMXQMdx3UqS8YqFkT1pqait8fY92u"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0/2147483647'/1/2147483646'/2] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd(&node, 2); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x3491a5e6); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "9452b549be8cea3ecb7a84bec10dcfd94afe4d129ebfd3b3cb58eedf394ed271"), + 32); + ck_assert_mem_eq( + node.private_key, + fromhex( + "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"), + 32); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + ck_assert_mem_eq( + node.public_key, + fromhex( + "024d902e1a2fc7a8755ab5b694c575fce742c48d9ff192e63df5193e4c7afe1f9c"), + 33); + hdnode_serialize_private(&node, fingerprint, DECRED_VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "dprv3s15tfqzxhw8Kmo7RBEqMeyvC7uGekLniSmvbs3bckpxQ6ks1KKqfmH" + "144Jgh3PLxkyZRcS367kp7DrtUmnG16NpnsoNhxSXRgKbJJ7MUQR"); + r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZJoBFoQJ35zvEBgsfhJBssnAp8TY5gvruzQFLmyxcqRb7enVtGfSkLo" + "2CkAZJMpa6T2fx6fUtvTgXtUvSVgAZ56bEwGxQsToeZfFV8VadE1"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // init m + hdnode_deserialize_public( + "dpubZF4LSCdF9YKZfNzTVYhz4RBxsjYXqms8AQnMBHXZ8GUKoRSigG7kQnKiJt5pzk93Q8Fx" + "cdVBEkQZruSXduGtWnkwXzGnjbSovQ97dCxqaXc", + DECRED_VERSION_PUBLIC, SECP256K1_DECRED_NAME, &node, NULL); + + // test public derivation + // [Chain m/0] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_public_ckd(&node, 0); + ck_assert_int_eq(r, 1); + ck_assert_uint_eq(fingerprint, 0x6a19cfb3); + ck_assert_mem_eq( + node.chain_code, + fromhex( + "dcfe00831741a3a4803955147cdfc7053d69b167b1d03b5f9e63934217a005fd"), + 32); + ck_assert_mem_eq( + node.public_key, + fromhex( + "029555ea7bde276cd2c42c4502f40b5d16469fb310ae3aeee2a9000455f41b0866"), + 33); + hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, + sizeof(str)); + ck_assert_str_eq(str, + "dpubZHJs2Z3PtHbbpaXQCi5wBKPhU8tC5ztBKUYBCYNGKk8eZ1EmBs3MhnL" + "JbxHFMAahGnDnZT7qZxC7AXKP8PB6BDNUZgkG77moNMRmXyQ6s6s"); + r = hdnode_deserialize_public(str, DECRED_VERSION_PUBLIC, + SECP256K1_DECRED_NAME, &node2, NULL); + ck_assert_int_eq(r, 0); + ck_assert_mem_eq(&node2, &node, sizeof(HDNode)); +} +END_TEST + +static void test_ecdsa_get_public_key33_helper(int (*ecdsa_get_public_key33_fn)( + const ecdsa_curve *, const uint8_t *, uint8_t *)) { + uint8_t privkey[32] = {0}; + uint8_t pubkey[65] = {0}; + const ecdsa_curve *curve = &secp256k1; + int res = 0; + + memcpy( + privkey, + fromhex( + "c46f5b217f04ff28886a89d3c762ed84e5fa318d1c9a635d541131e69f1f49f5"), + 32); + res = ecdsa_get_public_key33_fn(curve, privkey, pubkey); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "0232b062e9153f573c220b1be0299d6447e81577274bf11a7c08dff71384c6b6ec"), + 33); + + memcpy( + privkey, + fromhex( + "3b90a4de80fb00d77795762c389d1279d4b4ab5992ae3cde6bc12ca63116f74c"), + 32); + res = ecdsa_get_public_key33_fn(curve, privkey, pubkey); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "0332b062e9153f573c220b1be0299d6447e81577274bf11a7c08dff71384c6b6ec"), + 33); +} + +START_TEST(test_ecdsa_get_public_key33) { + test_ecdsa_get_public_key33_helper(ecdsa_get_public_key33); +} +END_TEST + +static void test_ecdsa_get_public_key65_helper(int (*ecdsa_get_public_key65_fn)( + const ecdsa_curve *, const uint8_t *, uint8_t *)) { + uint8_t privkey[32] = {0}; + uint8_t pubkey[65] = {0}; + const ecdsa_curve *curve = &secp256k1; + int res = 0; + + memcpy( + privkey, + fromhex( + "c46f5b217f04ff28886a89d3c762ed84e5fa318d1c9a635d541131e69f1f49f5"), + 32); + res = ecdsa_get_public_key65_fn(curve, privkey, pubkey); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "0432b062e9153f573c220b1be0299d6447e81577274bf11a7c08dff71384c6b6ec" + "179ca56b637a57e0fcd28cefa10c9433dc30532682647f4daa053d43d5cc960a"), + 65); +} + +START_TEST(test_ecdsa_get_public_key65) { + test_ecdsa_get_public_key65_helper(ecdsa_get_public_key65); +} +END_TEST + +static void test_ecdsa_recover_pub_from_sig_helper(int ( + *ecdsa_recover_pub_from_sig_fn)(const ecdsa_curve *, uint8_t *, + const uint8_t *, const uint8_t *, int)) { + int res; + uint8_t digest[32]; + uint8_t pubkey[65]; + const ecdsa_curve *curve = &secp256k1; + + // sha2(sha2("\x18Bitcoin Signed Message:\n\x0cHello World!")) + memcpy( + digest, + fromhex( + "de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb11432"), + 32); + // r = 2: Four points should exist + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000020123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 0); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "043fc5bf5fec35b6ffe6fd246226d312742a8c296bfa57dd22da509a2e348529b7dd" + "b9faf8afe1ecda3c05e7b2bda47ee1f5a87e952742b22afca560b29d972fcf"), + 65); + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000020123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 1); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "0456d8089137b1fd0d890f8c7d4a04d0fd4520a30b19518ee87bd168ea12ed809032" + "9274c4c6c0d9df04515776f2741eeffc30235d596065d718c3973e19711ad0"), + 65); + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000020123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 2); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "04cee0e740f41aab39156844afef0182dea2a8026885b10454a2d539df6f6df9023a" + "bfcb0f01c50bef3c0fa8e59a998d07441e18b1c60583ef75cc8b912fb21a15"), + 65); + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000020123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 3); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "0490d2bd2e9a564d6e1d8324fc6ad00aa4ae597684ecf4abea58bdfe7287ea4fa729" + "68c2e5b0b40999ede3d7898d94e82c3f8dc4536a567a4bd45998c826a4c4b2"), + 65); + // The point at infinity is not considered to be a valid public key. + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "220cf4c7b6d568f2256a8c30cc1784a625a28c3627dac404aa9a9ecd08314ec81a88" + "828f20d69d102bab5de5f6ee7ef040cb0ff7b8e1ba3f29d79efb5250f47d"), + digest, 0); + ck_assert_int_eq(res, 1); + + memcpy( + digest, + fromhex( + "0000000000000000000000000000000000000000000000000000000000000000"), + 32); + // r = 7: No point P with P.x = 7, but P.x = (order + 7) exists + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000070123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 2); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "044d81bb47a31ffc6cf1f780ecb1e201ec47214b651650867c07f13ad06e12a1b040" + "de78f8dbda700f4d3cd7ee21b3651a74c7661809699d2be7ea0992b0d39797"), + 65); + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000070123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 3); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "044d81bb47a31ffc6cf1f780ecb1e201ec47214b651650867c07f13ad06e12a1b0bf" + "21870724258ff0b2c32811de4c9ae58b3899e7f69662d41815f66c4f2c6498"), + 65); + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000070123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 0); + ck_assert_int_eq(res, 1); + + memcpy( + digest, + fromhex( + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), + 32); + // r = 1: Two points P with P.x = 1, but P.x = (order + 7) doesn't exist + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000010123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 0); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "045d330b2f89dbfca149828277bae852dd4aebfe136982cb531a88e9e7a89463fe71" + "519f34ea8feb9490c707f14bc38c9ece51762bfd034ea014719b7c85d2871b"), + 65); + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000010123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 1); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "049e609c3950e70d6f3e3f3c81a473b1d5ca72739d51debdd80230ae80cab05134a9" + "4285375c834a417e8115c546c41da83a263087b79ef1cae25c7b3c738daa2b"), + 65); + + // r = 0 is always invalid + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000010123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 2); + ck_assert_int_eq(res, 1); + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000000123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 0); + ck_assert_int_eq(res, 1); + // r >= order is always invalid + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 0); + ck_assert_int_eq(res, 1); + // check that overflow of r is handled + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "000000000000000000000000000000014551231950B75FC4402DA1722FC9BAEE0123" + "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), + digest, 2); + ck_assert_int_eq(res, 1); + // s = 0 is always invalid + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "00000000000000000000000000000000000000000000000000000000000000020000" + "000000000000000000000000000000000000000000000000000000000000"), + digest, 0); + ck_assert_int_eq(res, 1); + // s >= order is always invalid + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "0000000000000000000000000000000000000000000000000000000000000002ffff" + "fffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), + digest, 0); + ck_assert_int_eq(res, 1); +} + +START_TEST(test_ecdsa_recover_pub_from_sig) { + test_ecdsa_recover_pub_from_sig_helper(ecdsa_recover_pub_from_sig); +} +END_TEST + +static void test_ecdsa_verify_digest_helper(int (*ecdsa_verify_digest_fn)( + const ecdsa_curve *, const uint8_t *, const uint8_t *, const uint8_t *)) { + int res; + uint8_t digest[32]; + uint8_t pubkey[65]; + uint8_t sig[64]; + const ecdsa_curve *curve = &secp256k1; + + // Signature verification for a digest which is equal to the group order. + // https://github.com/trezor/trezor-firmware/pull/1374 + memcpy( + pubkey, + fromhex( + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179848" + "3ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"), + sizeof(pubkey)); + memcpy( + digest, + fromhex( + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), + sizeof(digest)); + memcpy(sig, + fromhex( + "a0b37f8fba683cc68f6574cd43b39f0343a50008bf6ccea9d13231d9e7e2e1e41" + "1edc8d307254296264aebfc3dc76cd8b668373a072fd64665b50000e9fcce52"), + sizeof(sig)); + res = ecdsa_verify_digest_fn(curve, pubkey, sig, digest); + ck_assert_int_eq(res, 0); +} + +START_TEST(test_ecdsa_verify_digest) { + test_ecdsa_verify_digest_helper(ecdsa_verify_digest); +} +END_TEST + +#define test_deterministic(KEY, MSG, K) \ + do { \ + sha256_Raw((uint8_t *)MSG, strlen(MSG), buf); \ + init_rfc6979(fromhex(KEY), buf, NULL, &rng); \ + generate_k_rfc6979(&k, &rng); \ + bn_write_be(&k, buf); \ + ck_assert_mem_eq(buf, fromhex(K), 32); \ + } while (0) + +START_TEST(test_rfc6979) { + bignum256 k; + uint8_t buf[32]; + rfc6979_state rng; + + test_deterministic( + "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721", + "sample", + "a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60"); + test_deterministic( + "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", + "sample", + "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3"); + test_deterministic( + "0000000000000000000000000000000000000000000000000000000000000001", + "Satoshi Nakamoto", + "8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15"); + test_deterministic( + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "Satoshi Nakamoto", + "33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90"); + test_deterministic( + "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181", + "Alan Turing", + "525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1"); + test_deterministic( + "0000000000000000000000000000000000000000000000000000000000000001", + "All those moments will be lost in time, like tears in rain. Time to " + "die...", + "38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3"); + test_deterministic( + "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2", + "There is a computer disease that anybody who works with computers knows " + "about. It's a very serious disease and it interferes completely with " + "the work. The trouble with computers is that you 'play' with them!", + "1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d"); +} +END_TEST + +static void test_ecdsa_sign_digest_deterministic_helper( + int (*ecdsa_sign_digest_fn)(const ecdsa_curve *, const uint8_t *, + const uint8_t *, uint8_t *, uint8_t *, + int (*)(uint8_t by, uint8_t sig[64]))) { + static struct { + const char *priv_key; + const char *digest; + const char *sig; + } tests[] = { + {"312155017c70a204106e034520e0cdf17b3e54516e2ece38e38e38e38e38e38e", + "ffffffffffffffffffffffffffffffff20202020202020202020202020202020", + "e3d70248ea2fc771fc8d5e62d76b9cfd5402c96990333549eaadce1ae9f737eb" + "5cfbdc7d1e0ec18cc9b57bbb18f0a57dc929ec3c4dfac9073c581705015f6a8a"}, + {"312155017c70a204106e034520e0cdf17b3e54516e2ece38e38e38e38e38e38e", + "2020202020202020202020202020202020202020202020202020202020202020", + "40666188895430715552a7e4c6b53851f37a93030fb94e043850921242db78e8" + "75aa2ac9fd7e5a19402973e60e64382cdc29a09ebf6cb37e92f23be5b9251aee"}, + }; + + const ecdsa_curve *curve = &secp256k1; + uint8_t priv_key[32] = {0}; + uint8_t digest[32] = {0}; + uint8_t expected_sig[64] = {0}; + uint8_t computed_sig[64] = {0}; + int res = 0; + + for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) { + memcpy(priv_key, fromhex(tests[i].priv_key), 32); + memcpy(digest, fromhex(tests[i].digest), 32); + memcpy(expected_sig, fromhex(tests[i].sig), 64); + + res = + ecdsa_sign_digest_fn(curve, priv_key, digest, computed_sig, NULL, NULL); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq(expected_sig, computed_sig, 64); + } +} + +START_TEST(test_ecdsa_sign_digest_deterministic) { + test_ecdsa_sign_digest_deterministic_helper(ecdsa_sign_digest); +} +END_TEST + +// test vectors from +// http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors +START_TEST(test_aes) { + aes_encrypt_ctx ctxe; + aes_decrypt_ctx ctxd; + uint8_t ibuf[16], obuf[16], iv[16], cbuf[16]; + const char **ivp, **plainp, **cipherp; + + // ECB + static const char *ecb_vector[] = { + // plain cipher + "6bc1bee22e409f96e93d7e117393172a", + "f3eed1bdb5d2a03c064b5a7e3db181f8", + "ae2d8a571e03ac9c9eb76fac45af8e51", + "591ccb10d410ed26dc5ba74a31362870", + "30c81c46a35ce411e5fbc1191a0a52ef", + "b6ed21b99ca6f4f9f153e7b1beafed1d", + "f69f2445df4f9b17ad2b417be66c3710", + "23304b7a39f9f3ff067d8d8f9e24ecc7", + 0, + 0, + }; + plainp = ecb_vector; + cipherp = ecb_vector + 1; + while (*plainp && *cipherp) { + // encrypt + aes_encrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxe); + memcpy(ibuf, fromhex(*plainp), 16); + aes_ecb_encrypt(ibuf, obuf, 16, &ctxe); + ck_assert_mem_eq(obuf, fromhex(*cipherp), 16); + // decrypt + aes_decrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxd); + memcpy(ibuf, fromhex(*cipherp), 16); + aes_ecb_decrypt(ibuf, obuf, 16, &ctxd); + ck_assert_mem_eq(obuf, fromhex(*plainp), 16); + plainp += 2; + cipherp += 2; + } + + // CBC + static const char *cbc_vector[] = { + // iv plain cipher + "000102030405060708090A0B0C0D0E0F", + "6bc1bee22e409f96e93d7e117393172a", + "f58c4c04d6e5f1ba779eabfb5f7bfbd6", + "F58C4C04D6E5F1BA779EABFB5F7BFBD6", + "ae2d8a571e03ac9c9eb76fac45af8e51", + "9cfc4e967edb808d679f777bc6702c7d", + "9CFC4E967EDB808D679F777BC6702C7D", + "30c81c46a35ce411e5fbc1191a0a52ef", + "39f23369a9d9bacfa530e26304231461", + "39F23369A9D9BACFA530E26304231461", + "f69f2445df4f9b17ad2b417be66c3710", + "b2eb05e2c39be9fcda6c19078c6a9d1b", + 0, + 0, + 0, + }; + ivp = cbc_vector; + plainp = cbc_vector + 1; + cipherp = cbc_vector + 2; + while (*plainp && *cipherp) { + // encrypt + aes_encrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxe); + memcpy(iv, fromhex(*ivp), 16); + memcpy(ibuf, fromhex(*plainp), 16); + aes_cbc_encrypt(ibuf, obuf, 16, iv, &ctxe); + ck_assert_mem_eq(obuf, fromhex(*cipherp), 16); + // decrypt + aes_decrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxd); + memcpy(iv, fromhex(*ivp), 16); + memcpy(ibuf, fromhex(*cipherp), 16); + aes_cbc_decrypt(ibuf, obuf, 16, iv, &ctxd); + ck_assert_mem_eq(obuf, fromhex(*plainp), 16); + ivp += 3; + plainp += 3; + cipherp += 3; + } + + // CFB + static const char *cfb_vector[] = { + "000102030405060708090A0B0C0D0E0F", + "6bc1bee22e409f96e93d7e117393172a", + "DC7E84BFDA79164B7ECD8486985D3860", + "DC7E84BFDA79164B7ECD8486985D3860", + "ae2d8a571e03ac9c9eb76fac45af8e51", + "39ffed143b28b1c832113c6331e5407b", + "39FFED143B28B1C832113C6331E5407B", + "30c81c46a35ce411e5fbc1191a0a52ef", + "df10132415e54b92a13ed0a8267ae2f9", + "DF10132415E54B92A13ED0A8267AE2F9", + "f69f2445df4f9b17ad2b417be66c3710", + "75a385741ab9cef82031623d55b1e471", + 0, + 0, + 0, + }; + ivp = cfb_vector; + plainp = cfb_vector + 1; + cipherp = cfb_vector + 2; + while (*plainp && *cipherp) { + // encrypt + aes_encrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxe); + memcpy(iv, fromhex(*ivp), 16); + memcpy(ibuf, fromhex(*plainp), 16); + aes_cfb_encrypt(ibuf, obuf, 16, iv, &ctxe); + ck_assert_mem_eq(obuf, fromhex(*cipherp), 16); + // decrypt (uses encryption) + aes_encrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxe); + memcpy(iv, fromhex(*ivp), 16); + memcpy(ibuf, fromhex(*cipherp), 16); + aes_cfb_decrypt(ibuf, obuf, 16, iv, &ctxe); + ck_assert_mem_eq(obuf, fromhex(*plainp), 16); + ivp += 3; + plainp += 3; + cipherp += 3; + } + + // OFB + static const char *ofb_vector[] = { + "000102030405060708090A0B0C0D0E0F", + "6bc1bee22e409f96e93d7e117393172a", + "dc7e84bfda79164b7ecd8486985d3860", + "B7BF3A5DF43989DD97F0FA97EBCE2F4A", + "ae2d8a571e03ac9c9eb76fac45af8e51", + "4febdc6740d20b3ac88f6ad82a4fb08d", + "E1C656305ED1A7A6563805746FE03EDC", + "30c81c46a35ce411e5fbc1191a0a52ef", + "71ab47a086e86eedf39d1c5bba97c408", + "41635BE625B48AFC1666DD42A09D96E7", + "f69f2445df4f9b17ad2b417be66c3710", + "0126141d67f37be8538f5a8be740e484", + 0, + 0, + 0, + }; + ivp = ofb_vector; + plainp = ofb_vector + 1; + cipherp = ofb_vector + 2; + while (*plainp && *cipherp) { + // encrypt + aes_encrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxe); + memcpy(iv, fromhex(*ivp), 16); + memcpy(ibuf, fromhex(*plainp), 16); + aes_ofb_encrypt(ibuf, obuf, 16, iv, &ctxe); + ck_assert_mem_eq(obuf, fromhex(*cipherp), 16); + // decrypt (uses encryption) + aes_encrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxe); + memcpy(iv, fromhex(*ivp), 16); + memcpy(ibuf, fromhex(*cipherp), 16); + aes_ofb_decrypt(ibuf, obuf, 16, iv, &ctxe); + ck_assert_mem_eq(obuf, fromhex(*plainp), 16); + ivp += 3; + plainp += 3; + cipherp += 3; + } + + // CTR + static const char *ctr_vector[] = { + // plain cipher + "6bc1bee22e409f96e93d7e117393172a", + "601ec313775789a5b7a7f504bbf3d228", + "ae2d8a571e03ac9c9eb76fac45af8e51", + "f443e3ca4d62b59aca84e990cacaf5c5", + "30c81c46a35ce411e5fbc1191a0a52ef", + "2b0930daa23de94ce87017ba2d84988d", + "f69f2445df4f9b17ad2b417be66c3710", + "dfc9c58db67aada613c2dd08457941a6", + 0, + 0, + }; + // encrypt + plainp = ctr_vector; + cipherp = ctr_vector + 1; + memcpy(cbuf, fromhex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"), 16); + aes_encrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxe); + while (*plainp && *cipherp) { + memcpy(ibuf, fromhex(*plainp), 16); + aes_ctr_encrypt(ibuf, obuf, 16, cbuf, aes_ctr_cbuf_inc, &ctxe); + ck_assert_mem_eq(obuf, fromhex(*cipherp), 16); + plainp += 2; + cipherp += 2; + } + // decrypt (uses encryption) + plainp = ctr_vector; + cipherp = ctr_vector + 1; + memcpy(cbuf, fromhex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"), 16); + aes_encrypt_key256( + fromhex( + "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"), + &ctxe); + while (*plainp && *cipherp) { + memcpy(ibuf, fromhex(*cipherp), 16); + aes_ctr_decrypt(ibuf, obuf, 16, cbuf, aes_ctr_cbuf_inc, &ctxe); + ck_assert_mem_eq(obuf, fromhex(*plainp), 16); + plainp += 2; + cipherp += 2; + } +} +END_TEST + +#define TEST1 "abc" +#define TEST2_1 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +#define TEST2_2a "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" +#define TEST2_2b "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" +#define TEST2_2 TEST2_2a TEST2_2b +#define TEST3 "a" /* times 1000000 */ +#define TEST4a "01234567012345670123456701234567" +#define TEST4b "01234567012345670123456701234567" +/* an exact multiple of 512 bits */ +#define TEST4 TEST4a TEST4b /* times 10 */ + +#define TEST7_1 "\x49\xb2\xae\xc2\x59\x4b\xbe\x3a\x3b\x11\x75\x42\xd9\x4a\xc8" +#define TEST8_1 \ + "\x9a\x7d\xfd\xf1\xec\xea\xd0\x6e\xd6\x46\xaa\x55\xfe\x75\x71\x46" +#define TEST9_1 \ + "\x65\xf9\x32\x99\x5b\xa4\xce\x2c\xb1\xb4\xa2\xe7\x1a\xe7\x02\x20" \ + "\xaa\xce\xc8\x96\x2d\xd4\x49\x9c\xbd\x7c\x88\x7a\x94\xea\xaa\x10" \ + "\x1e\xa5\xaa\xbc\x52\x9b\x4e\x7e\x43\x66\x5a\x5a\xf2\xcd\x03\xfe" \ + "\x67\x8e\xa6\xa5\x00\x5b\xba\x3b\x08\x22\x04\xc2\x8b\x91\x09\xf4" \ + "\x69\xda\xc9\x2a\xaa\xb3\xaa\x7c\x11\xa1\xb3\x2a" +#define TEST10_1 \ + "\xf7\x8f\x92\x14\x1b\xcd\x17\x0a\xe8\x9b\x4f\xba\x15\xa1\xd5\x9f" \ + "\x3f\xd8\x4d\x22\x3c\x92\x51\xbd\xac\xbb\xae\x61\xd0\x5e\xd1\x15" \ + "\xa0\x6a\x7c\xe1\x17\xb7\xbe\xea\xd2\x44\x21\xde\xd9\xc3\x25\x92" \ + "\xbd\x57\xed\xea\xe3\x9c\x39\xfa\x1f\xe8\x94\x6a\x84\xd0\xcf\x1f" \ + "\x7b\xee\xad\x17\x13\xe2\xe0\x95\x98\x97\x34\x7f\x67\xc8\x0b\x04" \ + "\x00\xc2\x09\x81\x5d\x6b\x10\xa6\x83\x83\x6f\xd5\x56\x2a\x56\xca" \ + "\xb1\xa2\x8e\x81\xb6\x57\x66\x54\x63\x1c\xf1\x65\x66\xb8\x6e\x3b" \ + "\x33\xa1\x08\xb0\x53\x07\xc0\x0a\xff\x14\xa7\x68\xed\x73\x50\x60" \ + "\x6a\x0f\x85\xe6\xa9\x1d\x39\x6f\x5b\x5c\xbe\x57\x7f\x9b\x38\x80" \ + "\x7c\x7d\x52\x3d\x6d\x79\x2f\x6e\xbc\x24\xa4\xec\xf2\xb3\xa4\x27" \ + "\xcd\xbb\xfb" +#define length(x) (sizeof(x) - 1) + +// test vectors from rfc-4634 +START_TEST(test_sha1) { + struct { + const char *test; + int length; + int repeatcount; + int extrabits; + int numberExtrabits; + const char *result; + } tests[] = { + /* 1 */ {TEST1, length(TEST1), 1, 0, 0, + "A9993E364706816ABA3E25717850C26C9CD0D89D"}, + /* 2 */ + {TEST2_1, length(TEST2_1), 1, 0, 0, + "84983E441C3BD26EBAAE4AA1F95129E5E54670F1"}, + /* 3 */ + {TEST3, length(TEST3), 1000000, 0, 0, + "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F"}, + /* 4 */ + {TEST4, length(TEST4), 10, 0, 0, + "DEA356A2CDDD90C7A7ECEDC5EBB563934F460452"}, + /* 5 */ {"", 0, 0, 0x98, 5, "29826B003B906E660EFF4027CE98AF3531AC75BA"}, + /* 6 */ {"\x5e", 1, 1, 0, 0, "5E6F80A34A9798CAFC6A5DB96CC57BA4C4DB59C2"}, + /* 7 */ + {TEST7_1, length(TEST7_1), 1, 0x80, 3, + "6239781E03729919C01955B3FFA8ACB60B988340"}, + /* 8 */ + {TEST8_1, length(TEST8_1), 1, 0, 0, + "82ABFF6605DBE1C17DEF12A394FA22A82B544A35"}, + /* 9 */ + {TEST9_1, length(TEST9_1), 1, 0xE0, 3, + "8C5B2A5DDAE5A97FC7F9D85661C672ADBF7933D4"}, + /* 10 */ + {TEST10_1, length(TEST10_1), 1, 0, 0, + "CB0082C8F197D260991BA6A460E76E202BAD27B3"}}; + + for (int i = 0; i < 10; i++) { + SHA1_CTX ctx; + uint8_t digest[SHA1_DIGEST_LENGTH]; + sha1_Init(&ctx); + /* extra bits are not supported */ + if (tests[i].numberExtrabits) continue; + for (int j = 0; j < tests[i].repeatcount; j++) { + sha1_Update(&ctx, (const uint8_t *)tests[i].test, tests[i].length); + } + sha1_Final(&ctx, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].result), SHA1_DIGEST_LENGTH); + } +} +END_TEST + +#define TEST7_256 "\xbe\x27\x46\xc6\xdb\x52\x76\x5f\xdb\x2f\x88\x70\x0f\x9a\x73" +#define TEST8_256 \ + "\xe3\xd7\x25\x70\xdc\xdd\x78\x7c\xe3\x88\x7a\xb2\xcd\x68\x46\x52" +#define TEST9_256 \ + "\x3e\x74\x03\x71\xc8\x10\xc2\xb9\x9f\xc0\x4e\x80\x49\x07\xef\x7c" \ + "\xf2\x6b\xe2\x8b\x57\xcb\x58\xa3\xe2\xf3\xc0\x07\x16\x6e\x49\xc1" \ + "\x2e\x9b\xa3\x4c\x01\x04\x06\x91\x29\xea\x76\x15\x64\x25\x45\x70" \ + "\x3a\x2b\xd9\x01\xe1\x6e\xb0\xe0\x5d\xeb\xa0\x14\xeb\xff\x64\x06" \ + "\xa0\x7d\x54\x36\x4e\xff\x74\x2d\xa7\x79\xb0\xb3" +#define TEST10_256 \ + "\x83\x26\x75\x4e\x22\x77\x37\x2f\x4f\xc1\x2b\x20\x52\x7a\xfe\xf0" \ + "\x4d\x8a\x05\x69\x71\xb1\x1a\xd5\x71\x23\xa7\xc1\x37\x76\x00\x00" \ + "\xd7\xbe\xf6\xf3\xc1\xf7\xa9\x08\x3a\xa3\x9d\x81\x0d\xb3\x10\x77" \ + "\x7d\xab\x8b\x1e\x7f\x02\xb8\x4a\x26\xc7\x73\x32\x5f\x8b\x23\x74" \ + "\xde\x7a\x4b\x5a\x58\xcb\x5c\x5c\xf3\x5b\xce\xe6\xfb\x94\x6e\x5b" \ + "\xd6\x94\xfa\x59\x3a\x8b\xeb\x3f\x9d\x65\x92\xec\xed\xaa\x66\xca" \ + "\x82\xa2\x9d\x0c\x51\xbc\xf9\x33\x62\x30\xe5\xd7\x84\xe4\xc0\xa4" \ + "\x3f\x8d\x79\xa3\x0a\x16\x5c\xba\xbe\x45\x2b\x77\x4b\x9c\x71\x09" \ + "\xa9\x7d\x13\x8f\x12\x92\x28\x96\x6f\x6c\x0a\xdc\x10\x6a\xad\x5a" \ + "\x9f\xdd\x30\x82\x57\x69\xb2\xc6\x71\xaf\x67\x59\xdf\x28\xeb\x39" \ + "\x3d\x54\xd6" + +// test vectors from rfc-4634 +START_TEST(test_sha256) { + struct { + const char *test; + int length; + int repeatcount; + int extrabits; + int numberExtrabits; + const char *result; + } tests[] = { + /* 1 */ {TEST1, length(TEST1), 1, 0, 0, + "BA7816BF8F01CFEA4141" + "40DE5DAE2223B00361A396177A9CB410FF61F20015AD"}, + /* 2 */ + {TEST2_1, length(TEST2_1), 1, 0, 0, + "248D6A61D20638B8" + "E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1"}, + /* 3 */ + {TEST3, length(TEST3), 1000000, 0, 0, + "CDC76E5C9914FB92" + "81A1C7E284D73E67F1809A48A497200E046D39CCC7112CD0"}, + /* 4 */ + {TEST4, length(TEST4), 10, 0, 0, + "594847328451BDFA" + "85056225462CC1D867D877FB388DF0CE35F25AB5562BFBB5"}, + /* 5 */ + {"", 0, 0, 0x68, 5, + "D6D3E02A31A84A8CAA9718ED6C2057BE" + "09DB45E7823EB5079CE7A573A3760F95"}, + /* 6 */ + {"\x19", 1, 1, 0, 0, + "68AA2E2EE5DFF96E3355E6C7EE373E3D" + "6A4E17F75F9518D843709C0C9BC3E3D4"}, + /* 7 */ + {TEST7_256, length(TEST7_256), 1, 0x60, 3, + "77EC1DC8" + "9C821FF2A1279089FA091B35B8CD960BCAF7DE01C6A7680756BEB972"}, + /* 8 */ + {TEST8_256, length(TEST8_256), 1, 0, 0, + "175EE69B02BA" + "9B58E2B0A5FD13819CEA573F3940A94F825128CF4209BEABB4E8"}, + /* 9 */ + {TEST9_256, length(TEST9_256), 1, 0xA0, 3, + "3E9AD646" + "8BBBAD2AC3C2CDC292E018BA5FD70B960CF1679777FCE708FDB066E9"}, + /* 10 */ + {TEST10_256, length(TEST10_256), 1, 0, 0, + "97DBCA7D" + "F46D62C8A422C941DD7E835B8AD3361763F7E9B2D95F4F0DA6E1CCBC"}, + }; + + for (int i = 0; i < 10; i++) { + SHA256_CTX ctx; + uint8_t digest[SHA256_DIGEST_LENGTH]; + sha256_Init(&ctx); + /* extra bits are not supported */ + if (tests[i].numberExtrabits) continue; + for (int j = 0; j < tests[i].repeatcount; j++) { + sha256_Update(&ctx, (const uint8_t *)tests[i].test, tests[i].length); + } + sha256_Final(&ctx, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].result), SHA256_DIGEST_LENGTH); + } +} +END_TEST + +#define TEST7_512 "\x08\xec\xb5\x2e\xba\xe1\xf7\x42\x2d\xb6\x2b\xcd\x54\x26\x70" +#define TEST8_512 \ + "\x8d\x4e\x3c\x0e\x38\x89\x19\x14\x91\x81\x6e\x9d\x98\xbf\xf0\xa0" +#define TEST9_512 \ + "\x3a\xdd\xec\x85\x59\x32\x16\xd1\x61\x9a\xa0\x2d\x97\x56\x97\x0b" \ + "\xfc\x70\xac\xe2\x74\x4f\x7c\x6b\x27\x88\x15\x10\x28\xf7\xb6\xa2" \ + "\x55\x0f\xd7\x4a\x7e\x6e\x69\xc2\xc9\xb4\x5f\xc4\x54\x96\x6d\xc3" \ + "\x1d\x2e\x10\xda\x1f\x95\xce\x02\xbe\xb4\xbf\x87\x65\x57\x4c\xbd" \ + "\x6e\x83\x37\xef\x42\x0a\xdc\x98\xc1\x5c\xb6\xd5\xe4\xa0\x24\x1b" \ + "\xa0\x04\x6d\x25\x0e\x51\x02\x31\xca\xc2\x04\x6c\x99\x16\x06\xab" \ + "\x4e\xe4\x14\x5b\xee\x2f\xf4\xbb\x12\x3a\xab\x49\x8d\x9d\x44\x79" \ + "\x4f\x99\xcc\xad\x89\xa9\xa1\x62\x12\x59\xed\xa7\x0a\x5b\x6d\xd4" \ + "\xbd\xd8\x77\x78\xc9\x04\x3b\x93\x84\xf5\x49\x06" +#define TEST10_512 \ + "\xa5\x5f\x20\xc4\x11\xaa\xd1\x32\x80\x7a\x50\x2d\x65\x82\x4e\x31" \ + "\xa2\x30\x54\x32\xaa\x3d\x06\xd3\xe2\x82\xa8\xd8\x4e\x0d\xe1\xde" \ + "\x69\x74\xbf\x49\x54\x69\xfc\x7f\x33\x8f\x80\x54\xd5\x8c\x26\xc4" \ + "\x93\x60\xc3\xe8\x7a\xf5\x65\x23\xac\xf6\xd8\x9d\x03\xe5\x6f\xf2" \ + "\xf8\x68\x00\x2b\xc3\xe4\x31\xed\xc4\x4d\xf2\xf0\x22\x3d\x4b\xb3" \ + "\xb2\x43\x58\x6e\x1a\x7d\x92\x49\x36\x69\x4f\xcb\xba\xf8\x8d\x95" \ + "\x19\xe4\xeb\x50\xa6\x44\xf8\xe4\xf9\x5e\xb0\xea\x95\xbc\x44\x65" \ + "\xc8\x82\x1a\xac\xd2\xfe\x15\xab\x49\x81\x16\x4b\xbb\x6d\xc3\x2f" \ + "\x96\x90\x87\xa1\x45\xb0\xd9\xcc\x9c\x67\xc2\x2b\x76\x32\x99\x41" \ + "\x9c\xc4\x12\x8b\xe9\xa0\x77\xb3\xac\xe6\x34\x06\x4e\x6d\x99\x28" \ + "\x35\x13\xdc\x06\xe7\x51\x5d\x0d\x73\x13\x2e\x9a\x0d\xc6\xd3\xb1" \ + "\xf8\xb2\x46\xf1\xa9\x8a\x3f\xc7\x29\x41\xb1\xe3\xbb\x20\x98\xe8" \ + "\xbf\x16\xf2\x68\xd6\x4f\x0b\x0f\x47\x07\xfe\x1e\xa1\xa1\x79\x1b" \ + "\xa2\xf3\xc0\xc7\x58\xe5\xf5\x51\x86\x3a\x96\xc9\x49\xad\x47\xd7" \ + "\xfb\x40\xd2" + +// test vectors from rfc-4634 +START_TEST(test_sha512) { + struct { + const char *test; + int length; + int repeatcount; + int extrabits; + int numberExtrabits; + const char *result; + } tests[] = {/* 1 */ {TEST1, length(TEST1), 1, 0, 0, + "DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2" + "0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD" + "454D4423643CE80E2A9AC94FA54CA49F"}, + /* 2 */ + {TEST2_2, length(TEST2_2), 1, 0, 0, + "8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7FA1" + "7299AEADB6889018501D289E4900F7E4331B99DEC4B5433A" + "C7D329EEB6DD26545E96E55B874BE909"}, + /* 3 */ + {TEST3, length(TEST3), 1000000, 0, 0, + "E718483D0CE769644E2E42C7BC15B4638E1F98B13B204428" + "5632A803AFA973EBDE0FF244877EA60A4CB0432CE577C31B" + "EB009C5C2C49AA2E4EADB217AD8CC09B"}, + /* 4 */ + {TEST4, length(TEST4), 10, 0, 0, + "89D05BA632C699C31231DED4FFC127D5A894DAD412C0E024" + "DB872D1ABD2BA8141A0F85072A9BE1E2AA04CF33C765CB51" + "0813A39CD5A84C4ACAA64D3F3FB7BAE9"}, + /* 5 */ + {"", 0, 0, 0xB0, 5, + "D4EE29A9E90985446B913CF1D1376C836F4BE2C1CF3CADA0" + "720A6BF4857D886A7ECB3C4E4C0FA8C7F95214E41DC1B0D2" + "1B22A84CC03BF8CE4845F34DD5BDBAD4"}, + /* 6 */ + {"\xD0", 1, 1, 0, 0, + "9992202938E882E73E20F6B69E68A0A7149090423D93C81B" + "AB3F21678D4ACEEEE50E4E8CAFADA4C85A54EA8306826C4A" + "D6E74CECE9631BFA8A549B4AB3FBBA15"}, + /* 7 */ + {TEST7_512, length(TEST7_512), 1, 0x80, 3, + "ED8DC78E8B01B69750053DBB7A0A9EDA0FB9E9D292B1ED71" + "5E80A7FE290A4E16664FD913E85854400C5AF05E6DAD316B" + "7359B43E64F8BEC3C1F237119986BBB6"}, + /* 8 */ + {TEST8_512, length(TEST8_512), 1, 0, 0, + "CB0B67A4B8712CD73C9AABC0B199E9269B20844AFB75ACBD" + "D1C153C9828924C3DDEDAAFE669C5FDD0BC66F630F677398" + "8213EB1B16F517AD0DE4B2F0C95C90F8"}, + /* 9 */ + {TEST9_512, length(TEST9_512), 1, 0x80, 3, + "32BA76FC30EAA0208AEB50FFB5AF1864FDBF17902A4DC0A6" + "82C61FCEA6D92B783267B21080301837F59DE79C6B337DB2" + "526F8A0A510E5E53CAFED4355FE7C2F1"}, + /* 10 */ + {TEST10_512, length(TEST10_512), 1, 0, 0, + "C665BEFB36DA189D78822D10528CBF3B12B3EEF726039909" + "C1A16A270D48719377966B957A878E720584779A62825C18" + "DA26415E49A7176A894E7510FD1451F5"}}; + + for (int i = 0; i < 10; i++) { + SHA512_CTX ctx; + uint8_t digest[SHA512_DIGEST_LENGTH]; + sha512_Init(&ctx); + /* extra bits are not supported */ + if (tests[i].numberExtrabits) continue; + for (int j = 0; j < tests[i].repeatcount; j++) { + sha512_Update(&ctx, (const uint8_t *)tests[i].test, tests[i].length); + } + sha512_Final(&ctx, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].result), SHA512_DIGEST_LENGTH); + } +} +END_TEST + +// test vectors from http://www.di-mgt.com.au/sha_testvectors.html +START_TEST(test_sha3_256) { + static const struct { + const char *data; + const char *hash; + } tests[] = { + { + "", + "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", + }, + { + "abc", + "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", + }, + { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", + }, + { + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijkl" + "mnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", + }, + }; + + uint8_t digest[SHA3_256_DIGEST_LENGTH]; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t len = strlen(tests[i].data); + sha3_256((uint8_t *)tests[i].data, len, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), SHA3_256_DIGEST_LENGTH); + + // Test progressive hashing. + size_t part_len = len; + SHA3_CTX ctx; + sha3_256_Init(&ctx); + sha3_Update(&ctx, (uint8_t *)tests[i].data, part_len); + sha3_Update(&ctx, NULL, 0); + sha3_Update(&ctx, (uint8_t *)tests[i].data + part_len, len - part_len); + sha3_Final(&ctx, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), SHA3_256_DIGEST_LENGTH); + } +} +END_TEST + +// test vectors from http://www.di-mgt.com.au/sha_testvectors.html +START_TEST(test_sha3_512) { + static const struct { + const char *data; + const char *hash; + } tests[] = { + { + "", + "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2" + "123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", + }, + { + "abc", + "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e1" + "16e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", + }, + { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee69" + "1fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e", + }, + { + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijkl" + "mnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3" + "261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185", + }, + }; + + uint8_t digest[SHA3_512_DIGEST_LENGTH]; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t len = strlen(tests[i].data); + sha3_512((uint8_t *)tests[i].data, len, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), SHA3_512_DIGEST_LENGTH); + + // Test progressive hashing. + size_t part_len = len; + SHA3_CTX ctx; + sha3_512_Init(&ctx); + sha3_Update(&ctx, (const uint8_t *)tests[i].data, part_len); + sha3_Update(&ctx, NULL, 0); + sha3_Update(&ctx, (const uint8_t *)tests[i].data + part_len, + len - part_len); + sha3_Final(&ctx, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), SHA3_512_DIGEST_LENGTH); + } +} +END_TEST + +// test vectors from +// https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/0.test-sha3-256.dat +START_TEST(test_keccak_256) { + static const struct { + const char *hash; + size_t length; + const char *data; + } tests[] = { + { + "4e9e79ab7434f6c7401fb3305d55052ee829b9e46d5d05d43b59fefb32e9a619", + 293, + "a6151d4904e18ec288243028ceda30556e6c42096af7150d6a7232ca5dba52bd2192" + "e23daa5fa2bea3d4bd95efa2389cd193fcd3376e70a5c097b32c1c62c80af9d71021" + "1545f7cdddf63747420281d64529477c61e721273cfd78f8890abb4070e97baa52ac" + "8ff61c26d195fc54c077def7a3f6f79b36e046c1a83ce9674ba1983ec2fb58947de6" + "16dd797d6499b0385d5e8a213db9ad5078a8e0c940ff0cb6bf92357ea5609f778c3d" + "1fb1e7e36c35db873361e2be5c125ea7148eff4a035b0cce880a41190b2e22924ad9" + "d1b82433d9c023924f2311315f07b88bfd42850047bf3be785c4ce11c09d7e02065d" + "30f6324365f93c5e7e423a07d754eb314b5fe9db4614275be4be26af017abdc9c338" + "d01368226fe9af1fb1f815e7317bdbb30a0f36dc69", + }, + { + "c1268babc42d00c3463dc388222100f7e525a74a64665c39f112f788ddb5da42", + 376, + "9db801077952c2324e0044a4994edfb09b3edfcf669bfdd029f4bf42d5b0eab3056b" + "0bf82708ca7bfadba43c9de806b10a19d0f00c2351ef1086b6b108f306e035c6b61b" + "2e70fd7087ba848601c8a3f626a66666423717ef305a1068bfa3a1f7ffc1e5a78cb6" + "182ffc8a577ca2a821630bf900d0fbba848bdf94b77c5946771b6c3f8c02269bc772" + "ca56098f724536d96be68c284ee1d81697989d40029b8ea63ac1fd85f8b3cae8b194" + "f6834ff65a5858f9498ddbb467995eb2d49cdfc6c05d92038c6e9aaeee85f8222b37" + "84165f12a2c3df4c7a142e26dddfd831d07e22dfecc0eded48a69c8a9e1b97f1a4e0" + "efcd4edd310de0edf82af38a6e4d5ab2a19da586e61210d4f75e7a07e2201f9c8154" + "ca52a414a70d2eb2ac1c5b9a2900b4d871f62fa56f70d03b3dd3704bd644808c45a1" + "3231918ea884645b8ec054e8bab2935a66811fe590ddc119ae901dfeb54fc2a87c1e" + "0a236778baab2fa8843709c6676d3c1888ba19d75ec52d73a7d035c143179b938237" + "26b7", + }, + { + "e83b50e8c83cb676a7dd64c055f53e5110d5a4c62245ceb8f683fd87b2b3ec77", + 166, + "c070a957550b7b34113ee6543a1918d96d241f27123425db7f7b9004e047ffbe0561" + "2e7fa8c54b23c83ea427e625e97b7a28b09a70bf6d91e478eeed01d7907931c29ea8" + "6e70f2cdcfb243ccf7f24a1619abf4b5b9e6f75cbf63fc02baf4a820a9790a6b053e" + "50fd94e0ed57037cfc2bab4d95472b97d3c25f434f1cc0b1ede5ba7f15907a42a223" + "933e5e2dfcb518c3531975268c326d60fa911fbb7997eee3ba87656c4fe7", + }, + { + "8ebd2c9d4ff00e285a9b6b140bfc3cef672016f0098100e1f6f250220af7ce1a", + 224, + "b502fbdce4045e49e147eff5463d4b3f37f43461518868368e2c78008c84c2db79d1" + "2b58107034f67e7d0abfee67add0342dd23dce623f26b9156def87b1d7ac15a6e073" + "01f832610fe869ada13a2b0e3d60aa6bb81bc04487e2e800f5106b0402ee0331df74" + "5e021b5ea5e32faf1c7fc1322041d221a54191c0af19948b5f34411937182e30d5cd" + "39b5a6c959d77d92d21bb1de51f1b3411cb6eec00600429916227fb62d2c88e69576" + "f4ac8e5efcde8efa512cc80ce7fb0dfaa6c74d26e898cefe9d4f7dce232a69f2a6a9" + "477aa08366efcdfca117c89cb79eba15a23755e0", + }, + { + "db3961fdddd0c314289efed5d57363459a6700a7bd015e7a03d3e1d03f046401", + 262, + "22e203a98ba2c43d8bc3658f0a48a35766df356d6a5e98b0c7222d16d85a00b31720" + "7d4aef3fc7cabb67b9d8f5838de0b733e1fd59c31f0667e53286972d7090421ad90d" + "54db2ea40047d0d1700c86f53dbf48da532396307e68edad877dcae481848801b0a5" + "db44dbdba6fc7c63b5cd15281d57ca9e6be96f530b209b59d6127ad2bd8750f3f807" + "98f62521f0d5b42633c2f5a9aaefbed38779b7aded2338d66850b0bb0e33c48e040c" + "99f2dcee7a7ebb3d7416e1c5bf038c19d09682dab67c96dbbfad472e45980aa27d1b" + "301b15f7de4d4f549bad2501931c9d4f1a3b1692dcb4b1b834ddd4a636126702307d" + "daeec61841693b21887d56e76cc2069dafb557fd6682160f", + }, + { + "25dd3acacd6bf688c0eace8d33eb7cc550271969142deb769a05b4012f7bb722", + 122, + "99e7f6e0ed46ec866c43a1ab494998d47e9309a79fde2a629eb63bb2160a5ffd0f22" + "06de9c32dd20e9b23e57ab7422cf82971cc2873ec0e173fe93281c7b33e1c76ac792" + "23a6f435f230bdd30260c00d00986c72a399d3ba70f6e783d834bbf8a6127844def5" + "59b8b6db742b2cfd715f7ff29e7b42bf7d567beb", + }, + { + "00d747c9045c093484290afc161437f11c2ddf5f8a9fc2acae9c7ef5fcf511e5", + 440, + "50c392f97f8788377f0ab2e2aab196cb017ad157c6f9d022673d39072cc198b06622" + "a5cbd269d1516089fa59e28c3373a92bd54b2ebf1a79811c7e40fdd7bce200e80983" + "fda6e77fc44c44c1b5f87e01cef2f41e1141103f73364e9c2f25a4597e6517ef31b3" + "16300b770c69595e0fa6d011df1566a8676a88c7698562273bbfa217cc69d4b5c89a" + "8907b902f7dc14481fefc7da4a810c15a60f5641aae854d2f8cc50cbc393015560f0" + "1c94e0d0c075dbcb150ad6eba29dc747919edcaf0231dba3eb3f2b1a87e136a1f0fd" + "4b3d8ee61bad2729e9526a32884f7bcfa41e361add1b4c51dc81463528372b4ec321" + "244de0c541ba00df22b8773cdf4cf898510c867829fa6b4ff11f9627338b9686d905" + "cb7bcdf085080ab842146e0035c808be58cce97827d8926a98bd1ff7c529be3bc14f" + "68c91b2ca4d2f6fc748f56bcf14853b7f8b9aa6d388f0fd82f53fdc4bacf9d9ba10a" + "165f404cf427e199f51bf6773b7c82531e17933f6d8b8d9181e22f8921a2dbb20fc7" + "c8023a87e716e245017c399d0942934f5e085219b3f8d26a196bf8b239438b8e561c" + "28a61ff08872ecb052c5fcb19e2fdbc09565924a50ebee1461c4b414219d4257", + }, + { + "dadcde7c3603ef419d319ba3d50cf00ad57f3e81566fd11b9b6f461cbb9dcb0f", + 338, + "18e1df97abccc91e07dc7b7ffab5ee8919d5610721453176aa2089fb96d9a477e147" + "6f507fa1129f04304e960e8017ff41246cacc0153055fc4b1dc6168a74067ebb077c" + "b5aa80a9df6e8b5b821e906531159668c4c164b9e511d8724aedbe17c1f41da88094" + "17d3c30b79ea5a2e3c961f6bac5436d9af6be24a36eebcb17863fed82c0eb8962339" + "eb612d58659dddd2ea06a120b3a2d8a17050be2de367db25a5bef4290c209bdb4c16" + "c4df5a1fe1ead635169a1c35f0a56bc07bcf6ef0e4c2d8573ed7a3b58030fa268c1a" + "5974b097288f01f34d5a1087946410688016882c6c7621aad680d9a25c7a3e5dbcbb" + "07ffdb7243b91031c08a121b40785e96b7ee46770c760f84aca8f36b7c7da64d25c8" + "f73b4d88ff3acb7eeefc0b75144dffea66d2d1f6b42b905b61929ab3f38538393ba5" + "ca9d3c62c61f46fa63789cac14e4e1d8722bf03cceef6e3de91f783b0072616c", + }, + { + "d184e84a2507fc0f187b640dd5b849a366c0383d9cbdbc6fa30904f054111255", + 141, + "13b8df9c1bcfddd0aa39b3055f52e2bc36562b6677535994b173f07041d141699db4" + "2589d6091ef0e71b645b41ab57577f58c98da966562d24823158f8e1d43b54edea4e" + "61dd66fe8c59ad8405f5a0d9a3eb509a77ae3d8ae4adf926fd3d8d31c3dcccfc1408" + "14541010937024cc554e1daaee1b333a66316e7fbebb07ac8dfb134a918b9090b141" + "68012c4824", + }, + { + "20c19635364a00b151d0168fe5ae03bac6dd7d06030475b40d2e8c577a192f53", + 84, + "e1e96da4b7d8dcc2b316006503a990ea26a5b200cb7a7edfc14f5ce827f06d8d232e" + "c95b1acdc1422ffc16da11d258f0c7b378f026d64c74b2fb41df8bfd3cd30066caec" + "dc6f76c8163de9309d9fd0cf33d54a29", + }, + { + "86cc2c428d469e43fb4ee8d38dffbf5128d20d1659dbc45edf4a855399ca730e", + 319, + "30391840ad14e66c53e1a5aaa03989ff059940b60c44c3b21295a93d023f2e6c7cdc" + "f60208b7d87a7605fb5cee94630d94cad90bc6955328357fa37fea47c09f9cee759c" + "31537187321c7d572e3554eeb90f441a9494575454dfbf8cfd86128da15de9418821" + "ca158856eb84ff6a29a2c8380711e9e6d4955388374fcd3c1ca45b49e0679fc7157f" + "96bc6e4f86ce20a89c12d4449b1ca7056e0b7296fc646f68f6ddbfa6a48e384d63ab" + "68bc75fd69a2add59b8e41c4a0f753935df9a703d7df82a430798b0a67710a780614" + "85a9d15de16f154582082459b4462485ce8a82d35ac6b9498ae40df3a23d5f00e0e8" + "6661cb02c52f677fd374c32969ec63028b5dd2c1d4bce67a6d9f79ba5e7eeb5a2763" + "dc9fe2a05aa2ebaad36aaec2541e343a677fb4e6b6a180eff33c93744a4624f6a79f" + "054c6c9e9c5b6928dbe7ba5fca", + }, + { + "e80eee72a76e6957f7cb7f68c41b92f0ad9aac6e58aa8fc272c1e7364af11c70", + 108, + "3c210ed15889ae938781d2cebd49d4a8007f163ffba1f7669bccdccf6ad5a1418299" + "d5f4348f5cd03b0ba9e6999ab154e46836c3546feb395d17bcc60f23d7ba0e8efe6a" + "a616c00b6bf552fe1cb5e28e3e7bc39dfc20c63ae3901035e91ddd110e43fe59ed74" + "4beeedb6bc1e", + }, + { + "f971bbae97dd8a034835269fb246867de358a889de6de13672e771d6fb4c89b7", + 468, + "64e9a3a99c021df8bea59368cfe1cd3b0a4aca33ffcd5cf6028d9307c0b904b8037d" + "056a3c12803f196f74c4d360a3132452d365922b1157e5b0d76f91fb94bebfdcb4d5" + "0fa23ed5be3d3c5712219e8666debc8abcd5e6c69a542761a6cbbd1b3c0f05248752" + "04b64d2788465f90cb19b6f6da9f8bec6d6e684196e713549ec83e47cbaeff77838a" + "c4936b312562e2de17c970449d49d214ec2597c6d4f642e6c94a613a0c53285abccd" + "7794a3d72241808594fb4e6e4d5d2c310ef1cdcbfd34805ca2408f554797a6cfd49d" + "0f25ed8927f206acb127e6436e1234902489ec2e7f3058e26c0eba80341bc7ad0da8" + "b8bd80bd1b43c9099269e3f8b68445c69b79d8cf5693d4a0d47a44f9e9114dbb3399" + "2d2ea9d3b5b86e4ea57a44a638848de4ac365bb6bb7855305ade62b07ebf0954d70b" + "7c2fb5e6fcc154c7a36fb1756df5f20a84d35696627ebf22d44f40f805c0878ad110" + "bc17dcd66821084ca87902e05bc0afa61161086956b85a6ea900d35c7784d4c361a4" + "3fe294e267d5762408be58962cdb4f45a9c0efd7d2335916df3acb98ccfbcf5ee395" + "30540e5f3d3c5f3326a9a536d7bfa37aae2b143e2499b81bf0670e3a418c26c7dc82" + "b293d9bd182dd6435670514237df88d8286e19ce93e0a0db2790", + }, + { + "b97fd51f4e4eaa40c7a2853010fc46be5be2f43b9520ea0c533b68f728c978a2", + 214, + "ced3a43193caceb269d2517f4ecb892bb7d57d7201869e28e669b0b17d1c44d286e0" + "2734e2210ea9009565832975cc6303b9b6008fe1165b99ae5f1b29962ef042ebad8b" + "676d7433ed2fe0d0d6f4f32b2cb4c519da61552328c2caea799bb2fd907308173a1c" + "d2b798fb0df7d2eaf2ff0be733af74f42889e211843fc80b09952ae7eb246725b91d" + "31c1f7a5503fdf3bc9c269c76519cf2dc3225e862436b587bb74adbad88c773056cf" + "ea3bddb1f6533c01125eeae0986e5c817359912c9d0472bf8320b824ee097f82a8e0" + "5b9f53a5be7d153225de", + }, + { + "f0fecf766e4f7522568b3be71843cce3e5fcb10ea96b1a236c8c0a71c9ad55c9", + 159, + "8aca4de41275f5c4102f66266d70cff1a2d56f58df8d12061c64cb6cd8f616a5bf19" + "c2bb3c91585c695326f561a2d0eb4eef2e202d82dcc9089e4bee82b62a199a11963c" + "d08987d3abd5914def2cdd3c1e4748d46b654f338e3959121e869c18d5327e88090d" + "0ba0ac6762a2b14514cc505af7499f1a22f421dbe978494f9ffe1e88f1c59228f21d" + "a5bc9fcc911d022300a443bca17258bdd6cfbbf52fde61", + }, + { + "5c4f16043c0084bf98499fc7dc4d674ce9c730b7135210acdbf5e41d3dcf317b", + 87, + "01bbc193d0ee2396a7d8267ad63f18149667b31d8f7f48c8bb0c634755febc9ef1a7" + "9e93c475f6cd137ee37d4dc243ea2fdcdc0d098844af2208337b7bbf6930e39e74e2" + "3952ac1a19b4d38b83810a10c3b069e4fafb06", + }, + { + "14b61fc981f7d9449b7b6a2d57eb48cc8f7896f4dced2005291b2a2f38cb4a63", + 358, + "cbc1709a531438d5ead32cea20a9e4ddc0101ec555ab42b2e378145013cc05a97b9e" + "2c43c89bfa63ae5e9e9ce1fc022035c6b68f0a906ee1f53396d9dbe41cb2bc4bfeb1" + "44b005b0f40e0fec872d9c4aca9929ba3cbacd84c58ab43c26f10d345a24692bbd55" + "a76506876768e8e32a461bf160cee953da88920d36ad4aff6eea7126aa6f44a7a6fc" + "e770ce43f0f90a20590bdaad3ffcda30ca8e3700f832c62caa5df030c16bcf74aff4" + "92466f781eb69863a80663535fc154abd7cfdd02eef1019221cf608b9780f807e507" + "fbbf559b1dfe4e971b4d08fe45263a3c697ba90f9f71bec97e12438b4b12f6a84ab6" + "6872b888097089d76c9c2502d9ed2eece6bef8eee1d439782e218f5cc75d38f98860" + "12cdcb4bbe6caf812e97c5a336bcceae38b1109e3243a291ce23d097aaee7d9a711d" + "e6886749a7a6d15d7e7cbc4a51b1b4da9fcf139e4a6fd7dc0bc017db624b17fc9b8f" + "847592ed42467c25ad9fe96acbf20c0ffd18", + }, + { + "47ec7f3a362becbb110867995a0f066a66152603c4d433f11bf51870c67e2864", + 354, + "0636983353c9ea3f75256ed00b70e8b7cfc6f4e4c0ba3aa9a8da59b6e6ad9dfb5bc2" + "c49f48cc0b4237f87dedf34b888e54ecebf1d435bcd4aab72eb4ce39e5262fb68c6f" + "86423dac123bf59e903989eda7df4a982822d0831521403cedcfe9a5bbea648bb2e7" + "ef8cd81442ea5abe468b3ee8b06376ef8099447255c2fdc1b73af37fe0e0b852ffbc" + "9339868db756680db99e6e9837dbd28c39a69f229044ad7ec772524a6e01f679d25f" + "dc2e736a2418e5dfd7c2ab1348d0f821b777c975244c6cfc2fca5c36ccae7cf1d07b" + "190a9d17a088a1276bd096250b92f53b29b6ef88ef69d744b56fb2ec5078cc0b68a9" + "106943ef242b466097b9e29df11eb5cb0c06c29d7917410ba1097215d6aa4dafd90a" + "dff0c3e7221b9e8832613bd9aca8bcc6b2aa7b43acedcbc11aee1b5ba56f77a210be" + "7cf3485ee813e1126c3eeccd8419bbf22c412cad32cc0fc7a73aca4e379651caac3d" + "13d6cf5ca05508fd2d96f3ad94e7", + }, + { + "73778e7f1943646a89d3c78909e0afbe584071ba5230546a39cd73e44e36d78a", + 91, + "6217504a26b3395855eab6ddeb79f2e3490d74b80eff343721150ee0c1c02b071867" + "43589f93c22a03dc5ed29fb5bf592de0a089763e83e5b95f9dd524d66c8da3e04c18" + "14e65e68b2810c1b517648aabc266ad62896c51864a7f4", + }, + { + "35ef6868e750cf0c1d5285992c231d93ec644670fb79cf85324067a9f77fde78", + 185, + "0118b7fb15f927a977e0b330b4fa351aeeec299d6ba090eb16e5114fc4a6749e5915" + "434a123c112697390c96ea2c26dc613eb5c75f5ecfb6c419317426367e34da0ddc6d" + "7b7612cefa70a22fea0025f5186593b22449dab71f90a49f7de7352e54e0c0bd8837" + "e661ca2127c3313a7268cafdd5ccfbf3bdd7c974b0e7551a2d96766579ef8d2e1f37" + "6af74cd1ab62162fc2dc61a8b7ed4163c1caccf20ed73e284da2ed257ec974eee96b" + "502acb2c60a04886465e44debb0317", + }, + }; + + uint8_t hash[SHA3_256_DIGEST_LENGTH]; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + keccak_256(fromhex(tests[i].data), tests[i].length, hash); + ck_assert_mem_eq(hash, fromhex(tests[i].hash), SHA3_256_DIGEST_LENGTH); + + // Test progressive hashing. + size_t part_len = tests[i].length / 2; + SHA3_CTX ctx = {0}; + keccak_256_Init(&ctx); + keccak_Update(&ctx, fromhex(tests[i].data), part_len); + keccak_Update(&ctx, fromhex(tests[i].data), 0); + keccak_Update(&ctx, fromhex(tests[i].data) + part_len, + tests[i].length - part_len); + keccak_Final(&ctx, hash); + ck_assert_mem_eq(hash, fromhex(tests[i].hash), SHA3_256_DIGEST_LENGTH); + } +} +END_TEST + +// test vectors from +// https://raw.githubusercontent.com/monero-project/monero/master/tests/hash/tests-extra-blake.txt +START_TEST(test_blake256) { + static const struct { + const char *hash; + const char *data; + } tests[] = { + { + "716f6e863f744b9ac22c97ec7b76ea5f5908bc5b2f67c61510bfc4751384ea7a", + "", + }, + { + "e104256a2bc501f459d03fac96b9014f593e22d30f4de525fa680c3aa189eb4f", + "cc", + }, + { + "8f341148be7e354fdf38b693d8c6b4e0bd57301a734f6fd35cd85b8491c3ddcd", + "41fb", + }, + { + "bc334d1069099f10c601883ac6f3e7e9787c6aa53171f76a21923cc5ad3ab937", + "1f877c", + }, + { + "b672a16f53982bab1e77685b71c0a5f6703ffd46a1c834be69f614bd128d658e", + "c1ecfdfc", + }, + { + "d9134b2899057a7d8d320cc99e3e116982bc99d3c69d260a7f1ed3da8be68d99", + "21f134ac57", + }, + { + "637923bd29a35aa3ecbbd2a50549fc32c14cf0fdcaf41c3194dd7414fd224815", + "c6f50bb74e29", + }, + { + "70c092fd5c8c21e9ef4bbc82a5c7819e262a530a748caf285ff0cba891954f1e", + "119713cc83eeef", + }, + { + "fdf092993edbb7a0dc7ca67f04051bbd14481639da0808947aff8bfab5abed4b", + "4a4f202484512526", + }, + { + "6f6fc234bf35beae1a366c44c520c59ad5aa70351b5f5085e21e1fe2bfcee709", + "1f66ab4185ed9b6375", + }, + { + "4fdaf89e2a0e78c000061b59455e0ea93a4445b440e7562c8f0cfa165c93de2e", + "eed7422227613b6f53c9", + }, + { + "d6b780eee9c811f664393dc2c58b5a68c92b3c9fe9ceb70371d33ece63b5787e", + "eaeed5cdffd89dece455f1", + }, + { + "d0015071d3e7ed048c764850d76406eceae52b8e2e6e5a2c3aa92ae880485b34", + "5be43c90f22902e4fe8ed2d3", + }, + { + "9b0207902f9932f7a85c24722e93e31f6ed2c75c406509aa0f2f6d1cab046ce4", + "a746273228122f381c3b46e4f1", + }, + { + "258020d5b04a814f2b72c1c661e1f5a5c395d9799e5eee8b8519cf7300e90cb1", + "3c5871cd619c69a63b540eb5a625", + }, + { + "4adae3b55baa907fefc253365fdd99d8398befd0551ed6bf9a2a2784d3c304d1", + "fa22874bcc068879e8ef11a69f0722", + }, + { + "6dd10d772f8d5b4a96c3c5d30878cd9a1073fa835bfe6d2b924fa64a1fab1711", + "52a608ab21ccdd8a4457a57ede782176", + }, + { + "0b8741ddf2259d3af2901eb1ae354f22836442c965556f5c1eb89501191cb46a", + "82e192e4043ddcd12ecf52969d0f807eed", + }, + { + "f48a754ca8193a82643150ab94038b5dd170b4ebd1e0751b78cfb0a98fa5076a", + "75683dcb556140c522543bb6e9098b21a21e", + }, + { + "5698409ab856b74d9fa5e9b259dfa46001f89041752da424e56e491577b88c86", + "06e4efe45035e61faaf4287b4d8d1f12ca97e5", + }, + }; + + uint8_t hash[BLAKE256_DIGEST_LENGTH]; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t len = strlen(tests[i].data) / 2; + blake256(fromhex(tests[i].data), len, hash); + ck_assert_mem_eq(hash, fromhex(tests[i].hash), BLAKE256_DIGEST_LENGTH); + + // Test progressive hashing. + size_t part_len = len / 2; + BLAKE256_CTX ctx; + blake256_Init(&ctx); + blake256_Update(&ctx, fromhex(tests[i].data), part_len); + blake256_Update(&ctx, NULL, 0); + blake256_Update(&ctx, fromhex(tests[i].data) + part_len, len - part_len); + blake256_Final(&ctx, hash); + ck_assert_mem_eq(hash, fromhex(tests[i].hash), BLAKE256_DIGEST_LENGTH); + } +} +END_TEST + +// test vectors from +// https://raw.githubusercontent.com/BLAKE2/BLAKE2/master/testvectors/blake2b-kat.txt +START_TEST(test_blake2b) { + static const struct { + const char *msg; + const char *hash; + } tests[] = { + { + "", + "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e9" + "96e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568", + }, + { + "000102", + "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e" + "364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f3031323334353637", + "f8f3726ac5a26cc80132493a6fedcb0e60760c09cfc84cad178175986819665e7684" + "2d7b9fedf76dddebf5d3f56faaad4477587af21606d396ae570d8e719af2", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243" + "4445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465" + "666768696a6b6c6d6e6f", + "227e3aed8d2cb10b918fcb04f9de3e6d0a57e08476d93759cd7b2ed54a1cbf0239c5" + "28fb04bbf288253e601d3bc38b21794afef90b17094a182cac557745e75f", + }, + }; + + uint8_t key[BLAKE2B_KEY_LENGTH]; + memcpy(key, + fromhex( + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2" + "02122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"), + BLAKE2B_KEY_LENGTH); + + uint8_t digest[BLAKE2B_DIGEST_LENGTH]; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t msg_len = strlen(tests[i].msg) / 2; + blake2b_Key(fromhex(tests[i].msg), msg_len, key, sizeof(key), digest, + sizeof(digest)); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), sizeof(digest)); + + // Test progressive hashing. + size_t part_len = msg_len / 2; + BLAKE2B_CTX ctx; + ck_assert_int_eq(blake2b_InitKey(&ctx, sizeof(digest), key, sizeof(key)), + 0); + ck_assert_int_eq(blake2b_Update(&ctx, fromhex(tests[i].msg), part_len), 0); + ck_assert_int_eq(blake2b_Update(&ctx, NULL, 0), 0); + ck_assert_int_eq(blake2b_Update(&ctx, fromhex(tests[i].msg) + part_len, + msg_len - part_len), + 0); + ck_assert_int_eq(blake2b_Final(&ctx, digest, sizeof(digest)), 0); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), BLAKE2B_DIGEST_LENGTH); + } +} +END_TEST + +// Blake2b-256 personalized, a la ZCash +// Test vectors from https://zips.z.cash/zip-0243 +START_TEST(test_blake2bp) { + static const struct { + const char *msg; + const char *personal; + const char *hash; + } tests[] = { + { + "", + "ZcashPrevoutHash", + "d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136", + }, + { + "", + "ZcashSequencHash", + "a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272", + + }, + { + "e7719811893e0000095200ac6551ac636565b2835a0805750200025151", + "ZcashOutputsHash", + "ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a", + }, + { + "0bbe32a598c22adfb48cef72ba5d4287c0cefbacfd8ce195b4963c34a94bba7a1" + "75dae4b090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b" + "63396e2b41d", + "ZcashPrevoutHash", + "cacf0f5210cce5fa65a59f314292b3111d299e7d9d582753cf61e1e408552ae4", + }}; + + uint8_t digest[32]; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t msg_len = strlen(tests[i].msg) / 2; + + // Test progressive hashing. + size_t part_len = msg_len / 2; + BLAKE2B_CTX ctx; + ck_assert_int_eq( + blake2b_InitPersonal(&ctx, sizeof(digest), tests[i].personal, + strlen(tests[i].personal)), + 0); + ck_assert_int_eq(blake2b_Update(&ctx, fromhex(tests[i].msg), part_len), 0); + ck_assert_int_eq(blake2b_Update(&ctx, NULL, 0), 0); + ck_assert_int_eq(blake2b_Update(&ctx, fromhex(tests[i].msg) + part_len, + msg_len - part_len), + 0); + ck_assert_int_eq(blake2b_Final(&ctx, digest, sizeof(digest)), 0); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), sizeof(digest)); + } +} +END_TEST + +// test vectors from +// https://raw.githubusercontent.com/BLAKE2/BLAKE2/master/testvectors/blake2s-kat.txt +START_TEST(test_blake2s) { + static const struct { + const char *msg; + const char *hash; + } tests[] = { + { + "", + "48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49", + }, + { + "000102", + "1d220dbe2ee134661fdf6d9e74b41704710556f2f6e5a091b227697445dbea6b", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f3031323334353637", + "2966b3cfae1e44ea996dc5d686cf25fa053fb6f67201b9e46eade85d0ad6b806", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243" + "4445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465" + "666768696a6b6c6d6e6f", + "90a83585717b75f0e9b725e055eeeeb9e7a028ea7e6cbc07b20917ec0363e38c", + }, + }; + + uint8_t key[BLAKE2S_KEY_LENGTH]; + memcpy( + key, + fromhex( + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + BLAKE2S_KEY_LENGTH); + + uint8_t digest[BLAKE2S_DIGEST_LENGTH]; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t msg_len = strlen(tests[i].msg) / 2; + blake2s_Key(fromhex(tests[i].msg), msg_len, key, sizeof(key), digest, + sizeof(digest)); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), sizeof(digest)); + + // Test progressive hashing. + size_t part_len = msg_len / 2; + BLAKE2S_CTX ctx; + ck_assert_int_eq(blake2s_InitKey(&ctx, sizeof(digest), key, sizeof(key)), + 0); + ck_assert_int_eq(blake2s_Update(&ctx, fromhex(tests[i].msg), part_len), 0); + ck_assert_int_eq(blake2s_Update(&ctx, NULL, 0), 0); + ck_assert_int_eq(blake2s_Update(&ctx, fromhex(tests[i].msg) + part_len, + msg_len - part_len), + 0); + ck_assert_int_eq(blake2s_Final(&ctx, digest, sizeof(digest)), 0); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), BLAKE2S_DIGEST_LENGTH); + } +} +END_TEST + +#include + +START_TEST(test_chacha_drbg) { + char entropy[] = + "06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d"; + char nonce[] = "0e66f71edc43e42a45ad3c6fc6cdc4df"; + char reseed[] = + "01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"; + char expected[] = + "e172c5d18f3e8c77e9f66f9e1c24560772117161a9a0a237ab490b0769ad5d910f5dfb36" + "22edc06c18be0495c52588b200893d90fd80ff2149ead0c45d062c90f5890149c0f9591c" + "41bf4110865129a0fe524f210cca1340bd16f71f57906946cbaaf1fa863897d70d203b5a" + "f9996f756eec08861ee5875f9d915adcddc38719"; + uint8_t result[128]; + uint8_t null_bytes[128] = {0}; + + uint8_t nonce_bytes[16]; + memcpy(nonce_bytes, fromhex(nonce), sizeof(nonce_bytes)); + CHACHA_DRBG_CTX ctx; + chacha_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2, nonce_bytes, + strlen(nonce) / 2); + chacha_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2, NULL, 0); + chacha_drbg_generate(&ctx, result, sizeof(result)); + chacha_drbg_generate(&ctx, result, sizeof(result)); + ck_assert_mem_eq(result, fromhex(expected), sizeof(result)); + + for (size_t i = 0; i <= sizeof(result); ++i) { + chacha_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2, nonce_bytes, + strlen(nonce) / 2); + chacha_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2, NULL, 0); + chacha_drbg_generate(&ctx, result, sizeof(result) - 13); + memset(result, 0, sizeof(result)); + chacha_drbg_generate(&ctx, result, i); + ck_assert_mem_eq(result, fromhex(expected), i); + ck_assert_mem_eq(result + i, null_bytes, sizeof(result) - i); + } +} +END_TEST + +START_TEST(test_pbkdf2_hmac_sha256) { + uint8_t k[64]; + + // test vectors from + // https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors + pbkdf2_hmac_sha256((const uint8_t *)"password", 8, (const uint8_t *)"salt", 4, + 1, k, 32); + ck_assert_mem_eq( + k, + fromhex( + "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b"), + 32); + + pbkdf2_hmac_sha256((const uint8_t *)"password", 8, (const uint8_t *)"salt", 4, + 2, k, 32); + ck_assert_mem_eq( + k, + fromhex( + "ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43"), + 32); + + pbkdf2_hmac_sha256((const uint8_t *)"password", 8, (const uint8_t *)"salt", 4, + 4096, k, 32); + ck_assert_mem_eq( + k, + fromhex( + "c5e478d59288c841aa530db6845c4c8d962893a001ce4e11a4963873aa98134a"), + 32); + + pbkdf2_hmac_sha256((const uint8_t *)"passwordPASSWORDpassword", 3 * 8, + (const uint8_t *)"saltSALTsaltSALTsaltSALTsaltSALTsalt", + 9 * 4, 4096, k, 40); + ck_assert_mem_eq(k, + fromhex("348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4" + "e2a1fb8dd53e1c635518c7dac47e9"), + 40); + + pbkdf2_hmac_sha256((const uint8_t *)"pass\x00word", 9, + (const uint8_t *)"sa\x00lt", 5, 4096, k, 16); + ck_assert_mem_eq(k, fromhex("89b69d0516f829893c696226650a8687"), 16); + + // test vector from https://tools.ietf.org/html/rfc7914.html#section-11 + pbkdf2_hmac_sha256((const uint8_t *)"passwd", 6, (const uint8_t *)"salt", 4, + 1, k, 64); + ck_assert_mem_eq( + k, + fromhex( + "55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca" + "9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783"), + 64); +} +END_TEST + +// test vectors from +// http://stackoverflow.com/questions/15593184/pbkdf2-hmac-sha-512-test-vectors +START_TEST(test_pbkdf2_hmac_sha512) { + uint8_t k[64]; + + pbkdf2_hmac_sha512((uint8_t *)"password", 8, (const uint8_t *)"salt", 4, 1, k, + 64); + ck_assert_mem_eq( + k, + fromhex( + "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d" + "470a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce"), + 64); + + pbkdf2_hmac_sha512((uint8_t *)"password", 8, (const uint8_t *)"salt", 4, 2, k, + 64); + ck_assert_mem_eq( + k, + fromhex( + "e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76c" + "ab2868a39b9f7840edce4fef5a82be67335c77a6068e04112754f27ccf4e"), + 64); + + pbkdf2_hmac_sha512((uint8_t *)"password", 8, (const uint8_t *)"salt", 4, 4096, + k, 64); + ck_assert_mem_eq( + k, + fromhex( + "d197b1b33db0143e018b12f3d1d1479e6cdebdcc97c5c0f87f6902e072f457b5143f" + "30602641b3d55cd335988cb36b84376060ecd532e039b742a239434af2d5"), + 64); + + pbkdf2_hmac_sha512((uint8_t *)"passwordPASSWORDpassword", 3 * 8, + (const uint8_t *)"saltSALTsaltSALTsaltSALTsaltSALTsalt", + 9 * 4, 4096, k, 64); + ck_assert_mem_eq( + k, + fromhex( + "8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868c005174dc4ee71115b" + "59f9e60cd9532fa33e0f75aefe30225c583a186cd82bd4daea9724a3d3b8"), + 64); +} +END_TEST + +START_TEST(test_hmac_drbg) { + char entropy[] = + "06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d"; + char nonce[] = "0e66f71edc43e42a45ad3c6fc6cdc4df"; + char reseed[] = + "01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"; + char expected[] = + "76fc79fe9b50beccc991a11b5635783a83536add03c157fb30645e611c2898bb2b1bc215" + "000209208cd506cb28da2a51bdb03826aaf2bd2335d576d519160842e7158ad0949d1a9e" + "c3e66ea1b1a064b005de914eac2e9d4f2d72a8616a80225422918250ff66a41bd2f864a6" + "a38cc5b6499dc43f7f2bd09e1e0f8f5885935124"; + uint8_t result[128]; + uint8_t null_bytes[128] = {0}; + + uint8_t nonce_bytes[16]; + memcpy(nonce_bytes, fromhex(nonce), sizeof(nonce_bytes)); + HMAC_DRBG_CTX ctx; + hmac_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2, nonce_bytes, + strlen(nonce) / 2); + hmac_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2, NULL, 0); + hmac_drbg_generate(&ctx, result, sizeof(result)); + hmac_drbg_generate(&ctx, result, sizeof(result)); + ck_assert_mem_eq(result, fromhex(expected), sizeof(result)); + + for (size_t i = 0; i <= sizeof(result); ++i) { + hmac_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2, nonce_bytes, + strlen(nonce) / 2); + hmac_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2, NULL, 0); + hmac_drbg_generate(&ctx, result, sizeof(result) - 13); + memset(result, 0, sizeof(result)); + hmac_drbg_generate(&ctx, result, i); + ck_assert_mem_eq(result, fromhex(expected), i); + ck_assert_mem_eq(result + i, null_bytes, sizeof(result) - i); + } +} +END_TEST + +START_TEST(test_mnemonic) { + static const char *vectors[] = { + "00000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon about", + "c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a698" + "7599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank " + "yellow", + "2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe12" + "96106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607", + "80808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice " + "cage above", + "d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12" + "eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8", + "ffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a1333257291" + "7f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069", + "000000000000000000000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon agent", + "035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a" + "565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank " + "year wave sausage worth useful legal will", + "f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c39" + "2d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd", + "808080808080808080808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice " + "cage absurd amount doctor acoustic avoid letter always", + "107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ff" + "b796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65", + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo " + "when", + "0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b433" + "48d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528", + "0000000000000000000000000000000000000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon art", + "bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d" + "73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank " + "year wave sausage worth useful legal winner thank year wave sausage " + "worth title", + "bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad" + "717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87", + "8080808080808080808080808080808080808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice " + "cage absurd amount doctor acoustic avoid letter advice cage absurd " + "amount doctor acoustic bless", + "c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af" + "0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo " + "zoo zoo zoo zoo zoo vote", + "dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a" + "5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad", + "77c2b00716cec7213839159e404db50d", + "jelly better achieve collect unaware mountain thought cargo oxygen act " + "hood bridge", + "b5b6d0127db1a9d2226af0c3346031d77af31e918dba64287a1b44b8ebf63cdd52676f67" + "2a290aae502472cf2d602c051f3e6f18055e84e4c43897fc4e51a6ff", + "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b", + "renew stay biology evidence goat welcome casual join adapt armor " + "shuffle fault little machine walk stumble urge swap", + "9248d83e06f4cd98debf5b6f010542760df925ce46cf38a1bdb4e4de7d21f5c39366941c" + "69e1bdbf2966e0f6e6dbece898a0e2f0a4c2b3e640953dfe8b7bbdc5", + "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982", + "dignity pass list indicate nasty swamp pool script soccer toe leaf " + "photo multiply desk host tomato cradle drill spread actor shine dismiss " + "champion exotic", + "ff7f3184df8696d8bef94b6c03114dbee0ef89ff938712301d27ed8336ca89ef9635da20" + "af07d4175f2bf5f3de130f39c9d9e8dd0472489c19b1a020a940da67", + "0460ef47585604c5660618db2e6a7e7f", + "afford alter spike radar gate glance object seek swamp infant panel " + "yellow", + "65f93a9f36b6c85cbe634ffc1f99f2b82cbb10b31edc7f087b4f6cb9e976e9faf76ff41f" + "8f27c99afdf38f7a303ba1136ee48a4c1e7fcd3dba7aa876113a36e4", + "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f", + "indicate race push merry suffer human cruise dwarf pole review arch " + "keep canvas theme poem divorce alter left", + "3bbf9daa0dfad8229786ace5ddb4e00fa98a044ae4c4975ffd5e094dba9e0bb289349dbe" + "2091761f30f382d4e35c4a670ee8ab50758d2c55881be69e327117ba", + "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416", + "clutch control vehicle tonight unusual clog visa ice plunge glimpse " + "recipe series open hour vintage deposit universe tip job dress radar " + "refuse motion taste", + "fe908f96f46668b2d5b37d82f558c77ed0d69dd0e7e043a5b0511c48c2f1064694a956f8" + "6360c93dd04052a8899497ce9e985ebe0c8c52b955e6ae86d4ff4449", + "eaebabb2383351fd31d703840b32e9e2", + "turtle front uncle idea crush write shrug there lottery flower risk " + "shell", + "bdfb76a0759f301b0b899a1e3985227e53b3f51e67e3f2a65363caedf3e32fde42a66c40" + "4f18d7b05818c95ef3ca1e5146646856c461c073169467511680876c", + "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78", + "kiss carry display unusual confirm curtain upgrade antique rotate hello " + "void custom frequent obey nut hole price segment", + "ed56ff6c833c07982eb7119a8f48fd363c4a9b1601cd2de736b01045c5eb8ab4f57b0794" + "03485d1c4924f0790dc10a971763337cb9f9c62226f64fff26397c79", + "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef", + "exile ask congress lamp submit jacket era scheme attend cousin alcohol " + "catch course end lucky hurt sentence oven short ball bird grab wing top", + "095ee6f817b4c2cb30a5a797360a81a40ab0f9a4e25ecd672a3f58a0b5ba0687c096a6b1" + "4d2c0deb3bdefce4f61d01ae07417d502429352e27695163f7447a8c", + "18ab19a9f54a9274f03e5209a2ac8a91", + "board flee heavy tunnel powder denial science ski answer betray cargo " + "cat", + "6eff1bb21562918509c73cb990260db07c0ce34ff0e3cc4a8cb3276129fbcb300bddfe00" + "5831350efd633909f476c45c88253276d9fd0df6ef48609e8bb7dca8", + "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4", + "board blade invite damage undo sun mimic interest slam gaze truly " + "inherit resist great inject rocket museum chief", + "f84521c777a13b61564234bf8f8b62b3afce27fc4062b51bb5e62bdfecb23864ee6ecf07" + "c1d5a97c0834307c5c852d8ceb88e7c97923c0a3b496bedd4e5f88a9", + "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419", + "beyond stage sleep clip because twist token leaf atom beauty genius " + "food business side grid unable middle armed observe pair crouch tonight " + "away coconut", + "b15509eaa2d09d3efd3e006ef42151b30367dc6e3aa5e44caba3fe4d3e352e65101fbdb8" + "6a96776b91946ff06f8eac594dc6ee1d3e82a42dfe1b40fef6bcc3fd", + 0, + 0, + 0, + }; + + const char **a, **b, **c, *m; + uint8_t seed[64]; + + a = vectors; + b = vectors + 1; + c = vectors + 2; + #define TC_BUF_SIZE 308 + char buf[TC_BUF_SIZE]; + + while (*a && *b && *c) { + m = mnemonic_from_data(fromhex(*a), strlen(*a) / 2, buf, TC_BUF_SIZE); + ck_assert_str_eq(m, *b); + mnemonic_to_seed(m, "TREZOR", seed, 0); + ck_assert_mem_eq(seed, fromhex(*c), strlen(*c) / 2); +#if USE_BIP39_CACHE + // try second time to check whether caching results work + mnemonic_to_seed(m, "TREZOR", seed, 0); + ck_assert_mem_eq(seed, fromhex(*c), strlen(*c) / 2); +#endif + a += 3; + b += 3; + c += 3; + memzero(buf, TC_BUF_SIZE ); +#undef TC_BUF_SIZE + } +} +END_TEST + +START_TEST(test_mnemonic_check) { + static const char *vectors_ok[] = { + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon about", + "legal winner thank year wave sausage worth useful legal winner thank " + "yellow", + "letter advice cage absurd amount doctor acoustic avoid letter advice " + "cage above", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon agent", + "legal winner thank year wave sausage worth useful legal winner thank " + "year wave sausage worth useful legal will", + "letter advice cage absurd amount doctor acoustic avoid letter advice " + "cage absurd amount doctor acoustic avoid letter always", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo " + "when", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon art", + "legal winner thank year wave sausage worth useful legal winner thank " + "year wave sausage worth useful legal winner thank year wave sausage " + "worth title", + "letter advice cage absurd amount doctor acoustic avoid letter advice " + "cage absurd amount doctor acoustic avoid letter advice cage absurd " + "amount doctor acoustic bless", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo " + "zoo zoo zoo zoo zoo vote", + "jelly better achieve collect unaware mountain thought cargo oxygen act " + "hood bridge", + "renew stay biology evidence goat welcome casual join adapt armor " + "shuffle fault little machine walk stumble urge swap", + "dignity pass list indicate nasty swamp pool script soccer toe leaf " + "photo multiply desk host tomato cradle drill spread actor shine dismiss " + "champion exotic", + "afford alter spike radar gate glance object seek swamp infant panel " + "yellow", + "indicate race push merry suffer human cruise dwarf pole review arch " + "keep canvas theme poem divorce alter left", + "clutch control vehicle tonight unusual clog visa ice plunge glimpse " + "recipe series open hour vintage deposit universe tip job dress radar " + "refuse motion taste", + "turtle front uncle idea crush write shrug there lottery flower risk " + "shell", + "kiss carry display unusual confirm curtain upgrade antique rotate hello " + "void custom frequent obey nut hole price segment", + "exile ask congress lamp submit jacket era scheme attend cousin alcohol " + "catch course end lucky hurt sentence oven short ball bird grab wing top", + "board flee heavy tunnel powder denial science ski answer betray cargo " + "cat", + "board blade invite damage undo sun mimic interest slam gaze truly " + "inherit resist great inject rocket museum chief", + "beyond stage sleep clip because twist token leaf atom beauty genius " + "food business side grid unable middle armed observe pair crouch tonight " + "away coconut", + 0, + }; + static const char *vectors_fail[] = { + "above abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon about", + "above winner thank year wave sausage worth useful legal winner thank " + "yellow", + "above advice cage absurd amount doctor acoustic avoid letter advice " + "cage above", + "above zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "above abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon agent", + "above winner thank year wave sausage worth useful legal winner thank " + "year wave sausage worth useful legal will", + "above advice cage absurd amount doctor acoustic avoid letter advice " + "cage absurd amount doctor acoustic avoid letter always", + "above zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo " + "when", + "above abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon art", + "above winner thank year wave sausage worth useful legal winner thank " + "year wave sausage worth useful legal winner thank year wave sausage " + "worth title", + "above advice cage absurd amount doctor acoustic avoid letter advice " + "cage absurd amount doctor acoustic avoid letter advice cage absurd " + "amount doctor acoustic bless", + "above zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo " + "zoo zoo zoo zoo zoo zoo vote", + "above better achieve collect unaware mountain thought cargo oxygen act " + "hood bridge", + "above stay biology evidence goat welcome casual join adapt armor " + "shuffle fault little machine walk stumble urge swap", + "above pass list indicate nasty swamp pool script soccer toe leaf photo " + "multiply desk host tomato cradle drill spread actor shine dismiss " + "champion exotic", + "above alter spike radar gate glance object seek swamp infant panel " + "yellow", + "above race push merry suffer human cruise dwarf pole review arch keep " + "canvas theme poem divorce alter left", + "above control vehicle tonight unusual clog visa ice plunge glimpse " + "recipe series open hour vintage deposit universe tip job dress radar " + "refuse motion taste", + "above front uncle idea crush write shrug there lottery flower risk " + "shell", + "above carry display unusual confirm curtain upgrade antique rotate " + "hello void custom frequent obey nut hole price segment", + "above ask congress lamp submit jacket era scheme attend cousin alcohol " + "catch course end lucky hurt sentence oven short ball bird grab wing top", + "above flee heavy tunnel powder denial science ski answer betray cargo " + "cat", + "above blade invite damage undo sun mimic interest slam gaze truly " + "inherit resist great inject rocket museum chief", + "above stage sleep clip because twist token leaf atom beauty genius food " + "business side grid unable middle armed observe pair crouch tonight away " + "coconut", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon about", + "winner thank year wave sausage worth useful legal winner thank yellow", + "advice cage absurd amount doctor acoustic avoid letter advice cage " + "above", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon agent", + "winner thank year wave sausage worth useful legal winner thank year " + "wave sausage worth useful legal will", + "advice cage absurd amount doctor acoustic avoid letter advice cage " + "absurd amount doctor acoustic avoid letter always", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon art", + "winner thank year wave sausage worth useful legal winner thank year " + "wave sausage worth useful legal winner thank year wave sausage worth " + "title", + "advice cage absurd amount doctor acoustic avoid letter advice cage " + "absurd amount doctor acoustic avoid letter advice cage absurd amount " + "doctor acoustic bless", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo " + "zoo zoo zoo zoo vote", + "better achieve collect unaware mountain thought cargo oxygen act hood " + "bridge", + "stay biology evidence goat welcome casual join adapt armor shuffle " + "fault little machine walk stumble urge swap", + "pass list indicate nasty swamp pool script soccer toe leaf photo " + "multiply desk host tomato cradle drill spread actor shine dismiss " + "champion exotic", + "alter spike radar gate glance object seek swamp infant panel yellow", + "race push merry suffer human cruise dwarf pole review arch keep canvas " + "theme poem divorce alter left", + "control vehicle tonight unusual clog visa ice plunge glimpse recipe " + "series open hour vintage deposit universe tip job dress radar refuse " + "motion taste", + "front uncle idea crush write shrug there lottery flower risk shell", + "carry display unusual confirm curtain upgrade antique rotate hello void " + "custom frequent obey nut hole price segment", + "ask congress lamp submit jacket era scheme attend cousin alcohol catch " + "course end lucky hurt sentence oven short ball bird grab wing top", + "flee heavy tunnel powder denial science ski answer betray cargo cat", + "blade invite damage undo sun mimic interest slam gaze truly inherit " + "resist great inject rocket museum chief", + "stage sleep clip because twist token leaf atom beauty genius food " + "business side grid unable middle armed observe pair crouch tonight away " + "coconut", + 0, + }; + + const char **m; + int r; + m = vectors_ok; + while (*m) { + r = mnemonic_check(*m); + ck_assert_int_eq(r, 1); + m++; + } + m = vectors_fail; + while (*m) { + r = mnemonic_check(*m); + ck_assert_int_eq(r, 0); + m++; + } +} +END_TEST + +START_TEST(test_mnemonic_to_bits) { + static const char *vectors[] = { + "00000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon about", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank " + "yellow", + "80808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice " + "cage above", + "ffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", + "000000000000000000000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon agent", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank " + "year wave sausage worth useful legal will", + "808080808080808080808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice " + "cage absurd amount doctor acoustic avoid letter always", + "ffffffffffffffffffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo " + "when", + "0000000000000000000000000000000000000000000000000000000000000000", + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon art", + "7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", + "legal winner thank year wave sausage worth useful legal winner thank " + "year wave sausage worth useful legal winner thank year wave sausage " + "worth title", + "8080808080808080808080808080808080808080808080808080808080808080", + "letter advice cage absurd amount doctor acoustic avoid letter advice " + "cage absurd amount doctor acoustic avoid letter advice cage absurd " + "amount doctor acoustic bless", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo " + "zoo zoo zoo zoo zoo vote", + "77c2b00716cec7213839159e404db50d", + "jelly better achieve collect unaware mountain thought cargo oxygen act " + "hood bridge", + "b63a9c59a6e641f288ebc103017f1da9f8290b3da6bdef7b", + "renew stay biology evidence goat welcome casual join adapt armor " + "shuffle fault little machine walk stumble urge swap", + "3e141609b97933b66a060dcddc71fad1d91677db872031e85f4c015c5e7e8982", + "dignity pass list indicate nasty swamp pool script soccer toe leaf " + "photo multiply desk host tomato cradle drill spread actor shine dismiss " + "champion exotic", + "0460ef47585604c5660618db2e6a7e7f", + "afford alter spike radar gate glance object seek swamp infant panel " + "yellow", + "72f60ebac5dd8add8d2a25a797102c3ce21bc029c200076f", + "indicate race push merry suffer human cruise dwarf pole review arch " + "keep canvas theme poem divorce alter left", + "2c85efc7f24ee4573d2b81a6ec66cee209b2dcbd09d8eddc51e0215b0b68e416", + "clutch control vehicle tonight unusual clog visa ice plunge glimpse " + "recipe series open hour vintage deposit universe tip job dress radar " + "refuse motion taste", + "eaebabb2383351fd31d703840b32e9e2", + "turtle front uncle idea crush write shrug there lottery flower risk " + "shell", + "7ac45cfe7722ee6c7ba84fbc2d5bd61b45cb2fe5eb65aa78", + "kiss carry display unusual confirm curtain upgrade antique rotate hello " + "void custom frequent obey nut hole price segment", + "4fa1a8bc3e6d80ee1316050e862c1812031493212b7ec3f3bb1b08f168cabeef", + "exile ask congress lamp submit jacket era scheme attend cousin alcohol " + "catch course end lucky hurt sentence oven short ball bird grab wing top", + "18ab19a9f54a9274f03e5209a2ac8a91", + "board flee heavy tunnel powder denial science ski answer betray cargo " + "cat", + "18a2e1d81b8ecfb2a333adcb0c17a5b9eb76cc5d05db91a4", + "board blade invite damage undo sun mimic interest slam gaze truly " + "inherit resist great inject rocket museum chief", + "15da872c95a13dd738fbf50e427583ad61f18fd99f628c417a61cf8343c90419", + "beyond stage sleep clip because twist token leaf atom beauty genius " + "food business side grid unable middle armed observe pair crouch tonight " + "away coconut", + 0, + 0, + }; + + const char **a, **b; + uint8_t mnemonic_bits[64]; + + a = vectors; + b = vectors + 1; + while (*a && *b) { + int mnemonic_bits_len = mnemonic_to_bits(*b, mnemonic_bits); + ck_assert_int_eq(mnemonic_bits_len % 33, 0); + mnemonic_bits_len = mnemonic_bits_len * 4 / 33; + ck_assert_uint_eq((size_t)mnemonic_bits_len, strlen(*a) / 2); + ck_assert_mem_eq(mnemonic_bits, fromhex(*a), mnemonic_bits_len); + a += 2; + b += 2; + } +} +END_TEST + +START_TEST(test_mnemonic_find_word) { + ck_assert_int_eq(-1, mnemonic_find_word("aaaa")); + ck_assert_int_eq(-1, mnemonic_find_word("zzzz")); + for (int i = 0; i < BIP39_WORD_COUNT; i++) { + const char *word = mnemonic_get_word(i); + int index = mnemonic_find_word(word); + ck_assert_int_eq(i, index); + } +} +END_TEST + +START_TEST(test_slip39_get_word) { + static const struct { + const int index; + const char *expected_word; + } vectors[] = {{573, "member"}, + {0, "academic"}, + {1023, "zero"}, + {245, "drove"}, + {781, "satoshi"}}; + for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) { + const char *a = get_word(vectors[i].index); + ck_assert_str_eq(a, vectors[i].expected_word); + } +} +END_TEST + +START_TEST(test_slip39_word_index) { + uint16_t index; + static const struct { + const char *word; + bool expected_result; + uint16_t expected_index; + } vectors[] = {{"academic", true, 0}, + {"zero", true, 1023}, + {"drove", true, 245}, + {"satoshi", true, 781}, + {"member", true, 573}, + // 9999 value is never checked since the word is not in list + {"fakeword", false, 9999}}; + for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) { + bool result = word_index(&index, vectors[i].word, strlen(vectors[i].word)); + ck_assert_int_eq(result, vectors[i].expected_result); + if (result) { + ck_assert_uint_eq(index, vectors[i].expected_index); + } + } +} +END_TEST + +START_TEST(test_slip39_word_completion_mask) { + static const struct { + const uint16_t prefix; + const uint16_t expected_mask; + } vectors[] = { + {12, 0xFD}, // 011111101 + {21, 0xF8}, // 011111000 + {75, 0xAD}, // 010101101 + {4, 0x1F7}, // 111110111 + {738, 0x6D}, // 001101101 + {9, 0x6D}, // 001101101 + {0, 0x1FF}, // 111111111 + {10, 0x00}, // 000000000 + {255, 0x00}, // 000000000 + {203, 0x00}, // 000000000 + {9999, 0x00}, // 000000000 + {20000, 0x00}, // 000000000 + }; + for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) { + uint16_t mask = slip39_word_completion_mask(vectors[i].prefix); + ck_assert_uint_eq(mask, vectors[i].expected_mask); + } +} +END_TEST + +START_TEST(test_slip39_sequence_to_word) { + static const struct { + const uint16_t prefix; + const char *expected_word; + } vectors[] = { + {7945, "swimming"}, {646, "pipeline"}, {5, "laden"}, {34, "fiber"}, + {62, "ocean"}, {0, "academic"}, {10, NULL}, {255, NULL}, + {203, NULL}, {9999, NULL}, {20000, NULL}, + }; + for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) { + const char *word = button_sequence_to_word(vectors[i].prefix); + if (vectors[i].expected_word != NULL) { + ck_assert_str_eq(word, vectors[i].expected_word); + } else { + ck_assert_ptr_eq(word, NULL); + } + } +} +END_TEST + +START_TEST(test_slip39_word_completion) { + const char t9[] = {1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9}; + for (size_t i = 0; i < WORDS_COUNT; ++i) { + const char *word = slip39_wordlist[i]; + uint16_t prefix = t9[word[0] - 'a']; + for (size_t j = 1; j < 4; ++j) { + uint16_t mask = slip39_word_completion_mask(prefix); + uint8_t next = t9[word[j] - 'a']; + ck_assert_uint_ne(mask & (1 << (next - 1)), 0); + prefix = prefix * 10 + next; + } + ck_assert_str_eq(button_sequence_to_word(prefix), word); + } +} +END_TEST + +START_TEST(test_shamir) { +#define SHAMIR_MAX_COUNT 16 + static const struct { + const uint8_t result[SHAMIR_MAX_LEN]; + uint8_t result_index; + const uint8_t share_indices[SHAMIR_MAX_COUNT]; + const uint8_t share_values[SHAMIR_MAX_COUNT][SHAMIR_MAX_LEN]; + uint8_t share_count; + size_t len; + bool ret; + } vectors[] = {{{7, 151, 168, 57, 186, 104, 218, 21, 209, 96, 106, + 152, 252, 35, 210, 208, 43, 47, 13, 21, 142, 122, + 24, 42, 149, 192, 95, 24, 240, 24, 148, 110}, + 0, + {2}, + { + {7, 151, 168, 57, 186, 104, 218, 21, 209, 96, 106, + 152, 252, 35, 210, 208, 43, 47, 13, 21, 142, 122, + 24, 42, 149, 192, 95, 24, 240, 24, 148, 110}, + }, + 1, + 32, + true}, + + {{53}, + 255, + {14, 10, 1, 13, 8, 7, 3, 11, 9, 4, 6, 0, 5, 12, 15, 2}, + { + {114}, + {41}, + {116}, + {67}, + {198}, + {109}, + {232}, + {39}, + {90}, + {241}, + {156}, + {75}, + {46}, + {181}, + {144}, + {175}, + }, + 16, + 1, + true}, + + {{91, 188, 226, 91, 254, 197, 225}, + 1, + {5, 1, 10}, + { + {129, 18, 104, 86, 236, 73, 176}, + {91, 188, 226, 91, 254, 197, 225}, + {69, 53, 151, 204, 224, 37, 19}, + }, + 3, + 7, + true}, + + {{0}, + 1, + {5, 1, 1}, + { + {129, 18, 104, 86, 236, 73, 176}, + {91, 188, 226, 91, 254, 197, 225}, + {69, 53, 151, 204, 224, 37, 19}, + }, + 3, + 7, + false}, + + {{0}, + 255, + {3, 12, 3}, + { + {100, 176, 99, 142, 115, 192, 138}, + {54, 139, 99, 172, 29, 137, 58}, + {216, 119, 222, 40, 87, 25, 147}, + }, + 3, + 7, + false}, + + {{163, 120, 30, 243, 179, 172, 196, 137, 119, 17}, + 3, + {1, 0, 12}, + {{80, 180, 198, 131, 111, 251, 45, 181, 2, 242}, + {121, 9, 79, 98, 132, 164, 9, 165, 19, 230}, + {86, 52, 173, 138, 189, 223, 122, 102, 248, 157}}, + 3, + 10, + true}}; + + for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); ++i) { + uint8_t result[SHAMIR_MAX_LEN]; + const uint8_t *share_values[SHAMIR_MAX_COUNT]; + for (size_t j = 0; j < vectors[i].share_count; ++j) { + share_values[j] = vectors[i].share_values[j]; + } + ck_assert_int_eq(shamir_interpolate(result, vectors[i].result_index, + vectors[i].share_indices, share_values, + vectors[i].share_count, vectors[i].len), + vectors[i].ret); + if (vectors[i].ret == true) { + ck_assert_mem_eq(result, vectors[i].result, vectors[i].len); + } + } +} +END_TEST + +START_TEST(test_address) { + char address[36]; + uint8_t pub_key[65]; + + memcpy( + pub_key, + fromhex( + "0226659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37"), + 33); + ecdsa_get_address(pub_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "139MaMHp3Vjo8o4x8N1ZLWEtovLGvBsg6s"); + ecdsa_get_address(pub_key, 111, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "mhfJsQNnrXB3uuYZqvywARTDfuvyjg4RBh"); + ecdsa_get_address(pub_key, 52, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "MxiimznnxsqMfLKTQBL8Z2PoY9jKpjgkCu"); + ecdsa_get_address(pub_key, 48, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "LMNJqZbe89yrPbm7JVzrcXJf28hZ1rKPaH"); + ecdsa_get_address(pub_key, 36, HASHER_SHA2_RIPEMD, HASHER_GROESTLD_TRUNC, + address, sizeof(address)); + ck_assert_str_eq(address, "FXK52G2BbzRLaQ651U12o23DU5cEQdhvU6"); + ecdsa_get_address_segwit_p2sh(pub_key, 5, HASHER_SHA2_RIPEMD, HASHER_SHA2D, + address, sizeof(address)); + ck_assert_str_eq(address, "34PyTHn74syS796eTgsyoLfwoBC3cwLn6p"); + + memcpy( + pub_key, + fromhex( + "025b1654a0e78d28810094f6c5a96b8efb8a65668b578f170ac2b1f83bc63ba856"), + 33); + ecdsa_get_address(pub_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "19Ywfm3witp6C1yBMy4NRYHY2347WCRBfQ"); + ecdsa_get_address(pub_key, 111, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "mp4txp8vXvFLy8So5Y2kFTVrt2epN6YzdP"); + ecdsa_get_address(pub_key, 52, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "N58JsQYveGueiZDgdnNwe4SSkGTAToutAY"); + ecdsa_get_address(pub_key, 48, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "LTmtvyMmoZ49SpfLY73fhZMJEFRPdyohKh"); + ecdsa_get_address(pub_key, 36, HASHER_SHA2_RIPEMD, HASHER_GROESTLD_TRUNC, + address, sizeof(address)); + ck_assert_str_eq(address, "Fdif7fnKHPVddczJF53qt45rgCL51yWN6x"); + ecdsa_get_address_segwit_p2sh(pub_key, 5, HASHER_SHA2_RIPEMD, HASHER_SHA2D, + address, sizeof(address)); + ck_assert_str_eq(address, "35trq6eeuHf6VL9L8pQv46x3vegHnHoTuB"); + + memcpy( + pub_key, + fromhex( + "03433f246a12e6486a51ff08802228c61cf895175a9b49ed4766ea9a9294a3c7fe"), + 33); + ecdsa_get_address(pub_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "1FWE2bn3MWhc4QidcF6AvEWpK77sSi2cAP"); + ecdsa_get_address(pub_key, 111, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "mv2BKes2AY8rqXCFKp4Yk9j9B6iaMfWRLN"); + ecdsa_get_address(pub_key, 52, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "NB5bEFH2GtoAawy8t4Qk8kfj3LWvQs3MhB"); + ecdsa_get_address(pub_key, 48, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "LZjBHp5sSAwfKDQnnP5UCFaaXKV9YheGxQ"); + ecdsa_get_address(pub_key, 36, HASHER_SHA2_RIPEMD, HASHER_GROESTLD_TRUNC, + address, sizeof(address)); + ck_assert_str_eq(address, "FjfwUWWQv1P9W1jkVM5eNkK8yGPq5XyZZy"); + ecdsa_get_address_segwit_p2sh(pub_key, 5, HASHER_SHA2_RIPEMD, HASHER_SHA2D, + address, sizeof(address)); + ck_assert_str_eq(address, "3456DYaKUWuY6RWWw8Hp5CftHLcQN29h9Y"); + + memcpy( + pub_key, + fromhex( + "03aeb03abeee0f0f8b4f7a5d65ce31f9570cef9f72c2dd8a19b4085a30ab033d48"), + 33); + ecdsa_get_address(pub_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "1yrZb8dhdevoqpUEGi2tUccUEeiMKeLcs"); + ecdsa_get_address(pub_key, 111, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "mgVoreDcWf6BaxJ5wqgQiPpwLEFRLSr8U8"); + ecdsa_get_address(pub_key, 52, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "MwZDmEdcd1kVLP4yW62c6zmXCU3mNbveDo"); + ecdsa_get_address(pub_key, 48, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "LLCopoSTnHtz4eWdQQhLAVgNgT1zTi4QBK"); + ecdsa_get_address(pub_key, 36, HASHER_SHA2_RIPEMD, HASHER_GROESTLD_TRUNC, + address, sizeof(address)); + ck_assert_str_eq(address, "FW9a1Vs1G8LUFSqb7NhWLzQw8PvfwAxmxA"); + ecdsa_get_address_segwit_p2sh(pub_key, 5, HASHER_SHA2_RIPEMD, HASHER_SHA2D, + address, sizeof(address)); + ck_assert_str_eq(address, "3DBU4tJ9tkMR9fnmCtjW48kjvseoNLQZXd"); + + memcpy( + pub_key, + fromhex( + "0496e8f2093f018aff6c2e2da5201ee528e2c8accbf9cac51563d33a7bb74a016054" + "201c025e2a5d96b1629b95194e806c63eb96facaedc733b1a4b70ab3b33e3a"), + 65); + ecdsa_get_address(pub_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "194SZbL75xCCGBbKtMsyWLE5r9s2V6mhVM"); + ecdsa_get_address(pub_key, 111, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "moaPreR5tydT3J4wbvrMLFSQi9TjPCiZc6"); + ecdsa_get_address(pub_key, 52, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "N4domEq61LHkniqqABCYirNzaPG5NRU8GH"); + ecdsa_get_address(pub_key, 48, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "LTHPpodwAcSFWzHV4VsGnMHr4NEJajMnKX"); + ecdsa_get_address(pub_key, 36, HASHER_SHA2_RIPEMD, HASHER_GROESTLD_TRUNC, + address, sizeof(address)); + ck_assert_str_eq(address, "FdEA1W4UeSsjhncSmTsSxr2QWK8z2xGkjc"); + + memcpy( + pub_key, + fromhex( + "0498010f8a687439ff497d3074beb4519754e72c4b6220fb669224749591dde416f3" + "961f8ece18f8689bb32235e436874d2174048b86118a00afbd5a4f33a24f0f"), + 65); + ecdsa_get_address(pub_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "1A2WfBD4BJFwYHFPc5KgktqtbdJLBuVKc4"); + ecdsa_get_address(pub_key, 111, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "mpYTxEJ2zKhCKPj1KeJ4ap4DTcu39T3uzD"); + ecdsa_get_address(pub_key, 52, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "N5bsrpi36gMW4pVtsteFyQzoKrhPE7nkxK"); + ecdsa_get_address(pub_key, 48, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "LUFTvPWtFxVzo5wYnDJz2uueoqfcMYiuxH"); + ecdsa_get_address(pub_key, 36, HASHER_SHA2_RIPEMD, HASHER_GROESTLD_TRUNC, + address, sizeof(address)); + ck_assert_str_eq(address, "FeCE75wRjnwUytGWVBKADQeDFnaHpJ8t3B"); + + memcpy( + pub_key, + fromhex( + "04f80490839af36d13701ec3f9eebdac901b51c362119d74553a3c537faff31b17e2" + "a59ebddbdac9e87b816307a7ed5b826b8f40b92719086238e1bebf19b77a4d"), + 65); + ecdsa_get_address(pub_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "19J81hrPnQxg9UGx45ibTieCkb2ttm8CLL"); + ecdsa_get_address(pub_key, 111, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "mop5JkwNbSPvvakZmegyHdrXcadbjLazww"); + ecdsa_get_address(pub_key, 52, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "N4sVDMMNho4Eg1XTKu3AgEo7UpRwq3aNbn"); + ecdsa_get_address(pub_key, 48, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "LTX5GvADs5CjQGy7EDhtjjhxxoQB2Uhicd"); + ecdsa_get_address(pub_key, 36, HASHER_SHA2_RIPEMD, HASHER_GROESTLD_TRUNC, + address, sizeof(address)); + ck_assert_str_eq(address, "FdTqTcamLueDb5J4wBi4vESXQkJrS54H6k"); +} +END_TEST + +START_TEST(test_pubkey_validity) { + uint8_t pub_key[65]; + curve_point pub; + int res; + const ecdsa_curve *curve = &secp256k1; + + memcpy( + pub_key, + fromhex( + "0226659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37"), + 33); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 1); + + memcpy( + pub_key, + fromhex( + "025b1654a0e78d28810094f6c5a96b8efb8a65668b578f170ac2b1f83bc63ba856"), + 33); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 1); + + memcpy( + pub_key, + fromhex( + "03433f246a12e6486a51ff08802228c61cf895175a9b49ed4766ea9a9294a3c7fe"), + 33); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 1); + + memcpy( + pub_key, + fromhex( + "03aeb03abeee0f0f8b4f7a5d65ce31f9570cef9f72c2dd8a19b4085a30ab033d48"), + 33); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 1); + + memcpy( + pub_key, + fromhex( + "0496e8f2093f018aff6c2e2da5201ee528e2c8accbf9cac51563d33a7bb74a016054" + "201c025e2a5d96b1629b95194e806c63eb96facaedc733b1a4b70ab3b33e3a"), + 65); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 1); + + memcpy( + pub_key, + fromhex( + "0498010f8a687439ff497d3074beb4519754e72c4b6220fb669224749591dde416f3" + "961f8ece18f8689bb32235e436874d2174048b86118a00afbd5a4f33a24f0f"), + 65); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 1); + + memcpy( + pub_key, + fromhex( + "04f80490839af36d13701ec3f9eebdac901b51c362119d74553a3c537faff31b17e2" + "a59ebddbdac9e87b816307a7ed5b826b8f40b92719086238e1bebf19b77a4d"), + 65); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 1); + + memcpy( + pub_key, + fromhex( + "04f80490839af36d13701ec3f9eebdac901b51c362119d74553a3c537faff31b17e2" + "a59ebddbdac9e87b816307a7ed5b826b8f40b92719086238e1bebf00000000"), + 65); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 0); + + memcpy( + pub_key, + fromhex( + "04f80490839af36d13701ec3f9eebdac901b51c362119d74553a3c537faff31b17e2" + "a59ebddbdac9e87b816307a7ed5b8211111111111111111111111111111111"), + 65); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 0); + + memcpy(pub_key, fromhex("00"), 1); + res = ecdsa_read_pubkey(curve, pub_key, &pub); + ck_assert_int_eq(res, 0); +} +END_TEST + +START_TEST(test_pubkey_uncompress) { + uint8_t pub_key[65]; + uint8_t uncompressed[65]; + int res; + const ecdsa_curve *curve = &secp256k1; + + memcpy( + pub_key, + fromhex( + "0226659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37"), + 33); + res = ecdsa_uncompress_pubkey(curve, pub_key, uncompressed); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq( + uncompressed, + fromhex( + "0426659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37b3" + "cfbad6b39a8ce8cb3a675f53b7b57e120fe067b8035d771fd99e3eba7cf4de"), + 65); + + memcpy( + pub_key, + fromhex( + "03433f246a12e6486a51ff08802228c61cf895175a9b49ed4766ea9a9294a3c7fe"), + 33); + res = ecdsa_uncompress_pubkey(curve, pub_key, uncompressed); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq( + uncompressed, + fromhex( + "04433f246a12e6486a51ff08802228c61cf895175a9b49ed4766ea9a9294a3c7feeb" + "4c25bcb840f720a16e8857a011e6b91e0ab2d03dbb5f9762844bb21a7b8ca7"), + 65); + + memcpy( + pub_key, + fromhex( + "0496e8f2093f018aff6c2e2da5201ee528e2c8accbf9cac51563d33a7bb74a016054" + "201c025e2a5d96b1629b95194e806c63eb96facaedc733b1a4b70ab3b33e3a"), + 65); + res = ecdsa_uncompress_pubkey(curve, pub_key, uncompressed); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq( + uncompressed, + fromhex( + "0496e8f2093f018aff6c2e2da5201ee528e2c8accbf9cac51563d33a7bb74a016054" + "201c025e2a5d96b1629b95194e806c63eb96facaedc733b1a4b70ab3b33e3a"), + 65); + + memcpy(pub_key, fromhex("00"), 1); + res = ecdsa_uncompress_pubkey(curve, pub_key, uncompressed); + ck_assert_int_eq(res, 0); +} +END_TEST + +START_TEST(test_wif) { + uint8_t priv_key[32]; + char wif[53]; + + memcpy( + priv_key, + fromhex( + "1111111111111111111111111111111111111111111111111111111111111111"), + 32); + ecdsa_get_wif(priv_key, 0x80, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, "KwntMbt59tTsj8xqpqYqRRWufyjGunvhSyeMo3NTYpFYzZbXJ5Hp"); + ecdsa_get_wif(priv_key, 0xEF, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, "cN9spWsvaxA8taS7DFMxnk1yJD2gaF2PX1npuTpy3vuZFJdwavaw"); + + memcpy( + priv_key, + fromhex( + "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"), + 32); + ecdsa_get_wif(priv_key, 0x80, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, "L4ezQvyC6QoBhxB4GVs9fAPhUKtbaXYUn8YTqoeXwbevQq4U92vN"); + ecdsa_get_wif(priv_key, 0xEF, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, "cV1ysqy3XUVSsPeKeugH2Utm6ZC1EyeArAgvxE73SiJvfa6AJng7"); + + memcpy( + priv_key, + fromhex( + "47f7616ea6f9b923076625b4488115de1ef1187f760e65f89eb6f4f7ff04b012"), + 32); + ecdsa_get_wif(priv_key, 0x80, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, "KydbzBtk6uc7M6dXwEgTEH2sphZxSPbmDSz6kUUHi4eUpSQuhEbq"); + ecdsa_get_wif(priv_key, 0xEF, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, "cPzbT6tbXyJNWY6oKeVabbXwSvsN6qhTHV8ZrtvoDBJV5BRY1G5Q"); +} +END_TEST + +START_TEST(test_address_decode) { + int res; + uint8_t decode[MAX_ADDR_RAW_SIZE]; + + res = ecdsa_address_decode("1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T", 0, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("00c4c5d791fcb4654a1ef5e03fe0ad3d9c598f9827"), 21); + + res = ecdsa_address_decode("myTPjxggahXyAzuMcYp5JTkbybANyLsYBW", 111, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("6fc4c5d791fcb4654a1ef5e03fe0ad3d9c598f9827"), 21); + + res = ecdsa_address_decode("NEWoeZ6gh4CGvRgFAoAGh4hBqpxizGT6gZ", 52, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("34c4c5d791fcb4654a1ef5e03fe0ad3d9c598f9827"), 21); + + res = ecdsa_address_decode("LdAPi7uXrLLmeh7u57pzkZc3KovxEDYRJq", 48, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("30c4c5d791fcb4654a1ef5e03fe0ad3d9c598f9827"), 21); + + res = ecdsa_address_decode("1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8", 0, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("0079fbfc3f34e7745860d76137da68f362380c606c"), 21); + + res = ecdsa_address_decode("mrdwvWkma2D6n9mGsbtkazedQQuoksnqJV", 111, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("6f79fbfc3f34e7745860d76137da68f362380c606c"), 21); + + res = ecdsa_address_decode("N7hMq7AmgNsQXaYARrEwybbDGei9mcPNqr", 52, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("3479fbfc3f34e7745860d76137da68f362380c606c"), 21); + + res = ecdsa_address_decode("LWLwtfycqf1uFqypLAug36W4kdgNwrZdNs", 48, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("3079fbfc3f34e7745860d76137da68f362380c606c"), 21); + + // invalid char + res = ecdsa_address_decode("1JwSSubhmg6i000jtyqhUYYH7bZg3Lfy1T", 0, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 0); + + // invalid address + res = ecdsa_address_decode("1111Subhmg6iPtRjtyqhUYYH7bZg3Lfy1T", 0, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 0); + + // invalid version + res = ecdsa_address_decode("LWLwtfycqf1uFqypLAug36W4kdgNwrZdNs", 0, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 0); +} +END_TEST + +START_TEST(test_ecdsa_der) { + static const struct { + const char *r; + const char *s; + const char *der; + } vectors[] = { + { + "9a0b7be0d4ed3146ee262b42202841834698bb3ee39c24e7437df208b8b70771", + "2b79ab1e7736219387dffe8d615bbdba87e11477104b867ef47afed1a5ede781", + "30450221009a0b7be0d4ed3146ee262b42202841834698bb3ee39c24e7437df208b8" + "b7077102202b79ab1e7736219387dffe8d615bbdba87e11477104b867ef47afed1a5" + "ede781", + }, + { + "6666666666666666666666666666666666666666666666666666666666666666", + "7777777777777777777777777777777777777777777777777777777777777777", + "30440220666666666666666666666666666666666666666666666666666666666666" + "66660220777777777777777777777777777777777777777777777777777777777777" + "7777", + }, + { + "6666666666666666666666666666666666666666666666666666666666666666", + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "30450220666666666666666666666666666666666666666666666666666666666666" + "6666022100eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + "eeeeee", + }, + { + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "7777777777777777777777777777777777777777777777777777777777777777", + "3045022100eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + "eeeeee02207777777777777777777777777777777777777777777777777777777777" + "777777", + }, + { + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "3046022100eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + "eeeeee022100ffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "ffffffff", + }, + { + "0000000000000000000000000000000000000000000000000000000000000066", + "0000000000000000000000000000000000000000000000000000000000000077", + "3006020166020177", + }, + { + "0000000000000000000000000000000000000000000000000000000000000066", + "00000000000000000000000000000000000000000000000000000000000000ee", + "3007020166020200ee", + }, + { + "00000000000000000000000000000000000000000000000000000000000000ee", + "0000000000000000000000000000000000000000000000000000000000000077", + "3007020200ee020177", + }, + { + "00000000000000000000000000000000000000000000000000000000000000ee", + "00000000000000000000000000000000000000000000000000000000000000ff", + "3008020200ee020200ff", + }, + { + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "3006020100020100", + }, + }; + + uint8_t sig[64]; + uint8_t der[72]; + uint8_t out[72]; + for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); ++i) { + size_t der_len = strlen(vectors[i].der) / 2; + memcpy(der, fromhex(vectors[i].der), der_len); + memcpy(sig, fromhex(vectors[i].r), 32); + memcpy(sig + 32, fromhex(vectors[i].s), 32); + ck_assert_int_eq(ecdsa_sig_to_der(sig, out), der_len); + ck_assert_mem_eq(out, der, der_len); + ck_assert_int_eq(ecdsa_sig_from_der(der, der_len, out), 0); + ck_assert_mem_eq(out, sig, 64); + } +} +END_TEST + +static void test_codepoints_curve(const ecdsa_curve *curve) { + int i, j; + bignum256 a; + curve_point p, p1; + for (i = 0; i < 64; i++) { + for (j = 0; j < 8; j++) { + bn_zero(&a); + a.val[(4 * i) / BN_BITS_PER_LIMB] = (uint32_t)(2 * j + 1) + << (4 * i % BN_BITS_PER_LIMB); + bn_normalize(&a); + // note that this is not a trivial test. We add 64 curve + // points in the table to get that particular curve point. + scalar_multiply(curve, &a, &p); + ck_assert_mem_eq(&p, &curve->cp[i][j], sizeof(curve_point)); + bn_zero(&p.y); // test that point_multiply curve, is not a noop + point_multiply(curve, &a, &curve->G, &p); + ck_assert_mem_eq(&p, &curve->cp[i][j], sizeof(curve_point)); + // mul 2 test. this should catch bugs + bn_lshift(&a); + bn_mod(&a, &curve->order); + p1 = curve->cp[i][j]; + point_double(curve, &p1); + // note that this is not a trivial test. We add 64 curve + // points in the table to get that particular curve point. + scalar_multiply(curve, &a, &p); + ck_assert_mem_eq(&p, &p1, sizeof(curve_point)); + bn_zero(&p.y); // test that point_multiply curve, is not a noop + point_multiply(curve, &a, &curve->G, &p); + ck_assert_mem_eq(&p, &p1, sizeof(curve_point)); + } + } +} + +START_TEST(test_codepoints_secp256k1) { test_codepoints_curve(&secp256k1); } +END_TEST +START_TEST(test_codepoints_nist256p1) { test_codepoints_curve(&nist256p1); } +END_TEST + +static void test_mult_border_cases_curve(const ecdsa_curve *curve) { + bignum256 a; + curve_point p; + curve_point expected; + bn_zero(&a); // a == 0 + scalar_multiply(curve, &a, &p); + ck_assert(point_is_infinity(&p)); + point_multiply(curve, &a, &p, &p); + ck_assert(point_is_infinity(&p)); + point_multiply(curve, &a, &curve->G, &p); + ck_assert(point_is_infinity(&p)); + + bn_addi(&a, 1); // a == 1 + scalar_multiply(curve, &a, &p); + ck_assert_mem_eq(&p, &curve->G, sizeof(curve_point)); + point_multiply(curve, &a, &curve->G, &p); + ck_assert_mem_eq(&p, &curve->G, sizeof(curve_point)); + + bn_subtract(&curve->order, &a, &a); // a == -1 + expected = curve->G; + bn_subtract(&curve->prime, &expected.y, &expected.y); + scalar_multiply(curve, &a, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + point_multiply(curve, &a, &curve->G, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + + bn_subtract(&curve->order, &a, &a); + bn_addi(&a, 1); // a == 2 + expected = curve->G; + point_add(curve, &expected, &expected); + scalar_multiply(curve, &a, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + point_multiply(curve, &a, &curve->G, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + + bn_subtract(&curve->order, &a, &a); // a == -2 + expected = curve->G; + point_add(curve, &expected, &expected); + bn_subtract(&curve->prime, &expected.y, &expected.y); + scalar_multiply(curve, &a, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); + point_multiply(curve, &a, &curve->G, &p); + ck_assert_mem_eq(&p, &expected, sizeof(curve_point)); +} + +START_TEST(test_mult_border_cases_secp256k1) { + test_mult_border_cases_curve(&secp256k1); +} +END_TEST +START_TEST(test_mult_border_cases_nist256p1) { + test_mult_border_cases_curve(&nist256p1); +} +END_TEST + +static void test_scalar_mult_curve(const ecdsa_curve *curve) { + int i; + // get two "random" numbers + bignum256 a = curve->G.x; + bignum256 b = curve->G.y; + curve_point p1, p2, p3; + for (i = 0; i < 1000; i++) { + /* test distributivity: (a + b)G = aG + bG */ + bn_mod(&a, &curve->order); + bn_mod(&b, &curve->order); + scalar_multiply(curve, &a, &p1); + scalar_multiply(curve, &b, &p2); + bn_addmod(&a, &b, &curve->order); + bn_mod(&a, &curve->order); + scalar_multiply(curve, &a, &p3); + point_add(curve, &p1, &p2); + ck_assert_mem_eq(&p2, &p3, sizeof(curve_point)); + // new "random" numbers + a = p3.x; + b = p3.y; + } +} + +START_TEST(test_scalar_mult_secp256k1) { test_scalar_mult_curve(&secp256k1); } +END_TEST +START_TEST(test_scalar_mult_nist256p1) { test_scalar_mult_curve(&nist256p1); } +END_TEST + +static void test_point_mult_curve(const ecdsa_curve *curve) { + int i; + // get two "random" numbers and a "random" point + bignum256 a = curve->G.x; + bignum256 b = curve->G.y; + curve_point p = curve->G; + curve_point p1, p2, p3; + for (i = 0; i < 200; i++) { + /* test distributivity: (a + b)P = aP + bP */ + bn_mod(&a, &curve->order); + bn_mod(&b, &curve->order); + ck_assert_int_eq(point_multiply(curve, &a, &p, &p1), 0); + ck_assert_int_eq(point_multiply(curve, &b, &p, &p2), 0); + bn_addmod(&a, &b, &curve->order); + bn_mod(&a, &curve->order); + ck_assert_int_eq(point_multiply(curve, &a, &p, &p3), 0); + point_add(curve, &p1, &p2); + ck_assert_mem_eq(&p2, &p3, sizeof(curve_point)); + // new "random" numbers and a "random" point + a = p1.x; + b = p1.y; + p = p3; + } +} + +START_TEST(test_point_mult_secp256k1) { test_point_mult_curve(&secp256k1); } +END_TEST +START_TEST(test_point_mult_nist256p1) { test_point_mult_curve(&nist256p1); } +END_TEST + +static void test_scalar_point_mult_curve(const ecdsa_curve *curve) { + int i; + // get two "random" numbers + bignum256 a = curve->G.x; + bignum256 b = curve->G.y; + curve_point p1, p2; + for (i = 0; i < 200; i++) { + /* test commutativity and associativity: + * a(bG) = (ab)G = b(aG) + */ + bn_mod(&a, &curve->order); + bn_mod(&b, &curve->order); + ck_assert_int_eq(scalar_multiply(curve, &a, &p1), 0); + ck_assert_int_eq(point_multiply(curve, &b, &p1, &p1), 0); + + ck_assert_int_eq(scalar_multiply(curve, &b, &p2), 0); + ck_assert_int_eq(point_multiply(curve, &a, &p2, &p2), 0); + + ck_assert_mem_eq(&p1, &p2, sizeof(curve_point)); + + bn_multiply(&a, &b, &curve->order); + bn_mod(&b, &curve->order); + ck_assert_int_eq(scalar_multiply(curve, &b, &p2), 0); + + ck_assert_mem_eq(&p1, &p2, sizeof(curve_point)); + + // new "random" numbers + a = p1.x; + b = p1.y; + } +} + +START_TEST(test_scalar_point_mult_secp256k1) { + test_scalar_point_mult_curve(&secp256k1); +} +END_TEST +START_TEST(test_scalar_point_mult_nist256p1) { + test_scalar_point_mult_curve(&nist256p1); +} +END_TEST + +START_TEST(test_ed25519) { + // test vectors from + // https://github.com/torproject/tor/blob/master/src/test/ed25519_vectors.inc + static const char *vectors[] = { + "26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d3" + "6", // secret + "c2247870536a192d142d056abefca68d6193158e7c1a59c1654c954eccaff89" + "4", // public + "d23188eac3773a316d46006fa59c095060be8b1a23582a0dd99002a82a0662bd" + "246d8449e172e04c5f46ac0d1404cebe4aabd8a75a1457aa06cae41f3334f10" + "4", // selfsig + "fba7a5366b5cb98c2667a18783f5cf8f4f8d1a2ce939ad22a6e685edde85128" + "d", + "1519a3b15816a1aafab0b213892026ebf5c0dc232c58b21088d88cb90e9b940" + "d", + "3a785ac1201c97ee5f6f0d99323960d5f264c7825e61aa7cc81262f15bef75eb" + "4fa5723add9b9d45b12311b6d403eb3ac79ff8e4e631fc3cd51e4ad2185b200" + "b", + "67e3aa7a14fac8445d15e45e38a523481a69ae35513c9e4143eb1c2196729a0" + "e", + "081faa81992e360ea22c06af1aba096e7a73f1c665bc8b3e4e531c46455fd1d" + "d", + "cf431fd0416bfbd20c9d95ef9b723e2acddffb33900edc72195dea95965d52d8" + "88d30b7b8a677c0bd8ae1417b1e1a0ec6700deadd5d8b54b6689275e04a0450" + "9", + "d51385942033a76dc17f089a59e6a5a7fe80d9c526ae8ddd8c3a506b99d3d0a" + "6", + "73cfa1189a723aad7966137cbffa35140bb40d7e16eae4c40b79b5f0360dd65" + "a", + "2375380cd72d1a6c642aeddff862be8a5804b916acb72c02d9ed052c1561881a" + "a658a5af856fcd6d43113e42f698cd6687c99efeef7f2ce045824440d26c5d0" + "0", + "5c8eac469bb3f1b85bc7cd893f52dc42a9ab66f1b02b5ce6a68e9b175d3bb43" + "3", + "66c1a77104d86461b6f98f73acf3cd229c80624495d2d74d6fda1e940080a96" + "b", + "2385a472f599ca965bbe4d610e391cdeabeba9c336694b0d6249e551458280be" + "122c2441dd9746a81bbfb9cd619364bab0df37ff4ceb7aefd24469c39d3bc50" + "8", + "eda433d483059b6d1ff8b7cfbd0fe406bfb23722c8f3c8252629284573b61b8" + "6", + "d21c294db0e64cb2d8976625786ede1d9754186ae8197a64d72f68c792eecc1" + "9", + "e500cd0b8cfff35442f88008d894f3a2fa26ef7d3a0ca5714ae0d3e2d40caae5" + "8ba7cdf69dd126994dad6be536fcda846d89dd8138d1683cc144c8853dce760" + "7", + "4377c40431c30883c5fbd9bc92ae48d1ed8a47b81d13806beac5351739b5533" + "d", + "c4d58b4cf85a348ff3d410dd936fa460c4f18da962c01b1963792b9dcc8a6ea" + "6", + "d187b9e334b0050154de10bf69b3e4208a584e1a65015ec28b14bcc252cf84b8" + "baa9c94867daa60f2a82d09ba9652d41e8dde292b624afc8d2c26441b95e3c0" + "e", + "c6bbcce615839756aed2cc78b1de13884dd3618f48367a17597a16c1cd7a290" + "b", + "95126f14d86494020665face03f2d42ee2b312a85bc729903eb17522954a1c4" + "a", + "815213640a643d198bd056e02bba74e1c8d2d931643e84497adf3347eb485079" + "c9afe0afce9284cdc084946b561abbb214f1304ca11228ff82702185cf28f60" + "d", + 0, + 0, + 0, + }; + const char **ssk, **spk, **ssig; + ssk = vectors; + spk = vectors + 1; + ssig = vectors + 2; + ed25519_public_key pk; + ed25519_secret_key sk; + ed25519_signature sig; + while (*ssk && *spk && *ssig) { + memcpy(sk, fromhex(*ssk), 32); + MARK_SECRET_DATA(sk, sizeof(sk)); + + ed25519_publickey(sk, pk); + UNMARK_SECRET_DATA(pk, sizeof(pk)); + ck_assert_mem_eq(pk, fromhex(*spk), 32); + + ed25519_sign(pk, 32, sk, sig); + UNMARK_SECRET_DATA(sig, sizeof(sig)); + ck_assert_mem_eq(sig, fromhex(*ssig), 64); + + ssk += 3; + spk += 3; + ssig += 3; + + UNMARK_SECRET_DATA(sk, sizeof(sk)); + } +} +END_TEST + +// test vectors from +// https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/2.test-sign.dat +START_TEST(test_ed25519_keccak) { + static const struct { + const char *private_key; + const char *public_key; + const char *signature; + size_t length; + const char *data; + } tests[] = { + { + "abf4cf55a2b3f742d7543d9cc17f50447b969e6e06f5ea9195d428ab12b7318d", + "8a558c728c21c126181e5e654b404a45b4f0137ce88177435a69978cc6bec1f4", + "d9cec0cc0e3465fab229f8e1d6db68ab9cc99a18cb0435f70deb6100948576cd5c0a" + "a1feb550bdd8693ef81eb10a556a622db1f9301986827b96716a7134230c", + 41, + "8ce03cd60514233b86789729102ea09e867fc6d964dea8c2018ef7d0a2e0e24bf7e3" + "48e917116690b9", + }, + { + "6aa6dad25d3acb3385d5643293133936cdddd7f7e11818771db1ff2f9d3f9215", + "bbc8cbb43dda3ecf70a555981a351a064493f09658fffe884c6fab2a69c845c6", + "98bca58b075d1748f1c3a7ae18f9341bc18e90d1beb8499e8a654c65d8a0b4fbd2e0" + "84661088d1e5069187a2811996ae31f59463668ef0f8cb0ac46a726e7902", + 49, + "e4a92208a6fc52282b620699191ee6fb9cf04daf48b48fd542c5e43daa9897763a19" + "9aaa4b6f10546109f47ac3564fade0", + }, + { + "8e32bc030a4c53de782ec75ba7d5e25e64a2a072a56e5170b77a4924ef3c32a9", + "72d0e65f1ede79c4af0ba7ec14204e10f0f7ea09f2bc43259cd60ea8c3a087e2", + "ef257d6e73706bb04878875c58aa385385bf439f7040ea8297f7798a0ea30c1c5eff" + "5ddc05443f801849c68e98111ae65d088e726d1d9b7eeca2eb93b677860c", + 40, + "13ed795344c4448a3b256f23665336645a853c5c44dbff6db1b9224b5303b6447fbf" + "8240a2249c55", + }, + { + "c83ce30fcb5b81a51ba58ff827ccbc0142d61c13e2ed39e78e876605da16d8d7", + "3ec8923f9ea5ea14f8aaa7e7c2784653ed8c7de44e352ef9fc1dee81fc3fa1a3", + "0c684e71b35fed4d92b222fc60561db34e0d8afe44bdd958aaf4ee965911bef59912" + "36f3e1bced59fc44030693bcac37f34d29e5ae946669dc326e706e81b804", + 49, + "a2704638434e9f7340f22d08019c4c8e3dbee0df8dd4454a1d70844de11694f4c8ca" + "67fdcb08fed0cec9abb2112b5e5f89", + }, + { + "2da2a0aae0f37235957b51d15843edde348a559692d8fa87b94848459899fc27", + "d73d0b14a9754eec825fcb25ef1cfa9ae3b1370074eda53fc64c22334a26c254", + "6f17f7b21ef9d6907a7ab104559f77d5a2532b557d95edffd6d88c073d87ac00fc83" + "8fc0d05282a0280368092a4bd67e95c20f3e14580be28d8b351968c65e03", + 40, + "d2488e854dbcdfdb2c9d16c8c0b2fdbc0abb6bac991bfe2b14d359a6bc99d66c00fd" + "60d731ae06d0", + }, + { + "0c066261fb1b18ebf2a9bcdeda81eb47d5a3745438b3d0b9d19b75885ad0a154", + "2e5773f0e725024bc0359ce93a44e15d6507e7b160b6c592200385fee4a269cf", + "13b5d2dd1b04f62cc2ec1544fed256423684f2dbca4538ceddda1d15c59dc7196c87" + "840ea303ea30f4f6914a6ec9167841980c1d717f47fd641225068de88507", + 41, + "f15cb706e29fcfbcb324e38cbac62bb355deddb845c142e970f0c029ea4d05e59fd6" + "adf85573cf1775", + }, + { + "ef3d8e22a592f04c3a31aa736e10901757a821d053f1a49a525b4ec91eacdee3", + "72a2b4910a502b30e13a96aba643c59c79328c1ba1462be6f254e817ef157fee", + "95f2437a0210d2d2f125a3c377ed666c0d596cd104185e70204924a182a11a6eb3bd" + "ba4395bbfc3f4e827d38805752657ee52d1ce0f17e70f59bfd4999282509", + 50, + "6c3e4387345740b8d62cf0c9dec48f98c292539431b2b54020d8072d9cb55f0197f7" + "d99ff066afcf9e41ea8b7aea78eb082d", + }, + { + "f7fb79743e9ba957d2a4f1bd95ceb1299552abecaf758bf840d2dc2c09f3e3cb", + "8b7d7531280f76a8abac8293d87508e3953894087112ae01b6ad32485d4e9b67", + "c868ecf31cee783fe8799ac7e6a662431c822967351d8b79687f4ddf608f79a080c4" + "ff9eed4fdee8c99fe1be905f734cae2a172f1cfdb00771625c0695a5260e", + 42, + "55d8e60c307ee533b1af9ff677a2de40a6eace722bcc9eb5d79907b420e533bc06db" + "674dafbd9f43d672", + }, + { + "8cc9a2469a77fad18b44b871b2b6932cd354641d2d1e84403f746c4fff829791", + "aed5da202d4983dac560faf6704dc76ac111616318570e244043e82ed1bbcd2b", + "aee9616db4135150818eaffa3e4503c2d7e9e834847a4c7d0a8856e952761d361a65" + "7104d36950c9b75770ded00d56a96e06f383fa2406bc935dcf51f272300e", + 42, + "d9b8be2f71b83261304e333d6e35563dc3c36c2eb5a23e1461b6e95aa7c6f381f9c3" + "bd39deaa1b6df2f9", + }, + { + "a247abbef0c1affbf021d1aff128888550532fc0edd77bc39f6ef5312317ec47", + "98ededbad1e5ad7a0d5a0cf4fcd7a794eb5c6900a65e7e921884a636f19b131d", + "f8cc02933851432f0c5df0b70f2067f740ccb72de7d6fa1e9a9b0d6de1402b9c6c52" + "5fd848e45aaaac1423b52880ec3474a2f64b38db6fc8e008d95a310e6e0c", + 47, + "4a5f07eb713932532fc3132c96efdc45862fe7a954c1d2ae4640afdf4728fb58c65e" + "8a4ebfe0d53d5797d5146442b9", + }, + { + "163d69079ddad1f16695c47d81c3b72f869b2fdd50e6e47113db6c85051a6ede", + "93fe602642ee5773f4aaf6a3bc21e98e354035225353f419e78e43c3ec36c88a", + "da747fa2cb47aae1effc1e4cfde0e39fa79937948592a712a7665bf948b8311e7f3f" + "80f966301679520d5c2afa3eadd60e061f0d264887500d8d03a17e10fd02", + 41, + "65fe5c1a0214a59644892e5ac4216f09fbb4e191b89bfb63d6540177d25ef9e37148" + "50b8453bd6b2b6", + }, + { + "7b061bf90eb760971b9ec66a96fd6609635ca4b531f33e3c126b9ae6fdb3d491", + "cb392ebb6912df4111efeeb1278160daf9da396e9291b83979a5ac479f7276d2", + "f6eebe86f7ea672e0707ee518e1798d6fbd118c11b2aa30be07d10e3882e3721f203" + "0f9f044b77c3a7a9a2f1feba7e7ce75d1f7f3807a96a764fded35d341d02", + 45, + "a17f5ce39b9ba7b7cf1147e515d6aa84b22fd0e2d8323a91367198fc6c3aff04ebb2" + "1fc2bdbe7bc0364e8040a9", + }, + { + "c9f8ccbf761cec00ab236c52651e76b5f46d90f8936d44d40561ed5c277104de", + "a3192641e343b669ffd43677c2e5cd4efaed174e876141f1d773bd6cfe30d875", + "d44f884ec9eae2e99e74194b5acc769b7aa369aaad359e92ba6ff0fe629af2a9a715" + "6c19b720e7de8c7f03c039563f160948073cab6f99b26a56a8bb1023ba08", + 47, + "3d7e33b0ecead8269966e9dcd192b73eb8a12573fc8a5fdfbe5753541026ef2e49f5" + "280cba9bc2515a049b3a1c1b49", + }, + { + "ebfa409ac6f987df476858dd35310879bf564eeb62984a52115d2e6c24590124", + "7bb1601fe7215f3f4da9c8ab5e804dc58f57ba41b03223f57ec80d9c9a2dd0e1", + "f3e7c1abfcc9f35556cb1e4c5a2b34445177ac188312d9148f1d1d8467ea8411fa3c" + "da031d023034e45bbe407ef7d1b937bfb098266138857d35cb4efe407306", + 52, + "0c37564f718eda683aa6f3e9ab2487620b1a8b5c8f20adb3b2d7550af0d635371e53" + "1f27cebe76a2abcc96de0875bdae987a45ac", + }, + { + "f993f61902b7da332f2bb001baa7accaf764d824eb0cd073315f7ec43158b8fb", + "55fc8e0da1b454cab6ddefb235311db2b01504bf9ac3f71c7e3f3d0d1f09f80b", + "178bd147673c0ca330e45da63cbd1f1811906bd5284bb44e4bb00f7d7163d1f39697" + "5610b6f71c1ae4686466fad4c5e7bb9685099e21ca4f1a45bb3fcf56ae0c", + 42, + "b7dd613bc9c364d9eeb9a52636d72bc881dfc81a836b6537bbb928bff5b738313589" + "47ea9edea1570550", + }, + { + "05188c09c31b4bb63f0d49b47ccc1654c2aba907b8c6c0a82ee403e950169167", + "e096d808dfabe8e44eb74950199dadcd586f9de6b141a0ce85ab94b3d97866eb", + "669491c8eb7cedbbc0252f3eafb048b39a2a37f60ac87837777c72c879ac8b726c39" + "e10060750c2f539102999b71889746111bc5f71ec8c158cc81cf566aef03", + 44, + "bb8e22469d1c7f1d5418563e8781f69eccb56678bd36d8919f358c2778562ff6b50d" + "e916c12d44f1a778a7f3", + }, + { + "eabe57e1a916ebbffa4ba7abc7f23e83d4deb1338816cc1784d7495d92e98d0b", + "3aad275642f48a46ed1032f3de9f4053e0fd35cf217e065d2e4579c3683932f7", + "b2e9dac2c83942ca374f29c8eff5a30c377c3db3c1c645e593e524d17484e7705b11" + "f79573e2d63495fc3ce3bf216a209f0cb7bea477ae0f8bd297f193af8805", + 44, + "3f2c2d6682ee597f2a92d7e560ac53d5623550311a4939d68adfb904045ed8d215a9" + "fdb757a2368ea4d89f5f", + }, + { + "fef7b893b4b517fab68ca12d36b603bc00826bf3c9b31a05149642ae10bb3f55", + "b3fb891868708dfa5da5b9b5234058767ab42c117f12c3228c02a1976d1c0f83", + "6243e289314b7c7587802909a9be6173a916b36f9de1e164954dfe5d1ebd57c869a7" + "9552d770e13b51855502be6b15e7be42a3675298a81284df58e609b06503", + 47, + "38c69f884045cdbeebe4478fdbd1ccc6cf00a08d8a3120c74e7167d3a2e26a67a043" + "b8e5bd198f7b0ce0358cef7cf9", + }, + { + "16228bec9b724300a37e88e535fc1c58548d34d7148b57c226f2b3af974c1822", + "3c92423a8360c9a5d9a093730d72831bec4601dcadfe84de19fc8c8f91fc3d4b", + "6aebfa9a4294ec888d54bcb517fcb6821e4c16d2708a2afe701f431a28149ff4f139" + "f9d16a52a63f1f91baf4c8dea37710c73f25c263a8035a39cc118ad0280f", + 44, + "a3d7b122cd4431b396b20d8cc46cc73ed4a5253a44a76fc83db62cdc845a2bf7081d" + "069a857955a161cccf84", + }, + { + "2dc3f5f0a0bc32c6632534e1e8f27e59cbe0bf7617d31aff98098e974c828be7", + "b998a416edc28ded988dcacb1caf2bd96c87354b0d1eeccb6980e54a3104f21f", + "76a2ddfc4bea48c47e0c82bcbfee28a37c61ec626af39a468e643e0ef9f6533056a5" + "a0b44e64d614ba3c641a40e5b003a99463445ae2c3c8e1e9882092d74b07", + 42, + "bdae276d738b9758ea3d322b54fd12fe82b767e8d817d8ef3d41f78705748e28d15e" + "9c506962a1b85901", + }, + }; + + ed25519_secret_key private_key; + ed25519_public_key public_key; + ed25519_signature signature; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + nem_private_key(tests[i].private_key, private_key); + MARK_SECRET_DATA(private_key, sizeof(private_key)); + + ed25519_publickey_keccak(private_key, public_key); + UNMARK_SECRET_DATA(public_key, sizeof(public_key)); + ck_assert_mem_eq(public_key, fromhex(tests[i].public_key), 32); + + ed25519_sign_keccak(fromhex(tests[i].data), tests[i].length, private_key, + signature); + UNMARK_SECRET_DATA(signature, sizeof(signature)); + ck_assert_mem_eq(signature, fromhex(tests[i].signature), 64); + + UNMARK_SECRET_DATA(private_key, sizeof(private_key)); + } +} +END_TEST + + +START_TEST(test_ed25519_cosi) { +//win +#ifdef _MSC_VER + #define MAXN 10 +#else + const int MAXN = 10; +#endif + + ed25519_secret_key keys[MAXN]; + ed25519_public_key pubkeys[MAXN]; + ed25519_secret_key nonces[MAXN]; + ed25519_public_key Rs[MAXN]; + ed25519_cosi_signature sigs[MAXN]; + uint8_t msg[32]; + rfc6979_state rng; + int res; + + init_rfc6979( + fromhex( + "26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d36"), + fromhex( + "26659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37"), + NULL, &rng); + + for (int N = 1; N < 11; N++) { + ed25519_public_key pk; + ed25519_public_key R; + ed25519_signature sig; + /* phase 0: create priv/pubkeys and combine pubkeys */ + for (int j = 0; j < N; j++) { + generate_rfc6979(keys[j], &rng); + ed25519_publickey(keys[j], pubkeys[j]); + } + res = ed25519_cosi_combine_publickeys(pk, pubkeys, N); + ck_assert_int_eq(res, 0); + + generate_rfc6979(msg, &rng); + + /* phase 1: create nonces, commitments (R values) and combine commitments */ + for (int j = 0; j < N; j++) { + generate_rfc6979(nonces[j], &rng); + ed25519_publickey(nonces[j], Rs[j]); + } + res = ed25519_cosi_combine_publickeys(R, Rs, N); + ck_assert_int_eq(res, 0); + + MARK_SECRET_DATA(keys, sizeof(keys)); + /* phase 2: sign and combine signatures */ + for (int j = 0; j < N; j++) { + ed25519_cosi_sign(msg, sizeof(msg), keys[j], nonces[j], R, pk, sigs[j]); + } + UNMARK_SECRET_DATA(sigs, sizeof(sigs)); + + ed25519_cosi_combine_signatures(sig, R, sigs, N); + + /* check signature */ + res = ed25519_sign_open(msg, sizeof(msg), pk, sig); + ck_assert_int_eq(res, 0); + + UNMARK_SECRET_DATA(keys, sizeof(keys)); + } +} +END_TEST + +START_TEST(test_ed25519_modl_add) { + char tests[][3][65] = { + { + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + }, + + {"eef80ad5a9aad8b35b84f6a4eb3a7e2b222f403d455d8cdf40ad27e4cd5ae90a", + "0000000000000000000000000000000000000000000000000000000000000000", + "eef80ad5a9aad8b35b84f6a4eb3a7e2b222f403d455d8cdf40ad27e4cd5ae90a"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "eef80ad5a9aad8b35b84f6a4eb3a7e2b222f403d455d8cdf40ad27e4cd5ae90a", + "eef80ad5a9aad8b35b84f6a4eb3a7e2b222f403d455d8cdf40ad27e4cd5ae90a"}, + + {"0100000000000000000000000000000000000000000000000000000000000000", + "0200000000000000000000000000000000000000000000000000000000000000", + "0300000000000000000000000000000000000000000000000000000000000000"}, + + {"e3d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "0a00000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000"}, + + {"f7bb3bf42b3e58e2edd06f173fc7bfbc7aaf657217946b75648447101136aa08", + "3c16b013109cc27ff39805be2abe04ba4cd6a8526a1d3023047693e950936c06", + "33d2eb073cda1a62e16975d56985c476c7850ec581b19b9868fadaf961c9160f"}, + }; + + unsigned char buff[32]; + bignum256modm a = {0}, b = {0}, c = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a, fromhex(tests[i][0]), 32); + expand256_modm(b, fromhex(tests[i][1]), 32); + add256_modm(c, a, b); + contract256_modm(buff, c); + ck_assert_mem_eq(buff, fromhex(tests[i][2]), 32); + } +} +END_TEST + +START_TEST(test_ed25519_modl_neg) { + char tests[][2][65] = { + {"05d0f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "e803000000000000000000000000000000000000000000000000000000000000"}, + + {"4d4df45c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "a086010000000000000000000000000000000000000000000000000000000000"}, + + {"25958944a1b7d4073975ca48996a1d740d0ed98ceec366760c5358da681e9608", + "c83e6c1879ab3d509d272d5a458fc1a0f2f12673113c9989f3aca72597e16907"}, + + {"0100000000000000000000000000000000000000000000000000000000000000", + "ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010"}, + + {"ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "0100000000000000000000000000000000000000000000000000000000000000"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000"}, + }; + + unsigned char buff[32]; + bignum256modm a = {0}, b = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a, fromhex(tests[i][0]), 32); + neg256_modm(b, a); + contract256_modm((unsigned char *)buff, b); + ck_assert_mem_eq(buff, fromhex(tests[i][1]), 32); + } +} +END_TEST + +START_TEST(test_ed25519_modl_sub) { + char tests[][3][65] = { + { + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + }, + + {"eef80ad5a9aad8b35b84f6a4eb3a7e2b222f403d455d8cdf40ad27e4cd5ae90a", + "53732f60e51ee3a48d21d2d526548c0dadbb79a185678fd7710613d0e76aad0c", + "8859d1d1deee0767a4ff1b72a3e0d0327573c69bbff5fc07cfa61414e6ef3b0e"}, + + {"9d91e26dbe7a14fdca9f5b20d13e828dc8c1ffe03fe90136a6bba507436ce500", + "9ca406705ccce65eb8cbf63706d3df09fcc67216c0dc3990270731aacbb2e607", + "eec0d15a7c1140f6e8705c8ba9658198ccfa8cca7f0cc8a57eb4745d77b9fe08"}, + + {"eef80ad5a9aad8b35b84f6a4eb3a7e2b222f403d455d8cdf40ad27e4cd5ae90a", + "0000000000000000000000000000000000000000000000000000000000000000", + "eef80ad5a9aad8b35b84f6a4eb3a7e2b222f403d455d8cdf40ad27e4cd5ae90a"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "39897fbebf137a34572b014b0638ac0186d17874e3cc142ebdfe24327f5b8509", + "b44a769e5a4f98237f71f657d8c132137a2e878b1c33ebd14201dbcd80a47a06"}, + + {"0200000000000000000000000000000000000000000000000000000000000000", + "e3d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "0c00000000000000000000000000000000000000000000000000000000000000"}, + + {"e3d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "0800000000000000000000000000000000000000000000000000000000000000", + "dbd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010"}, + + {"ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "0000000000000000000000000000000000000000000000000000000000000000", + "ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "0100000000000000000000000000000000000000000000000000000000000000"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000010", + "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000000"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "ffffff3f00000000000000000000000000000000000000000000000000000010", + "eed3f51c1a631258d69cf7a2def9de1400000000000000000000000000000000"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000010"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "e75f947f11d49d25a137fac8757538a980dec23811235cf63c48ee6bc6e4ed03", + "067461dd088f74323565fdd96884a66b7f213dc7eedca309c3b71194391b120c"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "ecd3f55c1a631258d69cf7a2def9de140000000000000000000000000000ff0f", + "0100000000000000000000000000000000000000000000000000000000000100"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "edd3f55c1a631258d69cf7a2def9de140000000000000000000004000000ff0f", + "0000000000000000000000000000000000000000000000000000fcffffff0000"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "edd3f55c1a631258d69cf7a2def9de150000c0ffffffffffffffffffffffff0f", + "000000000000000000000000000000ffffff3f00000000000000000000000000"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "edd3f55c1a631258d69cf7a2def9de1200000000000000000000000000000110", + "edd3f55c1a631258d69cf7a2def9de160000000000000000000000000000ff0f"}, + + {"0000000000000000000000000000000000000000000000000000000000000000", + "edd3f55c1a631258d69cf7a2def9de1300000000000000000000000000000010", + "0000000000000000000000000000000100000000000000000000000000000000"}, + }; + + unsigned char buff[32]; + bignum256modm a = {0}, b = {0}, c = {0}; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a, fromhex(tests[i][0]), 32); + expand256_modm(b, fromhex(tests[i][1]), 32); + sub256_modm(c, a, b); + contract256_modm(buff, c); + ck_assert_mem_eq(buff, fromhex(tests[i][2]), 32); + } +} +END_TEST + +#if USE_MONERO + +START_TEST(test_ge25519_double_scalarmult_vartime2) { + char tests[][5][65] = { + {"c537208ed4985e66e9f7a35c9a69448a732ba93960bbbd2823604f7ae9e3ed08", + "365233e5af17c8888d5ce508787464f4642e91a6212b1b104e6c3769535601b1", + "a84f871580176708b4ac21843cb197ad96e8456034442b50859c83c5807b9901", + "f022360d1bce903fa3ac58ae42f997328b31f477b8d576a9f6d26fc1d08f14ea", + "bf25da82c6b210948b823ae48422a2dcd205d3c94842e68ac27e5cbeaa704ebc"}, + {"4abfabc0dda33588a98127ef3bfe724fed286395fe15932e898b5621661ea102", + "e5fd79d03f5df8edfc8def663dcb96bba6cadf857f2ae6f6f51f52f8d14079b7", + "4754c286b23e3c1b50054fe3937ebdc4ec01b28da5d05fb6111798b42fc5bf06", + "b7e7f9464b98de5bfcf6b02c1b7053cc359df407ad59d943523c6d2ee773b2f6", + "6d7d5f729bfa4882dbff8e477cd2b4c354ba347f10e7b178a24f3f16a4e0fec6"}, + {"19f2af4d04cb8181f1fe0d01fe9bb9ecc476c67ceb4a9830dae1bc7fe5fe3b04", + "d3c462f4f30991220387a1fbbd1ba1dc45ce058c70a8fb1475071e7b4f0fc463", + "577790e025c1fd2014db44a8d613c4e2ab1f248a4a6d14b5d39cbbafd7b20f06", + "1376c6837f131f6cd1a45b1056297d2314aa0ac5f7d581d2d878261eb3259b4d", + "ce790760ada87dd819b59e4f6765d836d346567ec34f02bbcfcae0585c1d758f"}, + {"cf209db9e7ee85f1e648924ec97edd86b56a833b25707519d4fbe64fd50e150a", + "804f0806087dc665a26230ed5fd44c062980ee182a6bd7dbdb33df018c983778", + "30d3c448cb08935309753b3051366f52328ca1d9a0b63c72b989edee0da32b0e", + "98e3c973a7e85b5eab8111521c66ca584bed5597f060ab0c6b5cdeece502ac48", + "2646276e1305396a1b2473690066011a39789570a09e10ce1a013c8f32cd5bea"}, + {"b0a0ffeea67b656c4c585ba58ff528a6f45d2f915db98e4a14a8ff17f27fc105", + "4fabe16274f6af526ee053028485db6acd13804e02dcdddccc4183a319ab9e1c", + "1e140bb08a936ac6b7437644ca0769f3c165c7aa5501d49f064a0346179b4008", + "68fc1be64fb68761542a655b8dbebf50980f1fbc1845528df8d8a06bf89a1495", + "7dab86994b47014efe38493fc2b62ffcead806da6e0d73c992db8cb5618a19dc"}, + {"0fee422c2294b06ca83bc3704384dffc580e7ff5921881e51a755e5f9b80af03", + "4359a663ead3f7ffc3a0ead5c3c2bde348017e7bfa620f21759c32e469a16dfe", + "532066e3eec29334fffc37b17178dfbac3bee15f7845f01449ddbaf5e57a7b0c", + "32e46c2fb99402837631c8175db31cdd334c145f922be9070d62e6d9c493c3ea", + "8c7b7d2d61cdb648960434d894787426a76d16dd46949c7aa4b85dcf1054b4d5"}, + {"3a712d5b7ceb5257dcf6e6bb06548de6ef3deba5d456cd91fc305a12b46b5d01", + "5e7da62e3ec42cf3e554639dd4d2006754ee6839b720cadba94a26b73b1665ee", + "2a518ecab17a2d9dde219c775bcf4f2306b190bef2dea34fb65b8e4dccc13405", + "3b5d66a4dfb068923b3bc21cc8b40b59e12f845e0b85a86d394db0fa310bf185", + "2ec17f1cc0be093e9cdb741a991c0f417230dea275cd7babdad35e949e250521"}, + {"5f815f2d65cef584c5e5d48b2d3d3e4cae310d70b328f88af6e9f63c52b4c90d", + "8a539a8c6b2339922b31cf4bc064f1fedeb3912fd89585d79dfcff2a60aee295", + "385f7132b72db04146b9e472736b32adfca29556b4775a743c18e2bfab939007", + "884aaf96d625968ddb2582922a87abca131272884c47f6b86890ebccf0a79d5b", + "a7afdaf24fe8472d8b89e95c3ce4a40bdf700af7cedee44ed3aa5ccca09839bd"}, + {"a043340d072df16a8ab5135f8c1d601bff14c5aba01b9212b886ad71fe164506", + "52f6de5fa0fae32d4020a54d395319509d6b92092a0bf849fb34e73f8e71fc99", + "37d7472d360164da29e6dcb8f9796976022571c5df4ddf7e30e9a579ba13d509", + "8c369e3fd5b1112e4437b1f09e987acca4966f2f8c5227eb15ace240a2c64cc7", + "fc795fe7baff5c3ac98366e6882f25874ea2b0a649d16f139e5c54ea47042a1e"}, + {"97a3268db03fa184c8cba020bf216fc789292fa9615a28962385b86870ffd70f", + "a76c215587022bb9252ece4c5afeb0e65b820834cd41ac76e6c062d3eea75dc6", + "8310271017154cbddf7005e24eb9a9a86777b3f42fa5e35095eafaac4eb24802", + "b822665c2406083c851ecaa91ea67aa740c057e7679b5755cee60a6c63f17fd6", + "f83e2444527056eba595d49bde40b2e8da76d2c145f203331d26e94560993fbc"}, + {"edaad13efad39f26298e86ba8d06a46e59122232c9529bd22f2f656595421e00", + "f38e56a79f5159eb3b581dea537ec12c9c6fac381b2cf6073e27fc621197cb62", + "1eea79485954b5958d9d5478f86133af1088806d923535d483b115ab23099a0f", + "b32c5e57d57db7a349f4ab845f12a5045c52b4a7a5bce7fd54a1a255b0118185", + "3bfb42b4ffd2c6cfc8cce9e4187dc6fbcaecd9d44a4ca1d2b68b97410bb25b81"}, + {"b15eaebe0fc83cb11d755a6f067b710204d4a59101078d8286454b652879080a", + "4667a2e61d9df1690f5c33c4168e480f7e26d2f0998168ebdc0a39712946f741", + "125379da1a88bfdf5b928f8795d3ea5415ef8c3d9106eb16934c3842873fd707", + "8727a692a25e38b1afa98e3dd5bf88815dec6d9810c1fd8a31b56b3de8630f1e", + "540883dde400b909e9955a276c20e13d99252ebe542750b8bfbbe5c3b87c51e3"}, + {"e42bdd4af3121bea644a90a76b2007615621ee5b842b9a74c4334ac309478706", + "6dc4ab715d3bb975ebfd0f08e2b6f3f39922d0121ae518a8f8d2952ea2fe0b5d", + "0285059b0095c97f4a50d43c7726c64c2830bf2b55dfa934ebba7ad71064dc07", + "f738c0a3cee31fd8f438f282aa6c823fccfa49cf7b5c86fbf9d56bf0394b6d8d", + "a1bd106841e55010decd95a170a1d0dd11780fd00759819e024b15ea3a83b4be"}, + {"5077c30fd08795dbdc7a230c050ca07e316fa3b040fd0dac45907036ab25dd0e", + "96f0897f000e49e2439a9166cab40ebc125a31b82851f0541516c19683e7bfaf", + "2b67d79a2efdc6451508e7f3c97c4a61b135bb839c02338bb444ef8208dd970b", + "7ef4cd7cdc29c2b88ccff49898b5d0b7be5993f93c5772476feec9dc57d7b6e3", + "62449b901b25760c964704b28efc184fbd5947e83851ebaf3bbfeb6f742f679f"}, + {"a4b3ce6928fe8f77d13e65ae255eee8310ab0d75bca47028b4570f0511a66006", + "4e9da8d77ee337e3bcce3730ccfff2121728641c7bb4fdeb2155890f998af09a", + "ff01a5075569d0f6afee45da065c72f5841f46ce772917ef75eb4d230473580f", + "36ca32da8a10f4083f5a60ee21868d9d448548d49c56f19cbe6005005e34f816", + "99df362a3b762cc1cbb70bc5ddff3c8614ed306037013102e387ef32e7f2494f"}, + {"074aa76351dceb752aa09887d9aca932d5821f58eedb4988fd64d8548e3f2c09", + "588b4552f3b98b2f77aee2ef8cc72f88acd424c4373b3e3626393ed2ea24cbda", + "f2d9175633f2e3c661b01172b4b4176850cd5b3098ffb0f927e0a5e19c1c8a02", + "a6c34868736b2517fd46f57a4e30805ffd475e44a8b1413078f43d9cb3d6edd6", + "46e1e7d7b1e939dd5c07c8363af01f4f9dae7c3d10f237ff9776ddc4a1903771"}, + {"ae1c8abd5a542208ee0aa93ffbf0b8e5a957edc4854fe2b48153c5c85bbf3d08", + "5e084b9541a70bd5bef400be6525c5a806a5b7fb12de38b07dcd35a22c57edbe", + "d95f179a215fb322d81720bf3aecde78d6d676d6f941455d0e0920f1e3619707", + "c3e5d43221824de51d8f95705de69c80a2440c0483ca88549d639aee15390429", + "df9fea42d3b5ac243244abb4ca4948a69493becddc5d5906f9a4e4c5645b0eab"}, + {"2f1c5adedb7341dc7638bafacc6024bd48255197ea2347fc05714b9341dd4403", + "47f55263001542f796c928988f641f59d0cd43294fc8d8616b184bfe9dddf368", + "aa5e884e782ab116151c609680c37b1a49b52f23bce5e2ebf28dd8532510d20b", + "ef2d6d97ad1a18edfce6450c1e70295b2c7ed2bc749ea8b438a523eae078d1f3", + "2396a355c6ae8e2ac24da8f55a674c96fc4cc69b38678b2bd8eb91b96f462bca"}, + {"0242e14105ced74e91cf4d4dcd22a9c09279018901d2fb8319eb54c2a1c4900a", + "fcb62a6c520d31fa46efeb4a1000330653b3402f575c2ddc0c688f527e7b97be", + "73a7e2e0602e5345f040dedc4db67f6d8e37c5fca3bbb124fa43963d76dbbb08", + "152bf4a3305c656f77e292b1256cc470da4d3f6efc3667199db4316d7f431174", + "c21ba2080013dfb225e06378d9ac27df623df552526cfddbf9e71bb1d4705dd9"}, + {"07fab4fc7b02fbcf868ffb0326cf60425fef2af1fbad83a8926cc62c2b5dff05", + "29ff12c5e052eb5829e8334e0e082c5edde1f293d2b4ed499a79bcca20e48010", + "97afb3dd9167877b432a23503aad1ab39188b9be07cc124ceb3fbdbd8d8b890a", + "ed121240a2f4591eeedbfd880305ccd17e522673900b03279fb66e73583514ae", + "b27f209e88ce5701766565e231e8123adb1df9c9f1dc461920acbc2b38d9f6d7"}, + }; + + unsigned char buff[32]; + bignum256modm a = {0}, b = {0}; + ge25519 A, B, R; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + expand256_modm(a, fromhex(tests[i][0]), 32); + expand256_modm(b, fromhex(tests[i][2]), 32); + ge25519_unpack_negative_vartime(&A, fromhex(tests[i][1])); + curve25519_neg(A.x, A.x); + curve25519_neg(A.t, A.t); + ge25519_unpack_negative_vartime(&B, fromhex(tests[i][3])); + curve25519_neg(B.x, B.x); + curve25519_neg(B.t, B.t); + ge25519_double_scalarmult_vartime2(&R, &A, a, &B, b); + ge25519_pack(buff, &R); + ck_assert_mem_eq(buff, fromhex(tests[i][4]), 32); + } +} +END_TEST + +#endif + +static void test_bip32_ecdh_init_node(HDNode *node, const char *seed_str, + const char *curve_name) { + hdnode_from_seed((const uint8_t *)seed_str, strlen(seed_str), curve_name, + node); + ck_assert_int_eq(hdnode_fill_public_key(node), 0); + if (node->public_key[0] == 1) { + node->public_key[0] = 0x40; // Curve25519 public keys start with 0x40 byte + } +} + +static void test_bip32_ecdh(const char *curve_name, int expected_key_size, + const uint8_t *expected_key) { + #ifdef _MSC_VER + uint8_t * session_key1 = (uint8_t *)malloc(sizeof(uint8_t) * expected_key_size); + uint8_t * session_key2 = (uint8_t *)malloc(sizeof(uint8_t) * expected_key_size); + + int res, key_size; + HDNode alice, bob; + + test_bip32_ecdh_init_node(&alice, "Alice", curve_name); + test_bip32_ecdh_init_node(&bob, "Bob", curve_name); + // Generate shared key from Alice's secret key and Bob's public key + res = hdnode_get_shared_key(&alice, bob.public_key, session_key1, &key_size); + free (session_key1); + free (session_key2); + + ck_assert_int_eq(res, 0); + ck_assert_int_eq(key_size, expected_key_size); + ck_assert_mem_eq(session_key1, expected_key, key_size); + + // Generate shared key from Bob's secret key and Alice's public key + res = hdnode_get_shared_key(&bob, alice.public_key, session_key2, &key_size); + ck_assert_int_eq(res, 0); + ck_assert_int_eq(key_size, expected_key_size); + ck_assert_mem_eq(session_key2, expected_key, key_size); + #else + int res, key_size; + HDNode alice, bob; + uint8_t session_key1[expected_key_size], session_key2[expected_key_size]; + + test_bip32_ecdh_init_node(&alice, "Alice", curve_name); + test_bip32_ecdh_init_node(&bob, "Bob", curve_name); + + // Generate shared key from Alice's secret key and Bob's public key + res = hdnode_get_shared_key(&alice, bob.public_key, session_key1, &key_size); + ck_assert_int_eq(res, 0); + ck_assert_int_eq(key_size, expected_key_size); + ck_assert_mem_eq(session_key1, expected_key, key_size); + + // Generate shared key from Bob's secret key and Alice's public key + res = hdnode_get_shared_key(&bob, alice.public_key, session_key2, &key_size); + ck_assert_int_eq(res, 0); + ck_assert_int_eq(key_size, expected_key_size); + ck_assert_mem_eq(session_key2, expected_key, key_size); + #endif + +} + +START_TEST(test_bip32_ecdh_nist256p1) { + test_bip32_ecdh( + NIST256P1_NAME, 65, + fromhex( + "044aa56f917323f071148cd29aa423f6bee96e7fe87f914d0b91a0f95388c6631646" + "ea92e882773d7b0b1bec356b842c8559a1377673d3965fb931c8fe51e64873")); +} +END_TEST + +START_TEST(test_bip32_ecdh_curve25519) { + test_bip32_ecdh(CURVE25519_NAME, 33, + fromhex("04f34e35516325bb0d4a58507096c444a05ba13524ccf66910f1" + "1ce96c62224169")); +} +END_TEST + +START_TEST(test_bip32_ecdh_errors) { + HDNode node; + const uint8_t peer_public_key[65] = {0}; // invalid public key + uint8_t session_key[65]; + int res, key_size = 0; + + test_bip32_ecdh_init_node(&node, "Seed", ED25519_NAME); + res = hdnode_get_shared_key(&node, peer_public_key, session_key, &key_size); + ck_assert_int_eq(res, 1); + ck_assert_int_eq(key_size, 0); + + test_bip32_ecdh_init_node(&node, "Seed", CURVE25519_NAME); + res = hdnode_get_shared_key(&node, peer_public_key, session_key, &key_size); + ck_assert_int_eq(res, 1); + ck_assert_int_eq(key_size, 0); + + test_bip32_ecdh_init_node(&node, "Seed", NIST256P1_NAME); + res = hdnode_get_shared_key(&node, peer_public_key, session_key, &key_size); + ck_assert_int_eq(res, 1); + ck_assert_int_eq(key_size, 0); +} +END_TEST + +START_TEST(test_output_script) { + static const char *vectors[] = { + "76A914010966776006953D5567439E5E39F86A0D273BEE88AC", + "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", + "A914010966776006953D5567439E5E39F86A0D273BEE87", + "31nVrspaydBz8aMpxH9WkS2DuhgqS1fCuG", + "0014010966776006953D5567439E5E39F86A0D273BEE", + "p2xtZoXeX5X8BP8JfFhQK2nD3emtjch7UeFm", + "00200102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20", + "7XhPD7te3C6CVKnJWUhrTJbFTwudhHqfrjpS59AS6sMzL4RYFiCNg", + 0, + 0, + }; + const char **scr, **adr; + scr = vectors; + adr = vectors + 1; + char address[60]; + while (*scr && *adr) { + int r = + script_output_to_address(fromhex(*scr), strlen(*scr) / 2, address, 60); + ck_assert_uint_eq((size_t)r, strlen(*adr) + 1); + ck_assert_str_eq(address, *adr); + scr += 2; + adr += 2; + } +} +END_TEST + +#if USE_ETHEREUM + +START_TEST(test_ethereum_pubkeyhash) { + uint8_t pubkeyhash[20]; + int res; + HDNode node; + + // init m + hdnode_from_seed(fromhex("000102030405060708090a0b0c0d0e0f"), 16, + SECP256K1_NAME, &node); + + // [Chain m] + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("056db290f8ba3250ca64a45d16284d04bc6f5fbf"), 20); + + // [Chain m/0'] + hdnode_private_ckd_prime(&node, 0); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("bf6e48966d0dcf553b53e7b56cb2e0e72dca9e19"), 20); + + // [Chain m/0'/1] + hdnode_private_ckd(&node, 1); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("29379f45f515c494483298225d1b347f73d1babf"), 20); + + // [Chain m/0'/1/2'] + hdnode_private_ckd_prime(&node, 2); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("d8e85fbbb4b3b3c71c4e63a5580d0c12fb4d2f71"), 20); + + // [Chain m/0'/1/2'/2] + hdnode_private_ckd(&node, 2); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("1d3462d2319ac0bfc1a52e177a9d372492752130"), 20); + + // [Chain m/0'/1/2'/2/1000000000] + hdnode_private_ckd(&node, 1000000000); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("73659c60270d326c06ac204f1a9c63f889a3d14b"), 20); + + // init m + hdnode_from_seed( + fromhex( + "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c" + "999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542"), + 64, SECP256K1_NAME, &node); + + // [Chain m] + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("6dd2a6f3b05fd15d901fbeec61b87a34bdcfb843"), 20); + + // [Chain m/0] + hdnode_private_ckd(&node, 0); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("abbcd4471a0b6e76a2f6fdc44008fe53831e208e"), 20); + + // [Chain m/0/2147483647'] + hdnode_private_ckd_prime(&node, 2147483647); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("40ef2cef1b2588ae862e7a511162ec7ff33c30fd"), 20); + + // [Chain m/0/2147483647'/1] + hdnode_private_ckd(&node, 1); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("3f2e8905488f795ebc84a39560d133971ccf9b50"), 20); + + // [Chain m/0/2147483647'/1/2147483646'] + hdnode_private_ckd_prime(&node, 2147483646); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("a5016fdf975f767e4e6f355c7a82efa69bf42ea7"), 20); + + // [Chain m/0/2147483647'/1/2147483646'/2] + hdnode_private_ckd(&node, 2); + res = hdnode_get_ethereum_pubkeyhash(&node, pubkeyhash); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(pubkeyhash, + fromhex("8ff2a9f7e7917804e8c8ec150d931d9c5a6fbc50"), 20); +} +END_TEST + +START_TEST(test_ethereum_address) { + static const char *vectors[] = {"0x52908400098527886E0F7030069857D2E4169EE7", + "0x8617E340B3D01FA5F11F306F4090FD50E238070D", + "0xde709f2102306220921060314715629080e2fb77", + "0x27b1fdb04752bbc536007a920d24acb045561c26", + "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", + "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", + "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", + "0x5A4EAB120fB44eb6684E5e32785702FF45ea344D", + "0x5be4BDC48CeF65dbCbCaD5218B1A7D37F58A0741", + "0xa7dD84573f5ffF821baf2205745f768F8edCDD58", + "0x027a49d11d118c0060746F1990273FcB8c2fC196", + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + 0}; + uint8_t addr[20]; + char address[43]; + const char **vec = vectors; + while (*vec) { + memcpy(addr, fromhex(*vec + 2), 20); + ethereum_address_checksum(addr, address, false, 0); + ck_assert_str_eq(address, *vec); + vec++; + } +} +END_TEST + +// test vectors from +// https://github.com/rsksmart/RSKIPs/blob/master/IPs/RSKIP60.md +START_TEST(test_rsk_address) { + uint8_t addr[20]; + char address[43]; + + static const char *rskip60_chain30[] = { + "0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD", + "0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359", + "0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB", + "0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB", 0}; + const char **vec = rskip60_chain30; + while (*vec) { + memcpy(addr, fromhex(*vec + 2), 20); + ethereum_address_checksum(addr, address, true, 30); + ck_assert_str_eq(address, *vec); + vec++; + } + + static const char *rskip60_chain31[] = { + "0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd", + "0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359", + "0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB", + "0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB", 0}; + vec = rskip60_chain31; + while (*vec) { + memcpy(addr, fromhex(*vec + 2), 20); + ethereum_address_checksum(addr, address, true, 31); + ck_assert_str_eq(address, *vec); + vec++; + } +} +END_TEST + +#endif + +#if USE_NEM +// test vectors from +// https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/1.test-keys.dat +START_TEST(test_nem_address) { + static const struct { + const char *private_key; + const char *public_key; + const char *address; + } tests[] = { + { + "575dbb3062267eff57c970a336ebbc8fbcfe12c5bd3ed7bc11eb0481d7704ced", + "c5f54ba980fcbb657dbaaa42700539b207873e134d2375efeab5f1ab52f87844", + "NDD2CT6LQLIYQ56KIXI3ENTM6EK3D44P5JFXJ4R4", + }, + { + "5b0e3fa5d3b49a79022d7c1e121ba1cbbf4db5821f47ab8c708ef88defc29bfe", + "96eb2a145211b1b7ab5f0d4b14f8abc8d695c7aee31a3cfc2d4881313c68eea3", + "NABHFGE5ORQD3LE4O6B7JUFN47ECOFBFASC3SCAC", + }, + { + "738ba9bb9110aea8f15caa353aca5653b4bdfca1db9f34d0efed2ce1325aeeda", + "2d8425e4ca2d8926346c7a7ca39826acd881a8639e81bd68820409c6e30d142a", + "NAVOZX4HDVOAR4W6K4WJHWPD3MOFU27DFHC7KZOZ", + }, + { + "e8bf9bc0f35c12d8c8bf94dd3a8b5b4034f1063948e3cc5304e55e31aa4b95a6", + "4feed486777ed38e44c489c7c4e93a830e4c4a907fa19a174e630ef0f6ed0409", + "NBZ6JK5YOCU6UPSSZ5D3G27UHAPHTY5HDQMGE6TT", + }, + { + "c325ea529674396db5675939e7988883d59a5fc17a28ca977e3ba85370232a83", + "83ee32e4e145024d29bca54f71fa335a98b3e68283f1a3099c4d4ae113b53e54", + "NCQW2P5DNZ5BBXQVGS367DQ4AHC3RXOEVGRCLY6V", + }, + { + "a811cb7a80a7227ae61f6da536534ee3c2744e3c7e4b85f3e0df3c6a9c5613df", + "6d34c04f3a0e42f0c3c6f50e475ae018cfa2f56df58c481ad4300424a6270cbb", + "NA5IG3XFXZHIPJ5QLKX2FBJPEZYPMBPPK2ZRC3EH", + }, + { + "9c66de1ec77f4dfaaebdf9c8bc599ca7e8e6f0bc71390ffee2c9dd3f3619242a", + "a8fefd72a3b833dc7c7ed7d57ed86906dac22f88f1f4331873eb2da3152a3e77", + "NAABHVFJDBM74XMJJ52R7QN2MTTG2ZUXPQS62QZ7", + }, + { + "c56bc16ecf727878c15e24f4ae68569600ac7b251218a44ef50ce54175776edc", + "c92f761e6d83d20068fd46fe4bd5b97f4c6ba05d23180679b718d1f3e4fb066e", + "NCLK3OLMHR3F2E3KSBUIZ4K5PNWUDN37MLSJBJZP", + }, + { + "9dd73599283882fa1561ddfc9be5830b5dd453c90465d3fe5eeb646a3606374e", + "eaf16a4833e59370a04ccd5c63395058de34877b48c17174c71db5ed37b537ed", + "ND3AHW4VTI5R5QE5V44KIGPRU5FBJ5AFUCJXOY5H", + }, + { + "d9639dc6f49dad02a42fd8c217f1b1b4f8ce31ccd770388b645e639c72ff24fa", + "0f74a2f537cd9c986df018994dde75bdeee05e35eb9fe27adf506ca8475064f7", + "NCTZ4YAP43ONK3UYTASQVNDMBO24ZHJE65F3QPYE", + }, + { + "efc1992cd50b70ca55ac12c07aa5d026a8b78ffe28a7dbffc9228b26e02c38c1", + "2ebff201255f6cf948c78f528658b99a7c13ac791942fa22d59af610558111f5", + "NDQ2TMCMXBSFPZQPE2YKH6XLC24HD6LUMN6Z4GIC", + }, + { + "143a815e92e43f3ed1a921ee48cd143931b88b7c3d8e1e981f743c2a5be3c5ba", + "419ed11d48730e4ae2c93f0ea4df853b8d578713a36dab227517cf965861af4e", + "NA32IDDW2C53BDSBJNFL3Z6UU3J5CJZJMCZDXCF4", + }, + { + "bc1a082f5ac6fdd3a83ade211e5986ac0551bad6c7da96727ec744e5df963e2a", + "a160e6f9112233a7ce94202ed7a4443e1dac444b5095f9fecbb965fba3f92cac", + "NADUCEQLC3FTGB25GTA5HOUTB53CBVQNVOIP7NTJ", + }, + { + "4e47b4c6f4c7886e49ec109c61f4af5cfbb1637283218941d55a7f9fe1053f72", + "fbb91b16df828e21a9802980a44fc757c588bc1382a4cea429d6fa2ae0333f56", + "NBAF3BFLLPWH33MYE6VUPP5T6DQBZBKIDEQKZQOE", + }, + { + "efc4389da48ce49f85365cfa578c746530e9eac42db1b64ec346119b1becd347", + "2232f24dda0f2ded3ecd831210d4e8521a096b50cadd5a34f3f7083374e1ec12", + "NBOGTK2I2ATOGGD7ZFJHROG5MWL7XCKAUKSWIVSA", + }, + { + "bdba57c78ca7da16a3360efd13f06276284db8c40351de7fcd38ba0c35ac754d", + "c334c6c0dad5aaa2a0d0fb4c6032cb6a0edd96bf61125b5ea9062d5a00ee0eee", + "NCLERTEFYXKLK7RA4MVACEFMXMK3P7QMWTM7FBW2", + }, + { + "20694c1ec3c4a311bcdb29ed2edc428f6d4f9a4c429ad6a5bf3222084e35695f", + "518c4de412efa93de06a55947d11f697639443916ec8fcf04ebc3e6d17d0bd93", + "NB5V4BPIJHXVONO7UGMJDPFARMFA73BOBNOOYCOV", + }, + { + "e0d4f3760ac107b33c22c2cac24ab2f520b282684f5f66a4212ff95d926323ce", + "b3d16f4ead9de67c290144da535a0ed2504b03c05e5f1ceb8c7863762f786857", + "NC4PBAO5TPCAVQKBVOC4F6DMZP3CFSQBU46PSKBD", + }, + { + "efa9afc617412093c9c7a7c211a5332dd556f941e1a88c494ec860608610eea2", + "7e7716e4cebceb731d6f1fd28676f34888e9a0000fcfa1471db1c616c2ddf559", + "NCFW2LPXIWLBWAQN2QVIWEOD7IVDO3HQBD2OU56K", + }, + { + "d98499b3db61944684ce06a91735af4e14105338473fcf6ebe2b0bcada3dfd21", + "114171230ad6f8522a000cdc73fbc5c733b30bb71f2b146ccbdf34499f79a810", + "NCUKWDY3J3THKQHAKOK5ALF6ANJQABZHCH7VN6DP", + }, + }; + + HDNode node; + ed25519_secret_key private_key; + uint8_t chain_code[32]; + char address[41]; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + nem_private_key(tests[i].private_key, private_key); + + ck_assert(hdnode_from_xprv(0, 0, chain_code, private_key, + ED25519_KECCAK_NAME, &node)); + + ck_assert(hdnode_get_nem_address(&node, NEM_NETWORK_MAINNET, address)); + ck_assert_str_eq(address, tests[i].address); + + ck_assert_mem_eq(&node.public_key[1], fromhex(tests[i].public_key), 32); + } +} +END_TEST + +// test vectors from +// https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/3.test-derive.dat +START_TEST(test_nem_derive) { + static const struct { + const char *salt; + const char *private_key; + const char *public_key; + const char *mul; + const char *shared_key; + } tests[] = { + { + "ad63ac08f9afc85eb0bf4f8881ca6eaa0215924c87aa2f137d56109bb76c6f98", + "e8857f8e488d4e6d4b71bcd44bb4cff49208c32651e1f6500c3b58cafeb8def6", + "9d8e5f200b05a2638fb084a375408cabd6d5989590d96e3eea5f2cb34668178e", + "a8352060ba5718745ee4d78b9df564e0fbe13f50d50ab15a8dd524159d81d18b", + "990a5f611c65fbcde735378bdec38e1039c5466503511e8c645bbe42795c752b", + }, + { + "96104f0a28f9cca40901c066cd435134662a3b053eb6c8df80ee0d05dc941963", + "d7f67b5f52cbcd1a1367e0376a8eb1012b634acfcf35e8322bae8b22bb9e8dea", + "9735c92d150dcee0ade5a8d1822f46a4db22c9cda25f33773ae856fe374a3e8a", + "ea14d521d83328dba70982d42094300585818cc2da609fdb1f73bb32235576ff", + "b498aa21d4ba4879ea9fd4225e93bacc760dcd9c119f8f38ab0716457d1a6f85", + }, + { + "d8f94a0bbb1de80aea17aab42e2ffb982e73fc49b649a318479e951e392d8728", + "d026ddb445fb3bbf3020e4b55ed7b5f9b7fd1278c34978ca1a6ed6b358dadbae", + "d19e6beca3b26b9d1abc127835ebeb7a6c19c33dec8ec472e1c4d458202f4ec8", + "0d561f54728ad837ae108ec66c2ece2bb3b26041d3ee9b77fdc6d36d9ebfb2e3", + "d012afe3d1d932304e613c91545bf571cf2c7281d6cafa8e81393a551f675540", + }, + { + "3f8c969678a8abdbfb76866a142c284a6f01636c1c1607947436e0d2c30d5245", + "c522b38c391d1c3fa539cc58802bc66ac34bb3c73accd7f41b47f539bedcd016", + "ea5b6a0053237f7712b1d2347c447d3e83e0f2191762d07e1f53f8eb7f2dfeaa", + "23cccd3b63a9456e4425098b6df36f28c8999461a85e4b2b0c8d8f53c62c9ea9", + "7e27efa50eed1c2ac51a12089cbab6a192624709c7330c016d5bc9af146584c1", + }, + { + "e66415c58b981c7f1f2b8f45a42149e9144616ff6de49ff83d97000ac6f6f992", + "2f1b82be8e65d610a4e588f000a89a733a9de98ffc1ac9d55e138b3b0a855da0", + "65aeda1b47f66c978a4a41d4dcdfbd8eab5cdeb135695c2b0c28f54417b1486d", + "43e5b0a5cc8146c03ac63e6f8cf3d8825a9ca1ed53ea4a88304af4ddf5461b33", + "bb4ab31c334e55d378937978c90bb33779b23cd5ef4c68342a394f4ec8fa1ada", + }, + { + "58487c9818c9d28ddf97cb09c13331941e05d0b62bf4c35ee368de80b552e4d1", + "f3869b68183b2e4341307653e8f659bd7cd20e37ea5c00f5a9e203a8fa92359a", + "c7e4153a18b4162f5c1f60e1ba483264aa5bb3f4889dca45b434fcd30b9cf56f", + "5ae9408ab3156b8828c3e639730bd5e5db93d7afe2cee3fcda98079316c5bb3a", + "0224d85ae8f17bfe127ec24b8960b7639a0dbde9c8c39a0575b939448687bb14", + }, + { + "ad66f3b654844e53d6fb3568134fae75672ba63868c113659d3ca58c2c39d24f", + "d38f2ef8dfdc7224fef596130c3f4ff68ac83d3f932a56ee908061466ac28572", + "d0c79d0340dc66f0a895ce5ad60a933cf6be659569096fb9d4b81e5d32292372", + "1ea22db4708ed585ab541a4c70c3069f8e2c0c1faa188ddade3efaa53c8329f6", + "886a7187246934aedf2794748deadfe038c9fd7e78d4b7be76c5a651440ac843", + }, + { + "eed48604eab279c6ad8128aa83483a3da0928271a4cae1a5745671284e1fb89d", + "e2342a8450fc0adfa0ea2fbd0b1d28f100f0a3a905a3da29de34d1353afa7df7", + "d2dbe07e0f2dbc3dbb01c70092e3c4247d12827ddcd8d76534fd740a65c30de2", + "4c4b30eb6a2bfa17312f5729b4212cb51c2eee8fbfaea82a0e957ca68f4f6a30", + "dcae613ac5641ff9d4c3ca58632245f93b0b8657fe4d48bac7b062cc53dd21ad", + }, + { + "f35b315287b268c0d0b386fb5b31810f65e1c4497cffae24527f69a3abac3884", + "049016230dbef7a14a439e5ab2f6d12e78cb8df127db4e0c312673b3c361e350", + "1b3b1925a8a535cd7d78725d25298f45bba8ca3dee2cdaabf14241c9b51b37c4", + "04c9685dae1f8eb72a6438f24a87dc83a56d79c9024edf7e01aa1ae34656f29e", + "b48469d0428c223b93cd1fe34bb2cafd3fb78a8fa15f98f89f1ac9c0ce7c9001", + }, + { + "d6cf082c5d9a96e756a94a2e27703138580a7c7c1af505c96c3abf7ab6802e1d", + "67cd02b0b8b0adcf6fdd4d4d64a1f4193ced68bb8605d0ec941a62011326d140", + "a842d5127c93a435e4672dcadd9fccbfd0e9208c79c5404848b298597eccdbdb", + "d5c6bd6d81b99659d0bafe91025b6ecf73b16c6b07931cf44718b13f00fde3f7", + "8aa160795b587f4be53aa35d26e9b618b4cd6ec765b523bc908e53c258ca8fd4", + }, + { + "dda32c91c95527a645b00dd05d13f0b98ed612a726ce5f5221431430b7660944", + "eba026f92a8ffb5e95060a22e15d597fe838a99a0b2bbcb423c933b6bc718c50", + "7dbaf9c313a1ff9128c54d6cd740c7d0cc46bca588e7910d438dd619ca4fd69a", + "5bb20a145de83ba27a0c261e1f54bfd7dcea61888fc2eebbe6166876f7b000b8", + "3a96f152ad8bf355cccb307e4a40108aa17f8e925522a2b5bb0b3f1e1a262914", + }, + { + "63c500acbd4ff747f7dadde7d3286482894ac4d7fe68f396712bca29879aa65c", + "9663cd3c2030a5fe4a3ea3cc9a1d182b3a63ade68616aaeb4caf40b495f6f227", + "b1e7d9070ac820d986d839b79f7aa594dcf708473529dad87af8682cc6197374", + "1f7a97584d8db9f395b9ac4447de4b33c5c1f5020187cd4996286a49b07eb8a7", + "4d2a974ec12dcf525b5654d31053460850c3092648b7e15598b7131d2930e9fb", + }, + { + "91f340961029398cc8bcd906388044a6801d24328efdf919d8ed0c609862a073", + "45a588500d00142e2226231c01fd11ac6f842ab6a85872418f5b6a1999f8bd98", + "397233c96069b6f4a57d6e93f759fa327222eaef71fc981afa673b248206df3f", + "062123ef9782e3893f7b2e1d557b4ecce92b9f9aa8577e76380f228b75152f84", + "235848cb04230a16d8978aa7c19fe7fbff3ebe29389ea6eb24fb8bc3cb50afc6", + }, + { + "46120b4da6ba4eb54fb65213cfef99b812c86f7c42a1de1107f8e8c12c0c3b6b", + "cc19a97a99ad71ce348bcf831c0218e6a1f0a8d52186cabe6298b56f24e640f9", + "c54631bb62f0f9d86b3301fcb2a671621e655e578c95504af5f78da83f7fec4f", + "ab73cc20c75260ff3a7cefea8039291d4d15114a07a9250375628cca32225bb6", + "70040a360b6a2dfa881272ef4fa6616e2e8fcc45194fa2a21a1eef1160271cd5", + }, + { + "f0a69ded09f1d731ab9561d0c3a40b7ef30bcb2bf60f92beccd8734e2441403d", + "ea732822a381c46a7ac9999bf5ef85e16b7460b26aaf6c1a1c6ffa8c8c82c923", + "110decbff79c382b1e60af4259564a3c473087792350b24fca98ae9a74ba9dd9", + "81bdee95aecdcf821a9d682f79056f1abdcf1245d2f3b55244447881a283e0d4", + "1bc29d4470ccf97d4e35e8d3cd4b12e3ebf2cb0a82425d35984aeedf7ad0f6f9", + }, + { + "e79cf4536fb1547e57626c0f1a87f71a396fdfb985b00731c0c2876a00645eda", + "04213fc02b59c372e3e7f53faa71a2f73b31064102cb6fc8b68432ba7cdf7eb4", + "ca1c750aaed53bc30dac07d0696ed86bcd7cdbbcbd3d15bb90d90cb5c6117bac", + "c68cd0872a42a3a64e8a229ef7fcad3d722047d5af966f7dda4d4e32d0d57203", + "bfdd3d07563d966d95afe4b8abea4b567265fceea8c4ecddb0946256c33e07b2", + }, + { + "81a40db4cddaf076e0206bd2b0fa7470a72cc456bad34aa3a0469a4859f286be", + "52156799fd86cc63345cdbffd65ef4f5f8df0ffd9906a40af5f41d269bbcff5d", + "54d61aa0b0b17a87f1376fe89cd8cd6b314827c1f1b9e5e7b20e7a7eee2a8335", + "4553fb2cab8555068c32f86ceb692bbf1c2beeaf21627ef1b1be57344b52eea8", + "55096b6710ade3bbe38702458ee13faa10c24413261bc076f17675dcbf2c4ee6", + }, + { + "d28e4a9e6832a3a4dad014a2bf1f666f01093cbba8b9ad4d1dcad3ea10cb42b9", + "8ca134404c8fa199b0c72cb53cfa0adcf196dfa560fb521017cce5cbace3ba59", + "3a6c39a1e5f9f550f1debedd9a0bc84210cce5f9834797db8f14122bf5817e45", + "eb632ca818b4f659630226a339a3ce536b31c8e1e686fea8da3760e8abc20b8e", + "9fbb3fbaf1cd54ee0cd90685f59b082545983f1f662ef701332379306a6ad546", + }, + { + "f9c4bfad9e2a3d98c44c12734881a6f217d6c9523cc210772fad1297993454b4", + "b85960fcadda8d0a0960559b6b7818a0d8d4574b4e928b17c9b498fa9ffab4ef", + "6a1d0ef23ce0b40a7077ecb7b7264945054e3bdb58ee25e1b0ee8b3e19dbfcdc", + "bb145dddcb75074a6a03249fca1aa7d6fa9549e3ed965f138ca5e7071b7878f2", + "87d3faea4a98e41009eb8625611ea0fc12094c295af540c126c14a0f55afa76e", + }, + { + "656df4789a369d220aceb7b318517787d27004ecccedea019d623bcb2d79f5ff", + "acf83e30afb2a5066728ec5d93564c08abe5e68e3a2a2ff953bdcf4d44f9da06", + "bdda65efe56d7890286aada1452f62f85ba157d0b4621ba641de15d8d1c9e331", + "958beef5dc6babc6de383c32ad7dd3a6d6eb8bb3236ed5558eec0f9eb31e5458", + "6f6d4ee36d9d76e462c9635adfbb6073134a276cfc7cb86762004ec47197afa0", + }, + }; + + HDNode node; + ed25519_secret_key private_key; + uint8_t chain_code[32]; + ed25519_public_key public_key, mul; + uint8_t shared_key[SHA3_256_DIGEST_LENGTH]; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + nem_private_key(tests[i].private_key, private_key); + + ck_assert(hdnode_from_xprv(0, 0, chain_code, private_key, + ED25519_KECCAK_NAME, &node)); + memcpy(public_key, fromhex(tests[i].public_key), 32); + + ck_assert(hdnode_get_nem_shared_key( + &node, public_key, fromhex(tests[i].salt), mul, shared_key)); + ck_assert_mem_eq(mul, fromhex(tests[i].mul), sizeof(mul)); + ck_assert_mem_eq(shared_key, fromhex(tests[i].shared_key), + sizeof(shared_key)); + } +} +END_TEST + +// test vectors from +// https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/4.test-cipher.dat +START_TEST(test_nem_cipher) { + static const struct { + const char *private_key; + const char *public_key; + const char *salt; + const char *iv; + const char *input; + const char *output; + } tests[] = { + { + "3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6", + "57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21", + "83616c67f076d356fd1288a6e0fd7a60488ba312a3adf0088b1b33c7655c3e6a", + "a73ff5c32f8fd055b09775817a6a3f95", + "86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c", + "70815da779b1b954d7a7f00c16940e9917a0412a06a444b539bf147603eef87f", + }, + { + "3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6", + "57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21", + "703ce0b1d276b10eef35672df03234385a903460db18ba9d4e05b3ad31abb284", + "91246c2d5493867c4fa3e78f85963677", + "86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c", + "564b2f40d42c0efc1bd6f057115a5abd1564cae36d7ccacf5d825d38401aa894", + }, + { + "3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6", + "57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21", + "b22e8e8e7373ac31ca7f0f6eb8b93130aba5266772a658593f3a11792e7e8d92", + "9f8e33d82374dad6aac0e3dbe7aea704", + "86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c", + "7cab88d00a3fc656002eccbbd966e1d5d14a3090d92cf502cdbf843515625dcf", + }, + { + "3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6", + "57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21", + "af646c54cd153dffe453b60efbceeb85c1e95a414ea0036c4da94afb3366f5d9", + "6acdf8e01acc8074ddc807281b6af888", + "86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c", + "aa70543a485b63a4dd141bb7fd78019092ac6fad731e914280a287c7467bae1a", + }, + { + "3140f94c79f249787d1ec75a97a885980eb8f0a7d9b7aa03e7200296e422b2b6", + "57a70eb553a7b3fd621f0dba6abf51312ea2e2a2a1e19d0305516730f4bcbd21", + "d9c0d386636c8a024935c024589f9cd39e820a16485b14951e690a967830e269", + "f2e9f18aeb374965f54d2f4e31189a8f", + "86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c", + "33d97c216ea6498dfddabf94c2e2403d73efc495e9b284d9d90aaff840217d25", + }, + { + "d5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a", + "66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04", + "06c227baac1ae3b0b1dc583f4850f13f9ba5d53be4a98fa5c3ea16217847530d", + "3735123e78c44895df6ea33fa57e9a72", + "86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c", + "d5b5d66ba8cee0eb7ecf95b143fa77a46d6de13749e12eff40f5a7e649167ccb", + }, + { + "d5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a", + "66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04", + "92f55ba5bc6fc2f23e3eedc299357c71518e36ba2447a4da7a9dfe9dfeb107b5", + "1cbc4982e53e370052af97ab088fa942", + "86ddb9e713a8ebf67a51830eff03b837e147c20d75e67b2a54aa29e98c", + "d48ef1ef526d805656cfc932aff259eadb17aa3391dde1877a722cba31d935b2", + }, + { + "d5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a", + "66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04", + "10f15a39ba49866292a43b7781bc71ca8bbd4889f1616461caf056bcb91b0158", + "c40d531d92bfee969dce91417346c892", + "49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a" + "4", + "e6d75afdb542785669b42198577c5b358d95397d71ec6f5835dca46d332cc08dbf73" + "ea790b7bcb169a65719c0d55054c", + }, + { + "d5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a", + "66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04", + "9c01ed42b219b3bbe1a43ae9d7af5c1dd09363baacfdba8f4d03d1046915e26e", + "059a35d5f83249e632790015ed6518b9", + "49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a" + "4", + "5ef11aadff2eccee8b712dab968fa842eb770818ec0e6663ed242ea8b6bbc1c66d62" + "85ee5b5f03d55dfee382fb4fa25d", + }, + { + "d5c0762ecea2cd6b5c56751b58debcb32713aab348f4a59c493e38beb3244f3a", + "66a35941d615b5644d19c2a602c363ada8b1a8a0dac3682623852dcab4afac04", + "bc1067e2a7415ea45ff1ca9894338c591ff15f2e57ae2789ae31b9d5bea0f11e", + "8c73f0d6613898daeefa3cf8b0686d37", + "49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a" + "4", + "6d220213b1878cd40a458f2a1e6e3b48040455fdf504dcd857f4f2ca1ad642e3a44f" + "c401d04e339d302f66a9fad3d919", + }, + { + "9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921", + "441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094", + "cf4a21cb790552165827b678ca9695fcaf77566d382325112ff79483455de667", + "bfbf5482e06f55b88bdd9e053b7eee6e", + "49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a" + "4", + "1198a78c29c215d5c450f7b8513ead253160bc9fde80d9cc8e6bee2efe9713cf5a09" + "d6293c41033271c9e8c22036a28b", + }, + { + "9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921", + "441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094", + "eba5eae8aef79114082c3e70baef95bb02edf13b3897e8be7a70272962ef8838", + "af9a56da3da18e2fbd2948a16332532b", + "49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a" + "4", + "1062ab5fbbdee9042ad35bdadfd3047c0a2127fe0f001da1be1b0582185edfc9687b" + "e8d68f85795833bb04af9cedd3bb", + }, + { + "9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921", + "441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094", + "518f8dfd0c138f1ffb4ea8029db15441d70abd893c3d767dc668f23ba7770e27", + "42d28307974a1b2a2d921d270cfce03b", + "49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a" + "4", + "005e49fb7c5da540a84b034c853fc9f78a6b901ea495aed0c2abd4f08f1a96f9ffef" + "c6a57f1ac09e0aea95ca0f03ffd8", + }, + { + "9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921", + "441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094", + "582fdf58b53715c26e10ba809e8f2ab70502e5a3d4e9a81100b7227732ab0bbc", + "91f2aad3189bb2edc93bc891e73911ba", + "49de3cd5890e0cd0559f143807ff688ff62789b7236a332b7d7255ec0b4e73e6b3a" + "4", + "821a69cb16c57f0cb866e590b38069e35faec3ae18f158bb067db83a11237d29ab1e" + "6b868b3147236a0958f15c2e2167", + }, + { + "9ef87ba8aa2e664bdfdb978b98bc30fb61773d9298e7b8c72911683eeff41921", + "441e76d7e53be0a967181076a842f69c20fd8c0e3f0ce3aa421b490b059fe094", + "a415b4c006118fb72fc37b2746ef288e23ac45c8ff7ade5f368a31557b6ac93a", + "2b7c5f75606c0b8106c6489ea5657a9e", + "24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85", + "2781d5ee8ef1cb1596f8902b33dfae5045f84a987ca58173af5830dbce386062", + }, + { + "ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc", + "5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541", + "47e73ec362ea82d3a7c5d55532ad51d2cdf5316b981b2b2bd542b0efa027e8ea", + "b2193f59030c8d05a7d3577b7f64dd33", + "24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85", + "3f43912db8dd6672b9996e5272e18c4b88fec9d7e8372db9c5f4709a4af1d86f", + }, + { + "ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc", + "5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541", + "aaa006c57b6d1e402650577fe9787d8d285f4bacd7c01f998be49c766f8860c7", + "130304ddb9adc8870cf56bcae9487b7f", + "24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85", + "878cc7d8c0ef8dac0182a78eedc8080a402f59d8062a6b4ca8f4a74f3c3b3de7", + }, + { + "ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc", + "5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541", + "28dc7ccd6c2a939eef64b8be7b9ae248295e7fcd8471c22fa2f98733fea97611", + "cb13890d3a11bc0a7433738263006710", + "24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85", + "e74ded846bebfa912fa1720e4c1415e6e5df7e7a1a7fedb5665d68f1763209a4", + }, + { + "ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc", + "5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541", + "79974fa2cad95154d0873902c153ccc3e7d54b17f2eeb3f29b6344cad9365a9a", + "22123357979d20f44cc8eb0263d84e0e", + "24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85", + "eb14dec7b8b64d81a2ee4db07b0adf144d4f79a519bbf332b823583fa2d45405", + }, + { + "ed93c5a101ab53382ceee4f7e6b5aa112621d3bb9d18891509b1834ede235bcc", + "5a5e14c633d7d269302849d739d80344ff14db51d7bcda86045723f05c4e4541", + "3409a6f8c4dcd9bd04144eb67e55a98696b674735b01bf1196191f29871ef966", + "a823a0965969380ea1f8659ea5fd8fdd", + "24512b714aefd5cbc4bcc4ef44ce6c67ffc447c65460a6c6e4a92e85", + "00a7eb708eae745847173f8217efb05be13059710aee632e3f471ac3c6202b51", + }, + { + "a73a0b2686f7d699c018b6b08a352856e556070caa329c26241aec889eefde10", + "9b493403bee45ae6277430ef8d0c4163ffd81ace2db6c7821205da09a664a86c", + "c25701b9b7328c4ac3d23223d10623bd527c0a98e38ae9c62fbc403c80ab20ae", + "4b4ee0e4443779f3af429a749212f476", + "b6926d0ec82cec86c0d27ec9a33a0e0f", + "f39f7d66e0fde39ecdf58be2c0ef361a17cfd6843e310adbe0ec3118cd72800d", + }, + { + "a73a0b2686f7d699c018b6b08a352856e556070caa329c26241aec889eefde10", + "9b493403bee45ae6277430ef8d0c4163ffd81ace2db6c7821205da09a664a86c", + "31d18fdffc480310828778496ff817039df5d6f30bf6d9edd0b4396863d05f93", + "418bcbdf52860a450bfacc96920d02cf", + "b6926d0ec82cec86c0d27ec9a33a0e0f", + "0e6ce9889fe7b3cd82794b0ae27c1f5985d2f2a1f398371a138f8db1df1f54de", + }, + { + "e2e4dee102fad0f47f60202269605589cd9cf70f816b34016796c74b766f3041", + "c5ce283033a3255ae14d42dff1e4c18a224ac79d084b285123421b105ee654c9", + "56b4c645f81dbfb6ba0c6d3f1626e1e5cd648eeb36562715f7cd7e9ea86a0d7f", + "dc9bdce76d68d2e4d72267cf4e72b022", + "b6926d0ec82cec86c0d27ec9a33a0e0f", + "dc6f046c3008002041517a7c4f3ababe609cf02616fcccda39c075d1be4175f5", + }, + { + "e2e4dee102fad0f47f60202269605589cd9cf70f816b34016796c74b766f3041", + "c5ce283033a3255ae14d42dff1e4c18a224ac79d084b285123421b105ee654c9", + "df180b91986c8c7000792f96d1faa61e30138330430a402322be1855089b0e7f", + "ccf9b77341c866465b474e2f4a3b1cf8", + "b6926d0ec82cec86c0d27ec9a33a0e0f", + "94e4ae89041437f39826704f02cb5d775226f34344635e592846417497a5020b", + }, + { + "e2e4dee102fad0f47f60202269605589cd9cf70f816b34016796c74b766f3041", + "c5ce283033a3255ae14d42dff1e4c18a224ac79d084b285123421b105ee654c9", + "a0eee7e84c76e63fdae6e938b43330775eaf17d260e40b98c9e6616b668102a7", + "662c681cfec6f6d052ff0e2c1255f2c2", + "b6926d0ec82cec86c0d27ec9a33a0e0f", + "70bba3c48be9c75a144b1888ca3d21a6b21f52eec133981a024390a6a0ba36f9", + }, + { + "e2e4dee102fad0f47f60202269605589cd9cf70f816b34016796c74b766f3041", + "c5ce283033a3255ae14d42dff1e4c18a224ac79d084b285123421b105ee654c9", + "c6acd2d90eb782c3053b366680ffa0e148de81fea198c87bb643869fd97e5cb0", + "908dc33ba80520f2f0f04e7890e3a3c0", + "b6926d0ec82cec86c0d27ec9a33a0e0f", + "f6efe1d76d270aac264aa35d03049d9ce63be1996d543aef00559219c8666f71", + }, + }; + + HDNode node; + ed25519_secret_key private_key; + uint8_t chain_code[32]; + ed25519_public_key public_key; + uint8_t salt[sizeof(public_key)]; + + uint8_t iv[AES_BLOCK_SIZE]; + uint8_t buffer[FROMHEX_MAXLEN]; + + uint8_t input[FROMHEX_MAXLEN]; + uint8_t output[FROMHEX_MAXLEN]; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + nem_private_key(tests[i].private_key, private_key); + + ck_assert(hdnode_from_xprv(0, 0, chain_code, private_key, + ED25519_KECCAK_NAME, &node)); + memcpy(public_key, fromhex(tests[i].public_key), 32); + memcpy(salt, fromhex(tests[i].salt), sizeof(salt)); + + size_t input_size = strlen(tests[i].input) / 2; + size_t output_size = strlen(tests[i].output) / 2; + + memcpy(input, fromhex(tests[i].input), input_size); + memcpy(output, fromhex(tests[i].output), output_size); + + memcpy(iv, fromhex(tests[i].iv), sizeof(iv)); + ck_assert(hdnode_nem_encrypt(&node, public_key, iv, salt, input, input_size, + buffer)); + ck_assert_uint_eq(output_size, NEM_ENCRYPTED_SIZE(input_size)); + ck_assert_mem_eq(buffer, output, output_size); + + memcpy(iv, fromhex(tests[i].iv), sizeof(iv)); + ck_assert(hdnode_nem_decrypt(&node, public_key, iv, salt, output, + output_size, buffer)); + ck_assert_uint_eq(input_size, NEM_DECRYPTED_SIZE(buffer, output_size)); + ck_assert_mem_eq(buffer, input, input_size); + } +} +END_TEST + +START_TEST(test_nem_transaction_transfer) { + nem_transaction_ctx ctx; + + uint8_t buffer[1024], hash[SHA3_256_DIGEST_LENGTH]; + + // http://bob.nem.ninja:8765/#/transfer/0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f + + nem_transaction_start( + &ctx, + fromhex( + "e59ef184a612d4c3c4d89b5950eb57262c69862b2f96e59c5043bf41765c482f"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_transfer( + &ctx, NEM_NETWORK_TESTNET, 0, NULL, 0, 0, + "TBGIMRE4SBFRUJXMH7DVF2IBY36L2EDWZ37GVSC4", 50000000000000, NULL, 0, + false, 0)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "0acbf8df91e6a65dc56c56c43d65f31ff2a6a48d06fc66e78c7f3436faf3e74f"), + sizeof(hash)); + + // http://bob.nem.ninja:8765/#/transfer/3409d9ece28d6296d6d5e220a7e3cb8641a3fb235ffcbd20c95da64f003ace6c + + nem_transaction_start( + &ctx, + fromhex( + "994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_transfer( + &ctx, NEM_NETWORK_TESTNET, 14072100, NULL, 194000000, 14075700, + "TBLOODPLWOWMZ2TARX4RFPOSOWLULHXMROBN2WXI", 3000000, + (uint8_t *)"sending you 3 pairs of paddles\n", 31, false, 2)); + + ck_assert( + nem_transaction_write_mosaic(&ctx, "gimre.games.pong", "paddles", 2)); + + ck_assert(nem_transaction_write_mosaic(&ctx, "nem", "xem", 44000000)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "3409d9ece28d6296d6d5e220a7e3cb8641a3fb235ffcbd20c95da64f003ace6c"), + sizeof(hash)); + + // http://chain.nem.ninja/#/transfer/e90e98614c7598fbfa4db5411db1b331d157c2f86b558fb7c943d013ed9f71cb + + nem_transaction_start( + &ctx, + fromhex( + "8d07f90fb4bbe7715fa327c926770166a11be2e494a970605f2e12557f66c9b9"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_transfer( + &ctx, NEM_NETWORK_MAINNET, 0, NULL, 0, 0, + "NBT3WHA2YXG2IR4PWKFFMO772JWOITTD2V4PECSB", 5175000000000, + (uint8_t *)"Good luck!", 10, false, 0)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "e90e98614c7598fbfa4db5411db1b331d157c2f86b558fb7c943d013ed9f71cb"), + sizeof(hash)); + + // http://chain.nem.ninja/#/transfer/40e89160e6f83d37f7c82defc0afe2c1605ae8c919134570a51dd27ea1bb516c + + nem_transaction_start( + &ctx, + fromhex( + "f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_transfer( + &ctx, NEM_NETWORK_MAINNET, 77229, NULL, 30000000, 80829, + "NALICEPFLZQRZGPRIJTMJOCPWDNECXTNNG7QLSG3", 30000000, + fromhex("4d9dcf9186967d30be93d6d5404ded22812dbbae7c3f0de5" + "01bcd7228cba45bded13000eec7b4c6215fc4d3588168c92" + "18167cec98e6977359153a4132e050f594548e61e0dc61c1" + "53f0f53c5e65c595239c9eb7c4e7d48e0f4bb8b1dd2f5ddc"), + 96, true, 0)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "40e89160e6f83d37f7c82defc0afe2c1605ae8c919134570a51dd27ea1bb516c"), + sizeof(hash)); + + // http://chain.nem.ninja/#/transfer/882dca18dcbe075e15e0ec5a1d7e6ccd69cc0f1309ffd3fde227bfbc107b3f6e + + nem_transaction_start( + &ctx, + fromhex( + "f85ab43dad059b9d2331ddacc384ad925d3467f03207182e01296bacfb242d01"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_transfer( + &ctx, NEM_NETWORK_MAINNET, 26730750, NULL, 179500000, 26734350, + "NBE223WPKEBHQPCYUC4U4CDUQCRRFMPZLOQLB5OP", 1000000, + (uint8_t *)"enjoy! :)", 9, false, 1)); + + ck_assert(nem_transaction_write_mosaic(&ctx, "imre.g", "tokens", 1)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "882dca18dcbe075e15e0ec5a1d7e6ccd69cc0f1309ffd3fde227bfbc107b3f6e"), + sizeof(hash)); +} +END_TEST + +START_TEST(test_nem_transaction_multisig) { + nem_transaction_ctx ctx, other_trans; + + uint8_t buffer[1024], inner[1024]; + const uint8_t *signature; + + // http://bob.nem.ninja:8765/#/multisig/7d3a7087023ee29005262016706818579a2b5499eb9ca76bad98c1e6f4c46642 + + nem_transaction_start( + &other_trans, + fromhex( + "abac2ee3d4aaa7a3bfb65261a00cc04c761521527dd3f2cf741e2815cbba83ac"), + inner, sizeof(inner)); + + ck_assert(nem_transaction_create_aggregate_modification( + &other_trans, NEM_NETWORK_TESTNET, 3939039, NULL, 16000000, 3960639, 1, + false)); + + ck_assert(nem_transaction_write_cosignatory_modification( + &other_trans, 2, + fromhex( + "e6cff9b3725a91f31089c3acca0fac3e341c00b1c8c6e9578f66c4514509c3b3"))); + + nem_transaction_start( + &ctx, + fromhex( + "59d89076964742ef2a2089d26a5aa1d2c7a7bb052a46c1de159891e91ad3d76e"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_multisig(&ctx, NEM_NETWORK_TESTNET, 3939039, + NULL, 6000000, 3960639, + &other_trans)); + + signature = fromhex( + "933930a8828b560168bddb3137df9252048678d829aa5135fa27bb306ff6562efb927554" + "62988b852b0314bde058487d00e47816b6fb7df6bcfd7e1f150d1d00"); + ck_assert_int_eq(ed25519_sign_open_keccak(ctx.buffer, ctx.offset, + ctx.public_key, signature), + 0); + + nem_transaction_start( + &ctx, + fromhex( + "71cba4f2a28fd19f902ba40e9937994154d9eeaad0631d25d525ec37922567d4"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_multisig_signature(&ctx, NEM_NETWORK_TESTNET, + 3939891, NULL, 6000000, + 3961491, &other_trans)); + + signature = fromhex( + "a849f13bfeeba808a8a4a79d579febe584d831a3a6ad03da3b9d008530b3d7a79fcf7156" + "121cd7ee847029d94af7ea7a683ca8e643dc5e5f489557c2054b830b"); + ck_assert_int_eq(ed25519_sign_open_keccak(ctx.buffer, ctx.offset, + ctx.public_key, signature), + 0); + + // http://chain.nem.ninja/#/multisig/1016cf3bdd61bd57b9b2b07b6ff2dee390279d8d899265bdc23d42360abe2e6c + + nem_transaction_start( + &other_trans, + fromhex( + "a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a"), + inner, sizeof(inner)); + + ck_assert(nem_transaction_create_provision_namespace( + &other_trans, NEM_NETWORK_MAINNET, 59414272, NULL, 20000000, 59500672, + "dim", NULL, "NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA", 5000000000)); + + nem_transaction_start( + &ctx, + fromhex( + "cfe58463f0eaebceb5d00717f8aead49171a5d7c08f6b1299bd534f11715acc9"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_multisig(&ctx, NEM_NETWORK_MAINNET, 59414272, + NULL, 6000000, 59500672, + &other_trans)); + + signature = fromhex( + "52a876a37511068fe214bd710b2284823921ec7318c01e083419a062eae5369c9c11c3ab" + "fdb590f65c717fab82873431d52be62e10338cb5656d1833bbdac70c"); + ck_assert_int_eq(ed25519_sign_open_keccak(ctx.buffer, ctx.offset, + ctx.public_key, signature), + 0); + + nem_transaction_start( + &ctx, + fromhex( + "1b49b80203007117d034e45234ffcdf402c044aeef6dbb06351f346ca892bce2"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_multisig_signature(&ctx, NEM_NETWORK_MAINNET, + 59414342, NULL, 6000000, + 59500742, &other_trans)); + + signature = fromhex( + "b9a59239e5d06992c28840034ff7a7f13da9c4e6f4a6f72c1b1806c3b602f83a7d727a34" + "5371f5d15abf958208a32359c6dd77bde92273ada8ea6fda3dc76b00"); + ck_assert_int_eq(ed25519_sign_open_keccak(ctx.buffer, ctx.offset, + ctx.public_key, signature), + 0); + + nem_transaction_start( + &ctx, + fromhex( + "7ba4b39209f1b9846b098fe43f74381e43cb2882ccde780f558a63355840aa87"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_multisig_signature(&ctx, NEM_NETWORK_MAINNET, + 59414381, NULL, 6000000, + 59500781, &other_trans)); + + signature = fromhex( + "e874ae9f069f0538008631d2df9f2e8a59944ff182e8672f743d2700fb99224aafb7a0ab" + "09c4e9ea39ee7c8ca04a8a3d6103ae1122d87772e871761d4f00ca01"); + ck_assert_int_eq(ed25519_sign_open_keccak(ctx.buffer, ctx.offset, + ctx.public_key, signature), + 0); +} +END_TEST + +START_TEST(test_nem_transaction_provision_namespace) { + nem_transaction_ctx ctx; + + uint8_t buffer[1024], hash[SHA3_256_DIGEST_LENGTH]; + + // http://bob.nem.ninja:8765/#/namespace/f7cab28da57204d01a907c697836577a4ae755e6c9bac60dcc318494a22debb3 + + nem_transaction_start( + &ctx, + fromhex( + "84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_provision_namespace( + &ctx, NEM_NETWORK_TESTNET, 56999445, NULL, 20000000, 57003045, "gimre", + NULL, "TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35", 5000000000)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "f7cab28da57204d01a907c697836577a4ae755e6c9bac60dcc318494a22debb3"), + sizeof(hash)); + + // http://bob.nem.ninja:8765/#/namespace/7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d + + nem_transaction_start( + &ctx, + fromhex( + "244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_provision_namespace( + &ctx, NEM_NETWORK_TESTNET, 21496797, NULL, 108000000, 21500397, "misc", + "alice", "TAMESPACEWH4MKFMBCVFERDPOOP4FK7MTDJEYP35", 5000000000)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "7ddd5fe607e1bfb5606e0ac576024c318c8300d237273117d4db32a60c49524d"), + sizeof(hash)); + + // http://chain.nem.ninja/#/namespace/57071aad93ca125dc231dc02c07ad8610cd243d35068f9b36a7d231383907569 + + nem_transaction_start( + &ctx, + fromhex( + "9f3c14f304309c8b72b2821339c4428793b1518bea72d58dd01f19d523518614"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_provision_namespace( + &ctx, NEM_NETWORK_MAINNET, 26699717, NULL, 108000000, 26703317, "sex", + NULL, "NAMESPACEWH4MKFMBCVFERDPOOP4FK7MTBXDPZZA", 50000000000)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "57071aad93ca125dc231dc02c07ad8610cd243d35068f9b36a7d231383907569"), + sizeof(hash)); +} +END_TEST + +START_TEST(test_nem_transaction_mosaic_creation) { + nem_transaction_ctx ctx; + + uint8_t buffer[1024], hash[SHA3_256_DIGEST_LENGTH]; + + // http://bob.nem.ninja:8765/#/mosaic/68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa/0 + + nem_transaction_start( + &ctx, + fromhex( + "994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_mosaic_creation( + &ctx, NEM_NETWORK_TESTNET, 14070896, NULL, 108000000, 14074496, + "gimre.games.pong", "paddles", "Paddles for the bong game.\n", 0, 10000, + true, true, 0, 0, NULL, NULL, NULL, + "TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC", 50000000000)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "68364353c29105e6d361ad1a42abbccbf419cfc7adb8b74c8f35d8f8bdaca3fa"), + sizeof(hash)); + + // http://bob.nem.ninja:8765/#/mosaic/b2f4a98113ff1f3a8f1e9d7197aa982545297fe0aa3fa6094af8031569953a55/0 + + nem_transaction_start( + &ctx, + fromhex( + "244fa194e2509ac0d2fbc18779c2618d8c2ebb61c16a3bcbebcf448c661ba8dc"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_mosaic_creation( + &ctx, NEM_NETWORK_TESTNET, 21497248, NULL, 108000000, 21500848, + "alice.misc", "bar", "Special offer: get one bar extra by bying one foo!", + 0, 1000, false, true, 1, 1, "TALICE2GMA34CXHD7XLJQ536NM5UNKQHTORNNT2J", + "nem", "xem", "TBMOSAICOD4F54EE5CDMR23CCBGOAM2XSJBR5OLC", 50000000000)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "b2f4a98113ff1f3a8f1e9d7197aa982545297fe0aa3fa6094af8031569953a55"), + sizeof(hash)); + + // http://chain.nem.ninja/#/mosaic/269c6fda657aba3053a0e5b138c075808cc20e244e1182d9b730798b60a1f77b/0 + + nem_transaction_start( + &ctx, + fromhex( + "58956ac77951622dc5f1c938affbf017c458e30e6b21ddb5783d38b302531f23"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_mosaic_creation( + &ctx, NEM_NETWORK_MAINNET, 26729938, NULL, 108000000, 26733538, "jabo38", + "red_token", + "This token is to celebrate the release of Namespaces and Mosaics on the " + "NEM system. " + "This token was the fist ever mosaic created other than nem.xem. " + "There are only 10,000 Red Tokens that will ever be created. " + "It has no levy and can be traded freely among third parties.", + 2, 10000, false, true, 0, 0, NULL, NULL, NULL, + "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", 50000000000)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "269c6fda657aba3053a0e5b138c075808cc20e244e1182d9b730798b60a1f77b"), + sizeof(hash)); + + // http://chain.nem.ninja/#/mosaic/e8dc14821dbea4831d9051f86158ef348001447968fc22c01644fdaf2bda75c6/0 + + nem_transaction_start( + &ctx, + fromhex( + "a1df5306355766bd2f9a64efdc089eb294be265987b3359093ae474c051d7d5a"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_mosaic_creation( + &ctx, NEM_NETWORK_MAINNET, 69251020, NULL, 20000000, 69337420, "dim", + "coin", "DIM COIN", 6, 9000000000, false, true, 2, 10, + "NCGGLVO2G3CUACVI5GNX2KRBJSQCN4RDL2ZWJ4DP", "dim", "coin", + "NBMOSAICOD4F54EE5CDMR23CCBGOAM2XSIUX6TRS", 500000000)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "e8dc14821dbea4831d9051f86158ef348001447968fc22c01644fdaf2bda75c6"), + sizeof(hash)); +} +END_TEST + +START_TEST(test_nem_transaction_mosaic_supply_change) { + nem_transaction_ctx ctx; + + uint8_t buffer[1024], hash[SHA3_256_DIGEST_LENGTH]; + + // http://bigalice2.nem.ninja:7890/transaction/get?hash=33a50fdd4a54913643a580b2af08b9a5b51b7cee922bde380e84c573a7969c50 + + nem_transaction_start( + &ctx, + fromhex( + "994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_mosaic_supply_change( + &ctx, NEM_NETWORK_TESTNET, 14071648, NULL, 108000000, 14075248, + "gimre.games.pong", "paddles", 1, 1234)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "33a50fdd4a54913643a580b2af08b9a5b51b7cee922bde380e84c573a7969c50"), + sizeof(hash)); + + // http://bigalice2.nem.ninja:7890/transaction/get?hash=1ce8e8894d077a66ff22294b000825d090a60742ec407efd80eb8b19657704f2 + + nem_transaction_start( + &ctx, + fromhex( + "84afa1bbc993b7f5536344914dde86141e61f8cbecaf8c9cefc07391f3287cf5"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_mosaic_supply_change( + &ctx, NEM_NETWORK_TESTNET, 14126909, NULL, 108000000, 14130509, + "jabo38_ltd.fuzzy_kittens_cafe", "coupons", 2, 1)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "1ce8e8894d077a66ff22294b000825d090a60742ec407efd80eb8b19657704f2"), + sizeof(hash)); + + // http://bigalice3.nem.ninja:7890/transaction/get?hash=694e493e9576d2bcf60d85747e302ac2e1cc27783187947180d4275a713ff1ff + + nem_transaction_start( + &ctx, + fromhex( + "b7ccc27b21ba6cf5c699a8dc86ba6ba98950442597ff9fa30e0abe0f5f4dd05d"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_mosaic_supply_change( + &ctx, NEM_NETWORK_MAINNET, 53377685, NULL, 20000000, 53464085, "abvapp", + "abv", 1, 9000000)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "694e493e9576d2bcf60d85747e302ac2e1cc27783187947180d4275a713ff1ff"), + sizeof(hash)); + + // http://bigalice3.nem.ninja:7890/transaction/get?hash=09836334e123970e068d5b411e4d1df54a3ead10acf1ad5935a2cdd9f9680185 + + nem_transaction_start( + &ctx, + fromhex( + "75f001a8641e2ce5c4386883dda561399ed346177411b492a677b73899502f13"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_mosaic_supply_change( + &ctx, NEM_NETWORK_MAINNET, 55176304, NULL, 20000000, 55262704, "sushi", + "wasabi", 2, 20)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "09836334e123970e068d5b411e4d1df54a3ead10acf1ad5935a2cdd9f9680185"), + sizeof(hash)); +} +END_TEST + +START_TEST(test_nem_transaction_aggregate_modification) { + nem_transaction_ctx ctx; + + uint8_t buffer[1024], hash[SHA3_256_DIGEST_LENGTH]; + + // http://bob.nem.ninja:8765/#/aggregate/6a55471b17159e5b6cd579c421e95a4e39d92e3f78b0a55ee337e785a601d3a2 + + nem_transaction_start( + &ctx, + fromhex( + "462ee976890916e54fa825d26bdd0235f5eb5b6a143c199ab0ae5ee9328e08ce"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_aggregate_modification( + &ctx, NEM_NETWORK_TESTNET, 0, NULL, 22000000, 0, 2, false)); + + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "994793ba1c789fa9bdea918afc9b06e2d0309beb1081ac5b6952991e4defd324"))); + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "c54d6e33ed1446eedd7f7a80a588dd01857f723687a09200c1917d5524752f8b"))); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "6a55471b17159e5b6cd579c421e95a4e39d92e3f78b0a55ee337e785a601d3a2"), + sizeof(hash)); + + // http://bob.nem.ninja:8765/#/aggregate/1fbdae5ba753e68af270930413ae90f671eb8ab58988116684bac0abd5726584 + + nem_transaction_start( + &ctx, + fromhex( + "6bf7849c1eec6a2002995cc457dc00c4e29bad5c88de63f51e42dfdcd7b2131d"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_aggregate_modification( + &ctx, NEM_NETWORK_TESTNET, 6542254, NULL, 40000000, 6545854, 4, true)); + + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "5f53d076c8c3ec3110b98364bc423092c3ec2be2b1b3c40fd8ab68d54fa39295"))); + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "9eb199c2b4d406f64cb7aa5b2b0815264b56ba8fe44d558a6cb423a31a33c4c2"))); + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "94b2323dab23a3faba24fa6ddda0ece4fbb06acfedd74e76ad9fae38d006882b"))); + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "d88c6ee2a2cd3929d0d76b6b14ecb549d21296ab196a2b3a4cb2536bcce32e87"))); + + ck_assert(nem_transaction_write_minimum_cosignatories(&ctx, 2)); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "1fbdae5ba753e68af270930413ae90f671eb8ab58988116684bac0abd5726584"), + sizeof(hash)); + + // http://chain.nem.ninja/#/aggregate/cc64ca69bfa95db2ff7ac1e21fe6d27ece189c603200ebc9778d8bb80ca25c3c + + nem_transaction_start( + &ctx, + fromhex( + "f41b99320549741c5cce42d9e4bb836d98c50ed5415d0c3c2912d1bb50e6a0e5"), + buffer, sizeof(buffer)); + + ck_assert(nem_transaction_create_aggregate_modification( + &ctx, NEM_NETWORK_MAINNET, 0, NULL, 40000000, 0, 5, false)); + + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "1fbdbdde28daf828245e4533765726f0b7790e0b7146e2ce205df3e86366980b"))); + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "f94e8702eb1943b23570b1b83be1b81536df35538978820e98bfce8f999e2d37"))); + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "826cedee421ff66e708858c17815fcd831a4bb68e3d8956299334e9e24380ba8"))); + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "719862cd7d0f4e875a6a0274c9a1738f38f40ad9944179006a54c34724c1274d"))); + ck_assert(nem_transaction_write_cosignatory_modification( + &ctx, 1, + fromhex( + "43aa69177018fc3e2bdbeb259c81cddf24be50eef9c5386db51d82386c41475a"))); + + keccak_256(ctx.buffer, ctx.offset, hash); + ck_assert_mem_eq( + hash, + fromhex( + "cc64ca69bfa95db2ff7ac1e21fe6d27ece189c603200ebc9778d8bb80ca25c3c"), + sizeof(hash)); +} +END_TEST +#endif + +START_TEST(test_multibyte_address) { + uint8_t priv_key[32]; + char wif[57]; + uint8_t pub_key[33]; + char address[40]; + uint8_t decode[24]; + int res; + + memcpy( + priv_key, + fromhex( + "47f7616ea6f9b923076625b4488115de1ef1187f760e65f89eb6f4f7ff04b012"), + 32); + ecdsa_get_wif(priv_key, 0, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, "13QtoXmbhELWcrwD9YA9KzvXy5rTaptiNuFR8L8ArpBNn4xmQj4N"); + ecdsa_get_wif(priv_key, 0x12, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, "3hrF6SFnqzpzABB36uGDf8dJSuUCcMmoJrTmCWMshRkBr2Vx86qJ"); + ecdsa_get_wif(priv_key, 0x1234, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, + "CtPTF9awbVbfDWGepGdVhB3nBhr4HktUGya8nf8dLxgC8tbqBreB9"); + ecdsa_get_wif(priv_key, 0x123456, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, + "uTrDevVQt5QZgoL3iJ1cPWHaCz7ZMBncM7QXZfCegtxiMHqBvWoYJa"); + ecdsa_get_wif(priv_key, 0x12345678, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, + "4zZWMzv1SVbs95pmLXWrXJVp9ntPEam1mfwb6CXBLn9MpWNxLg9huYgv"); + ecdsa_get_wif(priv_key, 0xffffffff, HASHER_SHA2D, wif, sizeof(wif)); + ck_assert_str_eq(wif, + "y9KVfV1RJXcTxpVjeuh6WYWh8tMwnAUeyUwDEiRviYdrJ61njTmnfUjE"); + + memcpy( + pub_key, + fromhex( + "0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71"), + 33); + ecdsa_get_address(pub_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8"); + ecdsa_get_address(pub_key, 0x12, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "8SCrMR2yYF7ciqoDbav7VLLTsVx5dTVPPq"); + ecdsa_get_address(pub_key, 0x1234, HASHER_SHA2_RIPEMD, HASHER_SHA2D, address, + sizeof(address)); + ck_assert_str_eq(address, "ZLH8q1UgMPg8o2s1MD55YVMpPV7vqms9kiV"); + ecdsa_get_address(pub_key, 0x123456, HASHER_SHA2_RIPEMD, HASHER_SHA2D, + address, sizeof(address)); + ck_assert_str_eq(address, "3ThqvsQVFnbiF66NwHtfe2j6AKn75DpLKpQSq"); + ecdsa_get_address(pub_key, 0x12345678, HASHER_SHA2_RIPEMD, HASHER_SHA2D, + address, sizeof(address)); + ck_assert_str_eq(address, "BrsGxAHga3VbopvSnb3gmLvMBhJNCGuDxBZL44"); + ecdsa_get_address(pub_key, 0xffffffff, HASHER_SHA2_RIPEMD, HASHER_SHA2D, + address, sizeof(address)); + ck_assert_str_eq(address, "3diW7paWGJyZRLGqMJZ55DMfPExob8QxQHkrfYT"); + + res = ecdsa_address_decode("1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8", 0, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("0079fbfc3f34e7745860d76137da68f362380c606c"), 21); + res = ecdsa_address_decode("8SCrMR2yYF7ciqoDbav7VLLTsVx5dTVPPq", 0x12, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("1279fbfc3f34e7745860d76137da68f362380c606c"), 21); + res = ecdsa_address_decode("ZLH8q1UgMPg8o2s1MD55YVMpPV7vqms9kiV", 0x1234, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq(decode, + fromhex("123479fbfc3f34e7745860d76137da68f362380c606c"), 21); + res = ecdsa_address_decode("3ThqvsQVFnbiF66NwHtfe2j6AKn75DpLKpQSq", 0x123456, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq( + decode, fromhex("12345679fbfc3f34e7745860d76137da68f362380c606c"), 21); + res = ecdsa_address_decode("BrsGxAHga3VbopvSnb3gmLvMBhJNCGuDxBZL44", + 0x12345678, HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq( + decode, fromhex("1234567879fbfc3f34e7745860d76137da68f362380c606c"), 21); + res = ecdsa_address_decode("3diW7paWGJyZRLGqMJZ55DMfPExob8QxQHkrfYT", + 0xffffffff, HASHER_SHA2D, decode); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq( + decode, fromhex("ffffffff79fbfc3f34e7745860d76137da68f362380c606c"), 21); + + // wrong length + res = ecdsa_address_decode("BrsGxAHga3VbopvSnb3gmLvMBhJNCGuDxBZL44", 0x123456, + HASHER_SHA2D, decode); + ck_assert_int_eq(res, 0); + + // wrong address prefix + res = ecdsa_address_decode("BrsGxAHga3VbopvSnb3gmLvMBhJNCGuDxBZL44", + 0x22345678, HASHER_SHA2D, decode); + ck_assert_int_eq(res, 0); + + // wrong checksum + res = ecdsa_address_decode("BrsGxAHga3VbopvSnb3gmLvMBhJNCGuDxBZL45", + 0x12345678, HASHER_SHA2D, decode); + ck_assert_int_eq(res, 0); +} +END_TEST + +// https://tools.ietf.org/html/rfc6229#section-2 +START_TEST(test_rc4_rfc6229) { + static const size_t offsets[] = { + 0x0, 0xf0, 0x1f0, 0x2f0, 0x3f0, 0x5f0, 0x7f0, 0xbf0, 0xff0, + }; + + static const struct { + char key[65]; + char vectors[sizeof(offsets) / sizeof(*offsets)][65]; + } tests[] = { + {"0102030405", + { + "b2396305f03dc027ccc3524a0a1118a8" + "6982944f18fc82d589c403a47a0d0919", + "28cb1132c96ce286421dcaadb8b69eae" + "1cfcf62b03eddb641d77dfcf7f8d8c93", + "42b7d0cdd918a8a33dd51781c81f4041" + "6459844432a7da923cfb3eb4980661f6", + "ec10327bde2beefd18f9277680457e22" + "eb62638d4f0ba1fe9fca20e05bf8ff2b", + "45129048e6a0ed0b56b490338f078da5" + "30abbcc7c20b01609f23ee2d5f6bb7df", + "3294f744d8f9790507e70f62e5bbceea" + "d8729db41882259bee4f825325f5a130", + "1eb14a0c13b3bf47fa2a0ba93ad45b8b" + "cc582f8ba9f265e2b1be9112e975d2d7", + "f2e30f9bd102ecbf75aaade9bc35c43c" + "ec0e11c479dc329dc8da7968fe965681", + "068326a2118416d21f9d04b2cd1ca050" + "ff25b58995996707e51fbdf08b34d875", + }}, + {"01020304050607", + { + "293f02d47f37c9b633f2af5285feb46b" + "e620f1390d19bd84e2e0fd752031afc1", + "914f02531c9218810df60f67e338154c" + "d0fdb583073ce85ab83917740ec011d5", + "75f81411e871cffa70b90c74c592e454" + "0bb87202938dad609e87a5a1b079e5e4", + "c2911246b612e7e7b903dfeda1dad866" + "32828f91502b6291368de8081de36fc2", + "f3b9a7e3b297bf9ad804512f9063eff1" + "8ecb67a9ba1f55a5a067e2b026a3676f", + "d2aa902bd42d0d7cfd340cd45810529f" + "78b272c96e42eab4c60bd914e39d06e3", + "f4332fd31a079396ee3cee3f2a4ff049" + "05459781d41fda7f30c1be7e1246c623", + "adfd3868b8e51485d5e610017e3dd609" + "ad26581c0c5be45f4cea01db2f3805d5", + "f3172ceffc3b3d997c85ccd5af1a950c" + "e74b0b9731227fd37c0ec08a47ddd8b8", + }}, + {"0102030405060708", + { + "97ab8a1bf0afb96132f2f67258da15a8" + "8263efdb45c4a18684ef87e6b19e5b09", + "9636ebc9841926f4f7d1f362bddf6e18" + "d0a990ff2c05fef5b90373c9ff4b870a", + "73239f1db7f41d80b643c0c52518ec63" + "163b319923a6bdb4527c626126703c0f", + "49d6c8af0f97144a87df21d91472f966" + "44173a103b6616c5d5ad1cee40c863d0", + "273c9c4b27f322e4e716ef53a47de7a4" + "c6d0e7b226259fa9023490b26167ad1d", + "1fe8986713f07c3d9ae1c163ff8cf9d3" + "8369e1a965610be887fbd0c79162aafb", + "0a0127abb44484b9fbef5abcae1b579f" + "c2cdadc6402e8ee866e1f37bdb47e42c", + "26b51ea37df8e1d6f76fc3b66a7429b3" + "bc7683205d4f443dc1f29dda3315c87b", + "d5fa5a3469d29aaaf83d23589db8c85b" + "3fb46e2c8f0f068edce8cdcd7dfc5862", + }}, + {"0102030405060708090a", + { + "ede3b04643e586cc907dc21851709902" + "03516ba78f413beb223aa5d4d2df6711", + "3cfd6cb58ee0fdde640176ad0000044d" + "48532b21fb6079c9114c0ffd9c04a1ad", + "3e8cea98017109979084b1ef92f99d86" + "e20fb49bdb337ee48b8d8dc0f4afeffe", + "5c2521eacd7966f15e056544bea0d315" + "e067a7031931a246a6c3875d2f678acb", + "a64f70af88ae56b6f87581c0e23e6b08" + "f449031de312814ec6f319291f4a0516", + "bdae85924b3cb1d0a2e33a30c6d79599" + "8a0feddbac865a09bcd127fb562ed60a", + "b55a0a5b51a12a8be34899c3e047511a" + "d9a09cea3ce75fe39698070317a71339", + "552225ed1177f44584ac8cfa6c4eb5fc" + "7e82cbabfc95381b080998442129c2f8", + "1f135ed14ce60a91369d2322bef25e3c" + "08b6be45124a43e2eb77953f84dc8553", + }}, + {"0102030405060708090a0b0c0d0e0f10", + { + "9ac7cc9a609d1ef7b2932899cde41b97" + "5248c4959014126a6e8a84f11d1a9e1c", + "065902e4b620f6cc36c8589f66432f2b" + "d39d566bc6bce3010768151549f3873f", + "b6d1e6c4a5e4771cad79538df295fb11" + "c68c1d5c559a974123df1dbc52a43b89", + "c5ecf88de897fd57fed301701b82a259" + "eccbe13de1fcc91c11a0b26c0bc8fa4d", + "e7a72574f8782ae26aabcf9ebcd66065" + "bdf0324e6083dcc6d3cedd3ca8c53c16", + "b40110c4190b5622a96116b0017ed297" + "ffa0b514647ec04f6306b892ae661181", + "d03d1bc03cd33d70dff9fa5d71963ebd" + "8a44126411eaa78bd51e8d87a8879bf5", + "fabeb76028ade2d0e48722e46c4615a3" + "c05d88abd50357f935a63c59ee537623", + "ff38265c1642c1abe8d3c2fe5e572bf8" + "a36a4c301ae8ac13610ccbc12256cacc", + }}, + {"0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", + { + "eaa6bd25880bf93d3f5d1e4ca2611d91" + "cfa45c9f7e714b54bdfa80027cb14380", + "114ae344ded71b35f2e60febad727fd8" + "02e1e7056b0f623900496422943e97b6", + "91cb93c787964e10d9527d999c6f936b" + "49b18b42f8e8367cbeb5ef104ba1c7cd", + "87084b3ba700bade955610672745b374" + "e7a7b9e9ec540d5ff43bdb12792d1b35", + "c799b596738f6b018c76c74b1759bd90" + "7fec5bfd9f9b89ce6548309092d7e958", + "40f250b26d1f096a4afd4c340a588815" + "3e34135c79db010200767651cf263073", + "f656abccf88dd827027b2ce917d464ec" + "18b62503bfbc077fbabb98f20d98ab34", + "8aed95ee5b0dcbfbef4eb21d3a3f52f9" + "625a1ab00ee39a5327346bddb01a9c18", + "a13a7c79c7e119b5ab0296ab28c300b9" + "f3e4c0a2e02d1d01f7f0a74618af2b48", + }}, + {"833222772a", + { + "80ad97bdc973df8a2e879e92a497efda" + "20f060c2f2e5126501d3d4fea10d5fc0", + "faa148e99046181fec6b2085f3b20ed9" + "f0daf5bab3d596839857846f73fbfe5a", + "1c7e2fc4639232fe297584b296996bc8" + "3db9b249406cc8edffac55ccd322ba12", + "e4f9f7e0066154bbd125b745569bc897" + "75d5ef262b44c41a9cf63ae14568e1b9", + "6da453dbf81e82334a3d8866cb50a1e3" + "7828d074119cab5c22b294d7a9bfa0bb", + "adb89cea9a15fbe617295bd04b8ca05c" + "6251d87fd4aaae9a7e4ad5c217d3f300", + "e7119bd6dd9b22afe8f89585432881e2" + "785b60fd7ec4e9fcb6545f350d660fab", + "afecc037fdb7b0838eb3d70bcd268382" + "dbc1a7b49d57358cc9fa6d61d73b7cf0", + "6349d126a37afcba89794f9804914fdc" + "bf42c3018c2f7c66bfde524975768115", + }}, + {"1910833222772a", + { + "bc9222dbd3274d8fc66d14ccbda6690b" + "7ae627410c9a2be693df5bb7485a63e3", + "3f0931aa03defb300f060103826f2a64" + "beaa9ec8d59bb68129f3027c96361181", + "74e04db46d28648d7dee8a0064b06cfe" + "9b5e81c62fe023c55be42f87bbf932b8", + "ce178fc1826efecbc182f57999a46140" + "8bdf55cd55061c06dba6be11de4a578a", + "626f5f4dce652501f3087d39c92cc349" + "42daac6a8f9ab9a7fd137c6037825682", + "cc03fdb79192a207312f53f5d4dc33d9" + "f70f14122a1c98a3155d28b8a0a8a41d", + "2a3a307ab2708a9c00fe0b42f9c2d6a1" + "862617627d2261eab0b1246597ca0ae9", + "55f877ce4f2e1ddbbf8e13e2cde0fdc8" + "1b1556cb935f173337705fbb5d501fc1", + "ecd0e96602be7f8d5092816cccf2c2e9" + "027881fab4993a1c262024a94fff3f61", + }}, + {"641910833222772a", + { + "bbf609de9413172d07660cb680716926" + "46101a6dab43115d6c522b4fe93604a9", + "cbe1fff21c96f3eef61e8fe0542cbdf0" + "347938bffa4009c512cfb4034b0dd1a7", + "7867a786d00a7147904d76ddf1e520e3" + "8d3e9e1caefcccb3fbf8d18f64120b32", + "942337f8fd76f0fae8c52d7954810672" + "b8548c10f51667f6e60e182fa19b30f7", + "0211c7c6190c9efd1237c34c8f2e06c4" + "bda64f65276d2aacb8f90212203a808e", + "bd3820f732ffb53ec193e79d33e27c73" + "d0168616861907d482e36cdac8cf5749", + "97b0f0f224b2d2317114808fb03af7a0" + "e59616e469787939a063ceea9af956d1", + "c47e0dc1660919c11101208f9e69aa1f" + "5ae4f12896b8379a2aad89b5b553d6b0", + "6b6b098d0c293bc2993d80bf0518b6d9" + "8170cc3ccd92a698621b939dd38fe7b9", + }}, + {"8b37641910833222772a", + { + "ab65c26eddb287600db2fda10d1e605c" + "bb759010c29658f2c72d93a2d16d2930", + "b901e8036ed1c383cd3c4c4dd0a6ab05" + "3d25ce4922924c55f064943353d78a6c", + "12c1aa44bbf87e75e611f69b2c38f49b" + "28f2b3434b65c09877470044c6ea170d", + "bd9ef822de5288196134cf8af7839304" + "67559c23f052158470a296f725735a32", + "8bab26fbc2c12b0f13e2ab185eabf241" + "31185a6d696f0cfa9b42808b38e132a2", + "564d3dae183c5234c8af1e51061c44b5" + "3c0778a7b5f72d3c23a3135c7d67b9f4", + "f34369890fcf16fb517dcaae4463b2dd" + "02f31c81e8200731b899b028e791bfa7", + "72da646283228c14300853701795616f" + "4e0a8c6f7934a788e2265e81d6d0c8f4", + "438dd5eafea0111b6f36b4b938da2a68" + "5f6bfc73815874d97100f086979357d8", + }}, + {"ebb46227c6cc8b37641910833222772a", + { + "720c94b63edf44e131d950ca211a5a30" + "c366fdeacf9ca80436be7c358424d20b", + "b3394a40aabf75cba42282ef25a0059f" + "4847d81da4942dbc249defc48c922b9f", + "08128c469f275342adda202b2b58da95" + "970dacef40ad98723bac5d6955b81761", + "3cb89993b07b0ced93de13d2a11013ac" + "ef2d676f1545c2c13dc680a02f4adbfe", + "b60595514f24bc9fe522a6cad7393644" + "b515a8c5011754f59003058bdb81514e", + "3c70047e8cbc038e3b9820db601da495" + "1175da6ee756de46a53e2b075660b770", + "00a542bba02111cc2c65b38ebdba587e" + "5865fdbb5b48064104e830b380f2aede", + "34b21ad2ad44e999db2d7f0863f0d9b6" + "84a9218fc36e8a5f2ccfbeae53a27d25", + "a2221a11b833ccb498a59540f0545f4a" + "5bbeb4787d59e5373fdbea6c6f75c29b", + }}, + {"c109163908ebe51debb46227c6cc8b37641910833222772a", + { + "54b64e6b5a20b5e2ec84593dc7989da7" + "c135eee237a85465ff97dc03924f45ce", + "cfcc922fb4a14ab45d6175aabbf2d201" + "837b87e2a446ad0ef798acd02b94124f", + "17a6dbd664926a0636b3f4c37a4f4694" + "4a5f9f26aeeed4d4a25f632d305233d9", + "80a3d01ef00c8e9a4209c17f4eeb358c" + "d15e7d5ffaaabc0207bf200a117793a2", + "349682bf588eaa52d0aa1560346aeafa" + "f5854cdb76c889e3ad63354e5f7275e3", + "532c7ceccb39df3236318405a4b1279c" + "baefe6d9ceb651842260e0d1e05e3b90", + "e82d8c6db54e3c633f581c952ba04207" + "4b16e50abd381bd70900a9cd9a62cb23", + "3682ee33bd148bd9f58656cd8f30d9fb" + "1e5a0b8475045d9b20b2628624edfd9e", + "63edd684fb826282fe528f9c0e9237bc" + "e4dd2e98d6960fae0b43545456743391", + }}, + {"1ada31d5cf688221c109163908ebe51debb46227c6cc8b37641910833222772a", + { + "dd5bcb0018e922d494759d7c395d02d3" + "c8446f8f77abf737685353eb89a1c9eb", + "af3e30f9c095045938151575c3fb9098" + "f8cb6274db99b80b1d2012a98ed48f0e", + "25c3005a1cb85de076259839ab7198ab" + "9dcbc183e8cb994b727b75be3180769c", + "a1d3078dfa9169503ed9d4491dee4eb2" + "8514a5495858096f596e4bcd66b10665", + "5f40d59ec1b03b33738efa60b2255d31" + "3477c7f764a41baceff90bf14f92b7cc", + "ac4e95368d99b9eb78b8da8f81ffa795" + "8c3c13f8c2388bb73f38576e65b7c446", + "13c4b9c1dfb66579eddd8a280b9f7316" + "ddd27820550126698efaadc64b64f66e", + "f08f2e66d28ed143f3a237cf9de73559" + "9ea36c525531b880ba124334f57b0b70", + "d5a39e3dfcc50280bac4a6b5aa0dca7d" + "370b1c1fe655916d97fd0d47ca1d72b8", + }}}; + + RC4_CTX ctx; + uint8_t key[64]; + uint8_t buffer[0x1010]; + + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t length = strlen(tests[i].key) / 2; + memcpy(key, fromhex(tests[i].key), length); + memzero(buffer, sizeof(buffer)); + + rc4_init(&ctx, key, length); + rc4_encrypt(&ctx, buffer, sizeof(buffer)); + + for (size_t j = 0; j < (sizeof(offsets) / sizeof(*offsets)); j++) { + size_t size = strlen(tests[i].vectors[j]) / 2; + ck_assert_mem_eq(&buffer[offsets[j]], fromhex(tests[i].vectors[j]), size); + } + } +} +END_TEST + +static void test_compress_coord(const char *k_raw) { + const ecdsa_curve *curve = &secp256k1; + curve_point expected_coords; + + bignum256 k = {0}; + + bn_read_be(fromhex(k_raw), &k); + + point_multiply(curve, &k, &curve->G, &expected_coords); + + uint8_t compress[33] = {0}; + compress_coords(&expected_coords, compress); + + bignum256 x = {0}, y = {0}; + bn_read_be(compress + 1, &x); + uncompress_coords(curve, compress[0], &x, &y); + + ck_assert(bn_is_equal(&expected_coords.x, &x)); + ck_assert(bn_is_equal(&expected_coords.y, &y)); +} + +START_TEST(test_compress_coords) { + static const char *k_raw[] = { + "dc05960ac673fd59554c98655e26722d007bb7ada0c8ff00883fdee70783d0be", + "41e41e0a218c980411108a0a58cf88f528c828b4d6f0d2c86234bc2504bdc3cd", + "1d963ddcb79f6028a32cadd2421ff7fff969bff5774f73063dab41519b3da175", + "2414141f96da0874dbc374b58861589935b7f940806ddf8d2e6b911f62e240f3", + "01cc1fb182e29f60fe43e22d250de34f2d3f956bbef2aa9b182d09e5d9176873", + "89b3d621d813682692fd61b2baea6b2ea696a44abc76925d29c4887fc4db9367", + "20c80c633e05a3a7dfac05fa0e0a7c7a6b708b02323e687735cff81ea5944f59", + "5a803c263aa93a4f74648066c03e63fb00641193bae93dfa254dabd634e8b49c", + "05efbcc87007797dca68315b9271ac8fb75bddbece53f4dcbfb83fc21cb91fc0", + "0bed78ef43474630bd646eef2d7ec19a1acb8e9eecf6a0a3ac7241ac40a7706f", + }; + + for (int i = 0; i < (int)(sizeof(k_raw) / sizeof(*k_raw)); i++) + test_compress_coord(k_raw[i]); +} +END_TEST + +static int my_strncasecmp(const char *s1, const char *s2, size_t n) { + size_t i = 0; + while (i < n) { + char c1 = s1[i]; + char c2 = s2[i]; + if (c1 >= 'A' && c1 <= 'Z') c1 = (c1 - 'A') + 'a'; + if (c2 >= 'A' && c2 <= 'Z') c2 = (c2 - 'A') + 'a'; + if (c1 < c2) return -1; + if (c1 > c2) return 1; + if (c1 == 0) return 0; + ++i; + } + return 0; +} + +#include "test_check_cashaddr.h" +#include "test_check_zilliqa.h" // [wallet-core] +#if USE_SEGWIT +#include "test_check_segwit.h" +#endif + +#if USE_CARDANO +#include "test_check_cardano.h" +#endif + +#if USE_MONERO +#include "test_check_monero.h" +#endif + +// define test suite and cases +Suite *test_suite(void) { + Suite *s = suite_create("trezor-crypto"); + TCase *tc; + + tc = tcase_create("bignum"); + tcase_add_test(tc, test_bignum_read_be); + tcase_add_test(tc, test_bignum_write_be); + tcase_add_test(tc, test_bignum_is_equal); + tcase_add_test(tc, test_bignum_zero); + tcase_add_test(tc, test_bignum_is_zero); + tcase_add_test(tc, test_bignum_one); + tcase_add_test(tc, test_bignum_read_le); + tcase_add_test(tc, test_bignum_write_le); + tcase_add_test(tc, test_bignum_read_uint32); + tcase_add_test(tc, test_bignum_read_uint64); + tcase_add_test(tc, test_bignum_write_uint32); + tcase_add_test(tc, test_bignum_write_uint64); + tcase_add_test(tc, test_bignum_copy); + tcase_add_test(tc, test_bignum_is_even); + tcase_add_test(tc, test_bignum_is_odd); + tcase_add_test(tc, test_bignum_bitcount); + tcase_add_test(tc, test_bignum_digitcount); + tcase_add_test(tc, test_bignum_is_less); + tcase_add_test(tc, test_bignum_format); + tcase_add_test(tc, test_bignum_format_uint64); + tcase_add_test(tc, test_bignum_sqrt); + suite_add_tcase(s, tc); + + tc = tcase_create("base32"); + tcase_add_test(tc, test_base32_rfc4648); + suite_add_tcase(s, tc); + + tc = tcase_create("base58"); + tcase_add_test(tc, test_base58); + suite_add_tcase(s, tc); + +#if USE_GRAPHENE + tc = tcase_create("base58gph"); + tcase_add_test(tc, test_base58gph); + suite_add_tcase(s, tc); +#endif + + tc = tcase_create("bignum_divmod"); + tcase_add_test(tc, test_bignum_divmod); + suite_add_tcase(s, tc); + + tc = tcase_create("bip32"); + tcase_add_test(tc, test_bip32_vector_1); + tcase_add_test(tc, test_bip32_vector_2); + tcase_add_test(tc, test_bip32_vector_3); + tcase_add_test(tc, test_bip32_vector_4); + tcase_add_test(tc, test_bip32_compare); + tcase_add_test(tc, test_bip32_optimized); +#if USE_BIP32_CACHE + tcase_add_test(tc, test_bip32_cache_1); + tcase_add_test(tc, test_bip32_cache_2); +#endif + suite_add_tcase(s, tc); + + tc = tcase_create("bip32-nist"); + tcase_add_test(tc, test_bip32_nist_seed); + tcase_add_test(tc, test_bip32_nist_vector_1); + tcase_add_test(tc, test_bip32_nist_vector_2); + tcase_add_test(tc, test_bip32_nist_compare); + tcase_add_test(tc, test_bip32_nist_repeat); + suite_add_tcase(s, tc); + + tc = tcase_create("bip32-ed25519"); + tcase_add_test(tc, test_bip32_ed25519_vector_1); + tcase_add_test(tc, test_bip32_ed25519_vector_2); + suite_add_tcase(s, tc); + + tc = tcase_create("bip32-ecdh"); + tcase_add_test(tc, test_bip32_ecdh_nist256p1); + tcase_add_test(tc, test_bip32_ecdh_curve25519); + tcase_add_test(tc, test_bip32_ecdh_errors); + suite_add_tcase(s, tc); + + tc = tcase_create("bip32-decred"); + tcase_add_test(tc, test_bip32_decred_vector_1); + tcase_add_test(tc, test_bip32_decred_vector_2); + suite_add_tcase(s, tc); + + tc = tcase_create("ecdsa"); + tcase_add_test(tc, test_ecdsa_get_public_key33); + tcase_add_test(tc, test_ecdsa_get_public_key65); + tcase_add_test(tc, test_ecdsa_recover_pub_from_sig); + tcase_add_test(tc, test_ecdsa_verify_digest); +#if USE_RFC6979 + tcase_add_test(tc, test_ecdsa_sign_digest_deterministic); +#endif + suite_add_tcase(s, tc); + + tc = tcase_create("rfc6979"); + tcase_add_test(tc, test_rfc6979); + suite_add_tcase(s, tc); + + tc = tcase_create("address"); + tcase_add_test(tc, test_address); + suite_add_tcase(s, tc); + + tc = tcase_create("address_decode"); + tcase_add_test(tc, test_address_decode); + suite_add_tcase(s, tc); + +#if USE_ETHEREUM + tc = tcase_create("ethereum_address"); + tcase_add_test(tc, test_ethereum_address); + suite_add_tcase(s, tc); + + tc = tcase_create("rsk_address"); + tcase_add_test(tc, test_rsk_address); + suite_add_tcase(s, tc); +#endif + + tc = tcase_create("wif"); + tcase_add_test(tc, test_wif); + suite_add_tcase(s, tc); + + tc = tcase_create("ecdsa_der"); + tcase_add_test(tc, test_ecdsa_der); + suite_add_tcase(s, tc); + + tc = tcase_create("aes"); + tcase_add_test(tc, test_aes); + suite_add_tcase(s, tc); + + tc = tcase_create("sha2"); + tcase_add_test(tc, test_sha1); + tcase_add_test(tc, test_sha256); + tcase_add_test(tc, test_sha512); + suite_add_tcase(s, tc); + + tc = tcase_create("sha3"); + tcase_add_test(tc, test_sha3_256); + tcase_add_test(tc, test_sha3_512); + tcase_add_test(tc, test_keccak_256); + suite_add_tcase(s, tc); + + tc = tcase_create("blake"); + tcase_add_test(tc, test_blake256); + suite_add_tcase(s, tc); + + tc = tcase_create("blake2"); + tcase_add_test(tc, test_blake2b); + tcase_add_test(tc, test_blake2bp); + tcase_add_test(tc, test_blake2s); + suite_add_tcase(s, tc); + + tc = tcase_create("chacha_drbg"); + tcase_add_test(tc, test_chacha_drbg); + suite_add_tcase(s, tc); + + tc = tcase_create("pbkdf2"); + tcase_add_test(tc, test_pbkdf2_hmac_sha256); + tcase_add_test(tc, test_pbkdf2_hmac_sha512); + suite_add_tcase(s, tc); + + tc = tcase_create("hmac_drbg"); + tcase_add_test(tc, test_hmac_drbg); + suite_add_tcase(s, tc); + + tc = tcase_create("bip39"); + tcase_add_test(tc, test_mnemonic); + tcase_add_test(tc, test_mnemonic_check); + tcase_add_test(tc, test_mnemonic_to_bits); + tcase_add_test(tc, test_mnemonic_find_word); + suite_add_tcase(s, tc); + + tc = tcase_create("slip39"); + tcase_add_test(tc, test_slip39_get_word); + tcase_add_test(tc, test_slip39_word_index); + tcase_add_test(tc, test_slip39_word_completion_mask); + tcase_add_test(tc, test_slip39_sequence_to_word); + tcase_add_test(tc, test_slip39_word_completion); + suite_add_tcase(s, tc); + + tc = tcase_create("shamir"); + tcase_add_test(tc, test_shamir); + suite_add_tcase(s, tc); + + tc = tcase_create("pubkey_validity"); + tcase_add_test(tc, test_pubkey_validity); + suite_add_tcase(s, tc); + + tc = tcase_create("pubkey_uncompress"); + tcase_add_test(tc, test_pubkey_uncompress); + suite_add_tcase(s, tc); + + tc = tcase_create("codepoints"); + tcase_add_test(tc, test_codepoints_secp256k1); + tcase_add_test(tc, test_codepoints_nist256p1); + suite_add_tcase(s, tc); + + tc = tcase_create("mult_border_cases"); + tcase_add_test(tc, test_mult_border_cases_secp256k1); + tcase_add_test(tc, test_mult_border_cases_nist256p1); + suite_add_tcase(s, tc); + + tc = tcase_create("scalar_mult"); + tcase_add_test(tc, test_scalar_mult_secp256k1); + tcase_add_test(tc, test_scalar_mult_nist256p1); + suite_add_tcase(s, tc); + + tc = tcase_create("point_mult"); + tcase_add_test(tc, test_point_mult_secp256k1); + tcase_add_test(tc, test_point_mult_nist256p1); + suite_add_tcase(s, tc); + + tc = tcase_create("scalar_point_mult"); + tcase_add_test(tc, test_scalar_point_mult_secp256k1); + tcase_add_test(tc, test_scalar_point_mult_nist256p1); + suite_add_tcase(s, tc); + + tc = tcase_create("ed25519"); + tcase_add_test(tc, test_ed25519); + suite_add_tcase(s, tc); + + tc = tcase_create("ed25519_keccak"); + tcase_add_test(tc, test_ed25519_keccak); + suite_add_tcase(s, tc); + + tc = tcase_create("ed25519_cosi"); + tcase_add_test(tc, test_ed25519_cosi); + suite_add_tcase(s, tc); + + tc = tcase_create("ed25519_modm"); + tcase_add_test(tc, test_ed25519_modl_add); + tcase_add_test(tc, test_ed25519_modl_neg); + tcase_add_test(tc, test_ed25519_modl_sub); + suite_add_tcase(s, tc); + +#if USE_MONERO + tc = tcase_create("ed25519_ge"); + tcase_add_test(tc, test_ge25519_double_scalarmult_vartime2); + suite_add_tcase(s, tc); +#endif + + tc = tcase_create("script"); + tcase_add_test(tc, test_output_script); + suite_add_tcase(s, tc); + +#if USE_ETHEREUM + tc = tcase_create("ethereum_pubkeyhash"); + tcase_add_test(tc, test_ethereum_pubkeyhash); + suite_add_tcase(s, tc); +#endif + +#if USE_NEM + tc = tcase_create("nem_address"); + tcase_add_test(tc, test_nem_address); + suite_add_tcase(s, tc); + + tc = tcase_create("nem_encryption"); + tcase_add_test(tc, test_nem_derive); + tcase_add_test(tc, test_nem_cipher); + suite_add_tcase(s, tc); + + tc = tcase_create("nem_transaction"); + tcase_add_test(tc, test_nem_transaction_transfer); + tcase_add_test(tc, test_nem_transaction_multisig); + tcase_add_test(tc, test_nem_transaction_provision_namespace); + tcase_add_test(tc, test_nem_transaction_mosaic_creation); + tcase_add_test(tc, test_nem_transaction_mosaic_supply_change); + tcase_add_test(tc, test_nem_transaction_aggregate_modification); + suite_add_tcase(s, tc); +#endif + + tc = tcase_create("multibyte_address"); + tcase_add_test(tc, test_multibyte_address); + suite_add_tcase(s, tc); + + tc = tcase_create("rc4"); + tcase_add_test(tc, test_rc4_rfc6229); + suite_add_tcase(s, tc); + +#if USE_SEGWIT + tc = tcase_create("segwit"); + tcase_add_test(tc, test_segwit); + suite_add_tcase(s, tc); +#endif + + tc = tcase_create("cashaddr"); + tcase_add_test(tc, test_cashaddr); + suite_add_tcase(s, tc); + + tc = tcase_create("compress_coords"); + tcase_add_test(tc, test_compress_coords); + suite_add_tcase(s, tc); + +#if USE_CARDANO + tc = tcase_create("bip32-cardano"); + + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_1); + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_2); + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_3); + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_4); + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_5); + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_6); + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_7); + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_8); + tcase_add_test(tc, test_bip32_cardano_hdnode_vector_9); + + tcase_add_test(tc, test_cardano_ledger_vector_1); + tcase_add_test(tc, test_cardano_ledger_vector_2); + tcase_add_test(tc, test_cardano_ledger_vector_3); + + tcase_add_test(tc, test_ed25519_cardano_sign_vectors); + suite_add_tcase(s, tc); +#endif + +#if USE_MONERO + tc = tcase_create("xmr_base58"); + tcase_add_test(tc, test_xmr_base58); + suite_add_tcase(s, tc); + + tc = tcase_create("xmr_crypto"); + tcase_add_test(tc, test_xmr_getset256_modm); + tcase_add_test(tc, test_xmr_cmp256_modm); + tcase_add_test(tc, test_xmr_copy_check_modm); + tcase_add_test(tc, test_xmr_mulsub256_modm); + tcase_add_test(tc, test_xmr_muladd256_modm); + tcase_add_test(tc, test_xmr_curve25519_set); + tcase_add_test(tc, test_xmr_curve25519_consts); + tcase_add_test(tc, test_xmr_curve25519_tests); + tcase_add_test(tc, test_xmr_curve25519_expand_reduce); + tcase_add_test(tc, test_xmr_ge25519_base); + tcase_add_test(tc, test_xmr_ge25519_check); + tcase_add_test(tc, test_xmr_ge25519_scalarmult_base_wrapper); + tcase_add_test(tc, test_xmr_ge25519_scalarmult); + tcase_add_test(tc, test_xmr_ge25519_ops); + suite_add_tcase(s, tc); + + tc = tcase_create("xmr_xmr"); + tcase_add_test(tc, test_xmr_check_point); + tcase_add_test(tc, test_xmr_h); + tcase_add_test(tc, test_xmr_fast_hash); + tcase_add_test(tc, test_xmr_hasher); + tcase_add_test(tc, test_xmr_hash_to_scalar); + tcase_add_test(tc, test_xmr_hash_to_ec); + tcase_add_test(tc, test_xmr_derivation_to_scalar); + tcase_add_test(tc, test_xmr_generate_key_derivation); + tcase_add_test(tc, test_xmr_derive_private_key); + tcase_add_test(tc, test_xmr_derive_public_key); + tcase_add_test(tc, test_xmr_add_keys2); + tcase_add_test(tc, test_xmr_add_keys3); + tcase_add_test(tc, test_xmr_get_subaddress_secret_key); + tcase_add_test(tc, test_xmr_gen_c); + tcase_add_test(tc, test_xmr_varint); + suite_add_tcase(s, tc); +#endif + + return s; +} + +// run suite +int main(void) { + int number_failed; + Suite *s = test_suite(); + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_VERBOSE); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + if (number_failed == 0) { + printf("PASSED ALL TESTS\n"); + } + return number_failed; +} diff --git a/tools/windows-replace/trezor-crypto/include/TrezorCrypto/aes.h b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/aes.h new file mode 100644 index 00000000000..62e9518a6a6 --- /dev/null +++ b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/aes.h @@ -0,0 +1,228 @@ +/* +--------------------------------------------------------------------------- +Copyright (c) 1998-2013, Brian Gladman, Worcester, UK. All rights reserved. + +The redistribution and use of this software (with or without changes) +is allowed without the payment of fees or royalties provided that: + + source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation. + +This software is provided 'as is' with no explicit or implied warranties +in respect of its operation, including, but not limited to, correctness +and fitness for purpose. +--------------------------------------------------------------------------- +Issue Date: 02/08/2018 + + This file contains the definitions required to use AES in C. See aesopt.h + for optimisation details. +*/ + +#ifndef _AES_H +#define _AES_H + +#include + +#include +#include + +#define VOID_RETURN void +#define INT_RETURN int +#define ALIGN_OFFSET(x,n) (((intptr_t)(x)) & ((n) - 1)) +#define ALIGN_FLOOR(x,n) ((uint8_t*)(x) - ( ((intptr_t)(x)) & ((n) - 1))) +#define ALIGN_CEIL(x,n) ((uint8_t*)(x) + (-((intptr_t)(x)) & ((n) - 1))) + +#if defined(__cplusplus) +extern "C" +{ +#endif + +// #define AES_128 /* if a fast 128 bit key scheduler is needed */ +// #define AES_192 /* if a fast 192 bit key scheduler is needed */ +#define AES_256 /* if a fast 256 bit key scheduler is needed */ +// #define AES_VAR /* if variable key size scheduler is needed */ +#if 1 +# define AES_MODES /* if support is needed for modes in the C code */ +#endif /* (these will use AES_NI if it is present) */ +#if 0 /* add this to make direct calls to the AES_NI */ +# /* implemented CBC and CTR modes available */ +# define ADD_AESNI_MODE_CALLS +#endif + +/* The following must also be set in assembler files if being used */ + +#define AES_ENCRYPT /* if support for encryption is needed */ +#define AES_DECRYPT /* if support for decryption is needed */ + +#define AES_BLOCK_SIZE_P2 4 /* AES block size as a power of 2 */ +#define AES_BLOCK_SIZE (1 << AES_BLOCK_SIZE_P2) /* AES block size */ +#define N_COLS 4 /* the number of columns in the state */ + +/* The key schedule length is 11, 13 or 15 16-byte blocks for 128, */ +/* 192 or 256-bit keys respectively. That is 176, 208 or 240 bytes */ +/* or 44, 52 or 60 32-bit words. */ + +#if defined( AES_VAR ) || defined( AES_256 ) +#define KS_LENGTH 60 +#elif defined( AES_192 ) +#define KS_LENGTH 52 +#else +#define KS_LENGTH 44 +#endif + +#define AES_RETURN INT_RETURN + +/* the character array 'inf' in the following structures is used */ +/* to hold AES context information. This AES code uses cx->inf.b[0] */ +/* to hold the number of rounds multiplied by 16. The other three */ +/* elements can be used by code that implements additional modes */ + +typedef union +{ uint32_t l; + uint8_t b[4]; +} aes_inf; + +#ifdef _MSC_VER //win Ignore the warning 4324 +# pragma warning( disable : 4324 ) +#endif + +#if defined(_MSC_VER) && defined(_WIN64) +#define ALIGNED_(x) __declspec(align(x)) +#elif defined(__GNUC__) && defined(__x86_64__) +#define ALIGNED_(x) __attribute__ ((aligned(x))) +#else +#define ALIGNED_(x) +#endif + +typedef struct ALIGNED_(16) +{ uint32_t ks[KS_LENGTH]; + aes_inf inf; +} aes_encrypt_ctx; + +typedef struct ALIGNED_(16) +{ uint32_t ks[KS_LENGTH]; + aes_inf inf; +} aes_decrypt_ctx; + +#ifdef _MSC_VER +# pragma warning( default : 4324 ) +#endif + +/* This routine must be called before first use if non-static */ +/* tables are being used */ + +AES_RETURN aes_init(void); + +/* Key lengths in the range 16 <= key_len <= 32 are given in bytes, */ +/* those in the range 128 <= key_len <= 256 are given in bits */ + +#if defined( AES_ENCRYPT ) + +#if defined( AES_128 ) || defined( AES_VAR) +AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]); +#endif + +#if defined( AES_192 ) || defined( AES_VAR) +AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]); +#endif + +#if defined( AES_256 ) || defined( AES_VAR) +AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]); +#endif + +#if defined( AES_VAR ) +AES_RETURN aes_encrypt_key(const unsigned char *key, int key_len, aes_encrypt_ctx cx[1]); +#endif + +AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]); + +#endif + +#if defined( AES_DECRYPT ) + +#if defined( AES_128 ) || defined( AES_VAR) +AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]); +#endif + +#if defined( AES_192 ) || defined( AES_VAR) +AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]); +#endif + +#if defined( AES_256 ) || defined( AES_VAR) +AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]); +#endif + +#if defined( AES_VAR ) +AES_RETURN aes_decrypt_key(const unsigned char *key, int key_len, aes_decrypt_ctx cx[1]); +#endif + +AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]); + +#endif + +#if defined( AES_MODES ) + +/* Multiple calls to the following subroutines for multiple block */ +/* ECB, CBC, CFB, OFB and CTR mode encryption can be used to handle */ +/* long messages incrementally provided that the context AND the iv */ +/* are preserved between all such calls. For the ECB and CBC modes */ +/* each individual call within a series of incremental calls must */ +/* process only full blocks (i.e. len must be a multiple of 16) but */ +/* the CFB, OFB and CTR mode calls can handle multiple incremental */ +/* calls of any length. Each mode is reset when a new AES key is */ +/* set but ECB needs no reset and CBC can be reset without setting */ +/* a new key by setting a new IV value. To reset CFB, OFB and CTR */ +/* without setting the key, aes_mode_reset() must be called and the */ +/* IV must be set. NOTE: All these calls update the IV on exit so */ +/* this has to be reset if a new operation with the same IV as the */ +/* previous one is required (or decryption follows encryption with */ +/* the same IV array). */ + +AES_RETURN aes_test_alignment_detection(unsigned int n); + +AES_RETURN aes_ecb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_ecb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_cbc_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cbc_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_mode_reset(aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + +#define aes_ofb_encrypt aes_ofb_crypt +#define aes_ofb_decrypt aes_ofb_crypt + +AES_RETURN aes_ofb_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + +typedef void cbuf_inc(unsigned char *cbuf); + +#define aes_ctr_encrypt aes_ctr_crypt +#define aes_ctr_decrypt aes_ctr_crypt + +AES_RETURN aes_ctr_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *cbuf, cbuf_inc ctr_inc, aes_encrypt_ctx cx[1]); + +void aes_ctr_cbuf_inc(unsigned char *cbuf); + +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/tools/windows-replace/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-donna-portable.h b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-donna-portable.h new file mode 100644 index 00000000000..33dad64dcf1 --- /dev/null +++ b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-donna-portable.h @@ -0,0 +1,38 @@ +#define mul32x32_64(a,b) (((uint64_t)(a))*(b)) + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DONNA_INLINE +#undef ALIGN + +#ifdef _MSC_VER +#define ALIGN(x) __declspec(align(x)) +#else +#define ALIGN(x) __attribute__((aligned(x))) +#endif + + +static inline void U32TO8_LE(unsigned char *p, const uint32_t v) { + p[0] = (unsigned char)(v ); + p[1] = (unsigned char)(v >> 8); + p[2] = (unsigned char)(v >> 16); + p[3] = (unsigned char)(v >> 24); +} + +static inline uint32_t U8TO32_LE(const unsigned char *p) { + return + (((uint32_t)(p[0]) ) | + ((uint32_t)(p[1]) << 8) | + ((uint32_t)(p[2]) << 16) | + ((uint32_t)(p[3]) << 24)); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/tools/windows-replace/trezor-crypto/include/TrezorCrypto/endian.h b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/endian.h new file mode 100644 index 00000000000..e456cf945cd --- /dev/null +++ b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/endian.h @@ -0,0 +1,132 @@ +/*- + * Copyright 2007-2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ +#ifndef _ENDIAN_H_ +#define _ENDIAN_H_ + + +#include +#ifdef _MSC_VER + #define INLINE __inline +#else + #define INLINE inline +#endif + +static INLINE uint32_t +be32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); +} + +static INLINE void +be32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[3] = x & 0xff; + p[2] = (x >> 8) & 0xff; + p[1] = (x >> 16) & 0xff; + p[0] = (x >> 24) & 0xff; +} + +static INLINE uint64_t +be64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) + + ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) + + ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) + + ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56)); +} + +static INLINE void +be64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[7] = x & 0xff; + p[6] = (x >> 8) & 0xff; + p[5] = (x >> 16) & 0xff; + p[4] = (x >> 24) & 0xff; + p[3] = (x >> 32) & 0xff; + p[2] = (x >> 40) & 0xff; + p[1] = (x >> 48) & 0xff; + p[0] = (x >> 56) & 0xff; +} + +static INLINE uint32_t +le32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); +} + +static INLINE void +le32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; +} + +static INLINE uint64_t +le64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) + + ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) + + ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) + + ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56)); +} + +static INLINE void +le64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; + p[4] = (x >> 32) & 0xff; + p[5] = (x >> 40) & 0xff; + p[6] = (x >> 48) & 0xff; + p[7] = (x >> 56) & 0xff; +} + +#endif /* !_ENDIAN_H_ */ diff --git a/tools/windows-replace/trezor-crypto/include/TrezorCrypto/groestl_internal.h b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/groestl_internal.h new file mode 100644 index 00000000000..84587358e4a --- /dev/null +++ b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/groestl_internal.h @@ -0,0 +1,510 @@ +/* Groestl hash from https://github.com/Groestlcoin/vanitygen + * Trezor adaptation by Yura Pakhuchiy . */ +/** + * Basic type definitions. + * + * This header file defines the generic integer types that will be used + * for the implementation of hash functions; it also contains helper + * functions which encode and decode multi-byte integer values, using + * either little-endian or big-endian conventions. + * + * This file contains a compile-time test on the size of a byte + * (the unsigned char C type). If bytes are not octets, + * i.e. if they do not have a size of exactly 8 bits, then compilation + * is aborted. Architectures where bytes are not octets are relatively + * rare, even in the embedded devices market. We forbid non-octet bytes + * because there is no clear convention on how octet streams are encoded + * on such systems. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @file sph_types.h + * @author Thomas Pornin + */ + +#ifndef GROESTL_INTERNAL_H__ +#define GROESTL_INTERNAL_H__ + +#include + +/* + * All our I/O functions are defined over octet streams. We do not know + * how to handle input data if bytes are not octets. + */ +#if CHAR_BIT != 8 +#error This code requires 8-bit bytes +#endif + +//win #if defined __STDC__ && __STDC_VERSION__ >= 199901L +#if (defined __STDC__ && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) // win +#include + +typedef uint32_t sph_u32; +typedef int32_t sph_s32; +typedef uint64_t sph_u64; +typedef int64_t sph_s64; + +#define SPH_C32(x) ((sph_u32)(x)) +#define SPH_C64(x) ((sph_u64)(x)) + +#else +#error We need at least C99 compiler +#endif + +#define SPH_T32(x) ((x) & SPH_C32(0xFFFFFFFF)) +#define SPH_ROTL32(x, n) SPH_T32(((x) << (n)) | ((x) >> (32 - (n)))) +#define SPH_ROTR32(x, n) SPH_ROTL32(x, (32 - (n))) + +#define SPH_T64(x) ((x) & SPH_C64(0xFFFFFFFFFFFFFFFF)) +#define SPH_ROTL64(x, n) SPH_T64(((x) << (n)) | ((x) >> (64 - (n)))) +#define SPH_ROTR64(x, n) SPH_ROTL64(x, (64 - (n))) + +/* + * 32-bit x86, aka "i386 compatible". + */ +#if defined __i386__ || defined _M_IX86 + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +/* + * 64-bit x86, hereafter known as "amd64". + */ +#elif defined __x86_64 || defined _M_X64 + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +/* + * ARM, little-endian. + */ +#elif defined __arm__ && __ARMEL__ + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +/* + * ARM64, little-endian. + */ +#elif defined __aarch64__ + +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_BIG_ENDIAN 0 + +#endif + +#define SPH_LITTLE_ENDIAN 1 // [wallet-core] + +#if defined SPH_DETECT_LITTLE_ENDIAN && !defined SPH_LITTLE_ENDIAN +#define SPH_LITTLE_ENDIAN SPH_DETECT_LITTLE_ENDIAN +#endif +#if defined SPH_DETECT_BIG_ENDIAN && !defined SPH_BIG_ENDIAN +#define SPH_BIG_ENDIAN SPH_DETECT_BIG_ENDIAN +#endif + +static inline sph_u32 +sph_bswap32(sph_u32 x) +{ + x = SPH_T32((x << 16) | (x >> 16)); + x = ((x & SPH_C32(0xFF00FF00)) >> 8) + | ((x & SPH_C32(0x00FF00FF)) << 8); + return x; +} + +/** + * Byte-swap a 64-bit value. + * + * @param x the input value + * @return the byte-swapped value + */ +static inline sph_u64 +sph_bswap64(sph_u64 x) +{ + x = SPH_T64((x << 32) | (x >> 32)); + x = ((x & SPH_C64(0xFFFF0000FFFF0000)) >> 16) + | ((x & SPH_C64(0x0000FFFF0000FFFF)) << 16); + x = ((x & SPH_C64(0xFF00FF00FF00FF00)) >> 8) + | ((x & SPH_C64(0x00FF00FF00FF00FF)) << 8); + return x; +} + +static inline void +sph_enc16be(void *dst, unsigned val) +{ + ((unsigned char *)dst)[0] = (val >> 8); + ((unsigned char *)dst)[1] = val; +} + +static inline unsigned +sph_dec16be(const void *src) +{ + return ((unsigned)(((const unsigned char *)src)[0]) << 8) + | (unsigned)(((const unsigned char *)src)[1]); +} + +static inline void +sph_enc16le(void *dst, unsigned val) +{ + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = val >> 8; +} + +static inline unsigned +sph_dec16le(const void *src) +{ + return (unsigned)(((const unsigned char *)src)[0]) + | ((unsigned)(((const unsigned char *)src)[1]) << 8); +} + +/** + * Encode a 32-bit value into the provided buffer (big endian convention). + * + * @param dst the destination buffer + * @param val the 32-bit value to encode + */ +static inline void +sph_enc32be(void *dst, sph_u32 val) +{ + ((unsigned char *)dst)[0] = (val >> 24); + ((unsigned char *)dst)[1] = (val >> 16); + ((unsigned char *)dst)[2] = (val >> 8); + ((unsigned char *)dst)[3] = val; +} + +/** + * Encode a 32-bit value into the provided buffer (big endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (32-bit aligned) + * @param val the value to encode + */ +static inline void +sph_enc32be_aligned(void *dst, sph_u32 val) +{ +#if SPH_LITTLE_ENDIAN + *(sph_u32 *)dst = sph_bswap32(val); +#elif SPH_BIG_ENDIAN + *(sph_u32 *)dst = val; +#else + ((unsigned char *)dst)[0] = (val >> 24); + ((unsigned char *)dst)[1] = (val >> 16); + ((unsigned char *)dst)[2] = (val >> 8); + ((unsigned char *)dst)[3] = val; +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (big endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u32 +sph_dec32be(const void *src) +{ + return ((sph_u32)(((const unsigned char *)src)[0]) << 24) + | ((sph_u32)(((const unsigned char *)src)[1]) << 16) + | ((sph_u32)(((const unsigned char *)src)[2]) << 8) + | (sph_u32)(((const unsigned char *)src)[3]); +} + +/** + * Decode a 32-bit value from the provided buffer (big endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (32-bit aligned) + * @return the decoded value + */ +static inline sph_u32 +sph_dec32be_aligned(const void *src) +{ +#if SPH_LITTLE_ENDIAN + return sph_bswap32(*(const sph_u32 *)src); +#elif SPH_BIG_ENDIAN + return *(const sph_u32 *)src; +#else + return ((sph_u32)(((const unsigned char *)src)[0]) << 24) + | ((sph_u32)(((const unsigned char *)src)[1]) << 16) + | ((sph_u32)(((const unsigned char *)src)[2]) << 8) + | (sph_u32)(((const unsigned char *)src)[3]); +#endif +} + +/** + * Encode a 32-bit value into the provided buffer (little endian convention). + * + * @param dst the destination buffer + * @param val the 32-bit value to encode + */ +static inline void +sph_enc32le(void *dst, sph_u32 val) +{ + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); +} + +/** + * Encode a 32-bit value into the provided buffer (little endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (32-bit aligned) + * @param val the value to encode + */ +static inline void +sph_enc32le_aligned(void *dst, sph_u32 val) +{ +#if SPH_LITTLE_ENDIAN + *(sph_u32 *)dst = val; +#elif SPH_BIG_ENDIAN + *(sph_u32 *)dst = sph_bswap32(val); +#else + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (little endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u32 +sph_dec32le(const void *src) +{ + return (sph_u32)(((const unsigned char *)src)[0]) + | ((sph_u32)(((const unsigned char *)src)[1]) << 8) + | ((sph_u32)(((const unsigned char *)src)[2]) << 16) + | ((sph_u32)(((const unsigned char *)src)[3]) << 24); +} + +/** + * Decode a 32-bit value from the provided buffer (little endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (32-bit aligned) + * @return the decoded value + */ +static inline sph_u32 +sph_dec32le_aligned(const void *src) +{ +#if SPH_LITTLE_ENDIAN + return *(const sph_u32 *)src; +#elif SPH_BIG_ENDIAN + return sph_bswap32(*(const sph_u32 *)src); +#else + return (sph_u32)(((const unsigned char *)src)[0]) + | ((sph_u32)(((const unsigned char *)src)[1]) << 8) + | ((sph_u32)(((const unsigned char *)src)[2]) << 16) + | ((sph_u32)(((const unsigned char *)src)[3]) << 24); +#endif +} + +/** + * Encode a 64-bit value into the provided buffer (big endian convention). + * + * @param dst the destination buffer + * @param val the 64-bit value to encode + */ +static inline void +sph_enc64be(void *dst, sph_u64 val) +{ + ((unsigned char *)dst)[0] = (val >> 56); + ((unsigned char *)dst)[1] = (val >> 48); + ((unsigned char *)dst)[2] = (val >> 40); + ((unsigned char *)dst)[3] = (val >> 32); + ((unsigned char *)dst)[4] = (val >> 24); + ((unsigned char *)dst)[5] = (val >> 16); + ((unsigned char *)dst)[6] = (val >> 8); + ((unsigned char *)dst)[7] = val; +} + +/** + * Encode a 64-bit value into the provided buffer (big endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (64-bit aligned) + * @param val the value to encode + */ +static inline void +sph_enc64be_aligned(void *dst, sph_u64 val) +{ +#if SPH_LITTLE_ENDIAN + *(sph_u64 *)dst = sph_bswap64(val); +#elif SPH_BIG_ENDIAN + *(sph_u64 *)dst = val; +#else + ((unsigned char *)dst)[0] = (val >> 56); + ((unsigned char *)dst)[1] = (val >> 48); + ((unsigned char *)dst)[2] = (val >> 40); + ((unsigned char *)dst)[3] = (val >> 32); + ((unsigned char *)dst)[4] = (val >> 24); + ((unsigned char *)dst)[5] = (val >> 16); + ((unsigned char *)dst)[6] = (val >> 8); + ((unsigned char *)dst)[7] = val; +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (big endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u64 +sph_dec64be(const void *src) +{ + return ((sph_u64)(((const unsigned char *)src)[0]) << 56) + | ((sph_u64)(((const unsigned char *)src)[1]) << 48) + | ((sph_u64)(((const unsigned char *)src)[2]) << 40) + | ((sph_u64)(((const unsigned char *)src)[3]) << 32) + | ((sph_u64)(((const unsigned char *)src)[4]) << 24) + | ((sph_u64)(((const unsigned char *)src)[5]) << 16) + | ((sph_u64)(((const unsigned char *)src)[6]) << 8) + | (sph_u64)(((const unsigned char *)src)[7]); +} + +/** + * Decode a 64-bit value from the provided buffer (big endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (64-bit aligned) + * @return the decoded value + */ +static inline sph_u64 +sph_dec64be_aligned(const void *src) +{ +#if SPH_LITTLE_ENDIAN + return sph_bswap64(*(const sph_u64 *)src); +#elif SPH_BIG_ENDIAN + return *(const sph_u64 *)src; +#else + return ((sph_u64)(((const unsigned char *)src)[0]) << 56) + | ((sph_u64)(((const unsigned char *)src)[1]) << 48) + | ((sph_u64)(((const unsigned char *)src)[2]) << 40) + | ((sph_u64)(((const unsigned char *)src)[3]) << 32) + | ((sph_u64)(((const unsigned char *)src)[4]) << 24) + | ((sph_u64)(((const unsigned char *)src)[5]) << 16) + | ((sph_u64)(((const unsigned char *)src)[6]) << 8) + | (sph_u64)(((const unsigned char *)src)[7]); +#endif +} + +/** + * Encode a 64-bit value into the provided buffer (little endian convention). + * + * @param dst the destination buffer + * @param val the 64-bit value to encode + */ +static inline void +sph_enc64le(void *dst, sph_u64 val) +{ + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); + ((unsigned char *)dst)[4] = (val >> 32); + ((unsigned char *)dst)[5] = (val >> 40); + ((unsigned char *)dst)[6] = (val >> 48); + ((unsigned char *)dst)[7] = (val >> 56); +} + +/** + * Encode a 64-bit value into the provided buffer (little endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (64-bit aligned) + * @param val the value to encode + */ +static inline void +sph_enc64le_aligned(void *dst, sph_u64 val) +{ +#if SPH_LITTLE_ENDIAN + *(sph_u64 *)dst = val; +#elif SPH_BIG_ENDIAN + *(sph_u64 *)dst = sph_bswap64(val); +#else + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); + ((unsigned char *)dst)[4] = (val >> 32); + ((unsigned char *)dst)[5] = (val >> 40); + ((unsigned char *)dst)[6] = (val >> 48); + ((unsigned char *)dst)[7] = (val >> 56); +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (little endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static inline sph_u64 +sph_dec64le(const void *src) +{ + return (sph_u64)(((const unsigned char *)src)[0]) + | ((sph_u64)(((const unsigned char *)src)[1]) << 8) + | ((sph_u64)(((const unsigned char *)src)[2]) << 16) + | ((sph_u64)(((const unsigned char *)src)[3]) << 24) + | ((sph_u64)(((const unsigned char *)src)[4]) << 32) + | ((sph_u64)(((const unsigned char *)src)[5]) << 40) + | ((sph_u64)(((const unsigned char *)src)[6]) << 48) + | ((sph_u64)(((const unsigned char *)src)[7]) << 56); +} + +/** + * Decode a 64-bit value from the provided buffer (little endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (64-bit aligned) + * @return the decoded value + */ +static inline sph_u64 +sph_dec64le_aligned(const void *src) +{ +#if SPH_LITTLE_ENDIAN + return *(const sph_u64 *)src; +#elif SPH_BIG_ENDIAN + return sph_bswap64(*(const sph_u64 *)src); +#else + return (sph_u64)(((const unsigned char *)src)[0]) + | ((sph_u64)(((const unsigned char *)src)[1]) << 8) + | ((sph_u64)(((const unsigned char *)src)[2]) << 16) + | ((sph_u64)(((const unsigned char *)src)[3]) << 24) + | ((sph_u64)(((const unsigned char *)src)[4]) << 32) + | ((sph_u64)(((const unsigned char *)src)[5]) << 40) + | ((sph_u64)(((const unsigned char *)src)[6]) << 48) + | ((sph_u64)(((const unsigned char *)src)[7]) << 56); +#endif +} + +#endif diff --git a/tools/windows-replace/trezor-crypto/include/TrezorCrypto/nist256p1.h b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/nist256p1.h new file mode 100644 index 00000000000..0c0a743d566 --- /dev/null +++ b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/nist256p1.h @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __NIST256P1_H__ +#define __NIST256P1_H__ + +#include + +#include "bip32.h" +#include "ecdsa.h" + +//extern const ecdsa_curve nist256p1; +//extern const curve_info nist256p1_info; +//win +#ifdef __cplusplus +extern "C" { +#endif + +extern const ecdsa_curve nist256p1; +extern const curve_info nist256p1_info; + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif diff --git a/tools/windows-replace/trezor-crypto/include/TrezorCrypto/rand.h b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/rand.h new file mode 100644 index 00000000000..7171a9ad860 --- /dev/null +++ b/tools/windows-replace/trezor-crypto/include/TrezorCrypto/rand.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2013-2014 Tomas Dzetkulic + * Copyright (c) 2013-2014 Pavol Rusnak + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __RAND_H__ +#define __RAND_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//win +// [wallet-core] Reference counted init and release +void *random_init(void); +void random_release(void); + + +uint32_t random32(void); +void random_buffer(uint8_t *buf, size_t len); + +//uint32_t random_uniform(uint32_t n); +//void random_permute(char *buf, size_t len); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/tools/windows-replace/walletconsole/CMakeLists.txt b/tools/windows-replace/walletconsole/CMakeLists.txt new file mode 100644 index 00000000000..1c6bba462d5 --- /dev/null +++ b/tools/windows-replace/walletconsole/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + +# walletconsole executable +file(GLOB walletconsole_sources *.cpp) +add_executable(walletconsole ${walletconsole_sources}) + + +target_link_libraries(walletconsole walletconsolelib TrezorCrypto TrustWalletCore ${Protobuf_LIBRARIES} Boost::boost) + +target_include_directories(walletconsole PRIVATE ${CMAKE_SOURCE_DIR}/walletconsole/lib ${CMAKE_SOURCE_DIR}/src) + +INSTALL(TARGETS walletconsole DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/tools/windows-replace/walletconsole/lib/CMakeLists.txt b/tools/windows-replace/walletconsole/lib/CMakeLists.txt new file mode 100644 index 00000000000..b14eb51ec08 --- /dev/null +++ b/tools/windows-replace/walletconsole/lib/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + +# walletconsolelib library +file(GLOB_RECURSE walletconsolelib_sources *.cpp) +add_library(walletconsolelib ${walletconsolelib_sources}) +#target_link_libraries(tests gtest_main TrezorCrypto TrustWalletCore ${Protobuf_LIBRARIES} Boost::boost) +target_link_libraries(walletconsolelib TrezorCrypto TrustWalletCore ${Protobuf_LIBRARIES} Boost::boost) +target_include_directories(walletconsolelib PRIVATE ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src) diff --git a/tools/windows-replace/windows-dragon-king.pl b/tools/windows-replace/windows-dragon-king.pl new file mode 100644 index 00000000000..becb8409ba2 --- /dev/null +++ b/tools/windows-replace/windows-dragon-king.pl @@ -0,0 +1,82 @@ +if ($#ARGV+1 != 1 ) { + + print "Enter the command: -spouting or -suction,\n"; + print " -spouting : Place Windows dependencies in the tools folder and project home directory.\n"; + print " -suction : Bring Windows dependencies back into the tools folder.\n"; + exit; +} +sub replace_head +{ + @toolsPath = @_; + @TWtoolsFiles; + for($a = 0;$a < $#toolsPath + 1;$a = $a + 1) + { + $toolsPathString = join("",$toolsPath[$a]); + my $stringPos = rindex($toolsPathString,"/"); + my $toolsName = substr($toolsPathString,$stringPos + 1); + + my $TWtoolsFilesString = join( "",$tools_Dir ,$toolsName ); + $TWtoolsFiles[$a] = $TWtoolsFilesString; + } + return @TWtoolsFiles; + +} + + +$op=$ARGV[0]; +#path--> /wallet-core-win +use Cwd; +$TWdir = getcwd; + +$TWdir = join( "", $TWdir ,"/" ); + +$projectName = "windows-replace-3.0.3"; + +#path--> tools/ +$tools_Dir = join( "", $TWdir,"tools/" ); + +#path--> tools/windows-replace/powerShell/ +$powerShellDir = join( "", $TWdir,"tools/$projectName/powerShell" ); + + +#file--> tools/windows-replace/powerShell/*.* +@powerShellFiles = <$powerShellDir/*>; + +#file--> tools/windows-*.ps1 & tools/windows-*.pl +@TWtoolsFile = replace_head(@powerShellFiles); + +#file--> ./windows-bootstrap.ps1 +$TWwindowsBootstrapFile = join( "", $TWdir,"windows-bootstrap.ps1" ); + +#file--> tools/windows-replace/powerShell/windows-bootstrap.ps1 +$windowsBootstrapFile = join( "", $powerShellDir,"/windows-bootstrap.ps1" ); + +if ($op eq "-spouting") +{ + use File::Copy; + #Copy the windowsBootstrap file to the home directory. + copy $windowsBootstrapFile ,$TWdir or warn 'copy failed.'; + for($a = 0;$a < @powerShellFiles;$a = $a + 1) + { + copy $powerShellFiles[$a] ,$tools_Dir or warn 'copy failed.'; + } + +} +elsif($op eq "-suction") +{ + use File::Copy; + move($TWwindowsBootstrapFile,$powerShellDir); + for($a = 0;$a < @powerShellFiles;$a = $a + 1) + { + move($TWtoolsFile[$a],$powerShellDir); + } +} +else{ + + print "Enter the command: -spouting or -suction,\n"; + print " -spouting : Place Windows dependencies in the tools folder and project home directory.\n"; + print " -suction : Bring Windows dependencies back into the tools folder.\n"; + exit; +} + + diff --git a/tools/windows-samples.ps1 b/tools/windows-samples.ps1 index f67ecda997b..4ed82a3a656 100644 --- a/tools/windows-samples.ps1 +++ b/tools/windows-samples.ps1 @@ -1,15 +1,15 @@ # Build Samples +# Load dependencies version +. $PSScriptRoot\windows-dependencies-version.ps1 + $ErrorActionPreference = "Stop" $root = $pwd $prefix = Join-Path $pwd "build\local" $install = Join-Path $pwd "build\install" -$cmakeGenerator = "Visual Studio 17 2022" -$cmakePlatform = "x64" -$cmakeToolset = "v143" cd samples\cpp if (-not(Test-Path -Path "build" -PathType Container)) { diff --git a/tools/windows-tests.ps1 b/tools/windows-tests.ps1 index 2330dd680c5..d3883de0bb2 100644 --- a/tools/windows-tests.ps1 +++ b/tools/windows-tests.ps1 @@ -1,4 +1,3 @@ - # Run tests $ErrorActionPreference = "Stop" @@ -12,3 +11,4 @@ if ($LASTEXITCODE -ne 0) { if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + diff --git a/trezor-crypto/CMakeLists.txt b/trezor-crypto/CMakeLists.txt index 09eeb4b0f6e..eae01a61570 100644 --- a/trezor-crypto/CMakeLists.txt +++ b/trezor-crypto/CMakeLists.txt @@ -1,5 +1,32 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + +set(TW_WARNING_FLAGS + -W + -Wall + -Wextra + -Wimplicit-function-declaration + -Wredundant-decls + -Wstrict-prototypes + -Wundef + -Wshadow + -Wpointer-arith + -Wformat + -Wreturn-type + -Wsign-compare + -Wmultichar + -Wformat-nonliteral + -Winit-self + -Wuninitialized + -Wformat-security + -Wno-missing-braces +) + add_library(TrezorCrypto - crypto/bignum.c crypto/ecdsa.c crypto/curves.c crypto/secp256k1.c crypto/rand.c crypto/hmac.c crypto/bip32.c crypto/bip39.c crypto/pbkdf2.c crypto/base58.c crypto/base32.c + crypto/bignum.c crypto/ecdsa.c crypto/curves.c crypto/secp256k1.c crypto/rand.c crypto/hmac.c crypto/bip32.c crypto/bip39.c crypto/slip39.c crypto/pbkdf2.c crypto/base58.c crypto/base32.c crypto/address.c crypto/script.c crypto/ripemd160.c @@ -29,39 +56,29 @@ add_library(TrezorCrypto crypto/groestl.c crypto/hmac_drbg.c crypto/rfc6979.c - crypto/schnorr.c crypto/shamir.c + crypto/zilliqa.c + crypto/cardano.c ) +if (EMSCRIPTEN) + message(STATUS "Skip building trezor-crypto/tests") + set(TW_WARNING_FLAGS ${TW_WARNING_FLAGS} -Wno-bitwise-instead-of-logical) +else () + if(NOT ANDROID AND NOT IOS_PLATFORM AND NOT (WIN32 AND NOT TW_STATIC_LIBRARY)) + add_subdirectory(crypto/tests) + endif() +endif() + if(NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) - target_compile_options(TrezorCrypto - PRIVATE - -W - -Wall - -Wextra - -Wimplicit-function-declaration - -Wredundant-decls - -Wstrict-prototypes - -Wundef - -Wshadow - -Wpointer-arith - -Wformat - -Wreturn-type - -Wsign-compare - -Wmultichar - -Wformat-nonliteral - -Winit-self - -Wuninitialized - -Wformat-security - -Wno-missing-braces - -Werror - ) + target_compile_options(TrezorCrypto PRIVATE ${TW_WARNING_FLAGS} -Werror PUBLIC -Wno-deprecated-volatile) endif() if(WIN32) target_link_libraries(TrezorCrypto bcrypt) endif() + target_include_directories(TrezorCrypto PUBLIC $ @@ -70,13 +87,10 @@ target_include_directories(TrezorCrypto src ) -if(NOT ANDROID AND NOT IOS_PLATFORM AND NOT (WIN32 AND NOT TW_STATIC_LIBRARY)) - add_subdirectory(crypto/tests) -endif() - -install(TARGETS TrezorCrypto - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - ) +install( + TARGETS TrezorCrypto + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/trezor-crypto/crypto/base32.c b/trezor-crypto/crypto/base32.c index d4b50ac6dd3..d1cb294c77f 100644 --- a/trezor-crypto/crypto/base32.c +++ b/trezor-crypto/crypto/base32.c @@ -152,12 +152,12 @@ bool base32_8to5(const uint8_t *in, uint8_t length, uint8_t *out, } if (alphabet) { -#ifdef _MSC_VER - uint8_t *decoded = _alloca(length); -#else - uint8_t decoded[length]; -#endif - memset(decoded, 0, length); + #ifdef _MSC_VER + uint8_t *decoded = _alloca(length); + #else + uint8_t decoded[length]; + #endif + memset(decoded, 0, length); //win memset(decoded, 0, sizeof(decoded)); for (size_t i = 0; i < length; i++) { int ret = base32_decode_character(in[i], alphabet); diff --git a/trezor-crypto/crypto/base58.c b/trezor-crypto/crypto/base58.c index faa3ce9a24c..4c870cd50e5 100644 --- a/trezor-crypto/crypto/base58.c +++ b/trezor-crypto/crypto/base58.c @@ -60,11 +60,13 @@ bool b58tobin(void *bin, size_t *binszp, const char *b58) { unsigned char *binu = bin; size_t outisz = (binsz + sizeof(b58_almostmaxint_t) - 1) / sizeof(b58_almostmaxint_t); -#ifdef _MSC_VER + //win + #ifdef _MSC_VER b58_almostmaxint_t *outi = _alloca(sizeof(b58_almostmaxint_t) * outisz); #else b58_almostmaxint_t outi[outisz]; #endif +//win b58_maxint_t t = 0; b58_almostmaxint_t c = 0; size_t i = 0, j = 0; @@ -75,7 +77,7 @@ bool b58tobin(void *bin, size_t *binszp, const char *b58) { size_t b58sz = strlen(b58); - memzero(outi, sizeof(b58_almostmaxint_t) * outisz); + memzero(outi, sizeof(b58_almostmaxint_t) * outisz);//win // Leading zeros, just count for (i = 0; i < b58sz && b58u[i] == '1'; ++i) ++zerocount; @@ -158,13 +160,14 @@ bool b58enc(char *b58, size_t *b58sz, const void *data, size_t binsz) { while (zcount < binsz && !bin[zcount]) ++zcount; size = (binsz - zcount) * 138 / 100 + 1; -#ifdef _MSC_VER + #ifdef _MSC_VER uint8_t *buf = _alloca(size); #else uint8_t buf[size]; #endif memzero(buf, size); + for (i = zcount, high = size - 1; i < binsz; ++i, high = j) { for (carry = bin[i], j = size - 1; (j > high) || carry; --j) { carry += 256 * buf[j]; @@ -198,18 +201,19 @@ int base58_encode_check(const uint8_t *data, int datalen, if (datalen > 128) { return 0; } + #ifdef _MSC_VER uint8_t *buf = _alloca((size_t)datalen + 32); #else uint8_t buf[datalen + 32]; #endif - memset(buf, 0, (size_t)datalen + 32); + memset(buf, 0, (size_t)datalen + 32);//win memset(buf, 0, (size_t)datalen + 32); uint8_t *hash = buf + datalen; memcpy(buf, data, datalen); hasher_Raw(hasher_type, data, datalen, hash); size_t res = strsize; bool success = b58enc(str, &res, buf, datalen + 4); - memzero(buf, (size_t)datalen + 32); + memzero(buf, (size_t)datalen + 32); //win memzero(buf, (size_t)datalen + 32); return success ? res : 0; } @@ -218,12 +222,12 @@ int base58_decode_check(const char *str, HasherType hasher_type, uint8_t *data, if (datalen > 128) { return 0; } -#ifdef _MSC_VER + #ifdef _MSC_VER uint8_t *d = _alloca((size_t)datalen + 4); #else uint8_t d[datalen + 4]; -#endif - memset(d, 0, (size_t)datalen + 4); + #endif + memset(d, 0, (size_t)datalen + 4); //win memset(d, 0, sizeof(d)); size_t res = datalen + 4; if (b58tobin(d, &res, str) != true) { return 0; diff --git a/trezor-crypto/crypto/bignum.c b/trezor-crypto/crypto/bignum.c index 57abe8c8cfa..be739c611e5 100644 --- a/trezor-crypto/crypto/bignum.c +++ b/trezor-crypto/crypto/bignum.c @@ -161,7 +161,7 @@ void bn_read_uint64(uint64_t in_number, bignum256 *out_number) { out_number->val[2] = in_number >> BN_BITS_PER_LIMB; for (uint32_t i = 3; i < BN_LIMBS; i++) out_number->val[i] = 0; } - +//win #ifdef _MSC_VER #include uint32_t __forceinline bn_clz(uint32_t value) @@ -186,7 +186,7 @@ int bn_bitcount(const bignum256 *x) { if (limb != 0) { // __builtin_clz returns the number of leading zero bits starting at the // most significant bit position - return i * BN_BITS_PER_LIMB + (32 - bn_clz(limb)); + return i * BN_BITS_PER_LIMB + (32 - bn_clz(limb)); //win return i * BN_BITS_PER_LIMB + (32 - __builtin_clz(limb)); } } return 0; @@ -285,7 +285,7 @@ int bn_is_equal(const bignum256 *x, const bignum256 *y) { // &truecase == &falsecase or &res == &truecase == &falsecase void bn_cmov(bignum256 *res, volatile uint32_t cond, const bignum256 *truecase, const bignum256 *falsecase) { - assert((cond == 1) | (cond == 0)); + assert((int)(cond == 1) | (cond == 0)); uint32_t tmask = -cond; // tmask = 0xFFFFFFFF if cond else 0x00000000 uint32_t fmask = ~tmask; // fmask = 0x00000000 if cond else 0xFFFFFFFF @@ -304,7 +304,7 @@ void bn_cmov(bignum256 *res, volatile uint32_t cond, const bignum256 *truecase, // Assumes prime is normalized and // 0 < prime < 2**260 == 2**(BITS_PER_LIMB * LIMBS - 1) void bn_cnegate(volatile uint32_t cond, bignum256 *x, const bignum256 *prime) { - assert((cond == 1) | (cond == 0)); + assert((int)(cond == 1) | (cond == 0)); uint32_t tmask = -cond; // tmask = 0xFFFFFFFF if cond else 0x00000000 uint32_t fmask = ~tmask; // fmask = 0x00000000 if cond else 0xFFFFFFFF diff --git a/trezor-crypto/crypto/bip32.c b/trezor-crypto/crypto/bip32.c index 9c0da8217ae..4d821a8cf69 100644 --- a/trezor-crypto/crypto/bip32.c +++ b/trezor-crypto/crypto/bip32.c @@ -48,23 +48,12 @@ #include "nem.h" #endif #if USE_CARDANO -#include +#include #endif #include -#define CARDANO_MAX_NODE_DEPTH 1048576 - const curve_info ed25519_info = { - .bip32_name = "ed25519 seed", - .params = NULL, - .hasher_base58 = HASHER_SHA2D, - .hasher_sign = HASHER_SHA2D, - .hasher_pubkey = HASHER_SHA2_RIPEMD, - .hasher_script = HASHER_SHA2, -}; - -const curve_info ed25519_cardano_info = { - .bip32_name = "ed25519 cardano seed", + .bip32_name = ED25519_SEED_NAME, .params = NULL, .hasher_base58 = HASHER_SHA2D, .hasher_sign = HASHER_SHA2D, @@ -215,11 +204,17 @@ uint32_t hdnode_fingerprint(HDNode *node) { return fingerprint; } -int hdnode_private_ckd(HDNode *inout, uint32_t i) { +int hdnode_private_ckd_bip32(HDNode *inout, uint32_t i) { CONFIDENTIAL uint8_t data[1 + 32 + 4]; CONFIDENTIAL uint8_t I[32 + 32]; CONFIDENTIAL bignum256 a, b; +#if USE_CARDANO + if (inout->curve == &ed25519_cardano_info) { + return 0; + } +#endif + if (i & 0x80000000) { // private derivation data[0] = 0; memcpy(data + 1, inout->private_key, 32); @@ -227,7 +222,9 @@ int hdnode_private_ckd(HDNode *inout, uint32_t i) { if (!inout->curve->params) { return 0; } - hdnode_fill_public_key(inout); + if (hdnode_fill_public_key(inout) != 0) { + return 0; + } memcpy(data, inout->public_key, 33); } write_be(data + 33, i); @@ -281,156 +278,17 @@ int hdnode_private_ckd(HDNode *inout, uint32_t i) { return 1; } +int hdnode_private_ckd(HDNode *inout, uint32_t i) { #if USE_CARDANO -static void scalar_multiply8(const uint8_t *src, int bytes, uint8_t *dst) { - uint8_t prev_acc = 0; - for (int i = 0; i < bytes; i++) { - dst[i] = (src[i] << 3) + (prev_acc & 0x7); - prev_acc = src[i] >> 5; - } - dst[bytes] = src[bytes - 1] >> 5; -} - -static void scalar_add_256bits(const uint8_t *src1, const uint8_t *src2, - uint8_t *dst) { - uint16_t r = 0; - for (int i = 0; i < 32; i++) { - r = r + (uint16_t)src1[i] + (uint16_t)src2[i]; - dst[i] = r & 0xff; - r >>= 8; - } -} - -int hdnode_private_ckd_cardano(HDNode *inout, uint32_t index) { - if (inout->depth >= CARDANO_MAX_NODE_DEPTH) { - return 0; - } - - // checks for hardened/non-hardened derivation, keysize 32 means we are - // dealing with public key and thus non-h, keysize 64 is for private key - int keysize = 32; - if (index & 0x80000000) { - keysize = 64; - } - - CONFIDENTIAL uint8_t data[1 + 64 + 4]; - CONFIDENTIAL uint8_t z[32 + 32]; - CONFIDENTIAL uint8_t priv_key[64]; - CONFIDENTIAL uint8_t res_key[64]; - - write_le(data + keysize + 1, index); - - memcpy(priv_key, inout->private_key, 32); - memcpy(priv_key + 32, inout->private_key_extension, 32); - - if (keysize == 64) { // private derivation - data[0] = 0; - memcpy(data + 1, inout->private_key, 32); - memcpy(data + 1 + 32, inout->private_key_extension, 32); - } else { // public derivation - hdnode_fill_public_key(inout); - data[0] = 2; - memcpy(data + 1, inout->public_key + 1, 32); - } - - CONFIDENTIAL HMAC_SHA512_CTX ctx; - hmac_sha512_Init(&ctx, inout->chain_code, 32); - hmac_sha512_Update(&ctx, data, 1 + keysize + 4); - hmac_sha512_Final(&ctx, z); - - CONFIDENTIAL uint8_t zl8[32]; - memzero(zl8, 32); - - /* get 8 * Zl */ - scalar_multiply8(z, 28, zl8); - /* Kl = 8*Zl + parent(K)l */ - scalar_add_256bits(zl8, priv_key, res_key); - - /* Kr = Zr + parent(K)r */ - scalar_add_256bits(z + 32, priv_key + 32, res_key + 32); - - memcpy(inout->private_key, res_key, 32); - memcpy(inout->private_key_extension, res_key + 32, 32); - - if (keysize == 64) { - data[0] = 1; - } else { - data[0] = 3; + if (inout->curve == &ed25519_cardano_info) { + return hdnode_private_ckd_cardano(inout, i); + } else +#endif + { + return hdnode_private_ckd_bip32(inout, i); } - hmac_sha512_Init(&ctx, inout->chain_code, 32); - hmac_sha512_Update(&ctx, data, 1 + keysize + 4); - hmac_sha512_Final(&ctx, z); - - memcpy(inout->chain_code, z + 32, 32); - inout->depth++; - inout->child_num = index; - memzero(inout->public_key, sizeof(inout->public_key)); - - // making sure to wipe our memory - memzero(z, sizeof(z)); - memzero(data, sizeof(data)); - memzero(priv_key, sizeof(priv_key)); - memzero(res_key, sizeof(res_key)); - return 1; -} - -static int hdnode_from_secret_cardano(const uint8_t *k, - const uint8_t *chain_code, HDNode *out) { - memzero(out, sizeof(HDNode)); - out->depth = 0; - out->child_num = 0; - out->curve = &ed25519_cardano_info; - memcpy(out->private_key, k, 32); - memcpy(out->private_key_extension, k + 32, 32); - memcpy(out->chain_code, chain_code, 32); - - out->private_key[0] &= 0xf8; - out->private_key[31] &= 0x1f; - out->private_key[31] |= 0x40; - - out->public_key[0] = 0; - hdnode_fill_public_key(out); - - return 1; -} - -// Derives the root Cardano HDNode from a master secret, aka seed, as defined in -// SLIP-0023. -int hdnode_from_seed_cardano(const uint8_t *seed, int seed_len, HDNode *out) { - CONFIDENTIAL uint8_t I[SHA512_DIGEST_LENGTH]; - CONFIDENTIAL uint8_t k[SHA512_DIGEST_LENGTH]; - CONFIDENTIAL HMAC_SHA512_CTX ctx; - - hmac_sha512_Init(&ctx, (const uint8_t *)ED25519_CARDANO_NAME, - strlen(ED25519_CARDANO_NAME)); - hmac_sha512_Update(&ctx, seed, seed_len); - hmac_sha512_Final(&ctx, I); - - sha512_Raw(I, 32, k); - - int ret = hdnode_from_secret_cardano(k, I + 32, out); - - memzero(I, sizeof(I)); - memzero(k, sizeof(k)); - memzero(&ctx, sizeof(ctx)); - return ret; } -// Derives the root Cardano HDNode from a passphrase and the entropy encoded in -// a BIP-0039 mnemonic using the Icarus derivation scheme, aka V2 derivation -// scheme. -int hdnode_from_entropy_cardano_icarus(const uint8_t *pass, int pass_len, - const uint8_t *entropy, int entropy_len, - HDNode *out) { - CONFIDENTIAL uint8_t secret[96]; - pbkdf2_hmac_sha512(pass, pass_len, entropy, entropy_len, 4096, secret, 96); - - int ret = hdnode_from_secret_cardano(secret, secret + 64, out); - memzero(secret, sizeof(secret)); - return ret; -} -#endif - int hdnode_public_ckd_cp(const ecdsa_curve *curve, const curve_point *parent, const uint8_t *parent_chain_code, uint32_t i, curve_point *child, uint8_t *child_chain_code) { @@ -530,6 +388,13 @@ CONFIDENTIAL struct { HDNode node; } private_ckd_cache[BIP32_CACHE_SIZE]; +void bip32_cache_clear(void) { + private_ckd_cache_root_set = false; + private_ckd_cache_index = 0; + memzero(&private_ckd_cache_root, sizeof(private_ckd_cache_root)); + memzero(private_ckd_cache, sizeof(private_ckd_cache)); +} + int hdnode_private_ckd_cached(HDNode *inout, const uint32_t *i, size_t i_count, uint32_t *fingerprint) { if (i_count == 0) { @@ -597,26 +462,34 @@ int hdnode_private_ckd_cached(HDNode *inout, const uint32_t *i, size_t i_count, } #endif -void hdnode_get_address_raw(HDNode *node, uint32_t version, uint8_t *addr_raw) { - hdnode_fill_public_key(node); +int hdnode_get_address_raw(HDNode *node, uint32_t version, uint8_t *addr_raw) { + if (hdnode_fill_public_key(node) != 0) { + return 1; + } ecdsa_get_address_raw(node->public_key, version, node->curve->hasher_pubkey, addr_raw); + return 0; } -void hdnode_get_address(HDNode *node, uint32_t version, char *addr, - int addrsize) { - hdnode_fill_public_key(node); +int hdnode_get_address(HDNode *node, uint32_t version, char *addr, + int addrsize) { + if (hdnode_fill_public_key(node) != 0) { + return 1; + } ecdsa_get_address(node->public_key, version, node->curve->hasher_pubkey, node->curve->hasher_base58, addr, addrsize); + return 0; } -void hdnode_fill_public_key(HDNode *node) { - if (node->public_key[0] != 0) return; +int hdnode_fill_public_key(HDNode *node) { + if (node->public_key[0] != 0) return 0; #if USE_BIP32_25519_CURVES if (node->curve->params) { - ecdsa_get_public_key33(node->curve->params, node->private_key, - node->public_key); + if (ecdsa_get_public_key33(node->curve->params, node->private_key, + node->public_key) != 0) { + return 1; + } } else { node->public_key[0] = 1; if (node->curve == &ed25519_info) { @@ -631,16 +504,18 @@ void hdnode_fill_public_key(HDNode *node) { curve25519_scalarmult_basepoint(node->public_key + 1, node->private_key); #if USE_CARDANO } else if (node->curve == &ed25519_cardano_info) { - ed25519_publickey_ext(node->private_key, node->private_key_extension, - node->public_key + 1); + ed25519_publickey_ext(node->private_key, node->public_key + 1); #endif } } #else - ecdsa_get_public_key33(node->curve->params, node->private_key, - node->public_key); + if (ecdsa_get_public_key33(node->curve->params, node->private_key, + node->public_key) != 0) { + return 1; + } #endif + return 0; } #if USE_ETHEREUM @@ -649,7 +524,10 @@ int hdnode_get_ethereum_pubkeyhash(const HDNode *node, uint8_t *pubkeyhash) { SHA3_CTX ctx = {0}; /* get uncompressed public key */ - ecdsa_get_public_key65(node->curve->params, node->private_key, buf); + if (ecdsa_get_public_key65(node->curve->params, node->private_key, buf) != + 0) { + return 0; + } /* compute sha3 of x and y coordinate without 04 prefix */ sha3_256_Init(&ctx); @@ -669,7 +547,10 @@ int hdnode_get_nem_address(HDNode *node, uint8_t version, char *address) { return 0; } - hdnode_fill_public_key(node); + if (hdnode_fill_public_key(node) != 0) { + return 0; + } + return nem_get_address(&node->public_key[1], version, address); } @@ -778,17 +659,12 @@ int hdnode_sign(HDNode *node, const uint8_t *msg, uint32_t msg_len, return 1; // signatures are not supported } else { if (node->curve == &ed25519_info) { - hdnode_fill_public_key(node); - ed25519_sign(msg, msg_len, node->private_key, node->public_key + 1, sig); + ed25519_sign(msg, msg_len, node->private_key, sig); } else if (node->curve == &ed25519_sha3_info) { - hdnode_fill_public_key(node); - ed25519_sign_sha3(msg, msg_len, node->private_key, node->public_key + 1, - sig); + ed25519_sign_sha3(msg, msg_len, node->private_key, sig); #if USE_KECCAK } else if (node->curve == &ed25519_keccak_info) { - hdnode_fill_public_key(node); - ed25519_sign_keccak(msg, msg_len, node->private_key, node->public_key + 1, - sig); + ed25519_sign_keccak(msg, msg_len, node->private_key, sig); #endif } else { return 1; // unknown or unsupported curve diff --git a/trezor-crypto/crypto/bip39.c b/trezor-crypto/crypto/bip39.c index b64a75207c9..fc61539c938 100644 --- a/trezor-crypto/crypto/bip39.c +++ b/trezor-crypto/crypto/bip39.c @@ -44,6 +44,11 @@ CONFIDENTIAL struct { uint8_t seed[512 / 8]; } bip39_cache[BIP39_CACHE_SIZE]; +void bip39_cache_clear(void) { + memzero(bip39_cache, sizeof(bip39_cache)); + bip39_cache_index = 0; +} + #endif // [wallet-core] Added output buffer @@ -249,7 +254,7 @@ void mnemonic_to_seed(const char *mnemonic, const char *passphrase, // binary search for finding the word in the wordlist int mnemonic_find_word(const char *word) { - int lo = 0, hi = BIP39_WORDS - 1; + int lo = 0, hi = BIP39_WORD_COUNT - 1; while (lo <= hi) { int mid = lo + (hi - lo) / 2; int cmp = strcmp(word, wordlist[mid]); @@ -277,7 +282,7 @@ const char *mnemonic_complete_word(const char *prefix, int len) { } const char *mnemonic_get_word(int index) { - if (index >= 0 && index < BIP39_WORDS) { + if (index >= 0 && index < BIP39_WORD_COUNT) { return wordlist[index]; } else { return NULL; diff --git a/trezor-crypto/crypto/blake256.c b/trezor-crypto/crypto/blake256.c index 0da6918102a..a4e9b489c37 100644 --- a/trezor-crypto/crypto/blake256.c +++ b/trezor-crypto/crypto/blake256.c @@ -169,9 +169,8 @@ void blake256_Update( BLAKE256_CTX *S, const uint8_t *in, size_t inlen ) { memcpy( ( void * ) ( S->buf + left ), \ ( void * ) in, ( size_t ) inlen ); - S->buflen = left + ( int )inlen; } - else S->buflen = 0; + S->buflen = left + inlen; } diff --git a/trezor-crypto/crypto/blake2b.c b/trezor-crypto/crypto/blake2b.c index a279cc5d08b..c630ed0f8cf 100644 --- a/trezor-crypto/crypto/blake2b.c +++ b/trezor-crypto/crypto/blake2b.c @@ -36,7 +36,8 @@ typedef struct blake2b_param__ uint8_t reserved[14]; /* 32 */ uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ -} +} //win __attribute__((packed)) blake2b_param; + #ifndef _MSC_VER __attribute__((packed)) #endif diff --git a/trezor-crypto/crypto/blake2s.c b/trezor-crypto/crypto/blake2s.c index 7a828295540..6de68990302 100644 --- a/trezor-crypto/crypto/blake2s.c +++ b/trezor-crypto/crypto/blake2s.c @@ -22,6 +22,7 @@ #ifdef _MSC_VER #pragma pack(push, 1) #endif + typedef struct blake2s_param__ { uint8_t digest_length; /* 1 */ @@ -36,7 +37,7 @@ typedef struct blake2s_param__ /* uint8_t reserved[0]; */ uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ -} +} // win __attribute__((packed)) blake2s_param; #ifndef _MSC_VER __attribute__((packed)) #endif diff --git a/trezor-crypto/crypto/cardano.c b/trezor-crypto/crypto/cardano.c new file mode 100644 index 00000000000..650b66ad513 --- /dev/null +++ b/trezor-crypto/crypto/cardano.c @@ -0,0 +1,328 @@ +/** + * Copyright (c) 2013-2021 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if USE_CARDANO + +#define CARDANO_MAX_NODE_DEPTH 1048576 + +const curve_info ed25519_cardano_info = { + .bip32_name = ED25519_CARDANO_NAME, + .params = NULL, + .hasher_base58 = HASHER_SHA2D, + .hasher_sign = HASHER_SHA2D, + .hasher_pubkey = HASHER_SHA2_RIPEMD, + .hasher_script = HASHER_SHA2, +}; + +static void scalar_multiply8(const uint8_t *src, int bytes, uint8_t *dst) { + uint8_t prev_acc = 0; + for (int i = 0; i < bytes; i++) { + dst[i] = (src[i] << 3) + (prev_acc & 0x7); + prev_acc = src[i] >> 5; + } + dst[bytes] = src[bytes - 1] >> 5; +} + +static void scalar_add_256bits(const uint8_t *src1, const uint8_t *src2, + uint8_t *dst) { + uint16_t r = 0; + for (int i = 0; i < 32; i++) { + r = r + (uint16_t)src1[i] + (uint16_t)src2[i]; + dst[i] = r & 0xff; + r >>= 8; + } +} + +static void cardano_ed25519_tweak_bits(uint8_t private_key[32]) { + private_key[0] &= 0xf8; + private_key[31] &= 0x1f; + private_key[31] |= 0x40; +} + +int hdnode_private_ckd_cardano(HDNode *inout, uint32_t index) { + if (inout->curve != &ed25519_cardano_info) { + return 0; + } + + if (inout->depth >= CARDANO_MAX_NODE_DEPTH) { + return 0; + } + + // checks for hardened/non-hardened derivation, keysize 32 means we are + // dealing with public key and thus non-h, keysize 64 is for private key + int keysize = 32; + if (index & 0x80000000) { + keysize = 64; + } + + CONFIDENTIAL uint8_t data[1 + 64 + 4]; + CONFIDENTIAL uint8_t z[32 + 32]; + CONFIDENTIAL uint8_t priv_key[64]; + CONFIDENTIAL uint8_t res_key[64]; + + write_le(data + keysize + 1, index); + + memcpy(priv_key, inout->private_key, 32); + memcpy(priv_key + 32, inout->private_key_extension, 32); + + if (keysize == 64) { // private derivation + data[0] = 0; + memcpy(data + 1, inout->private_key, 32); + memcpy(data + 1 + 32, inout->private_key_extension, 32); + } else { // public derivation + if (hdnode_fill_public_key(inout) != 0) { + return 0; + } + data[0] = 2; + memcpy(data + 1, inout->public_key + 1, 32); + } + + CONFIDENTIAL HMAC_SHA512_CTX ctx; + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, 1 + keysize + 4); + hmac_sha512_Final(&ctx, z); + + CONFIDENTIAL uint8_t zl8[32]; + memzero(zl8, 32); + + /* get 8 * Zl */ + scalar_multiply8(z, 28, zl8); + /* Kl = 8*Zl + parent(K)l */ + scalar_add_256bits(zl8, priv_key, res_key); + + /* Kr = Zr + parent(K)r */ + scalar_add_256bits(z + 32, priv_key + 32, res_key + 32); + + memcpy(inout->private_key, res_key, 32); + memcpy(inout->private_key_extension, res_key + 32, 32); + + if (keysize == 64) { + data[0] = 1; + } else { + data[0] = 3; + } + hmac_sha512_Init(&ctx, inout->chain_code, 32); + hmac_sha512_Update(&ctx, data, 1 + keysize + 4); + hmac_sha512_Final(&ctx, z); + + memcpy(inout->chain_code, z + 32, 32); + inout->depth++; + inout->child_num = index; + memzero(inout->public_key, sizeof(inout->public_key)); + + // making sure to wipe our memory + memzero(z, sizeof(z)); + memzero(data, sizeof(data)); + memzero(priv_key, sizeof(priv_key)); + memzero(res_key, sizeof(res_key)); + return 1; +} + +int hdnode_from_secret_cardano(const uint8_t secret[CARDANO_SECRET_LENGTH], + HDNode *out) { + memzero(out, sizeof(HDNode)); + out->depth = 0; + out->child_num = 0; + out->curve = &ed25519_cardano_info; + memcpy(out->private_key, secret, 32); + memcpy(out->private_key_extension, secret + 32, 32); + memcpy(out->chain_code, secret + 64, 32); + + cardano_ed25519_tweak_bits(out->private_key); + + out->public_key[0] = 0; + if (hdnode_fill_public_key(out) != 0) { + return 0; + } + + return 1; +} + +// Derives the root Cardano secret from a master secret, aka seed, as defined in +// SLIP-0023. +int secret_from_seed_cardano_slip23(const uint8_t *seed, int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]) { + CONFIDENTIAL uint8_t I[SHA512_DIGEST_LENGTH]; + CONFIDENTIAL HMAC_SHA512_CTX ctx; + + hmac_sha512_Init(&ctx, (const uint8_t *)ED25519_CARDANO_NAME, + strlen(ED25519_CARDANO_NAME)); + hmac_sha512_Update(&ctx, seed, seed_len); + hmac_sha512_Final(&ctx, I); + + sha512_Raw(I, 32, secret_out); + + memcpy(secret_out + SHA512_DIGEST_LENGTH, I + 32, 32); + cardano_ed25519_tweak_bits(secret_out); + + memzero(I, sizeof(I)); + memzero(&ctx, sizeof(ctx)); + return 1; +} + +// Derives the root Cardano secret from a BIP-32 master secret via the Ledger +// derivation: +// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Ledger.md +int secret_from_seed_cardano_ledger(const uint8_t *seed, int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]) { + CONFIDENTIAL uint8_t chain_code[SHA256_DIGEST_LENGTH]; + CONFIDENTIAL uint8_t root_key[SHA512_DIGEST_LENGTH]; + CONFIDENTIAL HMAC_SHA256_CTX ctx; + CONFIDENTIAL HMAC_SHA512_CTX sctx; + + const uint8_t *intermediate_result = seed; + int intermediate_result_len = seed_len; + do { + // STEP 1: derive a master secret like in BIP-32/SLIP-10 + hmac_sha512_Init(&sctx, (const uint8_t *)ED25519_SEED_NAME, + strlen(ED25519_SEED_NAME)); + hmac_sha512_Update(&sctx, intermediate_result, intermediate_result_len); + hmac_sha512_Final(&sctx, root_key); + + // STEP 2: check that the resulting key does not have a particular bit set, + // otherwise iterate like in SLIP-10 + intermediate_result = root_key; + intermediate_result_len = sizeof(root_key); + } while (root_key[31] & 0x20); + + // STEP 3: calculate the chain code as a HMAC-SHA256 of "\x01" + seed, + // key is "ed25519 seed" + hmac_sha256_Init(&ctx, (const unsigned char *)ED25519_SEED_NAME, + strlen(ED25519_SEED_NAME)); + hmac_sha256_Update(&ctx, (const unsigned char *)"\x01", 1); + hmac_sha256_Update(&ctx, seed, seed_len); + hmac_sha256_Final(&ctx, chain_code); + + //win +#ifdef _MSC_VER +static_assert( + SHA512_DIGEST_LENGTH + SHA256_DIGEST_LENGTH == CARDANO_SECRET_LENGTH, + "Invalid configuration of Cardano secret size"); + memcpy(secret_out, root_key, SHA512_DIGEST_LENGTH); + memcpy(secret_out + SHA512_DIGEST_LENGTH, chain_code, SHA256_DIGEST_LENGTH); +#else +// STEP 4: extract information into output + _Static_assert( + SHA512_DIGEST_LENGTH + SHA256_DIGEST_LENGTH == CARDANO_SECRET_LENGTH, + "Invalid configuration of Cardano secret size"); + memcpy(secret_out, root_key, SHA512_DIGEST_LENGTH); + memcpy(secret_out + SHA512_DIGEST_LENGTH, chain_code, SHA256_DIGEST_LENGTH); +#endif +#define CARDANO_ICARUS_ROUNDS_PER_STEP \ + (CARDANO_ICARUS_PBKDF2_ROUNDS / CARDANO_ICARUS_STEPS) + + + + + // STEP 5: tweak bits of the private key + cardano_ed25519_tweak_bits(secret_out); + + memzero(&ctx, sizeof(ctx)); + memzero(&sctx, sizeof(sctx)); + memzero(root_key, sizeof(root_key)); + memzero(chain_code, sizeof(chain_code)); + return 1; +} + +#define CARDANO_ICARUS_STEPS 32 +//win +#ifdef _MSC_VER +static_assert( + CARDANO_ICARUS_PBKDF2_ROUNDS % CARDANO_ICARUS_STEPS == 0, + "CARDANO_ICARUS_STEPS does not divide CARDANO_ICARUS_PBKDF2_ROUNDS"); +#else +_Static_assert( + CARDANO_ICARUS_PBKDF2_ROUNDS % CARDANO_ICARUS_STEPS == 0, + "CARDANO_ICARUS_STEPS does not divide CARDANO_ICARUS_PBKDF2_ROUNDS"); +#endif +#define CARDANO_ICARUS_ROUNDS_PER_STEP \ + (CARDANO_ICARUS_PBKDF2_ROUNDS / CARDANO_ICARUS_STEPS) + +// Derives the root Cardano HDNode from a passphrase and the entropy encoded in +// a BIP-0039 mnemonic using the Icarus derivation scheme, aka V2 derivation +// scheme: +// https://github.com/cardano-foundation/CIPs/blob/09d7d8ee1bd64f7e6b20b5a6cae088039dce00cb/CIP-0003/Icarus.md +int secret_from_entropy_cardano_icarus( + const uint8_t *pass, int pass_len, const uint8_t *entropy, int entropy_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH], + void (*progress_callback)(uint32_t, uint32_t)) { + CONFIDENTIAL PBKDF2_HMAC_SHA512_CTX pctx; + CONFIDENTIAL uint8_t digest[SHA512_DIGEST_LENGTH]; + uint32_t progress = 0; + + // PASS 1: first 64 bytes + pbkdf2_hmac_sha512_Init(&pctx, pass, pass_len, entropy, entropy_len, 1); + if (progress_callback) { + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + for (int i = 0; i < CARDANO_ICARUS_STEPS; i++) { + pbkdf2_hmac_sha512_Update(&pctx, CARDANO_ICARUS_ROUNDS_PER_STEP); + if (progress_callback) { + progress += CARDANO_ICARUS_ROUNDS_PER_STEP; + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + } + pbkdf2_hmac_sha512_Final(&pctx, digest); + + memcpy(secret_out, digest, SHA512_DIGEST_LENGTH); + + // PASS 2: remaining 32 bytes + pbkdf2_hmac_sha512_Init(&pctx, pass, pass_len, entropy, entropy_len, 2); + if (progress_callback) { + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + for (int i = 0; i < CARDANO_ICARUS_STEPS; i++) { + pbkdf2_hmac_sha512_Update(&pctx, CARDANO_ICARUS_ROUNDS_PER_STEP); + if (progress_callback) { + progress += CARDANO_ICARUS_ROUNDS_PER_STEP; + progress_callback(progress, CARDANO_ICARUS_PBKDF2_ROUNDS * 2); + } + } + pbkdf2_hmac_sha512_Final(&pctx, digest); + + memcpy(secret_out + SHA512_DIGEST_LENGTH, digest, + CARDANO_SECRET_LENGTH - SHA512_DIGEST_LENGTH); + + cardano_ed25519_tweak_bits(secret_out); + + memzero(&pctx, sizeof(pctx)); + memzero(digest, sizeof(digest)); + return 1; +} + +#endif // USE_CARDANO \ No newline at end of file diff --git a/trezor-crypto/crypto/chacha20poly1305/LICENSE b/trezor-crypto/crypto/chacha20poly1305/LICENSE new file mode 100644 index 00000000000..95404966f07 --- /dev/null +++ b/trezor-crypto/crypto/chacha20poly1305/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (C) 2016 Will Glozer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/trezor-crypto/crypto/chacha20poly1305/chacha_merged.c b/trezor-crypto/crypto/chacha20poly1305/chacha_merged.c index 8d4ee90c755..3819b865a59 100644 --- a/trezor-crypto/crypto/chacha20poly1305/chacha_merged.c +++ b/trezor-crypto/crypto/chacha20poly1305/chacha_merged.c @@ -23,6 +23,7 @@ void ECRYPT_init(void) return; } +// [wallet-core][non static] rename to avoid duplicate symbol in blake256.c const char chacha_sigma[16] = "expand 32-byte k"; const char tau[16] = "expand 16-byte k"; @@ -59,6 +60,12 @@ void ECRYPT_ivsetup(ECRYPT_ctx *x,const u8 *iv) x->input[15] = U8TO32_LITTLE(iv + 4); } +void ECRYPT_ctrsetup(ECRYPT_ctx *x,const u8 *ctr) +{ + x->input[12] = U8TO32_LITTLE(ctr + 0); + x->input[13] = U8TO32_LITTLE(ctr + 4); +} + void ECRYPT_encrypt_bytes(ECRYPT_ctx *x,const u8 *m,u8 *c,u32 bytes) { u32 x0 = 0, x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 = 0, x7 = 0, x8 = 0, x9 = 0, x10 = 0, x11 = 0, x12 = 0, x13 = 0, x14 = 0, x15 = 0; diff --git a/trezor-crypto/crypto/chacha_drbg.c b/trezor-crypto/crypto/chacha_drbg.c index c1bd5d08e6f..e8027ffe939 100644 --- a/trezor-crypto/crypto/chacha_drbg.c +++ b/trezor-crypto/crypto/chacha_drbg.c @@ -19,44 +19,108 @@ #include +#include #include +#include + +#include +#include +#include + +#define CHACHA_DRBG_KEY_LENGTH 32 +#define CHACHA_DRBG_COUNTER_LENGTH 8 +#define CHACHA_DRBG_IV_LENGTH 8 +#define CHACHA_DRBG_SEED_LENGTH \ + (CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_COUNTER_LENGTH + CHACHA_DRBG_IV_LENGTH) #define MAX(a, b) (a) > (b) ? (a) : (b) -void chacha_drbg_init(CHACHA_DRBG_CTX *ctx, - const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]) { +static void derivation_function(const uint8_t *input1, size_t input1_length, + const uint8_t *input2, size_t input2_length, + uint8_t *output, size_t output_length) { + // Implementation of Hash_df from NIST SP 800-90A + uint32_t block_count = (output_length - 1) / SHA256_DIGEST_LENGTH + 1; + size_t partial_block_length = output_length % SHA256_DIGEST_LENGTH; + assert(block_count <= 255); + + uint32_t output_length_bits = output_length * 8; +#if BYTE_ORDER == LITTLE_ENDIAN + REVERSE32(output_length_bits, output_length_bits); +#endif + + SHA256_CTX ctx = {0}; + + for (uint8_t counter = 1; counter <= block_count; counter++) { + sha256_Init(&ctx); + sha256_Update(&ctx, &counter, sizeof(counter)); + sha256_Update(&ctx, (uint8_t *)&output_length_bits, + sizeof(output_length_bits)); + sha256_Update(&ctx, input1, input1_length); + sha256_Update(&ctx, input2, input2_length); + + if (counter != block_count || partial_block_length == 0) { + sha256_Final(&ctx, output); + output += SHA256_DIGEST_LENGTH; + } else { // last block is partial + uint8_t digest[SHA256_DIGEST_LENGTH] = {0}; + sha256_Final(&ctx, digest); + memcpy(output, digest, partial_block_length); + memzero(digest, sizeof(digest)); + } + } + + memzero(&ctx, sizeof(ctx)); +} + +void chacha_drbg_init(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy, + size_t entropy_length, const uint8_t *nonce, + size_t nonce_length) { uint8_t buffer[MAX(CHACHA_DRBG_KEY_LENGTH, CHACHA_DRBG_IV_LENGTH)] = {0}; ECRYPT_keysetup(&ctx->chacha_ctx, buffer, CHACHA_DRBG_KEY_LENGTH * 8, CHACHA_DRBG_IV_LENGTH * 8); ECRYPT_ivsetup(&ctx->chacha_ctx, buffer); - chacha_drbg_reseed(ctx, entropy); + chacha_drbg_reseed(ctx, entropy, entropy_length, nonce, nonce_length); } static void chacha_drbg_update(CHACHA_DRBG_CTX *ctx, const uint8_t data[CHACHA_DRBG_SEED_LENGTH]) { - uint8_t buffer[CHACHA_DRBG_SEED_LENGTH] = {0}; + uint8_t seed[CHACHA_DRBG_SEED_LENGTH] = {0}; if (data) - ECRYPT_encrypt_bytes(&ctx->chacha_ctx, data, buffer, - CHACHA_DRBG_SEED_LENGTH); + ECRYPT_encrypt_bytes(&ctx->chacha_ctx, data, seed, CHACHA_DRBG_SEED_LENGTH); else - ECRYPT_keystream_bytes(&ctx->chacha_ctx, buffer, CHACHA_DRBG_SEED_LENGTH); + ECRYPT_keystream_bytes(&ctx->chacha_ctx, seed, CHACHA_DRBG_SEED_LENGTH); - ECRYPT_keysetup(&ctx->chacha_ctx, buffer, CHACHA_DRBG_KEY_LENGTH * 8, + ECRYPT_keysetup(&ctx->chacha_ctx, seed, CHACHA_DRBG_KEY_LENGTH * 8, CHACHA_DRBG_IV_LENGTH * 8); - ECRYPT_ivsetup(&ctx->chacha_ctx, buffer + CHACHA_DRBG_KEY_LENGTH); + + ECRYPT_ivsetup(&ctx->chacha_ctx, + seed + CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_COUNTER_LENGTH); + + ECRYPT_ctrsetup(&ctx->chacha_ctx, seed + CHACHA_DRBG_KEY_LENGTH); + + memzero(seed, sizeof(seed)); } void chacha_drbg_generate(CHACHA_DRBG_CTX *ctx, uint8_t *output, - uint8_t output_length) { + size_t output_length) { + assert(output_length < 65536); + assert(ctx->reseed_counter + 1 != 0); + ECRYPT_keystream_bytes(&ctx->chacha_ctx, output, output_length); chacha_drbg_update(ctx, NULL); ctx->reseed_counter++; } -void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx, - const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]) { - chacha_drbg_update(ctx, entropy); +void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy, + size_t entropy_length, const uint8_t *additional_input, + size_t additional_input_length) { + uint8_t seed[CHACHA_DRBG_SEED_LENGTH] = {0}; + derivation_function(entropy, entropy_length, additional_input, + additional_input_length, seed, sizeof(seed)); + chacha_drbg_update(ctx, seed); + memzero(seed, sizeof(seed)); + ctx->reseed_counter = 1; } diff --git a/trezor-crypto/crypto/curves.c b/trezor-crypto/crypto/curves.c index c95ac8b7512..a6221a9943f 100644 --- a/trezor-crypto/crypto/curves.c +++ b/trezor-crypto/crypto/curves.c @@ -21,6 +21,7 @@ */ #include +#include const char SECP256K1_NAME[] = "secp256k1"; const char SECP256K1_DECRED_NAME[] = "secp256k1-decred"; @@ -28,10 +29,14 @@ const char SECP256K1_GROESTL_NAME[] = "secp256k1-groestl"; const char SECP256K1_SMART_NAME[] = "secp256k1-smart"; const char NIST256P1_NAME[] = "nist256p1"; const char ED25519_NAME[] = "ed25519"; +const char ED25519_SEED_NAME[] = "ed25519 seed"; +#if USE_CARDANO const char ED25519_CARDANO_NAME[] = "ed25519 cardano seed"; -const char ED25519_BLAKE2B_NANO_NAME[] = "ed25519-blake2b-nano"; // [wallet-core] +#endif const char ED25519_SHA3_NAME[] = "ed25519-sha3"; #if USE_KECCAK const char ED25519_KECCAK_NAME[] = "ed25519-keccak"; #endif const char CURVE25519_NAME[] = "curve25519"; + +const char ED25519_BLAKE2B_NANO_NAME[] = "ed25519-blake2b-nano"; // [wallet-core] diff --git a/trezor-crypto/crypto/ecdsa.c b/trezor-crypto/crypto/ecdsa.c index 0f2198fbbc0..445f29aa549 100644 --- a/trezor-crypto/crypto/ecdsa.c +++ b/trezor-crypto/crypto/ecdsa.c @@ -36,7 +36,6 @@ #include #include #include -#include // Set cp2 = cp1 void point_copy(const curve_point *cp1, curve_point *cp2) { *cp2 = *cp1; } @@ -404,13 +403,16 @@ void point_jacobian_double(jacobian_curve_point *p, const ecdsa_curve *curve) { } // res = k * p -void point_multiply(const ecdsa_curve *curve, const bignum256 *k, - const curve_point *p, curve_point *res) { +// returns 0 on success +int point_multiply(const ecdsa_curve *curve, const bignum256 *k, + const curve_point *p, curve_point *res) { // this algorithm is loosely based on // Katsuyuki Okeya and Tsuyoshi Takagi, The Width-w NAF Method Provides // Small Memory and Fast Elliptic Scalar Multiplications Secure against // Side Channel Attacks. - assert(bn_is_less(k, &curve->order)); + if (!bn_is_less(k, &curve->order)) { + return 1; + } int i = 0, j = 0; CONFIDENTIAL bignum256 a; @@ -442,7 +444,7 @@ void point_multiply(const ecdsa_curve *curve, const bignum256 *k, // special case 0*p: just return zero. We don't care about constant time. if (!is_non_zero) { point_set_infinity(res); - return; + return 1; } // Now a = k + 2^256 (mod curve->order) and a is odd. @@ -523,15 +525,20 @@ void point_multiply(const ecdsa_curve *curve, const bignum256 *k, jacobian_to_curve(&jres, res, prime); memzero(&a, sizeof(a)); memzero(&jres, sizeof(jres)); + + return 0; } #if USE_PRECOMPUTED_CP // res = k * G // k must be a normalized number with 0 <= k < curve->order -void scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, - curve_point *res) { - assert(bn_is_less(k, &curve->order)); +// returns 0 on success +int scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, + curve_point *res) { + if (!bn_is_less(k, &curve->order)) { + return 1; + } int i = {0}, j = {0}; CONFIDENTIAL bignum256 a; @@ -559,7 +566,7 @@ void scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, // special case 0*G: just return zero. We don't care about constant time. if (!is_non_zero) { point_set_infinity(res); - return; + return 0; } // Now a = k + 2^256 (mod curve->order) and a is odd. @@ -612,13 +619,15 @@ void scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, jacobian_to_curve(&jres, res, prime); memzero(&a, sizeof(a)); memzero(&jres, sizeof(jres)); + + return 0; } #else -void scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, - curve_point *res) { - point_multiply(curve, k, &curve->G, res); +int scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, + curve_point *res) { + return point_multiply(curve, k, &curve->G, res); } #endif @@ -632,6 +641,11 @@ int ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key, bignum256 k = {0}; bn_read_be(priv_key, &k); + if (bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + // Invalid private key. + return 2; + } + point_multiply(curve, &k, &point, &point); memzero(&k, sizeof(k)); @@ -673,10 +687,17 @@ int ecdsa_sign_digest(const ecdsa_curve *curve, const uint8_t *priv_key, #if USE_RFC6979 rfc6979_state rng = {0}; - init_rfc6979(priv_key, digest, &rng); + init_rfc6979(priv_key, digest, curve, &rng); #endif bn_read_be(digest, &z); + if (bn_is_zero(&z)) { + // The probability of the digest being all-zero by chance is infinitesimal, + // so this is most likely an indication of a bug. Furthermore, the signature + // has no value, because in this case it can be easily forged for any public + // key, see ecdsa_verify_digest(). + return 1; + } for (i = 0; i < 10000; i++) { #if USE_RFC6979 @@ -704,11 +725,16 @@ int ecdsa_sign_digest(const ecdsa_curve *curve, const uint8_t *priv_key, continue; } + bn_read_be(priv_key, s); + if (bn_is_zero(s) || !bn_is_less(s, &curve->order)) { + // Invalid private key. + return 2; + } + // randomize operations to counter side-channel attacks generate_k_random(&randk, &curve->order); bn_multiply(&randk, &k, &curve->order); // k*rand bn_inverse(&k, &curve->order); // (k*rand)^-1 - bn_read_be(priv_key, s); // priv bn_multiply(&R.x, s, &curve->order); // R.x*priv bn_add(s, &z); // R.x*priv + z bn_multiply(&k, s, &curve->order); // (k*rand)^-1 (R.x*priv + z) @@ -755,33 +781,55 @@ int ecdsa_sign_digest(const ecdsa_curve *curve, const uint8_t *priv_key, return -1; } -void ecdsa_get_public_key33(const ecdsa_curve *curve, const uint8_t *priv_key, - uint8_t *pub_key) { +// returns 0 on success +int ecdsa_get_public_key33(const ecdsa_curve *curve, const uint8_t *priv_key, + uint8_t *pub_key) { curve_point R = {0}; bignum256 k = {0}; bn_read_be(priv_key, &k); + if (bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + // Invalid private key. + memzero(pub_key, 33); + return -1; + } + // compute k*G - scalar_multiply(curve, &k, &R); + if (scalar_multiply(curve, &k, &R) != 0) { + memzero(&k, sizeof(k)); + return 1; + } pub_key[0] = 0x02 | (R.y.val[0] & 0x01); bn_write_be(&R.x, pub_key + 1); memzero(&R, sizeof(R)); memzero(&k, sizeof(k)); + return 0; } -void ecdsa_get_public_key65(const ecdsa_curve *curve, const uint8_t *priv_key, - uint8_t *pub_key) { +// returns 0 on success +int ecdsa_get_public_key65(const ecdsa_curve *curve, const uint8_t *priv_key, + uint8_t *pub_key) { curve_point R = {0}; bignum256 k = {0}; bn_read_be(priv_key, &k); + if (bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + // Invalid private key. + memzero(pub_key, 65); + return -1; + } + // compute k*G - scalar_multiply(curve, &k, &R); + if (scalar_multiply(curve, &k, &R) != 0) { + memzero(&k, sizeof(k)); + return 1; + } pub_key[0] = 0x04; bn_write_be(&R.x, pub_key + 1); bn_write_be(&R.y, pub_key + 33); memzero(&R, sizeof(R)); memzero(&k, sizeof(k)); + return 0; } int ecdsa_uncompress_pubkey(const ecdsa_curve *curve, const uint8_t *pub_key, @@ -1017,6 +1065,10 @@ int ecdsa_recover_pub_from_sig(const ecdsa_curve *curve, uint8_t *pub_key, scalar_multiply(curve, &e, &cp2); // cp = (s * r^-1 * k - digest * r^-1) * G = Pub point_add(curve, &cp2, &cp); + // The point at infinity is not considered to be a valid public key. + if (point_is_infinity(&cp)) { + return 1; + } pub_key[0] = 0x04; bn_write_be(&cp.x, pub_key + 1); bn_write_be(&cp.y, pub_key + 33); @@ -1107,7 +1159,7 @@ int ecdsa_sig_to_der(const uint8_t *sig, uint8_t *der) { // process R i = 0; - while (sig[i] == 0 && i < 32) { + while (i < 31 && sig[i] == 0) { i++; } // skip leading zeroes if (sig[i] >= 0x80) { // put zero in output if MSB set @@ -1130,7 +1182,7 @@ int ecdsa_sig_to_der(const uint8_t *sig, uint8_t *der) { // process S i = 32; - while (sig[i] == 0 && i < 64) { + while (i < 63 && sig[i] == 0) { i++; } // skip leading zeroes if (sig[i] >= 0x80) { // put zero in output if MSB set @@ -1197,56 +1249,3 @@ int ecdsa_sig_from_der(const uint8_t *der, size_t der_len, uint8_t sig[64]) { return 0; } - -// [wallet-core] -int zil_schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key, const uint8_t *msg, const uint32_t msg_len, uint8_t *sig) -{ - int i; - bignum256 k; - - uint8_t hash[32]; - sha256_Raw(msg, msg_len, hash); - - rfc6979_state rng; - init_rfc6979(priv_key, hash, &rng); - - for (i = 0; i < 10000; i++) { - // generate K deterministically - generate_k_rfc6979(&k, &rng); - // if k is too big or too small, we don't like it - if (bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { - continue; - } - - schnorr_sign_pair sign; - if (schnorr_sign(curve, priv_key, &k, msg, msg_len, &sign) != 0) { - continue; - } - - // we're done - memcpy(sig, sign.r, 32); - memcpy(sig + 32, sign.s, 32); - - memzero(&k, sizeof(k)); - memzero(&rng, sizeof(rng)); - memzero(&sign, sizeof(sign)); - return 0; - } - - // Too many retries without a valid signature - // -> fail with an error - memzero(&k, sizeof(k)); - memzero(&rng, sizeof(rng)); - return -1; -} - -// [wallet-core] -int zil_schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key, const uint8_t *sig, const uint8_t *msg, const uint32_t msg_len) -{ - schnorr_sign_pair sign; - - memcpy(sign.r, sig, 32); - memcpy(sign.s, sig + 32, 32); - - return schnorr_verify(curve, pub_key, msg, msg_len, &sign); -} diff --git a/trezor-crypto/crypto/ed25519-donna/ed25519.c b/trezor-crypto/crypto/ed25519-donna/ed25519.c index 8c3b837fa4d..6e01c0c1e98 100644 --- a/trezor-crypto/crypto/ed25519-donna/ed25519.c +++ b/trezor-crypto/crypto/ed25519-donna/ed25519.c @@ -18,6 +18,7 @@ #include #include +#include /* Generates a (extsk[0..31]) and aExt (extsk[32..63]) @@ -31,10 +32,10 @@ ed25519_extsk(hash_512bits extsk, const ed25519_secret_key sk) { } static void -ed25519_hram(hash_512bits hram, const ed25519_signature RS, const ed25519_public_key pk, const unsigned char *m, size_t mlen) { +ed25519_hram(hash_512bits hram, const ed25519_public_key R, const ed25519_public_key pk, const unsigned char *m, size_t mlen) { ed25519_hash_context ctx; ed25519_hash_init(&ctx); - ed25519_hash_update(&ctx, RS, 32); + ed25519_hash_update(&ctx, R, 32); ed25519_hash_update(&ctx, pk, 32); ed25519_hash_update(&ctx, m, mlen); ed25519_hash_final(&ctx, hram); @@ -42,34 +43,11 @@ ed25519_hram(hash_512bits hram, const ed25519_signature RS, const ed25519_public void ED25519_FN(ed25519_publickey) (const ed25519_secret_key sk, ed25519_public_key pk) { - bignum256modm a = {0}; - ge25519 ALIGN(16) A; hash_512bits extsk = {0}; - - /* A = aB */ ed25519_extsk(extsk, sk); - - expand256_modm(a, extsk, 32); - ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); - ge25519_pack(pk, &A); -} - -#if USE_CARDANO -void -ED25519_FN(ed25519_publickey_ext) (const ed25519_secret_key sk, const ed25519_secret_key skext, ed25519_public_key pk) { - bignum256modm a = {0}; - ge25519 ALIGN(16) A; - hash_512bits extsk = {0}; - - /* we don't stretch the key through hashing first since its already 64 bytes */ - - memcpy(extsk, sk, 32); - memcpy(extsk+32, skext, 32); - expand256_modm(a, extsk, 32); - ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); - ge25519_pack(pk, &A); + ed25519_publickey_ext(extsk, pk); + memzero(&extsk, sizeof(extsk)); } -#endif void ED25519_FN(ed25519_cosi_sign) (const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_secret_key nonce, const ed25519_public_key R, const ed25519_public_key pk, ed25519_cosi_signature sig) { @@ -81,6 +59,7 @@ ED25519_FN(ed25519_cosi_sign) (const unsigned char *m, size_t mlen, const ed2551 /* r = nonce */ expand256_modm(r, extnonce, 32); + memzero(&extnonce, sizeof(extnonce)); /* S = H(R,A,m).. */ ed25519_hram(hram, R, pk, m, mlen); @@ -88,57 +67,25 @@ ED25519_FN(ed25519_cosi_sign) (const unsigned char *m, size_t mlen, const ed2551 /* S = H(R,A,m)a */ expand256_modm(a, extsk, 32); + memzero(&extsk, sizeof(extsk)); mul256_modm(S, S, a); + memzero(&a, sizeof(a)); /* S = (r + H(R,A,m)a) */ add256_modm(S, S, r); + memzero(&r, sizeof(r)); /* S = (r + H(R,A,m)a) mod L */ contract256_modm(sig, S); } void -ED25519_FN(ed25519_sign) (const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_public_key pk, ed25519_signature RS) { - ed25519_hash_context ctx; - bignum256modm r = {0}, S = {0}, a = {0}; - ge25519 ALIGN(16) R = {0}; - hash_512bits extsk = {0}, hashr = {0}, hram = {0}; - - ed25519_extsk(extsk, sk); - - - /* r = H(aExt[32..64], m) */ - ed25519_hash_init(&ctx); - ed25519_hash_update(&ctx, extsk + 32, 32); - ed25519_hash_update(&ctx, m, mlen); - ed25519_hash_final(&ctx, hashr); - expand256_modm(r, hashr, 64); - - /* R = rB */ - ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r); - ge25519_pack(RS, &R); - - /* S = H(R,A,m).. */ - ed25519_hram(hram, RS, pk, m, mlen); - expand256_modm(S, hram, 64); - - /* S = H(R,A,m)a */ - expand256_modm(a, extsk, 32); - mul256_modm(S, S, a); - - /* S = (r + H(R,A,m)a) */ - add256_modm(S, S, r); - - /* S = (r + H(R,A,m)a) mod L */ - contract256_modm(RS + 32, S); -} - -#if USE_CARDANO -void -ED25519_FN(ed25519_sign_ext) (const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_secret_key skext, const ed25519_public_key pk, ed25519_signature RS) { +ED25519_FN(ed25519_sign_ext) (const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_secret_key skext, ed25519_signature RS) { ed25519_hash_context ctx; bignum256modm r = {0}, S = {0}, a = {0}; ge25519 ALIGN(16) R = {0}; + ge25519 ALIGN(16) A = {0}; + ed25519_public_key pk = {0}; hash_512bits extsk = {0}, hashr = {0}, hram = {0}; /* we don't stretch the key through hashing first since its already 64 bytes */ @@ -153,30 +100,47 @@ ED25519_FN(ed25519_sign_ext) (const unsigned char *m, size_t mlen, const ed25519 ed25519_hash_update(&ctx, m, mlen); ed25519_hash_final(&ctx, hashr); expand256_modm(r, hashr, 64); + memzero(&hashr, sizeof(hashr)); /* R = rB */ ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r); ge25519_pack(RS, &R); + /* a = aExt[0..31] */ + expand256_modm(a, extsk, 32); + memzero(&extsk, sizeof(extsk)); + + /* A = aB */ + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); + ge25519_pack(pk, &A); + /* S = H(R,A,m).. */ ed25519_hram(hram, RS, pk, m, mlen); expand256_modm(S, hram, 64); /* S = H(R,A,m)a */ - expand256_modm(a, extsk, 32); mul256_modm(S, S, a); + memzero(&a, sizeof(a)); /* S = (r + H(R,A,m)a) */ add256_modm(S, S, r); + memzero(&r, sizeof(r)); /* S = (r + H(R,A,m)a) mod L */ contract256_modm(RS + 32, S); } -#endif + +void +ED25519_FN(ed25519_sign) (const unsigned char *m, size_t mlen, const ed25519_secret_key sk, ed25519_signature RS) { + hash_512bits extsk = {0}; + ed25519_extsk(extsk, sk); + ED25519_FN(ed25519_sign_ext)(m, mlen, extsk, extsk + 32, RS); + memzero(&extsk, sizeof(extsk)); +} int ED25519_FN(ed25519_sign_open) (const unsigned char *m, size_t mlen, const ed25519_public_key pk, const ed25519_signature RS) { - ge25519 ALIGN(16) R, A; + ge25519 ALIGN(16) R = {0}, A = {0}; hash_512bits hash = {0}; bignum256modm hram = {0}, S = {0}; unsigned char checkR[32] = {0}; @@ -204,17 +168,19 @@ ED25519_FN(ed25519_sign_open) (const unsigned char *m, size_t mlen, const ed2551 int ED25519_FN(ed25519_scalarmult) (ed25519_public_key res, const ed25519_secret_key sk, const ed25519_public_key pk) { bignum256modm a = {0}; - ge25519 ALIGN(16) A, P; + ge25519 ALIGN(16) A = {0}, P = {0}; hash_512bits extsk = {0}; ed25519_extsk(extsk, sk); expand256_modm(a, extsk, 32); + memzero(&extsk, sizeof(extsk)); if (!ge25519_unpack_negative_vartime(&P, pk)) { return -1; } ge25519_scalarmult(&A, &P, a); + memzero(&a, sizeof(a)); curve25519_neg(A.x, A.x); ge25519_pack(res, &A); return 0; @@ -225,6 +191,19 @@ ED25519_FN(ed25519_scalarmult) (ed25519_public_key res, const ed25519_secret_key #include +void +ed25519_publickey_ext(const ed25519_secret_key extsk, ed25519_public_key pk) { + bignum256modm a = {0}; + ge25519 ALIGN(16) A = {0}; + + expand256_modm(a, extsk, 32); + + /* A = aB */ + ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a); + memzero(&a, sizeof(a)); + ge25519_pack(pk, &A); +} + int ed25519_cosi_combine_publickeys(ed25519_public_key res, CONST ed25519_public_key *pks, size_t n) { size_t i = 0; @@ -277,8 +256,8 @@ void curve25519_scalarmult_basepoint(curve25519_key pk, const curve25519_key e) { curve25519_key ec = {0}; bignum256modm s = {0}; - bignum25519 ALIGN(16) yplusz, zminusy; - ge25519 ALIGN(16) p; + bignum25519 ALIGN(16) yplusz = {0}, zminusy = {0}; + ge25519 ALIGN(16) p = {0}; size_t i = 0; /* clamp */ @@ -288,9 +267,11 @@ curve25519_scalarmult_basepoint(curve25519_key pk, const curve25519_key e) { ec[31] |= 64; expand_raw256_modm(s, ec); + memzero(&ec, sizeof(ec)); /* scalar * basepoint */ ge25519_scalarmult_base_niels(&p, ge25519_niels_base_multiples, s); + memzero(&s, sizeof(s)); /* u = (y + z) / (z - y) */ curve25519_add(yplusz, p.y, p.z); @@ -310,6 +291,7 @@ curve25519_scalarmult(curve25519_key mypublic, const curve25519_key secret, cons e[31] &= 0x7f; e[31] |= 0x40; curve25519_scalarmult_donna(mypublic, e, basepoint); + memzero(&e, sizeof(e)); } #endif // ED25519_SUFFIX diff --git a/trezor-crypto/crypto/monero/base58.c b/trezor-crypto/crypto/monero/base58.c index 278f23455b1..c5a6f1ccd2b 100644 --- a/trezor-crypto/crypto/monero/base58.c +++ b/trezor-crypto/crypto/monero/base58.c @@ -205,7 +205,7 @@ int xmr_base58_addr_encode_check(uint64_t tag, const uint8_t *data, size_t binsz #else uint8_t buf[(binsz + 1) + HASHER_DIGEST_LENGTH]; #endif - memset(buf, 0, sizeof((binsz + 1) + HASHER_DIGEST_LENGTH)); + memset(buf, 0, sizeof((binsz + 1) + HASHER_DIGEST_LENGTH)); // win memset(buf, 0, sizeof(buf)); uint8_t *hash = buf + binsz + 1; buf[0] = (uint8_t) tag; memcpy(buf + 1, data, binsz); @@ -218,12 +218,13 @@ int xmr_base58_addr_encode_check(uint64_t tag, const uint8_t *data, size_t binsz int xmr_base58_addr_decode_check(const char *addr, size_t sz, uint64_t *tag, void *data, size_t datalen) { size_t buflen = 1 + 64 + addr_checksum_size; -#ifdef _MSC_VER - uint8_t *buf = _alloca(buflen); -#else - uint8_t buf[buflen]; -#endif - memset(buf, 0, buflen); + #ifdef _MSC_VER + uint8_t *buf = _alloca(buflen); + #else + uint8_t buf[buflen]; + #endif + memset(buf, 0, buflen); //win memset(buf, 0, sizeof(buf)); + uint8_t hash[HASHER_DIGEST_LENGTH] = {0}; if (!xmr_base58_decode(addr, sz, buf, &buflen)){ diff --git a/trezor-crypto/crypto/nem.c b/trezor-crypto/crypto/nem.c index fd844156871..66de5cfa9e6 100644 --- a/trezor-crypto/crypto/nem.c +++ b/trezor-crypto/crypto/nem.c @@ -205,8 +205,7 @@ size_t nem_transaction_end(nem_transaction_ctx *ctx, const ed25519_secret_key private_key, ed25519_signature signature) { if (private_key != NULL && signature != NULL) { - ed25519_sign_keccak(ctx->buffer, ctx->offset, private_key, ctx->public_key, - signature); + ed25519_sign_keccak(ctx->buffer, ctx->offset, private_key, signature); } return ctx->offset; diff --git a/trezor-crypto/crypto/rand.c b/trezor-crypto/crypto/rand.c index d874e264aa8..caff5a4242c 100644 --- a/trezor-crypto/crypto/rand.c +++ b/trezor-crypto/crypto/rand.c @@ -20,7 +20,6 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ - #include #include @@ -108,7 +107,6 @@ void random_buffer(uint8_t *buf, size_t len) { } #else - #include #include @@ -124,7 +122,7 @@ void random_release() { } // [wallet-core] -uint32_t __attribute__((weak)) random32() { +uint32_t __attribute__((weak)) random32(void) { int randomData = open("/dev/urandom", O_RDONLY); if (randomData < 0) { return 0; @@ -150,5 +148,4 @@ void __attribute__((weak)) random_buffer(uint8_t *buf, size_t len) { } close(randomData); } - -#endif +#endif \ No newline at end of file diff --git a/trezor-crypto/crypto/rfc6979.c b/trezor-crypto/crypto/rfc6979.c index 98491594bf5..c781e47b926 100644 --- a/trezor-crypto/crypto/rfc6979.c +++ b/trezor-crypto/crypto/rfc6979.c @@ -21,14 +21,30 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ +#include -#include #include #include +#include void init_rfc6979(const uint8_t *priv_key, const uint8_t *hash, - rfc6979_state *state) { - hmac_drbg_init(state, priv_key, 32, hash, 32); + const ecdsa_curve *curve, rfc6979_state *state) { + if (curve) { + bignum256 hash_bn = {0}; + bn_read_be(hash, &hash_bn); + + // Make sure hash is partly reduced modulo order + assert(bn_bitcount(&curve->order) >= 256); + bn_mod(&hash_bn, &curve->order); + + uint8_t hash_reduced[32] = {0}; + bn_write_be(&hash_bn, hash_reduced); + memzero(&hash_bn, sizeof(hash_bn)); + hmac_drbg_init(state, priv_key, 32, hash_reduced, 32); + memzero(hash_reduced, sizeof(hash_reduced)); + } else { + hmac_drbg_init(state, priv_key, 32, hash, 32); + } } // generate next number from deterministic random number generator diff --git a/trezor-crypto/crypto/schnorr.c b/trezor-crypto/crypto/schnorr.c deleted file mode 100644 index c37e48f75ba..00000000000 --- a/trezor-crypto/crypto/schnorr.c +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright (c) 2019 Anatolii Kurotych - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES - * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#include - -// r = H(Q, kpub, m) -static void calc_r(const curve_point *Q, const uint8_t pub_key[33], - const uint8_t *msg, const uint32_t msg_len, bignum256 *r) { - uint8_t Q_compress[33]; - compress_coords(Q, Q_compress); - - SHA256_CTX ctx; - uint8_t digest[SHA256_DIGEST_LENGTH]; - sha256_Init(&ctx); - sha256_Update(&ctx, Q_compress, 33); - sha256_Update(&ctx, pub_key, 33); - sha256_Update(&ctx, msg, msg_len); - sha256_Final(&ctx, digest); - - // Convert the raw bigendian 256 bit value to a normalized, partly reduced bignum - bn_read_be(digest, r); -} - -// Returns 0 if signing succeeded -int schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key, - const bignum256 *k, const uint8_t *msg, const uint32_t msg_len, - schnorr_sign_pair *result) { - uint8_t pub_key[33]; - curve_point Q; - bignum256 private_key_scalar; - bignum256 r_temp; - bignum256 s_temp; - bignum256 r_kpriv_result; - - bn_read_be(priv_key, &private_key_scalar); - ecdsa_get_public_key33(curve, priv_key, pub_key); - - // Compute commitment Q = kG - point_multiply(curve, k, &curve->G, &Q); - - // Compute challenge r = H(Q, kpub, m) - calc_r(&Q, pub_key, msg, msg_len, &r_temp); - - // Fully reduce the bignum - bn_mod(&r_temp, &curve->order); - - // Convert the normalized, fully reduced bignum to a raw bigendian 256 bit value - bn_write_be(&r_temp, result->r); - - // Compute s = k - r*kpriv - bn_copy(&r_temp, &r_kpriv_result); - - // r*kpriv result is partly reduced - bn_multiply(&private_key_scalar, &r_kpriv_result, &curve->order); - - // k - r*kpriv result is normalized but not reduced - bn_subtractmod(k, &r_kpriv_result, &s_temp, &curve->order); - - // Partly reduce the result - bn_fast_mod(&s_temp, &curve->order); - - // Fully reduce the result - bn_mod(&s_temp, &curve->order); - - // Convert the normalized, fully reduced bignum to a raw bigendian 256 bit value - bn_write_be(&s_temp, result->s); - - if (bn_is_zero(&r_temp) || bn_is_zero(&s_temp)) return 1; - - return 0; -} - -// Returns 0 if verification succeeded -int schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key, - const uint8_t *msg, const uint32_t msg_len, - const schnorr_sign_pair *sign) { - curve_point pub_key_point; - curve_point sG, Q; - bignum256 r_temp; - bignum256 s_temp; - bignum256 r_computed; - - if (msg_len == 0) return 1; - - // Convert the raw bigendian 256 bit values to normalized, partly reduced bignums - bn_read_be(sign->r, &r_temp); - bn_read_be(sign->s, &s_temp); - - // Check if r,s are in [1, ..., order-1] - if (bn_is_zero(&r_temp)) return 2; - if (bn_is_zero(&s_temp)) return 3; - if (bn_is_less(&curve->order, &r_temp)) return 4; - if (bn_is_less(&curve->order, &s_temp)) return 5; - if (bn_is_equal(&curve->order, &r_temp)) return 6; - if (bn_is_equal(&curve->order, &s_temp)) return 7; - - if (!ecdsa_read_pubkey(curve, pub_key, &pub_key_point)) { - return 8; - } - - // Compute Q = sG + r*kpub - point_multiply(curve, &s_temp, &curve->G, &sG); - point_multiply(curve, &r_temp, &pub_key_point, &Q); - point_add(curve, &sG, &Q); - - // Compute r' = H(Q, kpub, m) - calc_r(&Q, pub_key, msg, msg_len, &r_computed); - - // Fully reduce the bignum - bn_mod(&r_computed, &curve->order); - - // Check r == r' - if (bn_is_equal(&r_temp, &r_computed)) return 0; // success - - return 10; -} diff --git a/trezor-crypto/crypto/sha2.c b/trezor-crypto/crypto/sha2.c index 0f14e970874..bea30dae7cf 100644 --- a/trezor-crypto/crypto/sha2.c +++ b/trezor-crypto/crypto/sha2.c @@ -643,7 +643,7 @@ void sha1_Final(SHA1_CTX* context, sha2_byte digest[]) { usedspace = 0; } -char *sha1_End(SHA1_CTX* context, char buffer[]) { +char *sha1_End(SHA1_CTX* context, char buffer[SHA1_DIGEST_STRING_LENGTH]) { sha2_byte digest[SHA1_DIGEST_LENGTH] = {0}, *d = digest; int i = 0; @@ -950,7 +950,7 @@ void sha256_Final(SHA256_CTX* context, sha2_byte digest[]) { usedspace = 0; } -char *sha256_End(SHA256_CTX* context, char buffer[]) { +char *sha256_End(SHA256_CTX* context, char buffer[SHA256_DIGEST_STRING_LENGTH]) { sha2_byte digest[SHA256_DIGEST_LENGTH] = {0}, *d = digest; int i = 0; @@ -1269,7 +1269,7 @@ void sha512_Final(SHA512_CTX* context, sha2_byte digest[]) { memzero(context, sizeof(SHA512_CTX)); } -char *sha512_End(SHA512_CTX* context, char buffer[]) { +char *sha512_End(SHA512_CTX* context, char buffer[SHA512_DIGEST_STRING_LENGTH]) { sha2_byte digest[SHA512_DIGEST_LENGTH] = {0}, *d = digest; int i = 0; diff --git a/trezor-crypto/crypto/sha3.c b/trezor-crypto/crypto/sha3.c index a2563450dcb..0033186fe59 100644 --- a/trezor-crypto/crypto/sha3.c +++ b/trezor-crypto/crypto/sha3.c @@ -26,8 +26,9 @@ #define I64(x) x##LL #define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n)))) #define le2me_64(x) (x) -#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) -# define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) +//#define IS_ALIGNED_64(p) (0 == (7 & ((long)(p)))) // [wallet-core] pointer/numerical type, for MacOS SDK 12.3 +#define IS_ALIGNED_64(p) (0 == (7 & ((const char*)(p) - (const char*)0))) //win +#define me64_to_le_str(to, from, length) memcpy((to), (from), (length)) /* constants */ #define NumberOfRounds 24 diff --git a/trezor-crypto/crypto/shamir.c b/trezor-crypto/crypto/shamir.c index 5e4ab24c205..5aea89bec6b 100644 --- a/trezor-crypto/crypto/shamir.c +++ b/trezor-crypto/crypto/shamir.c @@ -266,7 +266,7 @@ bool shamir_interpolate(uint8_t *result, uint8_t result_index, size_t len) { size_t i = 0, j = 0; uint32_t x[8] = {0}; -#ifdef _MSC_VER + #ifdef _MSC_VER uint32_t (*xs)[8] = _alloca(sizeof(uint32_t) * share_count * 8); memset(xs, 0, sizeof(uint32_t) * share_count * 8); uint32_t (*ys)[8] = _alloca(sizeof(uint32_t) * share_count * 8); @@ -276,7 +276,7 @@ bool shamir_interpolate(uint8_t *result, uint8_t result_index, memset(xs, 0, sizeof(xs)); uint32_t ys[share_count][8]; memset(ys, 0, sizeof(ys)); -#endif + #endif uint32_t num[8] = {~0}; /* num is the numerator (=1) */ uint32_t denom[8] = {0}; uint32_t tmp[8] = {0}; diff --git a/trezor-crypto/crypto/slip39.c b/trezor-crypto/crypto/slip39.c new file mode 100644 index 00000000000..ec1adf20169 --- /dev/null +++ b/trezor-crypto/crypto/slip39.c @@ -0,0 +1,151 @@ +/** + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +/** + * Returns word at position `index`. + */ +const char* get_word(uint16_t index) { + if (index >= WORDS_COUNT) { + return NULL; + } + + return slip39_wordlist[index]; +} + +/** + * Finds the index of a given word. + * Returns true on success and stores result in `index`. + */ +bool word_index(uint16_t* index, const char* word, uint8_t word_length) { + uint16_t lo = 0; + uint16_t hi = WORDS_COUNT; + uint16_t mid = 0; + + while ((hi - lo) > 1) { + mid = (hi + lo) / 2; + if (strncmp(slip39_wordlist[mid], word, word_length) > 0) { + hi = mid; + } else { + lo = mid; + } + } + if (strncmp(slip39_wordlist[lo], word, word_length) != 0) { + return false; + } + *index = lo; + return true; +} + +/** + * Returns the index of the first sequence in words_button_seq[] which is not + * less than the given sequence. Returns WORDS_COUNT if there is no such + * sequence. + */ +static uint16_t find_sequence(uint16_t sequence) { + if (sequence <= words_button_seq[0].sequence) { + return 0; + } + + uint16_t lo = 0; + uint16_t hi = WORDS_COUNT; + + while (hi - lo > 1) { + uint16_t mid = (hi + lo) / 2; + if (words_button_seq[mid].sequence >= sequence) { + hi = mid; + } else { + lo = mid; + } + } + + return hi; +} + +/** + * Returns a word matching the button sequence prefix or NULL if no match is + * found. + */ +const char* button_sequence_to_word(uint16_t sequence) { + if (sequence == 0) { + return slip39_wordlist[words_button_seq[0].index]; + } + + uint16_t multiplier = 1; + while (sequence < 1000) { + sequence *= 10; + multiplier *= 10; + } + + uint16_t i = find_sequence(sequence); + if (i >= WORDS_COUNT || + words_button_seq[i].sequence - sequence >= multiplier) { + return NULL; + } + + return slip39_wordlist[words_button_seq[i].index]; +} + +/** + * Calculates which buttons on the T9 keyboard can still be pressed after the + * prefix was entered. Returns a 9-bit bitmask, where each bit specifies which + * buttons can be pressed (there are still words in this combination). The least + * significant bit corresponds to the first button. + * + * Example: 110000110 - second, third, eighth and ninth button still can be + * pressed. + */ +uint16_t slip39_word_completion_mask(uint16_t prefix) { + if (prefix >= 1000) { + // Four char prefix -> the mask is zero. + return 0; + } + + // Determine the range of sequences [min, max), which have the given prefix. + uint16_t min = prefix; + uint16_t max = prefix + 1; + uint16_t divider = 1; + while (max <= 1000) { + min *= 10; + max *= 10; + divider *= 10; + } + divider /= 10; + + // Determine the range we will be searching in words_button_seq[]. + min = find_sequence(min); + max = find_sequence(max); + + uint16_t bitmap = 0; + for (uint16_t i = min; i < max; ++i) { + uint8_t digit = (words_button_seq[i].sequence / divider) % 10; + bitmap |= 1 << (digit - 1); + } + + return bitmap; +} diff --git a/trezor-crypto/crypto/tests/CMakeLists.txt b/trezor-crypto/crypto/tests/CMakeLists.txt index 39a32b5b96e..4960fbdd0ee 100644 --- a/trezor-crypto/crypto/tests/CMakeLists.txt +++ b/trezor-crypto/crypto/tests/CMakeLists.txt @@ -1,3 +1,9 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + enable_testing() if(WIN32) @@ -12,6 +18,8 @@ endif() # Test executable add_executable(TrezorCryptoTests test_check.c) target_link_libraries(TrezorCryptoTests TrezorCrypto ${CHECK_LIBRARIES}) -target_include_directories(TrezorCryptoTests PRIVATE ${CMAKE_SOURCE_DIR}/src) + +target_link_directories(TrezorCryptoTests PRIVATE ${PREFIX}/lib) +target_include_directories(TrezorCryptoTests PRIVATE ${CMAKE_SOURCE_DIR}/src ${PREFIX}/include) add_test(NAME test_check COMMAND TrezorCryptoTests) diff --git a/trezor-crypto/crypto/tests/test_check.c b/trezor-crypto/crypto/tests/test_check.c index 80ac09461ad..097689f21ed 100644 --- a/trezor-crypto/crypto/tests/test_check.c +++ b/trezor-crypto/crypto/tests/test_check.c @@ -21,6 +21,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -31,7 +32,7 @@ #include -#if VALGRIND +#ifdef VALGRIND #include #include #endif @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -70,11 +72,10 @@ #include #include #include -#include // [wallet-core] -//#include // [wallet-core] -//#include +#include +#include -#if VALGRIND +#ifdef VALGRIND /* * This is a clever trick to make Valgrind's Memcheck verify code * is constant-time with respect to secret data. @@ -139,7 +140,7 @@ START_TEST(test_bignum_read_be) { 0x14087f0a, 0x15498fe5, 0x10b161bb, 0xc55ece}}; for (int i = 0; i < 9; i++) { - ck_assert_int_eq(a.val[i], b.val[i]); + ck_assert_uint_eq(a.val[i], b.val[i]); } } END_TEST @@ -348,21 +349,21 @@ START_TEST(test_bignum_write_uint32) { fromhex( "000000000000000000000000000000000000000000000000000000001fffffff"), &a); - ck_assert_int_eq(bn_write_uint32(&a), 0x1fffffff); + ck_assert_uint_eq(bn_write_uint32(&a), 0x1fffffff); // lowest 30 bits set bn_read_be( fromhex( "000000000000000000000000000000000000000000000000000000003fffffff"), &a); - ck_assert_int_eq(bn_write_uint32(&a), 0x3fffffff); + ck_assert_uint_eq(bn_write_uint32(&a), 0x3fffffff); // bit 31 set bn_read_be( fromhex( "0000000000000000000000000000000000000000000000000000000040000000"), &a); - ck_assert_int_eq(bn_write_uint32(&a), 0x40000000); + ck_assert_uint_eq(bn_write_uint32(&a), 0x40000000); } END_TEST @@ -374,35 +375,35 @@ START_TEST(test_bignum_write_uint64) { fromhex( "000000000000000000000000000000000000000000000000000000003fffffff"), &a); - ck_assert_int_eq(bn_write_uint64(&a), 0x3fffffff); + ck_assert_uint_eq(bn_write_uint64(&a), 0x3fffffff); // bit 31 set bn_read_be( fromhex( "0000000000000000000000000000000000000000000000000000000040000000"), &a); - ck_assert_int_eq(bn_write_uint64(&a), 0x40000000); + ck_assert_uint_eq(bn_write_uint64(&a), 0x40000000); // bit 33 set bn_read_be( fromhex( "0000000000000000000000000000000000000000000000000000000100000000"), &a); - ck_assert_int_eq(bn_write_uint64(&a), 0x100000000LL); + ck_assert_uint_eq(bn_write_uint64(&a), 0x100000000LL); // bit 61 set bn_read_be( fromhex( "0000000000000000000000000000000000000000000000002000000000000000"), &a); - ck_assert_int_eq(bn_write_uint64(&a), 0x2000000000000000LL); + ck_assert_uint_eq(bn_write_uint64(&a), 0x2000000000000000LL); // all 64 bits set bn_read_be( fromhex( "000000000000000000000000000000000000000000000000ffffffffffffffff"), &a); - ck_assert_int_eq(bn_write_uint64(&a), 0xffffffffffffffffLL); + ck_assert_uint_eq(bn_write_uint64(&a), 0xffffffffffffffffLL); } END_TEST @@ -556,19 +557,19 @@ END_TEST START_TEST(test_bignum_format_uint64) { char buf[128], str[128]; - int r; + size_t r; // test for (10^i) and (10^i) - 1 uint64_t m = 1; for (int i = 0; i <= 19; i++, m *= 10) { sprintf(str, "%" PRIu64, m); r = bn_format_uint64(m, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, strlen(str)); + ck_assert_uint_eq(r, strlen(str)); ck_assert_str_eq(buf, str); uint64_t n = m - 1; sprintf(str, "%" PRIu64, n); r = bn_format_uint64(n, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, strlen(str)); + ck_assert_uint_eq(r, strlen(str)); ck_assert_str_eq(buf, str); } } @@ -577,14 +578,14 @@ END_TEST START_TEST(test_bignum_format) { bignum256 a; char buf[128]; - int r; + size_t r; bn_read_be( fromhex( "0000000000000000000000000000000000000000000000000000000000000000"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "0"); bn_read_be( @@ -592,7 +593,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000000"), &a); r = bn_format(&a, NULL, NULL, 20, 0, true, buf, sizeof(buf)); - ck_assert_int_eq(r, 22); + ck_assert_uint_eq(r, 22); ck_assert_str_eq(buf, "0.00000000000000000000"); bn_read_be( @@ -600,7 +601,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000000"), &a); r = bn_format(&a, NULL, NULL, 0, 5, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "0"); bn_read_be( @@ -608,7 +609,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000000"), &a); r = bn_format(&a, NULL, NULL, 0, -5, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "0"); bn_read_be( @@ -616,7 +617,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000000"), &a); r = bn_format(&a, "", "", 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "0"); bn_read_be( @@ -624,7 +625,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000000"), &a); r = bn_format(&a, NULL, "SFFX", 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1 + 4); + ck_assert_uint_eq(r, 1 + 4); ck_assert_str_eq(buf, "0SFFX"); bn_read_be( @@ -632,7 +633,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000000"), &a); r = bn_format(&a, "PRFX", NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 4 + 1); + ck_assert_uint_eq(r, 4 + 1); ck_assert_str_eq(buf, "PRFX0"); bn_read_be( @@ -640,7 +641,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000000"), &a); r = bn_format(&a, "PRFX", "SFFX", 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 4 + 1 + 4); + ck_assert_uint_eq(r, 4 + 1 + 4); ck_assert_str_eq(buf, "PRFX0SFFX"); bn_read_be( @@ -648,7 +649,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000000"), &a); r = bn_format(&a, NULL, NULL, 18, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "0"); bn_read_be( @@ -656,7 +657,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000001"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "1"); bn_read_be( @@ -664,7 +665,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000001"), &a); r = bn_format(&a, NULL, NULL, 6, 6, true, buf, sizeof(buf)); - ck_assert_int_eq(r, 8); + ck_assert_uint_eq(r, 8); ck_assert_str_eq(buf, "1.000000"); bn_read_be( @@ -672,7 +673,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000002"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "2"); bn_read_be( @@ -680,7 +681,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000005"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "5"); bn_read_be( @@ -688,7 +689,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000009"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "9"); bn_read_be( @@ -696,7 +697,7 @@ START_TEST(test_bignum_format) { "000000000000000000000000000000000000000000000000000000000000000a"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 2); + ck_assert_uint_eq(r, 2); ck_assert_str_eq(buf, "10"); bn_read_be( @@ -704,7 +705,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000014"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 2); + ck_assert_uint_eq(r, 2); ck_assert_str_eq(buf, "20"); bn_read_be( @@ -712,7 +713,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000032"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 2); + ck_assert_uint_eq(r, 2); ck_assert_str_eq(buf, "50"); bn_read_be( @@ -720,7 +721,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000063"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 2); + ck_assert_uint_eq(r, 2); ck_assert_str_eq(buf, "99"); bn_read_be( @@ -728,7 +729,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000000064"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 3); + ck_assert_uint_eq(r, 3); ck_assert_str_eq(buf, "100"); bn_read_be( @@ -736,7 +737,7 @@ START_TEST(test_bignum_format) { "00000000000000000000000000000000000000000000000000000000000000c8"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 3); + ck_assert_uint_eq(r, 3); ck_assert_str_eq(buf, "200"); bn_read_be( @@ -744,7 +745,7 @@ START_TEST(test_bignum_format) { "00000000000000000000000000000000000000000000000000000000000001f4"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 3); + ck_assert_uint_eq(r, 3); ck_assert_str_eq(buf, "500"); bn_read_be( @@ -752,7 +753,7 @@ START_TEST(test_bignum_format) { "00000000000000000000000000000000000000000000000000000000000003e7"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 3); + ck_assert_uint_eq(r, 3); ck_assert_str_eq(buf, "999"); bn_read_be( @@ -760,7 +761,7 @@ START_TEST(test_bignum_format) { "00000000000000000000000000000000000000000000000000000000000003e8"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 4); + ck_assert_uint_eq(r, 4); ck_assert_str_eq(buf, "1000"); bn_read_be( @@ -768,7 +769,7 @@ START_TEST(test_bignum_format) { "0000000000000000000000000000000000000000000000000000000000989680"), &a); r = bn_format(&a, NULL, NULL, 7, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 1); + ck_assert_uint_eq(r, 1); ck_assert_str_eq(buf, "1"); bn_read_be( @@ -776,7 +777,7 @@ START_TEST(test_bignum_format) { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); r = bn_format(&a, NULL, NULL, 0, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 78); + ck_assert_uint_eq(r, 78); ck_assert_str_eq(buf, "11579208923731619542357098500868790785326998466564056403945" "7584007913129639935"); @@ -786,7 +787,7 @@ START_TEST(test_bignum_format) { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); r = bn_format(&a, NULL, NULL, 1, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 79); + ck_assert_uint_eq(r, 79); ck_assert_str_eq(buf, "11579208923731619542357098500868790785326998466564056403945" "758400791312963993.5"); @@ -796,7 +797,7 @@ START_TEST(test_bignum_format) { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); r = bn_format(&a, NULL, NULL, 2, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 79); + ck_assert_uint_eq(r, 79); ck_assert_str_eq(buf, "11579208923731619542357098500868790785326998466564056403945" "75840079131296399.35"); @@ -806,7 +807,7 @@ START_TEST(test_bignum_format) { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); r = bn_format(&a, NULL, NULL, 8, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 79); + ck_assert_uint_eq(r, 79); ck_assert_str_eq(buf, "11579208923731619542357098500868790785326998466564056403945" "75840079131.29639935"); @@ -816,7 +817,7 @@ START_TEST(test_bignum_format) { "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe3bbb00"), &a); r = bn_format(&a, NULL, NULL, 8, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 70); + ck_assert_uint_eq(r, 70); ck_assert_str_eq(buf, "11579208923731619542357098500868790785326998466564056403945" "75840079131"); @@ -826,7 +827,7 @@ START_TEST(test_bignum_format) { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); r = bn_format(&a, NULL, NULL, 18, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 79); + ck_assert_uint_eq(r, 79); ck_assert_str_eq(buf, "11579208923731619542357098500868790785326998466564056403945" "7.584007913129639935"); @@ -836,7 +837,7 @@ START_TEST(test_bignum_format) { "fffffffffffffffffffffffffffffffffffffffffffffffff7e52fe5afe40000"), &a); r = bn_format(&a, NULL, NULL, 18, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 60); + ck_assert_uint_eq(r, 60); ck_assert_str_eq( buf, "115792089237316195423570985008687907853269984665640564039457"); @@ -845,7 +846,7 @@ START_TEST(test_bignum_format) { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); r = bn_format(&a, NULL, NULL, 78, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 80); + ck_assert_uint_eq(r, 80); ck_assert_str_eq(buf, "0." "11579208923731619542357098500868790785326998466564056403945" @@ -856,7 +857,7 @@ START_TEST(test_bignum_format) { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), &a); r = bn_format(&a, NULL, NULL, 0, 10, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 88); + ck_assert_uint_eq(r, 88); ck_assert_str_eq(buf, "11579208923731619542357098500868790785326998466564056403945" "75840079131296399350000000000"); @@ -867,7 +868,7 @@ START_TEST(test_bignum_format) { &a); r = bn_format(&a, "quite a long prefix", "even longer suffix", 60, 0, false, buf, sizeof(buf)); - ck_assert_int_eq(r, 116); + ck_assert_uint_eq(r, 116); ck_assert_str_eq(buf, "quite a long " "prefix115792089237316195." @@ -881,11 +882,11 @@ START_TEST(test_bignum_format) { memset(buf, 'a', sizeof(buf)); r = bn_format(&a, "prefix", "suffix", 10, 0, false, buf, 31); ck_assert_str_eq(buf, "prefix8198552.9216486895suffix"); - ck_assert_int_eq(r, 30); + ck_assert_uint_eq(r, 30); memset(buf, 'a', sizeof(buf)); r = bn_format(&a, "prefix", "suffix", 10, 0, false, buf, 30); - ck_assert_int_eq(r, 0); + ck_assert_uint_eq(r, 0); ck_assert_str_eq(buf, ""); } END_TEST @@ -954,7 +955,7 @@ END_TEST // https://tools.ietf.org/html/rfc4648#section-10 START_TEST(test_base32_rfc4648) { - const struct { + static const struct { const char *decoded; const char *encoded; const char *encoded_lowercase; @@ -978,8 +979,8 @@ START_TEST(test_base32_rfc4648) { size_t inlen = strlen(in); size_t outlen = strlen(out); - ck_assert_int_eq(outlen, base32_encoded_length(inlen)); - ck_assert_int_eq(inlen, base32_decoded_length(outlen)); + ck_assert_uint_eq(outlen, base32_encoded_length(inlen)); + ck_assert_uint_eq(inlen, base32_decoded_length(outlen)); ck_assert(base32_encode((uint8_t *)in, inlen, buffer, sizeof(buffer), BASE32_ALPHABET_RFC4648) != NULL); @@ -1003,7 +1004,7 @@ END_TEST // from // https://github.com/bitcoin/bitcoin/blob/master/src/test/data/base58_keys_valid.json START_TEST(test_base58) { - const char *base58_vector[] = { + static const char *base58_vector[] = { "0065a16059864a2fdbc7c99a4723a8395bc6f188eb", "1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i", "0574f209f6ea907e2ea48f74fae05782ae8a665257", @@ -1134,7 +1135,7 @@ END_TEST // Graphene Base85CheckEncoding START_TEST(test_base58gph) { - const char *base58_vector[] = { + static const char *base58_vector[] = { "02e649f63f8e8121345fd7f47d0d185a3ccaa843115cd2e9392dcd9b82263bc680", "6dumtt9swxCqwdPZBGXh9YmHoEjFFnNfwHaTqRbQTghGAY2gRz", "021c7359cd885c0e319924d97e3980206ad64387aff54908241125b3a88b55ca16", @@ -1188,7 +1189,7 @@ START_TEST(test_bignum_divmod) { i = 0; while (!bn_is_zero(&a) && i < 44) { bn_divmod58(&a, &r); - ck_assert_int_eq(r, ar[i]); + ck_assert_uint_eq(r, ar[i]); i++; } ck_assert_int_eq(i, 44); @@ -1205,7 +1206,7 @@ START_TEST(test_bignum_divmod) { i = 0; while (!bn_is_zero(&b) && i < 26) { bn_divmod1000(&b, &r); - ck_assert_int_eq(r, br[i]); + ck_assert_uint_eq(r, br[i]); i++; } ck_assert_int_eq(i, 26); @@ -1226,7 +1227,7 @@ START_TEST(test_bip32_vector_1) { // [Chain m] fingerprint = 0; - ck_assert_int_eq(fingerprint, 0x00000000); + ck_assert_uint_eq(fingerprint, 0x00000000); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1237,7 +1238,7 @@ START_TEST(test_bip32_vector_1) { fromhex( "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1251,8 +1252,7 @@ START_TEST(test_bip32_vector_1) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1268,7 +1268,7 @@ START_TEST(test_bip32_vector_1) { // [Chain m/0'] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd_prime(&node, 0); - ck_assert_int_eq(fingerprint, 0x3442193e); + ck_assert_uint_eq(fingerprint, 0x3442193e); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1279,7 +1279,7 @@ START_TEST(test_bip32_vector_1) { fromhex( "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1293,7 +1293,7 @@ START_TEST(test_bip32_vector_1) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1309,7 +1309,7 @@ START_TEST(test_bip32_vector_1) { // [Chain m/0'/1] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd(&node, 1); - ck_assert_int_eq(fingerprint, 0x5c1bd648); + ck_assert_uint_eq(fingerprint, 0x5c1bd648); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1320,7 +1320,7 @@ START_TEST(test_bip32_vector_1) { fromhex( "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1334,7 +1334,7 @@ START_TEST(test_bip32_vector_1) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1350,7 +1350,7 @@ START_TEST(test_bip32_vector_1) { // [Chain m/0'/1/2'] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd_prime(&node, 2); - ck_assert_int_eq(fingerprint, 0xbef5a2f9); + ck_assert_uint_eq(fingerprint, 0xbef5a2f9); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1361,7 +1361,7 @@ START_TEST(test_bip32_vector_1) { fromhex( "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1375,7 +1375,7 @@ START_TEST(test_bip32_vector_1) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1391,7 +1391,7 @@ START_TEST(test_bip32_vector_1) { // [Chain m/0'/1/2'/2] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd(&node, 2); - ck_assert_int_eq(fingerprint, 0xee7ab90c); + ck_assert_uint_eq(fingerprint, 0xee7ab90c); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1402,7 +1402,7 @@ START_TEST(test_bip32_vector_1) { fromhex( "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1416,7 +1416,7 @@ START_TEST(test_bip32_vector_1) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1432,7 +1432,7 @@ START_TEST(test_bip32_vector_1) { // [Chain m/0'/1/2'/2/1000000000] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd(&node, 1000000000); - ck_assert_int_eq(fingerprint, 0xd880d7d8); + ck_assert_uint_eq(fingerprint, 0xd880d7d8); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1443,7 +1443,7 @@ START_TEST(test_bip32_vector_1) { fromhex( "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1457,7 +1457,7 @@ START_TEST(test_bip32_vector_1) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1489,7 +1489,7 @@ START_TEST(test_bip32_vector_2) { // [Chain m] fingerprint = 0; - ck_assert_int_eq(fingerprint, 0x00000000); + ck_assert_uint_eq(fingerprint, 0x00000000); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1500,7 +1500,7 @@ START_TEST(test_bip32_vector_2) { fromhex( "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1514,7 +1514,7 @@ START_TEST(test_bip32_vector_2) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1531,7 +1531,7 @@ START_TEST(test_bip32_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node, 0); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0xbd16bee5); + ck_assert_uint_eq(fingerprint, 0xbd16bee5); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1542,7 +1542,7 @@ START_TEST(test_bip32_vector_2) { fromhex( "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1556,7 +1556,7 @@ START_TEST(test_bip32_vector_2) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1573,7 +1573,7 @@ START_TEST(test_bip32_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd_prime(&node, 2147483647); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x5a61ff8e); + ck_assert_uint_eq(fingerprint, 0x5a61ff8e); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1584,7 +1584,7 @@ START_TEST(test_bip32_vector_2) { fromhex( "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1598,7 +1598,7 @@ START_TEST(test_bip32_vector_2) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1615,7 +1615,7 @@ START_TEST(test_bip32_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node, 1); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0xd8ab4937); + ck_assert_uint_eq(fingerprint, 0xd8ab4937); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1626,7 +1626,7 @@ START_TEST(test_bip32_vector_2) { fromhex( "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1640,7 +1640,7 @@ START_TEST(test_bip32_vector_2) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1657,7 +1657,7 @@ START_TEST(test_bip32_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd_prime(&node, 2147483646); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x78412e3a); + ck_assert_uint_eq(fingerprint, 0x78412e3a); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1668,7 +1668,7 @@ START_TEST(test_bip32_vector_2) { fromhex( "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1682,7 +1682,7 @@ START_TEST(test_bip32_vector_2) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1699,7 +1699,7 @@ START_TEST(test_bip32_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node, 2); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x31a507b8); + ck_assert_uint_eq(fingerprint, 0x31a507b8); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1710,7 +1710,7 @@ START_TEST(test_bip32_vector_2) { fromhex( "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1724,7 +1724,7 @@ START_TEST(test_bip32_vector_2) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1749,7 +1749,7 @@ START_TEST(test_bip32_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_public_ckd(&node, 0); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0xbd16bee5); + ck_assert_uint_eq(fingerprint, 0xbd16bee5); ck_assert_mem_eq( node.chain_code, fromhex( @@ -1760,7 +1760,7 @@ START_TEST(test_bip32_vector_2) { fromhex( "0000000000000000000000000000000000000000000000000000000000000000"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -1786,8 +1786,8 @@ START_TEST(test_bip32_vector_3) { // [Chain m] fingerprint = 0; - ck_assert_int_eq(fingerprint, 0x00000000); - hdnode_fill_public_key(&node); + ck_assert_uint_eq(fingerprint, 0x00000000); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, sizeof(str)); ck_assert_str_eq(str, @@ -1796,7 +1796,7 @@ START_TEST(test_bip32_vector_3) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1813,7 +1813,7 @@ START_TEST(test_bip32_vector_3) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd_prime(&node, 0); ck_assert_int_eq(r, 1); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, sizeof(str)); ck_assert_str_eq(str, @@ -1822,7 +1822,7 @@ START_TEST(test_bip32_vector_3) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1854,7 +1854,7 @@ START_TEST(test_bip32_vector_4) { // [Chain m] fingerprint = 0; ck_assert_int_eq(fingerprint, 0x00000000); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, sizeof(str)); ck_assert_str_eq(str, @@ -1863,7 +1863,7 @@ START_TEST(test_bip32_vector_4) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1880,7 +1880,7 @@ START_TEST(test_bip32_vector_4) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd_prime(&node, 0); ck_assert_int_eq(r, 1); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, sizeof(str)); ck_assert_str_eq(str, @@ -1889,7 +1889,7 @@ START_TEST(test_bip32_vector_4) { r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); ck_assert_str_eq(str, @@ -1901,6 +1901,32 @@ START_TEST(test_bip32_vector_4) { memcpy(&node3, &node, sizeof(HDNode)); memzero(&node3.private_key, 32); ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); + + // [Chain m/0'/1'] + fingerprint = hdnode_fingerprint(&node); + r = hdnode_private_ckd_prime(&node, 1); + ck_assert_int_eq(r, 1); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); + hdnode_serialize_private(&node, fingerprint, VERSION_PRIVATE, str, + sizeof(str)); + ck_assert_str_eq(str, + "xprv9xJocDuwtYCMNAo3Zw76WENQeAS6WGXQ55RCy7tDJ8oALr4FWkuVoHJ" + "eHVAcAqiZLE7Je3vZJHxspZdFHfnBEjHqU5hG1Jaj32dVoS6XLT1"); + r = hdnode_deserialize_private(str, VERSION_PRIVATE, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); + ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); + hdnode_serialize_public(&node, fingerprint, VERSION_PUBLIC, str, sizeof(str)); + ck_assert_str_eq(str, + "xpub6BJA1jSqiukeaesWfxe6sNK9CCGaujFFSJLomWHprUL9DePQ4JDkM5d" + "88n49sMGJxrhpjazuXYWdMf17C9T5XnxkopaeS7jGk1GyyVziaMt"); + r = hdnode_deserialize_public(str, VERSION_PUBLIC, SECP256K1_NAME, &node2, + NULL); + ck_assert_int_eq(r, 0); + memcpy(&node3, &node, sizeof(HDNode)); + memzero(&node3.private_key, 32); + ck_assert_mem_eq(&node2, &node3, sizeof(HDNode)); } END_TEST @@ -1917,20 +1943,20 @@ START_TEST(test_bip32_compare) { "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, SECP256K1_NAME, &node2); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); for (i = 0; i < 100; i++) { memcpy(&node3, &node1, sizeof(HDNode)); - hdnode_fill_public_key(&node3); + ck_assert_int_eq(hdnode_fill_public_key(&node3), 0); r = hdnode_private_ckd(&node1, i); ck_assert_int_eq(r, 1); r = hdnode_public_ckd(&node2, i); ck_assert_int_eq(r, 1); r = hdnode_public_ckd(&node3, i); ck_assert_int_eq(r, 1); - ck_assert_int_eq(node1.depth, node2.depth); - ck_assert_int_eq(node1.depth, node3.depth); - ck_assert_int_eq(node1.child_num, node2.child_num); - ck_assert_int_eq(node1.child_num, node3.child_num); + ck_assert_uint_eq(node1.depth, node2.depth); + ck_assert_uint_eq(node1.depth, node3.depth); + ck_assert_uint_eq(node1.child_num, node2.child_num); + ck_assert_uint_eq(node1.child_num, node3.child_num); ck_assert_mem_eq(node1.chain_code, node2.chain_code, 32); ck_assert_mem_eq(node1.chain_code, node3.chain_code, 32); ck_assert_mem_eq( @@ -1943,7 +1969,7 @@ START_TEST(test_bip32_compare) { fromhex( "0000000000000000000000000000000000000000000000000000000000000000"), 32); - hdnode_fill_public_key(&node1); + ck_assert_int_eq(hdnode_fill_public_key(&node1), 0); ck_assert_mem_eq(node1.public_key, node2.public_key, 33); ck_assert_mem_eq(node1.public_key, node3.public_key, 33); } @@ -1953,7 +1979,7 @@ END_TEST START_TEST(test_bip32_optimized) { HDNode root; hdnode_from_seed((uint8_t *)"NothingToSeeHere", 16, SECP256K1_NAME, &root); - hdnode_fill_public_key(&root); + ck_assert_int_eq(hdnode_fill_public_key(&root), 0); curve_point pub; ecdsa_read_pubkey(&secp256k1, root.public_key, &pub); @@ -1965,7 +1991,7 @@ START_TEST(test_bip32_optimized) { // unoptimized memcpy(&node, &root, sizeof(HDNode)); hdnode_public_ckd(&node, i); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ecdsa_get_address(node.public_key, 0, HASHER_SHA2_RIPEMD, HASHER_SHA2D, addr1, sizeof(addr1)); // optimized @@ -1978,7 +2004,8 @@ START_TEST(test_bip32_optimized) { } END_TEST -#if USE_BIP32_CACHE // [wallet-core] +#if USE_BIP32_CACHE + START_TEST(test_bip32_cache_1) { HDNode node1, node2; int i, r; @@ -2112,7 +2139,7 @@ START_TEST(test_bip32_nist_seed) { fromhex( "7762f9729fed06121fd13f326884c82f59aa95c57ac492ce8c9654e60efd130c"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2136,7 +2163,7 @@ START_TEST(test_bip32_nist_seed) { fromhex( "0e49dc46ce1d8c29d9b80a05e40f5d0cd68cbf02ae98572186f5343be18084bf"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2155,7 +2182,7 @@ START_TEST(test_bip32_nist_vector_1) { // [Chain m] fingerprint = 0; - ck_assert_int_eq(fingerprint, 0x00000000); + ck_assert_uint_eq(fingerprint, 0x00000000); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2166,7 +2193,7 @@ START_TEST(test_bip32_nist_vector_1) { fromhex( "612091aaa12e22dd2abef664f8a01a82cae99ad7441b7ef8110424915c268bc2"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2176,7 +2203,7 @@ START_TEST(test_bip32_nist_vector_1) { // [Chain m/0'] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd_prime(&node, 0); - ck_assert_int_eq(fingerprint, 0xbe6105b5); + ck_assert_uint_eq(fingerprint, 0xbe6105b5); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2187,7 +2214,7 @@ START_TEST(test_bip32_nist_vector_1) { fromhex( "6939694369114c67917a182c59ddb8cafc3004e63ca5d3b84403ba8613debc0c"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2197,7 +2224,7 @@ START_TEST(test_bip32_nist_vector_1) { // [Chain m/0'/1] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd(&node, 1); - ck_assert_int_eq(fingerprint, 0x9b02312f); + ck_assert_uint_eq(fingerprint, 0x9b02312f); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2208,7 +2235,7 @@ START_TEST(test_bip32_nist_vector_1) { fromhex( "284e9d38d07d21e4e281b645089a94f4cf5a5a81369acf151a1c3a57f18b2129"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2218,7 +2245,7 @@ START_TEST(test_bip32_nist_vector_1) { // [Chain m/0'/1/2'] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd_prime(&node, 2); - ck_assert_int_eq(fingerprint, 0xb98005c1); + ck_assert_uint_eq(fingerprint, 0xb98005c1); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2229,7 +2256,7 @@ START_TEST(test_bip32_nist_vector_1) { fromhex( "694596e8a54f252c960eb771a3c41e7e32496d03b954aeb90f61635b8e092aa7"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2239,7 +2266,7 @@ START_TEST(test_bip32_nist_vector_1) { // [Chain m/0'/1/2'/2] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd(&node, 2); - ck_assert_int_eq(fingerprint, 0x0e9f3274); + ck_assert_uint_eq(fingerprint, 0x0e9f3274); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2250,7 +2277,7 @@ START_TEST(test_bip32_nist_vector_1) { fromhex( "5996c37fd3dd2679039b23ed6f70b506c6b56b3cb5e424681fb0fa64caf82aaa"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2260,7 +2287,7 @@ START_TEST(test_bip32_nist_vector_1) { // [Chain m/0'/1/2'/2/1000000000] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd(&node, 1000000000); - ck_assert_int_eq(fingerprint, 0x8b2b5c4b); + ck_assert_uint_eq(fingerprint, 0x8b2b5c4b); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2271,7 +2298,7 @@ START_TEST(test_bip32_nist_vector_1) { fromhex( "21c4f269ef0a5fd1badf47eeacebeeaa3de22eb8e5b0adcd0f27dd99d34d0119"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2294,7 +2321,7 @@ START_TEST(test_bip32_nist_vector_2) { // [Chain m] fingerprint = 0; - ck_assert_int_eq(fingerprint, 0x00000000); + ck_assert_uint_eq(fingerprint, 0x00000000); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2305,7 +2332,7 @@ START_TEST(test_bip32_nist_vector_2) { fromhex( "eaa31c2e46ca2962227cf21d73a7ef0ce8b31c756897521eb6c7b39796633357"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2316,7 +2343,7 @@ START_TEST(test_bip32_nist_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node, 0); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x607f628f); + ck_assert_uint_eq(fingerprint, 0x607f628f); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2327,7 +2354,7 @@ START_TEST(test_bip32_nist_vector_2) { fromhex( "d7d065f63a62624888500cdb4f88b6d59c2927fee9e6d0cdff9cad555884df6e"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2338,7 +2365,7 @@ START_TEST(test_bip32_nist_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd_prime(&node, 2147483647); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x946d2a54); + ck_assert_uint_eq(fingerprint, 0x946d2a54); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2349,7 +2376,7 @@ START_TEST(test_bip32_nist_vector_2) { fromhex( "96d2ec9316746a75e7793684ed01e3d51194d81a42a3276858a5b7376d4b94b9"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2360,7 +2387,7 @@ START_TEST(test_bip32_nist_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node, 1); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x218182d8); + ck_assert_uint_eq(fingerprint, 0x218182d8); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2371,7 +2398,7 @@ START_TEST(test_bip32_nist_vector_2) { fromhex( "974f9096ea6873a915910e82b29d7c338542ccde39d2064d1cc228f371542bbc"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2382,7 +2409,7 @@ START_TEST(test_bip32_nist_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd_prime(&node, 2147483646); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x931223e4); + ck_assert_uint_eq(fingerprint, 0x931223e4); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2393,7 +2420,7 @@ START_TEST(test_bip32_nist_vector_2) { fromhex( "da29649bbfaff095cd43819eda9a7be74236539a29094cd8336b07ed8d4eff63"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2404,7 +2431,7 @@ START_TEST(test_bip32_nist_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node, 2); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x956c4629); + ck_assert_uint_eq(fingerprint, 0x956c4629); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2415,7 +2442,7 @@ START_TEST(test_bip32_nist_vector_2) { fromhex( "bb0a77ba01cc31d77205d51d08bd313b979a71ef4de9b062f8958297e746bd67"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2434,7 +2461,7 @@ START_TEST(test_bip32_nist_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_public_ckd(&node, 0); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x607f628f); + ck_assert_uint_eq(fingerprint, 0x607f628f); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2466,20 +2493,20 @@ START_TEST(test_bip32_nist_compare) { "301133282ad079cbeb59bc446ad39d333928f74c46997d3609cd3e2801ca69d62788" "f9f174429946ff4e9be89f67c22fae28cb296a9b37734f75e73d1477af19"), 64, NIST256P1_NAME, &node2); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); for (i = 0; i < 100; i++) { memcpy(&node3, &node1, sizeof(HDNode)); - hdnode_fill_public_key(&node3); + ck_assert_int_eq(hdnode_fill_public_key(&node3), 0); r = hdnode_private_ckd(&node1, i); ck_assert_int_eq(r, 1); r = hdnode_public_ckd(&node2, i); ck_assert_int_eq(r, 1); r = hdnode_public_ckd(&node3, i); ck_assert_int_eq(r, 1); - ck_assert_int_eq(node1.depth, node2.depth); - ck_assert_int_eq(node1.depth, node3.depth); - ck_assert_int_eq(node1.child_num, node2.child_num); - ck_assert_int_eq(node1.child_num, node3.child_num); + ck_assert_uint_eq(node1.depth, node2.depth); + ck_assert_uint_eq(node1.depth, node3.depth); + ck_assert_uint_eq(node1.child_num, node2.child_num); + ck_assert_uint_eq(node1.child_num, node3.child_num); ck_assert_mem_eq(node1.chain_code, node2.chain_code, 32); ck_assert_mem_eq(node1.chain_code, node3.chain_code, 32); ck_assert_mem_eq( @@ -2492,7 +2519,7 @@ START_TEST(test_bip32_nist_compare) { fromhex( "0000000000000000000000000000000000000000000000000000000000000000"), 32); - hdnode_fill_public_key(&node1); + ck_assert_int_eq(hdnode_fill_public_key(&node1), 0); ck_assert_mem_eq(node1.public_key, node2.public_key, 33); ck_assert_mem_eq(node1.public_key, node3.public_key, 33); } @@ -2512,7 +2539,7 @@ START_TEST(test_bip32_nist_repeat) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd_prime(&node, 28578); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0xbe6105b5); + ck_assert_uint_eq(fingerprint, 0xbe6105b5); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2523,7 +2550,7 @@ START_TEST(test_bip32_nist_repeat) { fromhex( "06f0db126f023755d0b8d86d4591718a5210dd8d024e3e14b6159d63f53aa669"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2534,7 +2561,7 @@ START_TEST(test_bip32_nist_repeat) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node2, 33941); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x3e2b7bc6); + ck_assert_uint_eq(fingerprint, 0x3e2b7bc6); ck_assert_mem_eq( node2.chain_code, fromhex( @@ -2545,7 +2572,7 @@ START_TEST(test_bip32_nist_repeat) { fromhex( "092154eed4af83e078ff9b84322015aefe5769e31270f62c3f66c33888335f3a"), 32); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq( node2.public_key, fromhex( @@ -2556,13 +2583,13 @@ START_TEST(test_bip32_nist_repeat) { memzero(&node2.private_key, 32); r = hdnode_public_ckd(&node2, 33941); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x3e2b7bc6); + ck_assert_uint_eq(fingerprint, 0x3e2b7bc6); ck_assert_mem_eq( node2.chain_code, fromhex( "9e87fe95031f14736774cd82f25fd885065cb7c358c1edf813c72af535e83071"), 32); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq( node2.public_key, fromhex( @@ -2590,7 +2617,7 @@ START_TEST(test_bip32_ed25519_vector_1) { fromhex( "2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e7"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2609,7 +2636,7 @@ START_TEST(test_bip32_ed25519_vector_1) { fromhex( "68e0fe46dfb67e368c75379acec591dad19df3cde26e63b93a8e704f1dade7a3"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2628,7 +2655,7 @@ START_TEST(test_bip32_ed25519_vector_1) { fromhex( "b1d0bad404bf35da785a64ca1ac54b2617211d2777696fbffaf208f746ae84f2"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2647,7 +2674,7 @@ START_TEST(test_bip32_ed25519_vector_1) { fromhex( "92a5b23c0b8a99e37d07df3fb9966917f5d06e02ddbd909c7e184371463e9fc9"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2666,7 +2693,7 @@ START_TEST(test_bip32_ed25519_vector_1) { fromhex( "30d1dc7e5fc04c31219ab25a27ae00b50f6fd66622f6e9c913253d6511d1e662"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2685,7 +2712,7 @@ START_TEST(test_bip32_ed25519_vector_1) { fromhex( "8f94d394a8e8fd6b1bc2f3f49f5c47e385281d5c17e65324b0f62483e37e8793"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2717,7 +2744,7 @@ START_TEST(test_bip32_ed25519_vector_2) { fromhex( "171cb88b1b3c1db25add599712e36245d75bc65a1a5c9e18d76f9f2b1eab4012"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2737,7 +2764,7 @@ START_TEST(test_bip32_ed25519_vector_2) { fromhex( "1559eb2bbec5790b0c65d8693e4d0875b1747f4970ae8b650486ed7470845635"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2757,7 +2784,7 @@ START_TEST(test_bip32_ed25519_vector_2) { fromhex( "ea4f5bfe8694d8bb74b7b59404632fd5968b774ed545e810de9c32a4fb4192f4"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2777,7 +2804,7 @@ START_TEST(test_bip32_ed25519_vector_2) { fromhex( "3757c7577170179c7868353ada796c839135b3d30554bbb74a4b1e4a5a58505c"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2797,7 +2824,7 @@ START_TEST(test_bip32_ed25519_vector_2) { fromhex( "5837736c89570de861ebc173b1086da4f505d4adb387c6a1b1342d5e4ac9ec72"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2817,7 +2844,7 @@ START_TEST(test_bip32_ed25519_vector_2) { fromhex( "551d333177df541ad876a60ea71f00447931c0a9da16f227c11ea080d7391b8d"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2844,7 +2871,7 @@ START_TEST(test_bip32_decred_vector_1) { // [Chain m] fingerprint = 0; - ck_assert_int_eq(fingerprint, 0x00000000); + ck_assert_uint_eq(fingerprint, 0x00000000); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2855,7 +2882,7 @@ START_TEST(test_bip32_decred_vector_1) { fromhex( "e8f32e723decf4051aefac8e2c93c9c5b214313817cdb01a1494b917c8436b35"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2870,7 +2897,7 @@ START_TEST(test_bip32_decred_vector_1) { SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -2887,7 +2914,7 @@ START_TEST(test_bip32_decred_vector_1) { // [Chain m/0'] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd_prime(&node, 0); - ck_assert_int_eq(fingerprint, 0xbc495588); + ck_assert_uint_eq(fingerprint, 0xbc495588); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2898,7 +2925,7 @@ START_TEST(test_bip32_decred_vector_1) { fromhex( "edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2912,7 +2939,7 @@ START_TEST(test_bip32_decred_vector_1) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -2929,7 +2956,7 @@ START_TEST(test_bip32_decred_vector_1) { // [Chain m/0'/1] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd(&node, 1); - ck_assert_int_eq(fingerprint, 0xc67bc2ef); + ck_assert_uint_eq(fingerprint, 0xc67bc2ef); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2940,7 +2967,7 @@ START_TEST(test_bip32_decred_vector_1) { fromhex( "3c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2954,7 +2981,7 @@ START_TEST(test_bip32_decred_vector_1) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -2971,7 +2998,7 @@ START_TEST(test_bip32_decred_vector_1) { // [Chain m/0'/1/2'] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd_prime(&node, 2); - ck_assert_int_eq(fingerprint, 0xe7072187); + ck_assert_uint_eq(fingerprint, 0xe7072187); ck_assert_mem_eq( node.chain_code, fromhex( @@ -2982,7 +3009,7 @@ START_TEST(test_bip32_decred_vector_1) { fromhex( "cbce0d719ecf7431d88e6a89fa1483e02e35092af60c042b1df2ff59fa424dca"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -2996,7 +3023,7 @@ START_TEST(test_bip32_decred_vector_1) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -3013,7 +3040,7 @@ START_TEST(test_bip32_decred_vector_1) { // [Chain m/0'/1/2'/2] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd(&node, 2); - ck_assert_int_eq(fingerprint, 0xbcbbc1c4); + ck_assert_uint_eq(fingerprint, 0xbcbbc1c4); ck_assert_mem_eq( node.chain_code, fromhex( @@ -3024,7 +3051,7 @@ START_TEST(test_bip32_decred_vector_1) { fromhex( "0f479245fb19a38a1954c5c7c0ebab2f9bdfd96a17563ef28a6a4b1a2a764ef4"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -3038,7 +3065,7 @@ START_TEST(test_bip32_decred_vector_1) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -3055,7 +3082,7 @@ START_TEST(test_bip32_decred_vector_1) { // [Chain m/0'/1/2'/2/1000000000] fingerprint = hdnode_fingerprint(&node); hdnode_private_ckd(&node, 1000000000); - ck_assert_int_eq(fingerprint, 0xe58b52e4); + ck_assert_uint_eq(fingerprint, 0xe58b52e4); ck_assert_mem_eq( node.chain_code, fromhex( @@ -3066,7 +3093,7 @@ START_TEST(test_bip32_decred_vector_1) { fromhex( "471b76e389e528d6de6d816857e012c5455051cad6660850e58372a6c3e6e7c8"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -3080,7 +3107,7 @@ START_TEST(test_bip32_decred_vector_1) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -3117,7 +3144,7 @@ START_TEST(test_bip32_decred_vector_2) { // [Chain m] fingerprint = 0; - ck_assert_int_eq(fingerprint, 0x00000000); + ck_assert_uint_eq(fingerprint, 0x00000000); ck_assert_mem_eq( node.chain_code, fromhex( @@ -3128,7 +3155,7 @@ START_TEST(test_bip32_decred_vector_2) { fromhex( "4b03d6fc340455b363f51020ad3ecca4f0850280cf436c70c727923f6db46c3e"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -3142,7 +3169,7 @@ START_TEST(test_bip32_decred_vector_2) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -3160,7 +3187,7 @@ START_TEST(test_bip32_decred_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node, 0); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x2524c9d3); + ck_assert_uint_eq(fingerprint, 0x2524c9d3); ck_assert_mem_eq( node.chain_code, fromhex( @@ -3171,7 +3198,7 @@ START_TEST(test_bip32_decred_vector_2) { fromhex( "abe74a98f6c7eabee0428f53798f0ab8aa1bd37873999041703c742f15ac7e1e"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -3185,7 +3212,7 @@ START_TEST(test_bip32_decred_vector_2) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -3203,7 +3230,7 @@ START_TEST(test_bip32_decred_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd_prime(&node, 2147483647); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x6035c6ad); + ck_assert_uint_eq(fingerprint, 0x6035c6ad); ck_assert_mem_eq( node.chain_code, fromhex( @@ -3214,7 +3241,7 @@ START_TEST(test_bip32_decred_vector_2) { fromhex( "877c779ad9687164e9c2f4f0f4ff0340814392330693ce95a58fe18fd52e6e93"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -3228,7 +3255,7 @@ START_TEST(test_bip32_decred_vector_2) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -3246,7 +3273,7 @@ START_TEST(test_bip32_decred_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node, 1); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x36fc7080); + ck_assert_uint_eq(fingerprint, 0x36fc7080); ck_assert_mem_eq( node.chain_code, fromhex( @@ -3257,7 +3284,7 @@ START_TEST(test_bip32_decred_vector_2) { fromhex( "704addf544a06e5ee4bea37098463c23613da32020d604506da8c0518e1da4b7"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -3271,7 +3298,7 @@ START_TEST(test_bip32_decred_vector_2) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -3289,7 +3316,7 @@ START_TEST(test_bip32_decred_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd_prime(&node, 2147483646); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x45309b4c); + ck_assert_uint_eq(fingerprint, 0x45309b4c); ck_assert_mem_eq( node.chain_code, fromhex( @@ -3300,7 +3327,7 @@ START_TEST(test_bip32_decred_vector_2) { fromhex( "f1c7c871a54a804afe328b4c83a1c33b8e5ff48f5087273f04efa83b247d6a2d"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -3314,7 +3341,7 @@ START_TEST(test_bip32_decred_vector_2) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -3332,7 +3359,7 @@ START_TEST(test_bip32_decred_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_private_ckd(&node, 2); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x3491a5e6); + ck_assert_uint_eq(fingerprint, 0x3491a5e6); ck_assert_mem_eq( node.chain_code, fromhex( @@ -3343,7 +3370,7 @@ START_TEST(test_bip32_decred_vector_2) { fromhex( "bb7d39bdb83ecf58f2fd82b6d918341cbef428661ef01ab97c28a4842125ac23"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key, fromhex( @@ -3357,7 +3384,7 @@ START_TEST(test_bip32_decred_vector_2) { r = hdnode_deserialize_private(str, DECRED_VERSION_PRIVATE, SECP256K1_DECRED_NAME, &node2, NULL); ck_assert_int_eq(r, 0); - hdnode_fill_public_key(&node2); + ck_assert_int_eq(hdnode_fill_public_key(&node2), 0); ck_assert_mem_eq(&node, &node2, sizeof(HDNode)); hdnode_serialize_public(&node, fingerprint, DECRED_VERSION_PUBLIC, str, sizeof(str)); @@ -3382,7 +3409,7 @@ START_TEST(test_bip32_decred_vector_2) { fingerprint = hdnode_fingerprint(&node); r = hdnode_public_ckd(&node, 0); ck_assert_int_eq(r, 1); - ck_assert_int_eq(fingerprint, 0x6a19cfb3); + ck_assert_uint_eq(fingerprint, 0x6a19cfb3); ck_assert_mem_eq( node.chain_code, fromhex( @@ -3405,33 +3432,79 @@ START_TEST(test_bip32_decred_vector_2) { } END_TEST -START_TEST(test_ecdsa_signature) { - int res; - uint8_t digest[32]; - uint8_t pubkey[65]; - uint8_t sig[64]; +static void test_ecdsa_get_public_key33_helper(int (*ecdsa_get_public_key33_fn)( + const ecdsa_curve *, const uint8_t *, uint8_t *)) { + uint8_t privkey[32] = {0}; + uint8_t pubkey[65] = {0}; const ecdsa_curve *curve = &secp256k1; + int res = 0; + + memcpy( + privkey, + fromhex( + "c46f5b217f04ff28886a89d3c762ed84e5fa318d1c9a635d541131e69f1f49f5"), + 32); + res = ecdsa_get_public_key33_fn(curve, privkey, pubkey); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "0232b062e9153f573c220b1be0299d6447e81577274bf11a7c08dff71384c6b6ec"), + 33); - // Signature verification for a digest which is equal to the group order. - // https://github.com/trezor/trezor-firmware/pull/1374 memcpy( + privkey, + fromhex( + "3b90a4de80fb00d77795762c389d1279d4b4ab5992ae3cde6bc12ca63116f74c"), + 32); + res = ecdsa_get_public_key33_fn(curve, privkey, pubkey); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq( pubkey, fromhex( - "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179848" - "3ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"), - sizeof(pubkey)); + "0332b062e9153f573c220b1be0299d6447e81577274bf11a7c08dff71384c6b6ec"), + 33); +} + +START_TEST(test_ecdsa_get_public_key33) { + test_ecdsa_get_public_key33_helper(ecdsa_get_public_key33); +} +END_TEST + +static void test_ecdsa_get_public_key65_helper(int (*ecdsa_get_public_key65_fn)( + const ecdsa_curve *, const uint8_t *, uint8_t *)) { + uint8_t privkey[32] = {0}; + uint8_t pubkey[65] = {0}; + const ecdsa_curve *curve = &secp256k1; + int res = 0; + memcpy( - digest, + privkey, fromhex( - "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), - sizeof(digest)); - memcpy(sig, - fromhex( - "a0b37f8fba683cc68f6574cd43b39f0343a50008bf6ccea9d13231d9e7e2e1e41" - "1edc8d307254296264aebfc3dc76cd8b668373a072fd64665b50000e9fcce52"), - sizeof(sig)); - res = ecdsa_verify_digest(curve, pubkey, sig, digest); + "c46f5b217f04ff28886a89d3c762ed84e5fa318d1c9a635d541131e69f1f49f5"), + 32); + res = ecdsa_get_public_key65_fn(curve, privkey, pubkey); ck_assert_int_eq(res, 0); + ck_assert_mem_eq( + pubkey, + fromhex( + "0432b062e9153f573c220b1be0299d6447e81577274bf11a7c08dff71384c6b6ec" + "179ca56b637a57e0fcd28cefa10c9433dc30532682647f4daa053d43d5cc960a"), + 65); +} + +START_TEST(test_ecdsa_get_public_key65) { + test_ecdsa_get_public_key65_helper(ecdsa_get_public_key65); +} +END_TEST + +static void test_ecdsa_recover_pub_from_sig_helper(int ( + *ecdsa_recover_pub_from_sig_fn)(const ecdsa_curve *, uint8_t *, + const uint8_t *, const uint8_t *, int)) { + int res; + uint8_t digest[32]; + uint8_t pubkey[65]; + const ecdsa_curve *curve = &secp256k1; // sha2(sha2("\x18Bitcoin Signed Message:\n\x0cHello World!")) memcpy( @@ -3440,7 +3513,7 @@ START_TEST(test_ecdsa_signature) { "de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb11432"), 32); // r = 2: Four points should exist - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000020123" @@ -3453,7 +3526,7 @@ START_TEST(test_ecdsa_signature) { "043fc5bf5fec35b6ffe6fd246226d312742a8c296bfa57dd22da509a2e348529b7dd" "b9faf8afe1ecda3c05e7b2bda47ee1f5a87e952742b22afca560b29d972fcf"), 65); - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000020123" @@ -3466,7 +3539,7 @@ START_TEST(test_ecdsa_signature) { "0456d8089137b1fd0d890f8c7d4a04d0fd4520a30b19518ee87bd168ea12ed809032" "9274c4c6c0d9df04515776f2741eeffc30235d596065d718c3973e19711ad0"), 65); - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000020123" @@ -3479,7 +3552,7 @@ START_TEST(test_ecdsa_signature) { "04cee0e740f41aab39156844afef0182dea2a8026885b10454a2d539df6f6df9023a" "bfcb0f01c50bef3c0fa8e59a998d07441e18b1c60583ef75cc8b912fb21a15"), 65); - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000020123" @@ -3492,6 +3565,14 @@ START_TEST(test_ecdsa_signature) { "0490d2bd2e9a564d6e1d8324fc6ad00aa4ae597684ecf4abea58bdfe7287ea4fa729" "68c2e5b0b40999ede3d7898d94e82c3f8dc4536a567a4bd45998c826a4c4b2"), 65); + // The point at infinity is not considered to be a valid public key. + res = ecdsa_recover_pub_from_sig_fn( + curve, pubkey, + fromhex( + "220cf4c7b6d568f2256a8c30cc1784a625a28c3627dac404aa9a9ecd08314ec81a88" + "828f20d69d102bab5de5f6ee7ef040cb0ff7b8e1ba3f29d79efb5250f47d"), + digest, 0); + ck_assert_int_eq(res, 1); memcpy( digest, @@ -3499,7 +3580,7 @@ START_TEST(test_ecdsa_signature) { "0000000000000000000000000000000000000000000000000000000000000000"), 32); // r = 7: No point P with P.x = 7, but P.x = (order + 7) exists - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000070123" @@ -3512,7 +3593,7 @@ START_TEST(test_ecdsa_signature) { "044d81bb47a31ffc6cf1f780ecb1e201ec47214b651650867c07f13ad06e12a1b040" "de78f8dbda700f4d3cd7ee21b3651a74c7661809699d2be7ea0992b0d39797"), 65); - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000070123" @@ -3525,7 +3606,7 @@ START_TEST(test_ecdsa_signature) { "044d81bb47a31ffc6cf1f780ecb1e201ec47214b651650867c07f13ad06e12a1b0bf" "21870724258ff0b2c32811de4c9ae58b3899e7f69662d41815f66c4f2c6498"), 65); - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000070123" @@ -3539,7 +3620,7 @@ START_TEST(test_ecdsa_signature) { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), 32); // r = 1: Two points P with P.x = 1, but P.x = (order + 7) doesn't exist - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000010123" @@ -3552,7 +3633,7 @@ START_TEST(test_ecdsa_signature) { "045d330b2f89dbfca149828277bae852dd4aebfe136982cb531a88e9e7a89463fe71" "519f34ea8feb9490c707f14bc38c9ece51762bfd034ea014719b7c85d2871b"), 65); - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000010123" @@ -3567,14 +3648,14 @@ START_TEST(test_ecdsa_signature) { 65); // r = 0 is always invalid - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000010123" "456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"), digest, 2); ck_assert_int_eq(res, 1); - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000000123" @@ -3582,7 +3663,7 @@ START_TEST(test_ecdsa_signature) { digest, 0); ck_assert_int_eq(res, 1); // r >= order is always invalid - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410123" @@ -3590,7 +3671,7 @@ START_TEST(test_ecdsa_signature) { digest, 0); ck_assert_int_eq(res, 1); // check that overflow of r is handled - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "000000000000000000000000000000014551231950B75FC4402DA1722FC9BAEE0123" @@ -3598,7 +3679,7 @@ START_TEST(test_ecdsa_signature) { digest, 2); ck_assert_int_eq(res, 1); // s = 0 is always invalid - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "00000000000000000000000000000000000000000000000000000000000000020000" @@ -3606,7 +3687,7 @@ START_TEST(test_ecdsa_signature) { digest, 0); ck_assert_int_eq(res, 1); // s >= order is always invalid - res = ecdsa_recover_pub_from_sig( + res = ecdsa_recover_pub_from_sig_fn( curve, pubkey, fromhex( "0000000000000000000000000000000000000000000000000000000000000002ffff" @@ -3614,12 +3695,51 @@ START_TEST(test_ecdsa_signature) { digest, 0); ck_assert_int_eq(res, 1); } + +START_TEST(test_ecdsa_recover_pub_from_sig) { + test_ecdsa_recover_pub_from_sig_helper(ecdsa_recover_pub_from_sig); +} +END_TEST + +static void test_ecdsa_verify_digest_helper(int (*ecdsa_verify_digest_fn)( + const ecdsa_curve *, const uint8_t *, const uint8_t *, const uint8_t *)) { + int res; + uint8_t digest[32]; + uint8_t pubkey[65]; + uint8_t sig[64]; + const ecdsa_curve *curve = &secp256k1; + + // Signature verification for a digest which is equal to the group order. + // https://github.com/trezor/trezor-firmware/pull/1374 + memcpy( + pubkey, + fromhex( + "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179848" + "3ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"), + sizeof(pubkey)); + memcpy( + digest, + fromhex( + "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"), + sizeof(digest)); + memcpy(sig, + fromhex( + "a0b37f8fba683cc68f6574cd43b39f0343a50008bf6ccea9d13231d9e7e2e1e41" + "1edc8d307254296264aebfc3dc76cd8b668373a072fd64665b50000e9fcce52"), + sizeof(sig)); + res = ecdsa_verify_digest_fn(curve, pubkey, sig, digest); + ck_assert_int_eq(res, 0); +} + +START_TEST(test_ecdsa_verify_digest) { + test_ecdsa_verify_digest_helper(ecdsa_verify_digest); +} END_TEST #define test_deterministic(KEY, MSG, K) \ do { \ sha256_Raw((uint8_t *)MSG, strlen(MSG), buf); \ - init_rfc6979(fromhex(KEY), buf, &rng); \ + init_rfc6979(fromhex(KEY), buf, NULL, &rng); \ generate_k_rfc6979(&k, &rng); \ bn_write_be(&k, buf); \ ck_assert_mem_eq(buf, fromhex(K), 32); \ @@ -3664,6 +3784,49 @@ START_TEST(test_rfc6979) { } END_TEST +static void test_ecdsa_sign_digest_deterministic_helper( + int (*ecdsa_sign_digest_fn)(const ecdsa_curve *, const uint8_t *, + const uint8_t *, uint8_t *, uint8_t *, + int (*)(uint8_t by, uint8_t sig[64]))) { + static struct { + const char *priv_key; + const char *digest; + const char *sig; + } tests[] = { + {"312155017c70a204106e034520e0cdf17b3e54516e2ece38e38e38e38e38e38e", + "ffffffffffffffffffffffffffffffff20202020202020202020202020202020", + "e3d70248ea2fc771fc8d5e62d76b9cfd5402c96990333549eaadce1ae9f737eb" + "5cfbdc7d1e0ec18cc9b57bbb18f0a57dc929ec3c4dfac9073c581705015f6a8a"}, + {"312155017c70a204106e034520e0cdf17b3e54516e2ece38e38e38e38e38e38e", + "2020202020202020202020202020202020202020202020202020202020202020", + "40666188895430715552a7e4c6b53851f37a93030fb94e043850921242db78e8" + "75aa2ac9fd7e5a19402973e60e64382cdc29a09ebf6cb37e92f23be5b9251aee"}, + }; + + const ecdsa_curve *curve = &secp256k1; + uint8_t priv_key[32] = {0}; + uint8_t digest[32] = {0}; + uint8_t expected_sig[64] = {0}; + uint8_t computed_sig[64] = {0}; + int res = 0; + + for (size_t i = 0; i < sizeof(tests) / sizeof(*tests); i++) { + memcpy(priv_key, fromhex(tests[i].priv_key), 32); + memcpy(digest, fromhex(tests[i].digest), 32); + memcpy(expected_sig, fromhex(tests[i].sig), 64); + + res = + ecdsa_sign_digest_fn(curve, priv_key, digest, computed_sig, NULL, NULL); + ck_assert_int_eq(res, 0); + ck_assert_mem_eq(expected_sig, computed_sig, 64); + } +} + +START_TEST(test_ecdsa_sign_digest_deterministic) { + test_ecdsa_sign_digest_deterministic_helper(ecdsa_sign_digest); +} +END_TEST + // test vectors from // http://www.inconteam.com/software-development/41-encryption/55-aes-test-vectors START_TEST(test_aes) { @@ -3673,7 +3836,7 @@ START_TEST(test_aes) { const char **ivp, **plainp, **cipherp; // ECB - const char *ecb_vector[] = { + static const char *ecb_vector[] = { // plain cipher "6bc1bee22e409f96e93d7e117393172a", "f3eed1bdb5d2a03c064b5a7e3db181f8", @@ -3710,7 +3873,7 @@ START_TEST(test_aes) { } // CBC - const char *cbc_vector[] = { + static const char *cbc_vector[] = { // iv plain cipher "000102030405060708090A0B0C0D0E0F", "6bc1bee22e409f96e93d7e117393172a", @@ -3756,7 +3919,7 @@ START_TEST(test_aes) { } // CFB - const char *cfb_vector[] = { + static const char *cfb_vector[] = { "000102030405060708090A0B0C0D0E0F", "6bc1bee22e409f96e93d7e117393172a", "DC7E84BFDA79164B7ECD8486985D3860", @@ -3801,7 +3964,7 @@ START_TEST(test_aes) { } // OFB - const char *ofb_vector[] = { + static const char *ofb_vector[] = { "000102030405060708090A0B0C0D0E0F", "6bc1bee22e409f96e93d7e117393172a", "dc7e84bfda79164b7ecd8486985d3860", @@ -3846,7 +4009,7 @@ START_TEST(test_aes) { } // CTR - const char *ctr_vector[] = { + static const char *ctr_vector[] = { // plain cipher "6bc1bee22e409f96e93d7e117393172a", "601ec313775789a5b7a7f504bbf3d228", @@ -4171,84 +4334,101 @@ END_TEST // test vectors from http://www.di-mgt.com.au/sha_testvectors.html START_TEST(test_sha3_256) { - uint8_t digest[SHA3_256_DIGEST_LENGTH]; - - sha3_256((uint8_t *)"", 0, digest); - ck_assert_mem_eq( - digest, - fromhex( - "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"), - SHA3_256_DIGEST_LENGTH); - - sha3_256((uint8_t *)"abc", 3, digest); - ck_assert_mem_eq( - digest, - fromhex( - "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"), - SHA3_256_DIGEST_LENGTH); - - sha3_256( - (uint8_t *)"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, - digest); - ck_assert_mem_eq( - digest, - fromhex( - "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376"), - SHA3_256_DIGEST_LENGTH); + static const struct { + const char *data; + const char *hash; + } tests[] = { + { + "", + "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", + }, + { + "abc", + "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", + }, + { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376", + }, + { + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijkl" + "mnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18", + }, + }; - sha3_256((uint8_t *)"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, digest); - ck_assert_mem_eq( - digest, - fromhex( - "916f6061fe879741ca6469b43971dfdb28b1a32dc36cb3254e812be27aad1d18"), - SHA3_256_DIGEST_LENGTH); + uint8_t digest[SHA3_256_DIGEST_LENGTH]; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t len = strlen(tests[i].data); + sha3_256((uint8_t *)tests[i].data, len, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), SHA3_256_DIGEST_LENGTH); + + // Test progressive hashing. + size_t part_len = len; + SHA3_CTX ctx; + sha3_256_Init(&ctx); + sha3_Update(&ctx, (uint8_t *)tests[i].data, part_len); + sha3_Update(&ctx, NULL, 0); + sha3_Update(&ctx, (uint8_t *)tests[i].data + part_len, len - part_len); + sha3_Final(&ctx, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), SHA3_256_DIGEST_LENGTH); + } } END_TEST // test vectors from http://www.di-mgt.com.au/sha_testvectors.html START_TEST(test_sha3_512) { - uint8_t digest[SHA3_512_DIGEST_LENGTH]; - - sha3_512((uint8_t *)"", 0, digest); - ck_assert_mem_eq( - digest, - fromhex( + static const struct { + const char *data; + const char *hash; + } tests[] = { + { + "", "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2" - "123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"), - SHA3_512_DIGEST_LENGTH); - - sha3_512((uint8_t *)"abc", 3, digest); - ck_assert_mem_eq( - digest, - fromhex( + "123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26", + }, + { + "abc", "b751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e1" - "16e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0"), - SHA3_512_DIGEST_LENGTH); - - sha3_512( - (uint8_t *)"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, - digest); - ck_assert_mem_eq( - digest, - fromhex( + "16e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0", + }, + { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "04a371e84ecfb5b8b77cb48610fca8182dd457ce6f326a0fd3d7ec2f1e91636dee69" - "1fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e"), - SHA3_512_DIGEST_LENGTH); - - sha3_512((uint8_t *)"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", 112, digest); - ck_assert_mem_eq( - digest, - fromhex( + "1fbe0c985302ba1b0d8dc78c086346b533b49c030d99a27daf1139d6e75e", + }, + { + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijkl" + "mnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", "afebb2ef542e6579c50cad06d2e578f9f8dd6881d7dc824d26360feebf18a4fa73e3" - "261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185"), - SHA3_512_DIGEST_LENGTH); + "261122948efcfd492e74e82e2189ed0fb440d187f382270cb455f21dd185", + }, + }; + + uint8_t digest[SHA3_512_DIGEST_LENGTH]; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t len = strlen(tests[i].data); + sha3_512((uint8_t *)tests[i].data, len, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), SHA3_512_DIGEST_LENGTH); + + // Test progressive hashing. + size_t part_len = len; + SHA3_CTX ctx; + sha3_512_Init(&ctx); + sha3_Update(&ctx, (const uint8_t *)tests[i].data, part_len); + sha3_Update(&ctx, NULL, 0); + sha3_Update(&ctx, (const uint8_t *)tests[i].data + part_len, + len - part_len); + sha3_Final(&ctx, digest); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), SHA3_512_DIGEST_LENGTH); + } } END_TEST // test vectors from // https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/0.test-sha3-256.dat START_TEST(test_keccak_256) { - const struct { + static const struct { const char *hash; size_t length; const char *data; @@ -4490,6 +4670,17 @@ START_TEST(test_keccak_256) { for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { keccak_256(fromhex(tests[i].data), tests[i].length, hash); ck_assert_mem_eq(hash, fromhex(tests[i].hash), SHA3_256_DIGEST_LENGTH); + + // Test progressive hashing. + size_t part_len = tests[i].length / 2; + SHA3_CTX ctx = {0}; + keccak_256_Init(&ctx); + keccak_Update(&ctx, fromhex(tests[i].data), part_len); + keccak_Update(&ctx, fromhex(tests[i].data), 0); + keccak_Update(&ctx, fromhex(tests[i].data) + part_len, + tests[i].length - part_len); + keccak_Final(&ctx, hash); + ck_assert_mem_eq(hash, fromhex(tests[i].hash), SHA3_256_DIGEST_LENGTH); } } END_TEST @@ -4497,7 +4688,7 @@ END_TEST // test vectors from // https://raw.githubusercontent.com/monero-project/monero/master/tests/hash/tests-extra-blake.txt START_TEST(test_blake256) { - struct { + static const struct { const char *hash; const char *data; } tests[] = { @@ -4586,7 +4777,18 @@ START_TEST(test_blake256) { uint8_t hash[BLAKE256_DIGEST_LENGTH]; for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { - blake256(fromhex(tests[i].data), i, hash); + size_t len = strlen(tests[i].data) / 2; + blake256(fromhex(tests[i].data), len, hash); + ck_assert_mem_eq(hash, fromhex(tests[i].hash), BLAKE256_DIGEST_LENGTH); + + // Test progressive hashing. + size_t part_len = len / 2; + BLAKE256_CTX ctx; + blake256_Init(&ctx); + blake256_Update(&ctx, fromhex(tests[i].data), part_len); + blake256_Update(&ctx, NULL, 0); + blake256_Update(&ctx, fromhex(tests[i].data) + part_len, len - part_len); + blake256_Final(&ctx, hash); ck_assert_mem_eq(hash, fromhex(tests[i].hash), BLAKE256_DIGEST_LENGTH); } } @@ -4595,6 +4797,36 @@ END_TEST // test vectors from // https://raw.githubusercontent.com/BLAKE2/BLAKE2/master/testvectors/blake2b-kat.txt START_TEST(test_blake2b) { + static const struct { + const char *msg; + const char *hash; + } tests[] = { + { + "", + "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e9" + "96e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568", + }, + { + "000102", + "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e" + "364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f3031323334353637", + "f8f3726ac5a26cc80132493a6fedcb0e60760c09cfc84cad178175986819665e7684" + "2d7b9fedf76dddebf5d3f56faaad4477587af21606d396ae570d8e719af2", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243" + "4445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465" + "666768696a6b6c6d6e6f", + "227e3aed8d2cb10b918fcb04f9de3e6d0a57e08476d93759cd7b2ed54a1cbf0239c5" + "28fb04bbf288253e601d3bc38b21794afef90b17094a182cac557745e75f", + }, + }; + uint8_t key[BLAKE2B_KEY_LENGTH]; memcpy(key, fromhex( @@ -4603,54 +4835,111 @@ START_TEST(test_blake2b) { BLAKE2B_KEY_LENGTH); uint8_t digest[BLAKE2B_DIGEST_LENGTH]; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t msg_len = strlen(tests[i].msg) / 2; + blake2b_Key(fromhex(tests[i].msg), msg_len, key, sizeof(key), digest, + sizeof(digest)); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), sizeof(digest)); + + // Test progressive hashing. + size_t part_len = msg_len / 2; + BLAKE2B_CTX ctx; + ck_assert_int_eq(blake2b_InitKey(&ctx, sizeof(digest), key, sizeof(key)), + 0); + ck_assert_int_eq(blake2b_Update(&ctx, fromhex(tests[i].msg), part_len), 0); + ck_assert_int_eq(blake2b_Update(&ctx, NULL, 0), 0); + ck_assert_int_eq(blake2b_Update(&ctx, fromhex(tests[i].msg) + part_len, + msg_len - part_len), + 0); + ck_assert_int_eq(blake2b_Final(&ctx, digest, sizeof(digest)), 0); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), BLAKE2B_DIGEST_LENGTH); + } +} +END_TEST - blake2b_Key((uint8_t *)"", 0, key, BLAKE2B_KEY_LENGTH, digest, - BLAKE2B_DIGEST_LENGTH); - ck_assert_mem_eq( - digest, - fromhex( - "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e9" - "96e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"), - BLAKE2B_DIGEST_LENGTH); - - blake2b_Key(fromhex("000102"), 3, key, BLAKE2B_KEY_LENGTH, digest, - BLAKE2B_DIGEST_LENGTH); - ck_assert_mem_eq( - digest, - fromhex( - "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e" - "364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1"), - BLAKE2B_DIGEST_LENGTH); +// Blake2b-256 personalized, a la ZCash +// Test vectors from https://zips.z.cash/zip-0243 +START_TEST(test_blake2bp) { + static const struct { + const char *msg; + const char *personal; + const char *hash; + } tests[] = { + { + "", + "ZcashPrevoutHash", + "d53a633bbecf82fe9e9484d8a0e727c73bb9e68c96e72dec30144f6a84afa136", + }, + { + "", + "ZcashSequencHash", + "a5f25f01959361ee6eb56a7401210ee268226f6ce764a4f10b7f29e54db37272", - blake2b_Key( - fromhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f3031323334353637"), - 56, key, BLAKE2B_KEY_LENGTH, digest, BLAKE2B_DIGEST_LENGTH); - ck_assert_mem_eq( - digest, - fromhex( - "f8f3726ac5a26cc80132493a6fedcb0e60760c09cfc84cad178175986819665e7684" - "2d7b9fedf76dddebf5d3f56faaad4477587af21606d396ae570d8e719af2"), - BLAKE2B_DIGEST_LENGTH); + }, + { + "e7719811893e0000095200ac6551ac636565b2835a0805750200025151", + "ZcashOutputsHash", + "ab6f7f6c5ad6b56357b5f37e16981723db6c32411753e28c175e15589172194a", + }, + { + "0bbe32a598c22adfb48cef72ba5d4287c0cefbacfd8ce195b4963c34a94bba7a1" + "75dae4b090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b" + "63396e2b41d", + "ZcashPrevoutHash", + "cacf0f5210cce5fa65a59f314292b3111d299e7d9d582753cf61e1e408552ae4", + }}; - blake2b_Key( - fromhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f"), - 112, key, BLAKE2B_KEY_LENGTH, digest, BLAKE2B_DIGEST_LENGTH); - ck_assert_mem_eq( - digest, - fromhex( - "227e3aed8d2cb10b918fcb04f9de3e6d0a57e08476d93759cd7b2ed54a1cbf0239c5" - "28fb04bbf288253e601d3bc38b21794afef90b17094a182cac557745e75f"), - BLAKE2B_DIGEST_LENGTH); + uint8_t digest[32]; + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t msg_len = strlen(tests[i].msg) / 2; + + // Test progressive hashing. + size_t part_len = msg_len / 2; + BLAKE2B_CTX ctx; + ck_assert_int_eq( + blake2b_InitPersonal(&ctx, sizeof(digest), tests[i].personal, + strlen(tests[i].personal)), + 0); + ck_assert_int_eq(blake2b_Update(&ctx, fromhex(tests[i].msg), part_len), 0); + ck_assert_int_eq(blake2b_Update(&ctx, NULL, 0), 0); + ck_assert_int_eq(blake2b_Update(&ctx, fromhex(tests[i].msg) + part_len, + msg_len - part_len), + 0); + ck_assert_int_eq(blake2b_Final(&ctx, digest, sizeof(digest)), 0); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), sizeof(digest)); + } } END_TEST // test vectors from // https://raw.githubusercontent.com/BLAKE2/BLAKE2/master/testvectors/blake2s-kat.txt START_TEST(test_blake2s) { + static const struct { + const char *msg; + const char *hash; + } tests[] = { + { + "", + "48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49", + }, + { + "000102", + "1d220dbe2ee134661fdf6d9e74b41704710556f2f6e5a091b227697445dbea6b", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f3031323334353637", + "2966b3cfae1e44ea996dc5d686cf25fa053fb6f67201b9e46eade85d0ad6b806", + }, + { + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40414243" + "4445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465" + "666768696a6b6c6d6e6f", + "90a83585717b75f0e9b725e055eeeeb9e7a028ea7e6cbc07b20917ec0363e38c", + }, + }; + uint8_t key[BLAKE2S_KEY_LENGTH]; memcpy( key, @@ -4659,62 +4948,64 @@ START_TEST(test_blake2s) { BLAKE2S_KEY_LENGTH); uint8_t digest[BLAKE2S_DIGEST_LENGTH]; - - blake2s_Key((uint8_t *)"", 0, key, BLAKE2S_KEY_LENGTH, digest, - BLAKE2S_DIGEST_LENGTH); - ck_assert_mem_eq( - digest, - fromhex( - "48a8997da407876b3d79c0d92325ad3b89cbb754d86ab71aee047ad345fd2c49"), - BLAKE2S_DIGEST_LENGTH); - - blake2s_Key(fromhex("000102"), 3, key, BLAKE2S_KEY_LENGTH, digest, - BLAKE2S_DIGEST_LENGTH); - ck_assert_mem_eq( - digest, - fromhex( - "1d220dbe2ee134661fdf6d9e74b41704710556f2f6e5a091b227697445dbea6b"), - BLAKE2S_DIGEST_LENGTH); - - blake2s_Key( - fromhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f3031323334353637"), - 56, key, BLAKE2S_KEY_LENGTH, digest, BLAKE2S_DIGEST_LENGTH); - ck_assert_mem_eq( - digest, - fromhex( - "2966b3cfae1e44ea996dc5d686cf25fa053fb6f67201b9e46eade85d0ad6b806"), - BLAKE2S_DIGEST_LENGTH); - - blake2s_Key( - fromhex("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" - "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" - "404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f" - "606162636465666768696a6b6c6d6e6f"), - 112, key, BLAKE2S_KEY_LENGTH, digest, BLAKE2S_DIGEST_LENGTH); - ck_assert_mem_eq( - digest, - fromhex( - "90a83585717b75f0e9b725e055eeeeb9e7a028ea7e6cbc07b20917ec0363e38c"), - BLAKE2S_DIGEST_LENGTH); + for (size_t i = 0; i < (sizeof(tests) / sizeof(*tests)); i++) { + size_t msg_len = strlen(tests[i].msg) / 2; + blake2s_Key(fromhex(tests[i].msg), msg_len, key, sizeof(key), digest, + sizeof(digest)); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), sizeof(digest)); + + // Test progressive hashing. + size_t part_len = msg_len / 2; + BLAKE2S_CTX ctx; + ck_assert_int_eq(blake2s_InitKey(&ctx, sizeof(digest), key, sizeof(key)), + 0); + ck_assert_int_eq(blake2s_Update(&ctx, fromhex(tests[i].msg), part_len), 0); + ck_assert_int_eq(blake2s_Update(&ctx, NULL, 0), 0); + ck_assert_int_eq(blake2s_Update(&ctx, fromhex(tests[i].msg) + part_len, + msg_len - part_len), + 0); + ck_assert_int_eq(blake2s_Final(&ctx, digest, sizeof(digest)), 0); + ck_assert_mem_eq(digest, fromhex(tests[i].hash), BLAKE2S_DIGEST_LENGTH); + } } END_TEST +#include + START_TEST(test_chacha_drbg) { - char entropy[] = "8a09b482de30c12ee1d2eb69dd49753d4252b3d36128ee1e"; - char reseed[] = "9ec4b991f939dbb44355392d05cd793a2e281809d2ed7139"; + char entropy[] = + "06032cd5eed33f39265f49ecb142c511da9aff2af71203bffaf34a9ca5bd9c0d"; + char nonce[] = "0e66f71edc43e42a45ad3c6fc6cdc4df"; + char reseed[] = + "01920a4e669ed3a85ae8a33b35a74ad7fb2a6bb4cf395ce00334a9c9a5a5d552"; char expected[] = - "4caaeb7db073d34b37b5b26f8a3863849f298dab754966e0f75526823216057c2626e044" - "9f7ffda7c3dba8841c06af01029eebfd4d4cae951c19c9f6ff6812783e58438840883401" - "2a05cd24c38cd22d18296aceed6829299190ebb9455eb8fd8d1cac1d"; - uint8_t result[100]; + "e172c5d18f3e8c77e9f66f9e1c24560772117161a9a0a237ab490b0769ad5d910f5dfb36" + "22edc06c18be0495c52588b200893d90fd80ff2149ead0c45d062c90f5890149c0f9591c" + "41bf4110865129a0fe524f210cca1340bd16f71f57906946cbaaf1fa863897d70d203b5a" + "f9996f756eec08861ee5875f9d915adcddc38719"; + uint8_t result[128]; + uint8_t null_bytes[128] = {0}; + uint8_t nonce_bytes[16]; + memcpy(nonce_bytes, fromhex(nonce), sizeof(nonce_bytes)); CHACHA_DRBG_CTX ctx; - chacha_drbg_init(&ctx, fromhex(entropy)); - chacha_drbg_reseed(&ctx, fromhex(reseed)); + chacha_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2, nonce_bytes, + strlen(nonce) / 2); + chacha_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2, NULL, 0); chacha_drbg_generate(&ctx, result, sizeof(result)); chacha_drbg_generate(&ctx, result, sizeof(result)); ck_assert_mem_eq(result, fromhex(expected), sizeof(result)); + + for (size_t i = 0; i <= sizeof(result); ++i) { + chacha_drbg_init(&ctx, fromhex(entropy), strlen(entropy) / 2, nonce_bytes, + strlen(nonce) / 2); + chacha_drbg_reseed(&ctx, fromhex(reseed), strlen(reseed) / 2, NULL, 0); + chacha_drbg_generate(&ctx, result, sizeof(result) - 13); + memset(result, 0, sizeof(result)); + chacha_drbg_generate(&ctx, result, i); + ck_assert_mem_eq(result, fromhex(expected), i); + ck_assert_mem_eq(result + i, null_bytes, sizeof(result) - i); + } } END_TEST @@ -4853,7 +5144,7 @@ START_TEST(test_hmac_drbg) { END_TEST START_TEST(test_mnemonic) { - const char *vectors[] = { + static const char *vectors[] = { "00000000000000000000000000000000", "abandon abandon abandon abandon abandon abandon abandon abandon abandon " "abandon abandon about", @@ -4990,8 +5281,9 @@ START_TEST(test_mnemonic) { a = vectors; b = vectors + 1; c = vectors + 2; -#define TC_BUF_SIZE 300 + #define TC_BUF_SIZE 308 char buf[TC_BUF_SIZE]; + while (*a && *b && *c) { m = mnemonic_from_data(fromhex(*a), strlen(*a) / 2, buf, TC_BUF_SIZE); ck_assert_str_eq(m, *b); @@ -5005,18 +5297,14 @@ START_TEST(test_mnemonic) { a += 3; b += 3; c += 3; + memzero(buf, TC_BUF_SIZE ); #undef TC_BUF_SIZE } - - // [wallet-core] negative test: provided buffer invalid (too small or null) - ck_assert_int_eq((int)(mnemonic_from_data(fromhex(vectors[0]), strlen(vectors[0]) / 2, buf, 200)), 0); - ck_assert_int_eq((int)(mnemonic_from_data(fromhex(vectors[0]), strlen(vectors[0]) / 2, buf, 0)), 0); - ck_assert_int_eq((int)(mnemonic_from_data(fromhex(vectors[0]), strlen(vectors[0]) / 2, NULL, 240)), 0); } END_TEST START_TEST(test_mnemonic_check) { - const char *vectors_ok[] = { + static const char *vectors_ok[] = { "abandon abandon abandon abandon abandon abandon abandon abandon abandon " "abandon abandon about", "legal winner thank year wave sausage worth useful legal winner thank " @@ -5072,7 +5360,7 @@ START_TEST(test_mnemonic_check) { "away coconut", 0, }; - const char *vectors_fail[] = { + static const char *vectors_fail[] = { "above abandon abandon abandon abandon abandon abandon abandon abandon " "abandon abandon about", "above winner thank year wave sausage worth useful legal winner thank " @@ -5195,7 +5483,7 @@ START_TEST(test_mnemonic_check) { END_TEST START_TEST(test_mnemonic_to_bits) { - const char *vectors[] = { + static const char *vectors[] = { "00000000000000000000000000000000", "abandon abandon abandon abandon abandon abandon abandon abandon abandon " "abandon abandon about", @@ -5286,7 +5574,7 @@ START_TEST(test_mnemonic_to_bits) { int mnemonic_bits_len = mnemonic_to_bits(*b, mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len % 33, 0); mnemonic_bits_len = mnemonic_bits_len * 4 / 33; - ck_assert_int_eq(mnemonic_bits_len, strlen(*a) / 2); + ck_assert_uint_eq((size_t)mnemonic_bits_len, strlen(*a) / 2); ck_assert_mem_eq(mnemonic_bits, fromhex(*a), mnemonic_bits_len); a += 2; b += 2; @@ -5297,7 +5585,7 @@ END_TEST START_TEST(test_mnemonic_find_word) { ck_assert_int_eq(-1, mnemonic_find_word("aaaa")); ck_assert_int_eq(-1, mnemonic_find_word("zzzz")); - for (int i = 0; i < BIP39_WORDS; i++) { + for (int i = 0; i < BIP39_WORD_COUNT; i++) { const char *word = mnemonic_get_word(i); int index = mnemonic_find_word(word); ck_assert_int_eq(i, index); @@ -5305,9 +5593,8 @@ START_TEST(test_mnemonic_find_word) { } END_TEST -/* // [wallet-core] START_TEST(test_slip39_get_word) { - const struct { + static const struct { const int index; const char *expected_word; } vectors[] = {{573, "member"}, @@ -5324,7 +5611,7 @@ END_TEST START_TEST(test_slip39_word_index) { uint16_t index; - const struct { + static const struct { const char *word; bool expected_result; uint16_t expected_index; @@ -5336,17 +5623,17 @@ START_TEST(test_slip39_word_index) { // 9999 value is never checked since the word is not in list {"fakeword", false, 9999}}; for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) { - bool result = word_index(&index, vectors[i].word, sizeof(vectors[i].word)); + bool result = word_index(&index, vectors[i].word, strlen(vectors[i].word)); ck_assert_int_eq(result, vectors[i].expected_result); if (result) { - ck_assert_int_eq(index, vectors[i].expected_index); + ck_assert_uint_eq(index, vectors[i].expected_index); } } } END_TEST START_TEST(test_slip39_word_completion_mask) { - const struct { + static const struct { const uint16_t prefix; const uint16_t expected_mask; } vectors[] = { @@ -5365,13 +5652,13 @@ START_TEST(test_slip39_word_completion_mask) { }; for (size_t i = 0; i < (sizeof(vectors) / sizeof(*vectors)); i++) { uint16_t mask = slip39_word_completion_mask(vectors[i].prefix); - ck_assert_int_eq(mask, vectors[i].expected_mask); + ck_assert_uint_eq(mask, vectors[i].expected_mask); } } END_TEST START_TEST(test_slip39_sequence_to_word) { - const struct { + static const struct { const uint16_t prefix; const char *expected_word; } vectors[] = { @@ -5406,11 +5693,10 @@ START_TEST(test_slip39_word_completion) { } } END_TEST -*/ START_TEST(test_shamir) { #define SHAMIR_MAX_COUNT 16 - const struct { + static const struct { const uint8_t result[SHAMIR_MAX_LEN]; uint8_t result_index; const uint8_t share_indices[SHAMIR_MAX_COUNT]; @@ -5938,7 +6224,7 @@ START_TEST(test_address_decode) { END_TEST START_TEST(test_ecdsa_der) { - const struct { + static const struct { const char *r; const char *s; const char *der; @@ -5998,6 +6284,11 @@ START_TEST(test_ecdsa_der) { "00000000000000000000000000000000000000000000000000000000000000ff", "3008020200ee020200ff", }, + { + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "3006020100020100", + }, }; uint8_t sig[64]; @@ -6147,11 +6438,11 @@ static void test_point_mult_curve(const ecdsa_curve *curve) { /* test distributivity: (a + b)P = aP + bP */ bn_mod(&a, &curve->order); bn_mod(&b, &curve->order); - point_multiply(curve, &a, &p, &p1); - point_multiply(curve, &b, &p, &p2); + ck_assert_int_eq(point_multiply(curve, &a, &p, &p1), 0); + ck_assert_int_eq(point_multiply(curve, &b, &p, &p2), 0); bn_addmod(&a, &b, &curve->order); bn_mod(&a, &curve->order); - point_multiply(curve, &a, &p, &p3); + ck_assert_int_eq(point_multiply(curve, &a, &p, &p3), 0); point_add(curve, &p1, &p2); ck_assert_mem_eq(&p2, &p3, sizeof(curve_point)); // new "random" numbers and a "random" point @@ -6178,17 +6469,17 @@ static void test_scalar_point_mult_curve(const ecdsa_curve *curve) { */ bn_mod(&a, &curve->order); bn_mod(&b, &curve->order); - scalar_multiply(curve, &a, &p1); - point_multiply(curve, &b, &p1, &p1); + ck_assert_int_eq(scalar_multiply(curve, &a, &p1), 0); + ck_assert_int_eq(point_multiply(curve, &b, &p1, &p1), 0); - scalar_multiply(curve, &b, &p2); - point_multiply(curve, &a, &p2, &p2); + ck_assert_int_eq(scalar_multiply(curve, &b, &p2), 0); + ck_assert_int_eq(point_multiply(curve, &a, &p2, &p2), 0); ck_assert_mem_eq(&p1, &p2, sizeof(curve_point)); bn_multiply(&a, &b, &curve->order); bn_mod(&b, &curve->order); - scalar_multiply(curve, &b, &p2); + ck_assert_int_eq(scalar_multiply(curve, &b, &p2), 0); ck_assert_mem_eq(&p1, &p2, sizeof(curve_point)); @@ -6210,7 +6501,7 @@ END_TEST START_TEST(test_ed25519) { // test vectors from // https://github.com/torproject/tor/blob/master/src/test/ed25519_vectors.inc - const char *vectors[] = { + static const char *vectors[] = { "26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d3" "6", // secret "c2247870536a192d142d056abefca68d6193158e7c1a59c1654c954eccaff89" @@ -6286,7 +6577,7 @@ START_TEST(test_ed25519) { UNMARK_SECRET_DATA(pk, sizeof(pk)); ck_assert_mem_eq(pk, fromhex(*spk), 32); - ed25519_sign(pk, 32, sk, pk, sig); + ed25519_sign(pk, 32, sk, sig); UNMARK_SECRET_DATA(sig, sizeof(sig)); ck_assert_mem_eq(sig, fromhex(*ssig), 64); @@ -6302,7 +6593,7 @@ END_TEST // test vectors from // https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/2.test-sign.dat START_TEST(test_ed25519_keccak) { - const struct { + static const struct { const char *private_key; const char *public_key; const char *signature; @@ -6504,7 +6795,7 @@ START_TEST(test_ed25519_keccak) { ck_assert_mem_eq(public_key, fromhex(tests[i].public_key), 32); ed25519_sign_keccak(fromhex(tests[i].data), tests[i].length, private_key, - public_key, signature); + signature); UNMARK_SECRET_DATA(signature, sizeof(signature)); ck_assert_mem_eq(signature, fromhex(tests[i].signature), 64); @@ -6513,8 +6804,15 @@ START_TEST(test_ed25519_keccak) { } END_TEST + START_TEST(test_ed25519_cosi) { -#define MAXN 10 +//win +#ifdef _MSC_VER + #define MAXN 10 +#else + const int MAXN = 10; +#endif + ed25519_secret_key keys[MAXN]; ed25519_public_key pubkeys[MAXN]; ed25519_secret_key nonces[MAXN]; @@ -6529,7 +6827,7 @@ START_TEST(test_ed25519_cosi) { "26c76712d89d906e6672dafa614c42e5cb1caac8c6568e4d2493087db51f0d36"), fromhex( "26659c1cf7321c178c07437150639ff0c5b7679c7ea195253ed9abda2e081a37"), - &rng); + NULL, &rng); for (int N = 1; N < 11; N++) { ed25519_public_key pk; @@ -6568,7 +6866,6 @@ START_TEST(test_ed25519_cosi) { UNMARK_SECRET_DATA(keys, sizeof(keys)); } -#undef MAXN } END_TEST @@ -6737,7 +7034,8 @@ START_TEST(test_ed25519_modl_sub) { } END_TEST -#if USE_MONERO // [wallet-core] +#if USE_MONERO + START_TEST(test_ge25519_double_scalarmult_vartime2) { char tests[][5][65] = { {"c537208ed4985e66e9f7a35c9a69448a732ba93960bbbd2823604f7ae9e3ed08", @@ -6861,13 +7159,14 @@ START_TEST(test_ge25519_double_scalarmult_vartime2) { } } END_TEST + #endif static void test_bip32_ecdh_init_node(HDNode *node, const char *seed_str, const char *curve_name) { hdnode_from_seed((const uint8_t *)seed_str, strlen(seed_str), curve_name, node); - hdnode_fill_public_key(node); + ck_assert_int_eq(hdnode_fill_public_key(node), 0); if (node->public_key[0] == 1) { node->public_key[0] = 0x40; // Curve25519 public keys start with 0x40 byte } @@ -6875,29 +7174,50 @@ static void test_bip32_ecdh_init_node(HDNode *node, const char *seed_str, static void test_bip32_ecdh(const char *curve_name, int expected_key_size, const uint8_t *expected_key) { - int res, key_size; - HDNode alice, bob; -#ifdef _MSC_VER - uint8_t *session_key1 = _alloca(expected_key_size); - uint8_t *session_key2 = _alloca(expected_key_size); -#else - uint8_t session_key1[expected_key_size], session_key2[expected_key_size]; -#endif - - test_bip32_ecdh_init_node(&alice, "Alice", curve_name); - test_bip32_ecdh_init_node(&bob, "Bob", curve_name); - - // Generate shared key from Alice's secret key and Bob's public key - res = hdnode_get_shared_key(&alice, bob.public_key, session_key1, &key_size); - ck_assert_int_eq(res, 0); - ck_assert_int_eq(key_size, expected_key_size); - ck_assert_mem_eq(session_key1, expected_key, key_size); - - // Generate shared key from Bob's secret key and Alice's public key - res = hdnode_get_shared_key(&bob, alice.public_key, session_key2, &key_size); - ck_assert_int_eq(res, 0); - ck_assert_int_eq(key_size, expected_key_size); - ck_assert_mem_eq(session_key2, expected_key, key_size); + #ifdef _MSC_VER + uint8_t * session_key1 = (uint8_t *)malloc(sizeof(uint8_t) * expected_key_size); + uint8_t * session_key2 = (uint8_t *)malloc(sizeof(uint8_t) * expected_key_size); + + int res, key_size; + HDNode alice, bob; + + test_bip32_ecdh_init_node(&alice, "Alice", curve_name); + test_bip32_ecdh_init_node(&bob, "Bob", curve_name); + // Generate shared key from Alice's secret key and Bob's public key + res = hdnode_get_shared_key(&alice, bob.public_key, session_key1, &key_size); + free (session_key1); + free (session_key2); + + ck_assert_int_eq(res, 0); + ck_assert_int_eq(key_size, expected_key_size); + ck_assert_mem_eq(session_key1, expected_key, key_size); + + // Generate shared key from Bob's secret key and Alice's public key + res = hdnode_get_shared_key(&bob, alice.public_key, session_key2, &key_size); + ck_assert_int_eq(res, 0); + ck_assert_int_eq(key_size, expected_key_size); + ck_assert_mem_eq(session_key2, expected_key, key_size); + #else + int res, key_size; + HDNode alice, bob; + uint8_t session_key1[expected_key_size], session_key2[expected_key_size]; + + test_bip32_ecdh_init_node(&alice, "Alice", curve_name); + test_bip32_ecdh_init_node(&bob, "Bob", curve_name); + + // Generate shared key from Alice's secret key and Bob's public key + res = hdnode_get_shared_key(&alice, bob.public_key, session_key1, &key_size); + ck_assert_int_eq(res, 0); + ck_assert_int_eq(key_size, expected_key_size); + ck_assert_mem_eq(session_key1, expected_key, key_size); + + // Generate shared key from Bob's secret key and Alice's public key + res = hdnode_get_shared_key(&bob, alice.public_key, session_key2, &key_size); + ck_assert_int_eq(res, 0); + ck_assert_int_eq(key_size, expected_key_size); + ck_assert_mem_eq(session_key2, expected_key, key_size); + #endif + } START_TEST(test_bip32_ecdh_nist256p1) { @@ -6940,7 +7260,7 @@ START_TEST(test_bip32_ecdh_errors) { END_TEST START_TEST(test_output_script) { - const char *vectors[] = { + static const char *vectors[] = { "76A914010966776006953D5567439E5E39F86A0D273BEE88AC", "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM", "A914010966776006953D5567439E5E39F86A0D273BEE87", @@ -6959,7 +7279,7 @@ START_TEST(test_output_script) { while (*scr && *adr) { int r = script_output_to_address(fromhex(*scr), strlen(*scr) / 2, address, 60); - ck_assert_int_eq(r, (int)(strlen(*adr) + 1)); + ck_assert_uint_eq((size_t)r, strlen(*adr) + 1); ck_assert_str_eq(address, *adr); scr += 2; adr += 2; @@ -6968,6 +7288,7 @@ START_TEST(test_output_script) { END_TEST #if USE_ETHEREUM + START_TEST(test_ethereum_pubkeyhash) { uint8_t pubkeyhash[20]; int res; @@ -7069,25 +7390,25 @@ START_TEST(test_ethereum_pubkeyhash) { END_TEST START_TEST(test_ethereum_address) { - const char *vectors[] = {"52908400098527886E0F7030069857D2E4169EE7", - "8617E340B3D01FA5F11F306F4090FD50E238070D", - "de709f2102306220921060314715629080e2fb77", - "27b1fdb04752bbc536007a920d24acb045561c26", - "5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", - "fB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", - "dbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", - "D1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", - "5A4EAB120fB44eb6684E5e32785702FF45ea344D", - "5be4BDC48CeF65dbCbCaD5218B1A7D37F58A0741", - "a7dD84573f5ffF821baf2205745f768F8edCDD58", - "027a49d11d118c0060746F1990273FcB8c2fC196", - "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + static const char *vectors[] = {"0x52908400098527886E0F7030069857D2E4169EE7", + "0x8617E340B3D01FA5F11F306F4090FD50E238070D", + "0xde709f2102306220921060314715629080e2fb77", + "0x27b1fdb04752bbc536007a920d24acb045561c26", + "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", + "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", + "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", + "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", + "0x5A4EAB120fB44eb6684E5e32785702FF45ea344D", + "0x5be4BDC48CeF65dbCbCaD5218B1A7D37F58A0741", + "0xa7dD84573f5ffF821baf2205745f768F8edCDD58", + "0x027a49d11d118c0060746F1990273FcB8c2fC196", + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", 0}; uint8_t addr[20]; - char address[41]; + char address[43]; const char **vec = vectors; while (*vec) { - memcpy(addr, fromhex(*vec), 20); + memcpy(addr, fromhex(*vec + 2), 20); ethereum_address_checksum(addr, address, false, 0); ck_assert_str_eq(address, *vec); vec++; @@ -7099,42 +7420,43 @@ END_TEST // https://github.com/rsksmart/RSKIPs/blob/master/IPs/RSKIP60.md START_TEST(test_rsk_address) { uint8_t addr[20]; - char address[41]; + char address[43]; - const char *rskip60_chain30[] = { - "5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD", - "Fb6916095cA1Df60bb79ce92cE3EA74c37c5d359", - "DBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB", - "D1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB", 0}; + static const char *rskip60_chain30[] = { + "0x5aaEB6053f3e94c9b9a09f33669435E7ef1bEAeD", + "0xFb6916095cA1Df60bb79ce92cE3EA74c37c5d359", + "0xDBF03B407c01E7CD3cBea99509D93F8Dddc8C6FB", + "0xD1220A0Cf47c7B9BE7a2e6ba89F429762E7B9adB", 0}; const char **vec = rskip60_chain30; while (*vec) { - memcpy(addr, fromhex(*vec), 20); + memcpy(addr, fromhex(*vec + 2), 20); ethereum_address_checksum(addr, address, true, 30); ck_assert_str_eq(address, *vec); vec++; } - const char *rskip60_chain31[] = { - "5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd", - "Fb6916095CA1dF60bb79CE92ce3Ea74C37c5D359", - "dbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB", - "d1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB", 0}; + static const char *rskip60_chain31[] = { + "0x5aAeb6053F3e94c9b9A09F33669435E7EF1BEaEd", + "0xFb6916095CA1dF60bb79CE92ce3Ea74C37c5D359", + "0xdbF03B407C01E7cd3cbEa99509D93f8dDDc8C6fB", + "0xd1220a0CF47c7B9Be7A2E6Ba89f429762E7b9adB", 0}; vec = rskip60_chain31; while (*vec) { - memcpy(addr, fromhex(*vec), 20); + memcpy(addr, fromhex(*vec + 2), 20); ethereum_address_checksum(addr, address, true, 31); ck_assert_str_eq(address, *vec); vec++; } } END_TEST + #endif #if USE_NEM // test vectors from // https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/1.test-keys.dat START_TEST(test_nem_address) { - const struct { + static const struct { const char *private_key; const char *public_key; const char *address; @@ -7263,7 +7585,7 @@ END_TEST // test vectors from // https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/3.test-derive.dat START_TEST(test_nem_derive) { - const struct { + static const struct { const char *salt; const char *private_key; const char *public_key; @@ -7437,7 +7759,7 @@ END_TEST // test vectors from // https://raw.githubusercontent.com/NemProject/nem-test-vectors/master/4.test-cipher.dat START_TEST(test_nem_cipher) { - const struct { + static const struct { const char *private_key; const char *public_key; const char *salt; @@ -7698,13 +8020,13 @@ START_TEST(test_nem_cipher) { memcpy(iv, fromhex(tests[i].iv), sizeof(iv)); ck_assert(hdnode_nem_encrypt(&node, public_key, iv, salt, input, input_size, buffer)); - ck_assert_int_eq(output_size, NEM_ENCRYPTED_SIZE(input_size)); + ck_assert_uint_eq(output_size, NEM_ENCRYPTED_SIZE(input_size)); ck_assert_mem_eq(buffer, output, output_size); memcpy(iv, fromhex(tests[i].iv), sizeof(iv)); ck_assert(hdnode_nem_decrypt(&node, public_key, iv, salt, output, output_size, buffer)); - ck_assert_int_eq(input_size, NEM_DECRYPTED_SIZE(buffer, output_size)); + ck_assert_uint_eq(input_size, NEM_DECRYPTED_SIZE(buffer, output_size)); ck_assert_mem_eq(buffer, input, input_size); } } @@ -8408,11 +8730,11 @@ END_TEST // https://tools.ietf.org/html/rfc6229#section-2 START_TEST(test_rc4_rfc6229) { - const size_t offsets[] = { + static const size_t offsets[] = { 0x0, 0xf0, 0x1f0, 0x2f0, 0x3f0, 0x5f0, 0x7f0, 0xbf0, 0xff0, }; - const struct { + static const struct { char key[65]; char vectors[sizeof(offsets) / sizeof(*offsets)][65]; } tests[] = { @@ -8732,7 +9054,7 @@ static void test_compress_coord(const char *k_raw) { } START_TEST(test_compress_coords) { - const char *k_raw[] = { + static const char *k_raw[] = { "dc05960ac673fd59554c98655e26722d007bb7ada0c8ff00883fdee70783d0be", "41e41e0a218c980411108a0a58cf88f528c828b4d6f0d2c86234bc2504bdc3cd", "1d963ddcb79f6028a32cadd2421ff7fff969bff5774f73063dab41519b3da175", @@ -8750,219 +9072,6 @@ START_TEST(test_compress_coords) { } END_TEST -// [wallet-core] -START_TEST(test_schnorr_sign_verify) { - static struct { - const char *message; - const char *priv_key; - const char *k_hex; - const char *s_hex; - const char *r_hex; - } test_cases[] = { - { - "123", - "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", - "669301F724C555D7BB1185C04909E9CACA3EC7A292B3A1C92DDCCD5A5A7DDDD3", - "FFD72C290B98C93A4BCEDC0EDCDF040C35579BE962FE83E6821D4F3CB4B795D2", - "74AAE9C3E069E2806E1B0D890970BE387AEBED8040F37991AACAD70B27895E39", - }, - { - "1234", - "51a2758eed776c40b367364909c8a9c98cc969104f69ff316f7a287495c37c9b", - "A0A1A9B3570AAE963535B8D4376C58A61646C18182C9FDDA5FB13703F88D4D1E", - "99A0CB942C81571B77C682F79CD3CB663CE9E1C55BB425BA24B9F11A0DE84FE2", - "C3C10363E38158BBA20556A36DE9358DFD81A31C180ABC9E7617C1CC1CAF03B3", - }, - { - "12345", - "2685adffdbb4b2c515054cffc25cfcbfe2e462df65bbe82fb50f71e1e68dd285", - "38DE7B3315F201433D271E91FBE62966576CA05CBFEC1770B77D7EC9D6A01D6D", - "28982FA6C2B620CBC550F7EF9EAB605F409C584FBE5A765678877B79AB517086", - "9A0788E5B0947DEDEDE386DF57A006CF3FE43919A74D9CA630F8A1A9D97B4650", - }, - { - "fun", - "7457dc574d927e5dae84b05264a5b637b5a68e34a85b3965084ed6fed5b7f12d", - "E005ABD242C7C602AB5EED080C5083C7C5F8DAEC6D046A54F384A8B8CDECF740", - "51070ABCA039DAC294F6BA3BFC8C36CFC66020EDF66D1ACF1A9B545B0BF09F52", - "330A924525EF722FA20E8E25CB6E8BD7DF4394886FA4414E4A0B6812AA25BBC0", - }, - { - "funny", - "52c395a6d304de1a959e73e4604e32c5ad3f2bf01c8f730af426b38d7d5dd908", - "0CF28B5C40A8830F3195BB99A9F0E2808F576105F41D16ABCF596AC5A8CFE88A", - "3D60FB4664C994AD956378B9402BC68F7B4799D74F4783A6199C0D74865EA2B6", - "5ED5EDEE0314DFFBEE39EE4E9C76DE8BC3EB8CB891AEC32B83957514284B205B", - }, - { - "What is great in man is that he is a bridge and not a goal", - "52c395a6d304de1a959e73e4604e32c5ad3f2bf01c8f730af426b38d7d5dd908", - "000000000000000000000000000000000000000000000000000000000000007B", - "546F70AA1FEE3718C95508240CDC073B9FEFED05959C5319DD8E2BF07A1DD028", - "B8667BE5E10B113608BFE5327C44E9F0462BE26F789177E10DCE53019AA33DAA", - }, - { - "123456789147258369qwertyuiopasdfghjklzxcvbnm,", - "2685adffdbb4b2c515054cffc25cfcbfe2e462df65bbe82fb50f71e1e68dd285", - "1D0CB70310C4D793A4561FE592B7C156771E3E26283B28AB588E968243B52DD0", - "54D7A435E5E3F2811AA542F8895C20CCB760F2713DBDDB7291DAB6DA4E4F927E", - "20A3BDABFFF2C1BF8E2AF709F6CDCAFE70DA9A1DBC22305B6332E36844092984", - }, - { - "11111111111111111111111111111111111111111111111111111111111111111" - "11111111111111111111111111111111111111111111111111111111111111111" - "111111111111111111", - "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", - "A669F372B3C2EEA351210082CAEC3B96767A7B222D19FF2EE3D814860F0D703A", - "4890F9AC3A8D102EE3A2A473930C01CAD29DCE3860ACB7A5DADAEF16FE808991", - "979F088E58F1814D5E462CB9F935D2924ABD8D32211D8F02DD7E0991726DF573", - }, - { - "qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=", - "7457dc574d927e5dae84b05264a5b637b5a68e34a85b3965084ed6fed5b7f12d", - "000000000000000000000000000000000000000000000000000000000000007C", - "0AA595A649E517133D3448CA657424DD07BBED289030F0C0AA6738D26AB9A910", - "83812632F1443A70B198D112D075D886BE7BBC6EC6275AE52661E52B7358BB8B", - }, - }; - - const ecdsa_curve *curve = &secp256k1; - bignum256 k; - uint8_t priv_key[32]; - uint8_t pub_key[33]; - uint8_t buf_raw[32]; - schnorr_sign_pair result; - schnorr_sign_pair expected; - int res; - - for (size_t i = 0; i < sizeof(test_cases) / sizeof(*test_cases); i++) { - memcpy(priv_key, fromhex(test_cases[i].priv_key), 32); - memcpy(&buf_raw, fromhex(test_cases[i].k_hex), 32); - bn_read_be(buf_raw, &k); - schnorr_sign(curve, priv_key, &k, (const uint8_t *)test_cases[i].message, - strlen(test_cases[i].message), &result); - - memcpy(&expected.s, fromhex(test_cases[i].s_hex), 32); - memcpy(&expected.r, fromhex(test_cases[i].r_hex), 32); - - ck_assert_mem_eq(&expected.r, &result.r, 32); - ck_assert_mem_eq(&expected.s, &result.s, 32); - - ecdsa_get_public_key33(curve, priv_key, pub_key); - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_cases[i].message, - strlen(test_cases[i].message), &result); - ck_assert_int_eq(res, 0); - } -} -END_TEST - -START_TEST(test_schnorr_fail_verify) { - static struct { - const char *message; - const char *priv_key; - const char *k_hex; - const char *s_hex; - const char *r_hex; - } test_case = { - "123", - "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", - "669301F724C555D7BB1185C04909E9CACA3EC7A292B3A1C92DDCCD5A5A7DDDD3", - "FFD72C290B98C93A4BCEDC0EDCDF040C35579BE962FE83E6821D4F3CB4B795D2", - "74AAE9C3E069E2806E1B0D890970BE387AEBED8040F37991AACAD70B27895E39", - }; - - const ecdsa_curve *curve = &secp256k1; - bignum256 k; - bignum256 bn_temp; - uint8_t priv_key[32]; - uint8_t pub_key[33]; - uint8_t buf_raw[32]; - schnorr_sign_pair result; - schnorr_sign_pair bad_result; - int res; - - memcpy(priv_key, fromhex(test_case.priv_key), 32); - memcpy(&buf_raw, fromhex(test_case.k_hex), 32); - bn_read_be(buf_raw, &k); - - schnorr_sign(curve, priv_key, &k, (const uint8_t *)test_case.message, - strlen(test_case.message), &result); - - ecdsa_get_public_key33(curve, priv_key, pub_key); - - // Test result = 0 (OK) - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, - strlen(test_case.message), &result); - ck_assert_int_eq(res, 0); - - // Test result = 1 (empty message) - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, 0, - &result); - ck_assert_int_eq(res, 1); - - // Test result = 2 (r = 0) - bn_zero(&bn_temp); - bn_write_be(&bn_temp, bad_result.r); - memcpy(bad_result.s, result.s, 32); - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, - strlen(test_case.message), &bad_result); - ck_assert_int_eq(res, 2); - - // Test result = 3 (s = 0) - memcpy(bad_result.r, result.r, 32); - bn_zero(&bn_temp); - bn_write_be(&bn_temp, bad_result.s); - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, - strlen(test_case.message), &bad_result); - ck_assert_int_eq(res, 3); - - // Test result = 4 (curve->order < r) - bn_copy(&curve->order, &bn_temp); - bn_addi(&bn_temp, 1); - bn_write_be(&bn_temp, bad_result.r); - memcpy(bad_result.s, result.s, 32); - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, - strlen(test_case.message), &bad_result); - ck_assert_int_eq(res, 4); - - // Test result = 5 (curve->order < s) - memcpy(bad_result.r, result.r, 32); - bn_copy(&curve->order, &bn_temp); - bn_addi(&bn_temp, 1); - bn_write_be(&bn_temp, bad_result.s); - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, - strlen(test_case.message), &bad_result); - ck_assert_int_eq(res, 5); - - // Test result = 6 (curve->order = r) - bn_copy(&curve->order, &bn_temp); - bn_write_be(&bn_temp, bad_result.r); - memcpy(bad_result.s, result.s, 32); - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, - strlen(test_case.message), &bad_result); - ck_assert_int_eq(res, 6); - - // Test result = 7 (curve->order = s) - memcpy(bad_result.r, result.r, 32); - bn_copy(&curve->order, &bn_temp); - bn_write_be(&bn_temp, bad_result.s); - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, - strlen(test_case.message), &bad_result); - ck_assert_int_eq(res, 7); - - // Test result = 8 (failed ecdsa_read_pubkey) - // TBD - - // Test result = 10 (r != r') - memcpy(bad_result.r, result.r, 32); - memcpy(bad_result.s, result.s, 32); - test_case.message = "12"; - res = schnorr_verify(curve, pub_key, (const uint8_t *)test_case.message, - strlen(test_case.message), &bad_result); - ck_assert_int_eq(res, 10); -} -END_TEST - static int my_strncasecmp(const char *s1, const char *s2, size_t n) { size_t i = 0; while (i < n) { @@ -8979,6 +9088,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) { } #include "test_check_cashaddr.h" +#include "test_check_zilliqa.h" // [wallet-core] #if USE_SEGWIT #include "test_check_segwit.h" #endif @@ -9076,7 +9186,13 @@ Suite *test_suite(void) { suite_add_tcase(s, tc); tc = tcase_create("ecdsa"); - tcase_add_test(tc, test_ecdsa_signature); + tcase_add_test(tc, test_ecdsa_get_public_key33); + tcase_add_test(tc, test_ecdsa_get_public_key65); + tcase_add_test(tc, test_ecdsa_recover_pub_from_sig); + tcase_add_test(tc, test_ecdsa_verify_digest); +#if USE_RFC6979 + tcase_add_test(tc, test_ecdsa_sign_digest_deterministic); +#endif suite_add_tcase(s, tc); tc = tcase_create("rfc6979"); @@ -9131,6 +9247,7 @@ Suite *test_suite(void) { tc = tcase_create("blake2"); tcase_add_test(tc, test_blake2b); + tcase_add_test(tc, test_blake2bp); tcase_add_test(tc, test_blake2s); suite_add_tcase(s, tc); @@ -9154,7 +9271,6 @@ Suite *test_suite(void) { tcase_add_test(tc, test_mnemonic_find_word); suite_add_tcase(s, tc); -/* tc = tcase_create("slip39"); tcase_add_test(tc, test_slip39_get_word); tcase_add_test(tc, test_slip39_word_index); @@ -9162,7 +9278,6 @@ Suite *test_suite(void) { tcase_add_test(tc, test_slip39_sequence_to_word); tcase_add_test(tc, test_slip39_word_completion); suite_add_tcase(s, tc); -*/ tc = tcase_create("shamir"); tcase_add_test(tc, test_shamir); @@ -9290,6 +9405,10 @@ Suite *test_suite(void) { tcase_add_test(tc, test_bip32_cardano_hdnode_vector_8); tcase_add_test(tc, test_bip32_cardano_hdnode_vector_9); + tcase_add_test(tc, test_cardano_ledger_vector_1); + tcase_add_test(tc, test_cardano_ledger_vector_2); + tcase_add_test(tc, test_cardano_ledger_vector_3); + tcase_add_test(tc, test_ed25519_cardano_sign_vectors); suite_add_tcase(s, tc); #endif @@ -9332,16 +9451,9 @@ Suite *test_suite(void) { tcase_add_test(tc, test_xmr_get_subaddress_secret_key); tcase_add_test(tc, test_xmr_gen_c); tcase_add_test(tc, test_xmr_varint); - tcase_add_test(tc, test_xmr_gen_range_sig); suite_add_tcase(s, tc); #endif - // [wallet-core] - tc = tcase_create("schnorr"); - tcase_add_test(tc, test_schnorr_sign_verify); - tcase_add_test(tc, test_schnorr_fail_verify); - suite_add_tcase(s, tc); - return s; } diff --git a/trezor-crypto/crypto/tests/test_check_cardano.h b/trezor-crypto/crypto/tests/test_check_cardano.h index 4fc03cb3ee1..4f31a55309f 100644 --- a/trezor-crypto/crypto/tests/test_check_cardano.h +++ b/trezor-crypto/crypto/tests/test_check_cardano.h @@ -5,7 +5,7 @@ START_TEST(test_ed25519_cardano_sign_vectors) { ed25519_secret_key secret_key_extension; ed25519_signature signature; - const char *vectors[] = { + static const char *vectors[] = { "6065a956b1b34145c4416fdc3ba3276801850e91a77a31a7be782463288aea5" "3", // private key "60ba6e25b1a02157fb69c5d1d7b96c4619736e545447069a6a6f0ba90844bc8" @@ -89,14 +89,13 @@ START_TEST(test_ed25519_cardano_sign_vectors) { memcpy(secret_key_extension, fromhex(*(test_data + 1)), 32); MARK_SECRET_DATA(secret_key_extension, sizeof(secret_key_extension)); - ed25519_publickey_ext(secret_key, secret_key_extension, public_key); + ed25519_publickey_ext(secret_key, public_key); UNMARK_SECRET_DATA(public_key, sizeof(public_key)); ck_assert_mem_eq(public_key, fromhex(*(test_data + 2)), 32); const uint8_t *message = (const uint8_t *)"Hello World"; - ed25519_sign_ext(message, 11, secret_key, secret_key_extension, public_key, - signature); + ed25519_sign_ext(message, 11, secret_key, secret_key_extension, signature); UNMARK_SECRET_DATA(signature, sizeof(signature)); ck_assert_mem_eq(signature, fromhex(*(test_data + 3)), 64); @@ -113,14 +112,24 @@ START_TEST(test_bip32_cardano_hdnode_vector_1) { HDNode node; uint8_t mnemonic_bits[66]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; int mnemonic_bits_len = mnemonic_to_bits( "ring crime symptom enough erupt lady behave ramp apart settle citizen " "junk", mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len, 132); - hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, - mnemonic_bits_len / 8, &node); + secret_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, + mnemonic_bits_len / 8, cardano_secret, + NULL); + hdnode_from_secret_cardano(cardano_secret, &node); + ck_assert_mem_eq( + cardano_secret, + fromhex( + "08a14df748e477a69d21c97c56db151fc19e2521f31dd0ac5360f269e5b6ea46" + "daeb991f2d2128e2525415c56a07f4366baa26c1e48572a5e073934b6de35fbc" + "affbc325d9027c0f2d9f925b1dcf6c12bf5c1dd08904474066a4f2c00db56173"), + 96); ck_assert_mem_eq( node.chain_code, fromhex( @@ -136,7 +145,7 @@ START_TEST(test_bip32_cardano_hdnode_vector_1) { fromhex( "daeb991f2d2128e2525415c56a07f4366baa26c1e48572a5e073934b6de35fbc"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key + 1, fromhex( @@ -149,15 +158,18 @@ START_TEST(test_bip32_cardano_hdnode_vector_2) { HDNode node; uint8_t mnemonic_bits[66]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; int mnemonic_bits_len = mnemonic_to_bits( "ring crime symptom enough erupt lady behave ramp apart settle citizen " "junk", mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len, 132); - hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, - mnemonic_bits_len / 8, &node); + secret_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, + mnemonic_bits_len / 8, cardano_secret, + NULL); + hdnode_from_secret_cardano(cardano_secret, &node); - hdnode_private_ckd_cardano(&node, 0x80000000); + hdnode_private_ckd(&node, 0x80000000); ck_assert_mem_eq( node.chain_code, @@ -174,7 +186,7 @@ START_TEST(test_bip32_cardano_hdnode_vector_2) { fromhex( "64aa9a16331f14c981b769efcf96addcc4c6db44047fe7a7feae0be23d33bf54"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key + 1, fromhex( @@ -187,15 +199,18 @@ START_TEST(test_bip32_cardano_hdnode_vector_3) { HDNode node; uint8_t mnemonic_bits[66]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; int mnemonic_bits_len = mnemonic_to_bits( "ring crime symptom enough erupt lady behave ramp apart settle citizen " "junk", mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len, 132); - hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, - mnemonic_bits_len / 8, &node); + secret_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, + mnemonic_bits_len / 8, cardano_secret, + NULL); + hdnode_from_secret_cardano(cardano_secret, &node); - hdnode_private_ckd_cardano(&node, 0x80000001); + hdnode_private_ckd(&node, 0x80000001); ck_assert_mem_eq( node.chain_code, @@ -212,7 +227,7 @@ START_TEST(test_bip32_cardano_hdnode_vector_3) { fromhex( "b4fc241feffe840b8a54a26ab447f5a5caa31032db3a8091fca14f38b86ed539"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key + 1, fromhex( @@ -225,16 +240,19 @@ START_TEST(test_bip32_cardano_hdnode_vector_4) { HDNode node; uint8_t mnemonic_bits[66]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; int mnemonic_bits_len = mnemonic_to_bits( "ring crime symptom enough erupt lady behave ramp apart settle citizen " "junk", mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len, 132); - hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, - mnemonic_bits_len / 8, &node); + secret_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, + mnemonic_bits_len / 8, cardano_secret, + NULL); + hdnode_from_secret_cardano(cardano_secret, &node); - hdnode_private_ckd_cardano(&node, 0x80000000); - hdnode_private_ckd_cardano(&node, 0x80000001); + hdnode_private_ckd(&node, 0x80000000); + hdnode_private_ckd(&node, 0x80000001); ck_assert_mem_eq( node.chain_code, @@ -251,7 +269,7 @@ START_TEST(test_bip32_cardano_hdnode_vector_4) { fromhex( "a3071959013af95aaecf78a7a2e1b9838bbbc4864d6a8a2295243782078345cd"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key + 1, fromhex( @@ -264,17 +282,20 @@ START_TEST(test_bip32_cardano_hdnode_vector_5) { HDNode node; uint8_t mnemonic_bits[66]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; int mnemonic_bits_len = mnemonic_to_bits( "ring crime symptom enough erupt lady behave ramp apart settle citizen " "junk", mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len, 132); - hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, - mnemonic_bits_len / 8, &node); + secret_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, + mnemonic_bits_len / 8, cardano_secret, + NULL); + hdnode_from_secret_cardano(cardano_secret, &node); - hdnode_private_ckd_cardano(&node, 0x80000000); - hdnode_private_ckd_cardano(&node, 0x80000001); - hdnode_private_ckd_cardano(&node, 0x80000002); + hdnode_private_ckd(&node, 0x80000000); + hdnode_private_ckd(&node, 0x80000001); + hdnode_private_ckd(&node, 0x80000002); ck_assert_mem_eq( node.chain_code, @@ -291,7 +312,7 @@ START_TEST(test_bip32_cardano_hdnode_vector_5) { fromhex( "5bebf1eea68acd04932653d944b064b10baaf5886dd73c185cc285059bf93363"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key + 1, fromhex( @@ -304,18 +325,21 @@ START_TEST(test_bip32_cardano_hdnode_vector_6) { HDNode node; uint8_t mnemonic_bits[66]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; int mnemonic_bits_len = mnemonic_to_bits( "ring crime symptom enough erupt lady behave ramp apart settle citizen " "junk", mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len, 132); - hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, - mnemonic_bits_len / 8, &node); + secret_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, + mnemonic_bits_len / 8, cardano_secret, + NULL); + hdnode_from_secret_cardano(cardano_secret, &node); - hdnode_private_ckd_cardano(&node, 0x80000000); - hdnode_private_ckd_cardano(&node, 0x80000001); - hdnode_private_ckd_cardano(&node, 0x80000002); - hdnode_private_ckd_cardano(&node, 0x80000002); + hdnode_private_ckd(&node, 0x80000000); + hdnode_private_ckd(&node, 0x80000001); + hdnode_private_ckd(&node, 0x80000002); + hdnode_private_ckd(&node, 0x80000002); ck_assert_mem_eq( node.chain_code, @@ -332,7 +356,7 @@ START_TEST(test_bip32_cardano_hdnode_vector_6) { fromhex( "466332cb097934b43008701e7e27044aa56c7859019e4eba18d91a3bea23dff7"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key + 1, fromhex( @@ -345,19 +369,22 @@ START_TEST(test_bip32_cardano_hdnode_vector_7) { HDNode node; uint8_t mnemonic_bits[66]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; int mnemonic_bits_len = mnemonic_to_bits( "ring crime symptom enough erupt lady behave ramp apart settle citizen " "junk", mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len, 132); - hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, - mnemonic_bits_len / 8, &node); + secret_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, + mnemonic_bits_len / 8, cardano_secret, + NULL); + hdnode_from_secret_cardano(cardano_secret, &node); - hdnode_private_ckd_cardano(&node, 0x80000000); - hdnode_private_ckd_cardano(&node, 0x80000001); - hdnode_private_ckd_cardano(&node, 0x80000002); - hdnode_private_ckd_cardano(&node, 0x80000002); - hdnode_private_ckd_cardano(&node, 0xBB9ACA00); + hdnode_private_ckd(&node, 0x80000000); + hdnode_private_ckd(&node, 0x80000001); + hdnode_private_ckd(&node, 0x80000002); + hdnode_private_ckd(&node, 0x80000002); + hdnode_private_ckd(&node, 0xBB9ACA00); ck_assert_mem_eq( node.chain_code, @@ -374,7 +401,7 @@ START_TEST(test_bip32_cardano_hdnode_vector_7) { fromhex( "01eccef768a79859f824a1d3c3e35e131184e2940c3fca9a4c9b307741f65363"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key + 1, fromhex( @@ -387,19 +414,22 @@ START_TEST(test_bip32_cardano_hdnode_vector_8) { HDNode node; uint8_t mnemonic_bits[66]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; int mnemonic_bits_len = mnemonic_to_bits( "found differ bulb shadow wrist blue bind vessel deposit tip pelican " "action surprise weapon check fiction muscle this", mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len, 198); - hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, - mnemonic_bits_len / 8, &node); + secret_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, + mnemonic_bits_len / 8, cardano_secret, + NULL); + hdnode_from_secret_cardano(cardano_secret, &node); - hdnode_private_ckd_cardano(&node, 0x80000000); - hdnode_private_ckd_cardano(&node, 0x80000001); - hdnode_private_ckd_cardano(&node, 0x80000002); - hdnode_private_ckd_cardano(&node, 0x80000002); - hdnode_private_ckd_cardano(&node, 0xBB9ACA00); + hdnode_private_ckd(&node, 0x80000000); + hdnode_private_ckd(&node, 0x80000001); + hdnode_private_ckd(&node, 0x80000002); + hdnode_private_ckd(&node, 0x80000002); + hdnode_private_ckd(&node, 0xBB9ACA00); ck_assert_mem_eq( node.chain_code, @@ -416,7 +446,7 @@ START_TEST(test_bip32_cardano_hdnode_vector_8) { fromhex( "170e0d3b65ba8d71f27a6db60d0ac26dcb16e52e08cc259db72066f206b258d5"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key + 1, fromhex( @@ -429,20 +459,23 @@ START_TEST(test_bip32_cardano_hdnode_vector_9) { HDNode node; uint8_t mnemonic_bits[66]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; int mnemonic_bits_len = mnemonic_to_bits( "balance exotic ranch knife glory slow tape favorite yard gym awake " "ill exist useless parent aim pig stay effort into square gasp credit " "butter", mnemonic_bits); ck_assert_int_eq(mnemonic_bits_len, 264); - hdnode_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, - mnemonic_bits_len / 8, &node); + secret_from_entropy_cardano_icarus((const uint8_t *)"", 0, mnemonic_bits, + mnemonic_bits_len / 8, cardano_secret, + NULL); + hdnode_from_secret_cardano(cardano_secret, &node); - hdnode_private_ckd_cardano(&node, 0x80000000); - hdnode_private_ckd_cardano(&node, 0x80000001); - hdnode_private_ckd_cardano(&node, 0x80000002); - hdnode_private_ckd_cardano(&node, 0x80000002); - hdnode_private_ckd_cardano(&node, 0xBB9ACA00); + hdnode_private_ckd(&node, 0x80000000); + hdnode_private_ckd(&node, 0x80000001); + hdnode_private_ckd(&node, 0x80000002); + hdnode_private_ckd(&node, 0x80000002); + hdnode_private_ckd(&node, 0xBB9ACA00); ck_assert_mem_eq( node.chain_code, @@ -459,7 +492,7 @@ START_TEST(test_bip32_cardano_hdnode_vector_9) { fromhex( "80d2c677638e5dbd4395cdec279bf2a42077f2797c9e887949d37cdb317fce6a"), 32); - hdnode_fill_public_key(&node); + ck_assert_int_eq(hdnode_fill_public_key(&node), 0); ck_assert_mem_eq( node.public_key + 1, fromhex( @@ -467,3 +500,72 @@ START_TEST(test_bip32_cardano_hdnode_vector_9) { 32); } END_TEST + +START_TEST(test_cardano_ledger_vector_1) { + uint8_t seed[512 / 8]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; + + const char *mnemonic = + "recall grace sport punch exhibit mad harbor stand obey " + "short width stem awkward used stairs wool ugly " + "trap season stove worth toward congress jaguar"; + + mnemonic_to_seed(mnemonic, "", seed, NULL); + const int res = + secret_from_seed_cardano_ledger(seed, sizeof(seed), cardano_secret); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq( + cardano_secret, + fromhex( + "a08cf85b564ecf3b947d8d4321fb96d70ee7bb760877e371899b14e2ccf88658" + "104b884682b57efd97decbb318a45c05a527b9cc5c2f64f7352935a049ceea60" + "680d52308194ccef2a18e6812b452a5815fbd7f5babc083856919aaf668fe7e4"), + CARDANO_SECRET_LENGTH); +} +END_TEST + +START_TEST(test_cardano_ledger_vector_2) { + uint8_t seed[512 / 8]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; + + const char *mnemonic = + "correct cherry mammal bubble want mandate polar hazard " + "crater better craft exotic choice fun tourist census " + "gap lottery neglect address glow carry old business"; + + mnemonic_to_seed(mnemonic, "", seed, NULL); + const int res = + secret_from_seed_cardano_ledger(seed, sizeof(seed), cardano_secret); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq( + cardano_secret, + fromhex( + "587c6774357ecbf840d4db6404ff7af016dace0400769751ad2abfc77b9a3844" + "cc71702520ef1a4d1b68b91187787a9b8faab0a9bb6b160de541b6ee62469901" + "fc0beda0975fe4763beabd83b7051a5fd5cbce5b88e82c4bbaca265014e524bd"), + CARDANO_SECRET_LENGTH); +} +END_TEST + +START_TEST(test_cardano_ledger_vector_3) { + uint8_t seed[512 / 8]; + uint8_t cardano_secret[CARDANO_SECRET_LENGTH]; + + const char *mnemonic = + "abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon abandon " + "abandon abandon abandon abandon abandon abandon abandon art"; + + mnemonic_to_seed(mnemonic, "foo", seed, NULL); + const int res = + secret_from_seed_cardano_ledger(seed, sizeof(seed), cardano_secret); + ck_assert_int_eq(res, 1); + ck_assert_mem_eq( + cardano_secret, + fromhex( + "f053a1e752de5c26197b60f032a4809f08bb3e5d90484fe42024be31efcba757" + "8d914d3ff992e21652fee6a4d99f6091006938fac2c0c0f9d2de0ba64b754e92" + "a4f3723f23472077aa4cd4dd8a8a175dba07ea1852dad1cf268c61a2679c3890"), + CARDANO_SECRET_LENGTH); +} +END_TEST diff --git a/trezor-crypto/crypto/tests/test_check_zilliqa.h b/trezor-crypto/crypto/tests/test_check_zilliqa.h new file mode 100644 index 00000000000..b8b16824b51 --- /dev/null +++ b/trezor-crypto/crypto/tests/test_check_zilliqa.h @@ -0,0 +1,214 @@ +#include +#include + +START_TEST(test_zil_schnorr_sign_verify) { + static struct { + const char *message; + const char *priv_key; + const char *k_hex; + const char *s_hex; + const char *r_hex; + } test_cases[] = { + { + "123", + "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + "669301F724C555D7BB1185C04909E9CACA3EC7A292B3A1C92DDCCD5A5A7DDDD3", + "FFD72C290B98C93A4BCEDC0EDCDF040C35579BE962FE83E6821D4F3CB4B795D2", + "74AAE9C3E069E2806E1B0D890970BE387AEBED8040F37991AACAD70B27895E39", + }, + { + "1234", + "51a2758eed776c40b367364909c8a9c98cc969104f69ff316f7a287495c37c9b", + "A0A1A9B3570AAE963535B8D4376C58A61646C18182C9FDDA5FB13703F88D4D1E", + "99A0CB942C81571B77C682F79CD3CB663CE9E1C55BB425BA24B9F11A0DE84FE2", + "C3C10363E38158BBA20556A36DE9358DFD81A31C180ABC9E7617C1CC1CAF03B3", + }, + { + "12345", + "2685adffdbb4b2c515054cffc25cfcbfe2e462df65bbe82fb50f71e1e68dd285", + "38DE7B3315F201433D271E91FBE62966576CA05CBFEC1770B77D7EC9D6A01D6D", + "28982FA6C2B620CBC550F7EF9EAB605F409C584FBE5A765678877B79AB517086", + "9A0788E5B0947DEDEDE386DF57A006CF3FE43919A74D9CA630F8A1A9D97B4650", + }, + { + "fun", + "7457dc574d927e5dae84b05264a5b637b5a68e34a85b3965084ed6fed5b7f12d", + "E005ABD242C7C602AB5EED080C5083C7C5F8DAEC6D046A54F384A8B8CDECF740", + "51070ABCA039DAC294F6BA3BFC8C36CFC66020EDF66D1ACF1A9B545B0BF09F52", + "330A924525EF722FA20E8E25CB6E8BD7DF4394886FA4414E4A0B6812AA25BBC0", + }, + { + "funny", + "52c395a6d304de1a959e73e4604e32c5ad3f2bf01c8f730af426b38d7d5dd908", + "0CF28B5C40A8830F3195BB99A9F0E2808F576105F41D16ABCF596AC5A8CFE88A", + "3D60FB4664C994AD956378B9402BC68F7B4799D74F4783A6199C0D74865EA2B6", + "5ED5EDEE0314DFFBEE39EE4E9C76DE8BC3EB8CB891AEC32B83957514284B205B", + }, + { + "What is great in man is that he is a bridge and not a goal", + "52c395a6d304de1a959e73e4604e32c5ad3f2bf01c8f730af426b38d7d5dd908", + "000000000000000000000000000000000000000000000000000000000000007B", + "546F70AA1FEE3718C95508240CDC073B9FEFED05959C5319DD8E2BF07A1DD028", + "B8667BE5E10B113608BFE5327C44E9F0462BE26F789177E10DCE53019AA33DAA", + }, + { + "123456789147258369qwertyuiopasdfghjklzxcvbnm,", + "2685adffdbb4b2c515054cffc25cfcbfe2e462df65bbe82fb50f71e1e68dd285", + "1D0CB70310C4D793A4561FE592B7C156771E3E26283B28AB588E968243B52DD0", + "54D7A435E5E3F2811AA542F8895C20CCB760F2713DBDDB7291DAB6DA4E4F927E", + "20A3BDABFFF2C1BF8E2AF709F6CDCAFE70DA9A1DBC22305B6332E36844092984", + }, + { + "11111111111111111111111111111111111111111111111111111111111111111" + "11111111111111111111111111111111111111111111111111111111111111111" + "111111111111111111", + "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + "A669F372B3C2EEA351210082CAEC3B96767A7B222D19FF2EE3D814860F0D703A", + "4890F9AC3A8D102EE3A2A473930C01CAD29DCE3860ACB7A5DADAEF16FE808991", + "979F088E58F1814D5E462CB9F935D2924ABD8D32211D8F02DD7E0991726DF573", + }, + { + "qwertyuiop[]asdfghjkl;'zxcvbnm,./1234567890-=", + "7457dc574d927e5dae84b05264a5b637b5a68e34a85b3965084ed6fed5b7f12d", + "000000000000000000000000000000000000000000000000000000000000007C", + "0AA595A649E517133D3448CA657424DD07BBED289030F0C0AA6738D26AB9A910", + "83812632F1443A70B198D112D075D886BE7BBC6EC6275AE52661E52B7358BB8B", + }, + }; + + const ecdsa_curve *curve = &secp256k1; + bignum256 k; + uint8_t priv_key[32]; + uint8_t pub_key[33]; + uint8_t buf_raw[32]; + schnorr_sign_pair result; + schnorr_sign_pair expected; + int res; + + for (size_t i = 0; i < sizeof(test_cases) / sizeof(*test_cases); i++) { + memcpy(priv_key, fromhex(test_cases[i].priv_key), 32); + memcpy(&buf_raw, fromhex(test_cases[i].k_hex), 32); + bn_read_be(buf_raw, &k); + zil_schnorr_sign_k(curve, priv_key, &k, (const uint8_t *)test_cases[i].message, + strlen(test_cases[i].message), &result); + + memcpy(&expected.s, fromhex(test_cases[i].s_hex), 32); + memcpy(&expected.r, fromhex(test_cases[i].r_hex), 32); + + ck_assert_mem_eq(&expected.r, &result.r, 32); + ck_assert_mem_eq(&expected.s, &result.s, 32); + + ecdsa_get_public_key33(curve, priv_key, pub_key); + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_cases[i].message, + strlen(test_cases[i].message), &result); + ck_assert_int_eq(res, 0); + } +} +END_TEST + +START_TEST(test_zil_schnorr_fail_verify) { + static struct { + const char *message; + const char *priv_key; + const char *k_hex; + const char *s_hex; + const char *r_hex; + } test_case = { + "123", + "3382266517e2ebe6df51faf4bfe612236ad46fb8bd59ac982a223b045e080ac6", + "669301F724C555D7BB1185C04909E9CACA3EC7A292B3A1C92DDCCD5A5A7DDDD3", + "FFD72C290B98C93A4BCEDC0EDCDF040C35579BE962FE83E6821D4F3CB4B795D2", + "74AAE9C3E069E2806E1B0D890970BE387AEBED8040F37991AACAD70B27895E39", + }; + + const ecdsa_curve *curve = &secp256k1; + bignum256 k; + bignum256 bn_temp; + uint8_t priv_key[32]; + uint8_t pub_key[33]; + uint8_t buf_raw[32]; + schnorr_sign_pair result; + schnorr_sign_pair bad_result; + int res; + + memcpy(priv_key, fromhex(test_case.priv_key), 32); + memcpy(&buf_raw, fromhex(test_case.k_hex), 32); + bn_read_be(buf_raw, &k); + + zil_schnorr_sign_k(curve, priv_key, &k, (const uint8_t *)test_case.message, + strlen(test_case.message), &result); + + ecdsa_get_public_key33(curve, priv_key, pub_key); + + // Test result = 0 (OK) + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &result); + ck_assert_int_eq(res, 0); + + // Test result = 1 (empty message) + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_case.message, 0, + &result); + ck_assert_int_eq(res, 1); + + // Test result = 2 (r = 0) + bn_zero(&bn_temp); + bn_write_be(&bn_temp, bad_result.r); + memcpy(bad_result.s, result.s, 32); + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 2); + + // Test result = 3 (s = 0) + memcpy(bad_result.r, result.r, 32); + bn_zero(&bn_temp); + bn_write_be(&bn_temp, bad_result.s); + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 3); + + // Test result = 4 (curve->order < r) + bn_copy(&curve->order, &bn_temp); + bn_addi(&bn_temp, 1); + bn_write_be(&bn_temp, bad_result.r); + memcpy(bad_result.s, result.s, 32); + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 4); + + // Test result = 5 (curve->order < s) + memcpy(bad_result.r, result.r, 32); + bn_copy(&curve->order, &bn_temp); + bn_addi(&bn_temp, 1); + bn_write_be(&bn_temp, bad_result.s); + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 5); + + // Test result = 6 (curve->order = r) + bn_copy(&curve->order, &bn_temp); + bn_write_be(&bn_temp, bad_result.r); + memcpy(bad_result.s, result.s, 32); + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 6); + + // Test result = 7 (curve->order = s) + memcpy(bad_result.r, result.r, 32); + bn_copy(&curve->order, &bn_temp); + bn_write_be(&bn_temp, bad_result.s); + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 7); + + // Test result = 8 (failed ecdsa_read_pubkey) + // TBD + + // Test result = 10 (r != r') + memcpy(bad_result.r, result.r, 32); + memcpy(bad_result.s, result.s, 32); + test_case.message = "12"; + res = zil_schnorr_verify_pair(curve, pub_key, (const uint8_t *)test_case.message, + strlen(test_case.message), &bad_result); + ck_assert_int_eq(res, 10); +} +END_TEST diff --git a/trezor-crypto/crypto/tests/test_openssl.c b/trezor-crypto/crypto/tests/test_openssl.c index 4b38658f811..b9390846cb9 100644 --- a/trezor-crypto/crypto/tests/test_openssl.c +++ b/trezor-crypto/crypto/tests/test_openssl.c @@ -79,8 +79,15 @@ void openssl_check(unsigned int iterations, int nid, const ecdsa_curve *curve) { } // generate public key from private key - ecdsa_get_public_key33(curve, priv_key, pub_key33); - ecdsa_get_public_key65(curve, priv_key, pub_key65); + if (ecdsa_get_public_key33(curve, priv_key, pub_key33) != 0) { + printf("ecdsa_get_public_key33 failed\n"); + return; + } + + if (ecdsa_get_public_key65(curve, priv_key, pub_key65) != 0) { + printf("ecdsa_get_public_key65 failed\n"); + return; + } // use our ECDSA verifier to verify the message signature if (ecdsa_verify(curve, HASHER_SHA2, pub_key65, sig, msg, msg_len) != 0) { diff --git a/trezor-crypto/crypto/tests/test_speed.c b/trezor-crypto/crypto/tests/test_speed.c index 8a675eca576..a82f67f3e65 100644 --- a/trezor-crypto/crypto/tests/test_speed.c +++ b/trezor-crypto/crypto/tests/test_speed.c @@ -11,7 +11,7 @@ #include "nist256p1.h" #include -uint8_t msg[256]; +static uint8_t msg[256]; void prepare_msg(void) { for (size_t i = 0; i < sizeof(msg); i++) { @@ -50,18 +50,16 @@ void bench_sign_nist256p1(int iterations) { } void bench_sign_ed25519(int iterations) { - ed25519_public_key pk; ed25519_secret_key sk; ed25519_signature sig; - memcpy(pk, + memcpy(sk, "\xc5\x5e\xce\x85\x8b\x0d\xdd\x52\x63\xf9\x68\x10\xfe\x14\x43\x7c\xd3" "\xb5\xe1\xfb\xd7\xc6\xa2\xec\x1e\x03\x1f\x05\xe8\x6d\x8b\xd5", 32); - ed25519_publickey(sk, pk); for (int i = 0; i < iterations; i++) { - ed25519_sign(msg, sizeof(msg), sk, pk, sig); + ed25519_sign(msg, sizeof(msg), sk, sig); } } @@ -138,12 +136,12 @@ void bench_verify_ed25519(int iterations) { ed25519_secret_key sk; ed25519_signature sig; - memcpy(pk, + memcpy(sk, "\xc5\x5e\xce\x85\x8b\x0d\xdd\x52\x63\xf9\x68\x10\xfe\x14\x43\x7c\xd3" "\xb5\xe1\xfb\xd7\xc6\xa2\xec\x1e\x03\x1f\x05\xe8\x6d\x8b\xd5", 32); ed25519_publickey(sk, pk); - ed25519_sign(msg, sizeof(msg), sk, pk, sig); + ed25519_sign(msg, sizeof(msg), sk, sig); for (int i = 0; i < iterations; i++) { ed25519_sign_open(msg, sizeof(msg), pk, sig); @@ -169,7 +167,7 @@ void bench_multiply_curve25519(int iterations) { } } -HDNode root; +static HDNode root; void prepare_node(void) { hdnode_from_seed((uint8_t *)"NothingToSeeHere", 16, SECP256K1_NAME, &root); diff --git a/trezor-crypto/crypto/zilliqa.c b/trezor-crypto/crypto/zilliqa.c new file mode 100644 index 00000000000..5d30b56bf6c --- /dev/null +++ b/trezor-crypto/crypto/zilliqa.c @@ -0,0 +1,191 @@ +/** + * Copyright (c) 2019 Anatolii Kurotych + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "string.h" + +#include +#include +#include +#include + +int zil_schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key, const uint8_t *msg, const uint32_t msg_len, uint8_t *sig) +{ + int i; + bignum256 k; + + uint8_t hash[32]; + sha256_Raw(msg, msg_len, hash); + + rfc6979_state rng; + init_rfc6979(priv_key, hash, curve, &rng); + + for (i = 0; i < 10000; i++) { + // generate K deterministically + generate_k_rfc6979(&k, &rng); + // if k is too big or too small, we don't like it + if (bn_is_zero(&k) || !bn_is_less(&k, &curve->order)) { + continue; + } + + schnorr_sign_pair sign; + if (zil_schnorr_sign_k(curve, priv_key, &k, msg, msg_len, &sign) != 0) { + continue; + } + + // we're done + memcpy(sig, sign.r, 32); + memcpy(sig + 32, sign.s, 32); + + memzero(&k, sizeof(k)); + memzero(&rng, sizeof(rng)); + memzero(&sign, sizeof(sign)); + return 0; + } + + // Too many retries without a valid signature + // -> fail with an error + memzero(&k, sizeof(k)); + memzero(&rng, sizeof(rng)); + return -1; +} + +// r = H(Q, kpub, m) +static void calc_r(const curve_point *Q, const uint8_t pub_key[33], + const uint8_t *msg, const uint32_t msg_len, bignum256 *r) { + uint8_t Q_compress[33]; + compress_coords(Q, Q_compress); + + SHA256_CTX ctx; + uint8_t digest[SHA256_DIGEST_LENGTH]; + sha256_Init(&ctx); + sha256_Update(&ctx, Q_compress, 33); + sha256_Update(&ctx, pub_key, 33); + sha256_Update(&ctx, msg, msg_len); + sha256_Final(&ctx, digest); + + // Convert the raw bigendian 256 bit value to a normalized, partly reduced bignum + bn_read_be(digest, r); +} + +// Returns 0 if signing succeeded +int zil_schnorr_sign_k(const ecdsa_curve *curve, const uint8_t *priv_key, + const bignum256 *k, const uint8_t *msg, const uint32_t msg_len, + schnorr_sign_pair *result) { + uint8_t pub_key[33]; + curve_point Q; + bignum256 private_key_scalar; + bignum256 r_temp; + bignum256 s_temp; + bignum256 r_kpriv_result; + + bn_read_be(priv_key, &private_key_scalar); + ecdsa_get_public_key33(curve, priv_key, pub_key); + + // Compute commitment Q = kG + point_multiply(curve, k, &curve->G, &Q); + + // Compute challenge r = H(Q, kpub, m) + calc_r(&Q, pub_key, msg, msg_len, &r_temp); + + // Fully reduce the bignum + bn_mod(&r_temp, &curve->order); + + // Convert the normalized, fully reduced bignum to a raw bigendian 256 bit value + bn_write_be(&r_temp, result->r); + + // Compute s = k - r*kpriv + bn_copy(&r_temp, &r_kpriv_result); + + // r*kpriv result is partly reduced + bn_multiply(&private_key_scalar, &r_kpriv_result, &curve->order); + + // k - r*kpriv result is normalized but not reduced + bn_subtractmod(k, &r_kpriv_result, &s_temp, &curve->order); + + // Partly reduce the result + bn_fast_mod(&s_temp, &curve->order); + + // Fully reduce the result + bn_mod(&s_temp, &curve->order); + + // Convert the normalized, fully reduced bignum to a raw bigendian 256 bit value + bn_write_be(&s_temp, result->s); + + if (bn_is_zero(&r_temp) || bn_is_zero(&s_temp)) return 1; + + return 0; +} + +int zil_schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key, const uint8_t *sig, const uint8_t *msg, const uint32_t msg_len) +{ + schnorr_sign_pair sign; + + memcpy(sign.r, sig, 32); + memcpy(sign.s, sig + 32, 32); + + return zil_schnorr_verify_pair(curve, pub_key, msg, msg_len, &sign); +} + +// Returns 0 if verification succeeded +int zil_schnorr_verify_pair(const ecdsa_curve *curve, const uint8_t *pub_key, + const uint8_t *msg, const uint32_t msg_len, + const schnorr_sign_pair *sign) { + curve_point pub_key_point; + curve_point sG, Q; + bignum256 r_temp; + bignum256 s_temp; + bignum256 r_computed; + + if (msg_len == 0) return 1; + + // Convert the raw bigendian 256 bit values to normalized, partly reduced bignums + bn_read_be(sign->r, &r_temp); + bn_read_be(sign->s, &s_temp); + + // Check if r,s are in [1, ..., order-1] + if (bn_is_zero(&r_temp)) return 2; + if (bn_is_zero(&s_temp)) return 3; + if (bn_is_less(&curve->order, &r_temp)) return 4; + if (bn_is_less(&curve->order, &s_temp)) return 5; + if (bn_is_equal(&curve->order, &r_temp)) return 6; + if (bn_is_equal(&curve->order, &s_temp)) return 7; + + if (!ecdsa_read_pubkey(curve, pub_key, &pub_key_point)) { + return 8; + } + + // Compute Q = sG + r*kpub + point_multiply(curve, &s_temp, &curve->G, &sG); + point_multiply(curve, &r_temp, &pub_key_point, &Q); + point_add(curve, &sG, &Q); + + // Compute r' = H(Q, kpub, m) + calc_r(&Q, pub_key, msg, msg_len, &r_computed); + + // Fully reduce the bignum + bn_mod(&r_computed, &curve->order); + + // Check r == r' + if (bn_is_equal(&r_temp, &r_computed)) return 0; // success + + return 10; +} diff --git a/trezor-crypto/include/TrezorCrypto/aes.h b/trezor-crypto/include/TrezorCrypto/aes.h index 615f19db0da..62e9518a6a6 100644 --- a/trezor-crypto/include/TrezorCrypto/aes.h +++ b/trezor-crypto/include/TrezorCrypto/aes.h @@ -85,7 +85,7 @@ typedef union uint8_t b[4]; } aes_inf; -#ifdef _MSC_VER +#ifdef _MSC_VER //win Ignore the warning 4324 # pragma warning( disable : 4324 ) #endif diff --git a/trezor-crypto/include/TrezorCrypto/bip32.h b/trezor-crypto/include/TrezorCrypto/bip32.h index 99b35762469..4b698bcc600 100644 --- a/trezor-crypto/include/TrezorCrypto/bip32.h +++ b/trezor-crypto/include/TrezorCrypto/bip32.h @@ -79,14 +79,6 @@ int hdnode_from_seed(const uint8_t *seed, int seed_len, const char *curve, int hdnode_private_ckd(HDNode *inout, uint32_t i); -#if USE_CARDANO -int hdnode_private_ckd_cardano(HDNode *inout, uint32_t i); -int hdnode_from_seed_cardano(const uint8_t *seed, int seed_len, HDNode *out); -int hdnode_from_entropy_cardano_icarus(const uint8_t *pass, int pass_len, - const uint8_t *seed, int seed_len, - HDNode *out); -#endif - int hdnode_public_ckd_cp(const ecdsa_curve *curve, const curve_point *parent, const uint8_t *parent_chain_code, uint32_t i, curve_point *child, uint8_t *child_chain_code); @@ -101,13 +93,14 @@ void hdnode_public_ckd_address_optimized(const curve_point *pub, int addrsize, int addrformat); #if USE_BIP32_CACHE +void bip32_cache_clear(void); int hdnode_private_ckd_cached(HDNode *inout, const uint32_t *i, size_t i_count, uint32_t *fingerprint); #endif uint32_t hdnode_fingerprint(HDNode *node); -void hdnode_fill_public_key(HDNode *node); +int hdnode_fill_public_key(HDNode *node); #if USE_ETHEREUM int hdnode_get_ethereum_pubkeyhash(const HDNode *node, uint8_t *pubkeyhash); @@ -151,9 +144,9 @@ int hdnode_deserialize_private(const char *str, uint32_t version, const char *curve, HDNode *node, uint32_t *fingerprint); -void hdnode_get_address_raw(HDNode *node, uint32_t version, uint8_t *addr_raw); -void hdnode_get_address(HDNode *node, uint32_t version, char *addr, - int addrsize); +int hdnode_get_address_raw(HDNode *node, uint32_t version, uint8_t *addr_raw); +int hdnode_get_address(HDNode *node, uint32_t version, char *addr, + int addrsize); const curve_info *get_curve_by_name(const char *curve_name); diff --git a/trezor-crypto/include/TrezorCrypto/bip39.h b/trezor-crypto/include/TrezorCrypto/bip39.h index cd073a2d62e..46ec5b0229c 100644 --- a/trezor-crypto/include/TrezorCrypto/bip39.h +++ b/trezor-crypto/include/TrezorCrypto/bip39.h @@ -24,25 +24,30 @@ #ifndef __BIP39_H__ #define __BIP39_H__ -#include -#include - #ifdef __cplusplus extern "C" { #endif -#define BIP39_WORDS 2048 +#include +#include + +#include + +#define BIP39_WORD_COUNT 2048 #define BIP39_PBKDF2_ROUNDS 2048 -#define BIP39_MAX_WORDS 24 // [wallet-core] -#define BIP39_MAX_WORD_LENGTH 9 // [wallet-core] +#if USE_BIP39_CACHE +void bip39_cache_clear(void); +#endif + +// [wallet-core] +#define BIP39_MAX_WORDS 24 +#define BIP39_MAX_WORD_LENGTH 9 // [wallet-core] Added output buffer -const char *mnemonic_generate(int strength, char *buf, int buflen); // strength in bits -// [wallet-core] Added output buffer +const char *mnemonic_generate(int strength, char *buf, int buflen); // strength in bits const char *mnemonic_from_data(const uint8_t *data, int datalen, char *buf, int buflen); -// [wallet-core] No longer used -//void mnemonic_clear(void); +void mnemonic_clear(void); int mnemonic_check(const char *mnemonic); @@ -54,13 +59,13 @@ void mnemonic_to_seed(const char *mnemonic, const char *passphrase, void (*progress_callback)(uint32_t current, uint32_t total)); -#ifdef __cplusplus -} /* extern "C" */ -#endif - int mnemonic_find_word(const char *word); const char *mnemonic_complete_word(const char *prefix, int len); const char *mnemonic_get_word(int index); uint32_t mnemonic_word_completion_mask(const char *prefix, int len); +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif diff --git a/trezor-crypto/include/TrezorCrypto/cardano.h b/trezor-crypto/include/TrezorCrypto/cardano.h new file mode 100644 index 00000000000..3e9986c52bd --- /dev/null +++ b/trezor-crypto/include/TrezorCrypto/cardano.h @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2013-2021 SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __CARDANO_H__ +#define __CARDANO_H__ + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#include +#include +#include +#include + +#if USE_CARDANO + +#define CARDANO_SECRET_LENGTH 96 +#define CARDANO_ICARUS_PBKDF2_ROUNDS 4096 + +extern const curve_info ed25519_cardano_info; + +int hdnode_private_ckd_cardano(HDNode *inout, uint32_t i); + +int secret_from_entropy_cardano_icarus( + const uint8_t *pass, int pass_len, const uint8_t *entropy, int entropy_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH], + void (*progress_callback)(uint32_t current, uint32_t total)); +int secret_from_seed_cardano_ledger(const uint8_t *seed, int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]); +int secret_from_seed_cardano_slip23(const uint8_t *seed, int seed_len, + uint8_t secret_out[CARDANO_SECRET_LENGTH]); + +int hdnode_from_secret_cardano(const uint8_t secret[CARDANO_SECRET_LENGTH], + HDNode *out); + +#endif // USE_CARDANO + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif // __CARDANO_H__ diff --git a/trezor-crypto/include/TrezorCrypto/chacha20poly1305/ecrypt-sync.h b/trezor-crypto/include/TrezorCrypto/chacha20poly1305/ecrypt-sync.h index 39be5c9bc88..efce9dde273 100644 --- a/trezor-crypto/include/TrezorCrypto/chacha20poly1305/ecrypt-sync.h +++ b/trezor-crypto/include/TrezorCrypto/chacha20poly1305/ecrypt-sync.h @@ -90,12 +90,21 @@ void ECRYPT_keysetup( * IV setup. After having called ECRYPT_keysetup(), the user is * allowed to call ECRYPT_ivsetup() different times in order to * encrypt/decrypt different messages with the same key but different - * IV's. + * IV's. ECRYPT_ivsetup() also sets block counter to zero. */ void ECRYPT_ivsetup( ECRYPT_ctx* ctx, const u8* iv); +/* + * Block counter setup. It is used only for special purposes, + * since block counter is usually initialized with ECRYPT_ivsetup. + * ECRYPT_ctrsetup has to be called after ECRYPT_ivsetup. + */ +void ECRYPT_ctrsetup( + ECRYPT_ctx* ctx, + const u8* ctr); + /* * Encryption/decryption of arbitrary length messages. * diff --git a/trezor-crypto/include/TrezorCrypto/chacha_drbg.h b/trezor-crypto/include/TrezorCrypto/chacha_drbg.h index 416ace82414..0676d126cd3 100644 --- a/trezor-crypto/include/TrezorCrypto/chacha_drbg.h +++ b/trezor-crypto/include/TrezorCrypto/chacha_drbg.h @@ -21,23 +21,34 @@ #define __CHACHA_DRBG__ #include +#include -// Very fast deterministic random bit generator inspired by CTR_DRBG in NIST SP -// 800-90A +// A very fast deterministic random bit generator based on CTR_DRBG in NIST SP +// 800-90A. Chacha is used instead of a block cipher in the counter mode, SHA256 +// is used as a derivation function. The highest supported security strength is +// at least 256 bits. Reseeding is left up to caller. -#define CHACHA_DRBG_KEY_LENGTH 16 -#define CHACHA_DRBG_IV_LENGTH 8 -#define CHACHA_DRBG_SEED_LENGTH (CHACHA_DRBG_KEY_LENGTH + CHACHA_DRBG_IV_LENGTH) +// Length of inputs of chacha_drbg_init (entropy and nonce) or +// chacha_drbg_reseed (entropy and additional_input) that fill exactly +// block_count blocks of hash function in derivation_function. There is no need +// the input to have this length, it's just an optimalization. +#define CHACHA_DRBG_OPTIMAL_RESEED_LENGTH(block_count) \ + ((block_count)*SHA256_BLOCK_LENGTH - 1 - 4 - 9) +// 1 = sizeof(counter), 4 = sizeof(output_length) in +// derivation_function, 9 is length of SHA256 padding of message +// aligned to bytes typedef struct _CHACHA_DRBG_CTX { ECRYPT_ctx chacha_ctx; uint32_t reseed_counter; } CHACHA_DRBG_CTX; -void chacha_drbg_init(CHACHA_DRBG_CTX *ctx, - const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]); -void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx, - const uint8_t entropy[CHACHA_DRBG_SEED_LENGTH]); +void chacha_drbg_init(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy, + size_t entropy_length, const uint8_t *nonce, + size_t nonce_length); void chacha_drbg_generate(CHACHA_DRBG_CTX *ctx, uint8_t *output, - uint8_t output_length); + size_t output_length); +void chacha_drbg_reseed(CHACHA_DRBG_CTX *ctx, const uint8_t *entropy, + size_t entropy_length, const uint8_t *additional_input, + size_t additional_input_length); #endif // __CHACHA_DRBG__ diff --git a/trezor-crypto/include/TrezorCrypto/curves.h b/trezor-crypto/include/TrezorCrypto/curves.h index d65d1fe953f..d8d423563c6 100644 --- a/trezor-crypto/include/TrezorCrypto/curves.h +++ b/trezor-crypto/include/TrezorCrypto/curves.h @@ -35,16 +35,16 @@ extern const char SECP256K1_GROESTL_NAME[]; extern const char SECP256K1_SMART_NAME[]; extern const char NIST256P1_NAME[]; extern const char ED25519_NAME[]; -// [wallet-core] +extern const char ED25519_SEED_NAME[]; extern const char ED25519_CARDANO_NAME[]; -// [wallet-core] -extern const char ED25519_BLAKE2B_NANO_NAME[]; extern const char ED25519_SHA3_NAME[]; #if USE_KECCAK extern const char ED25519_KECCAK_NAME[]; #endif extern const char CURVE25519_NAME[]; +extern const char ED25519_BLAKE2B_NANO_NAME[]; // [wallet-core] + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/trezor-crypto/include/TrezorCrypto/ecdsa.h b/trezor-crypto/include/TrezorCrypto/ecdsa.h index 4d9046d5c82..48033642325 100644 --- a/trezor-crypto/include/TrezorCrypto/ecdsa.h +++ b/trezor-crypto/include/TrezorCrypto/ecdsa.h @@ -70,14 +70,14 @@ void point_copy(const curve_point *cp1, curve_point *cp2); void point_add(const ecdsa_curve *curve, const curve_point *cp1, curve_point *cp2); void point_double(const ecdsa_curve *curve, curve_point *cp); -void point_multiply(const ecdsa_curve *curve, const bignum256 *k, - const curve_point *p, curve_point *res); +int point_multiply(const ecdsa_curve *curve, const bignum256 *k, + const curve_point *p, curve_point *res); void point_set_infinity(curve_point *p); int point_is_infinity(const curve_point *p); int point_is_equal(const curve_point *p, const curve_point *q); int point_is_negative_of(const curve_point *p, const curve_point *q); -void scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, - curve_point *res); +int scalar_multiply(const ecdsa_curve *curve, const bignum256 *k, + curve_point *res); int ecdh_multiply(const ecdsa_curve *curve, const uint8_t *priv_key, const uint8_t *pub_key, uint8_t *session_key); void compress_coords(const curve_point *cp, uint8_t *compressed); @@ -93,10 +93,10 @@ int ecdsa_sign(const ecdsa_curve *curve, HasherType hasher_sign, int ecdsa_sign_digest(const ecdsa_curve *curve, const uint8_t *priv_key, const uint8_t *digest, uint8_t *sig, uint8_t *pby, int (*is_canonical)(uint8_t by, uint8_t sig[64])); -void ecdsa_get_public_key33(const ecdsa_curve *curve, const uint8_t *priv_key, - uint8_t *pub_key); -void ecdsa_get_public_key65(const ecdsa_curve *curve, const uint8_t *priv_key, - uint8_t *pub_key); +int ecdsa_get_public_key33(const ecdsa_curve *curve, const uint8_t *priv_key, + uint8_t *pub_key); +int ecdsa_get_public_key65(const ecdsa_curve *curve, const uint8_t *priv_key, + uint8_t *pub_key); void ecdsa_get_pubkeyhash(const uint8_t *pub_key, HasherType hasher_pubkey, uint8_t *pubkeyhash); void ecdsa_get_address_raw(const uint8_t *pub_key, uint32_t version, @@ -130,10 +130,6 @@ int ecdsa_recover_pub_from_sig(const ecdsa_curve *curve, uint8_t *pub_key, int ecdsa_sig_to_der(const uint8_t *sig, uint8_t *der); int ecdsa_sig_from_der(const uint8_t *der, size_t der_len, uint8_t sig[64]); -// [wallet-core] -int zil_schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key, const uint8_t *msg, const uint32_t msg_len, uint8_t *sig); -int zil_schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key, const uint8_t *sig, const uint8_t *msg, const uint32_t msg_len); - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-blake2b.h b/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-blake2b.h index 9dddc5f7401..f9bd619e93c 100644 --- a/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-blake2b.h +++ b/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-blake2b.h @@ -10,7 +10,7 @@ extern "C" { void ed25519_publickey_blake2b(const ed25519_secret_key sk, ed25519_public_key pk); int ed25519_sign_open_blake2b(const unsigned char *m, size_t mlen, const ed25519_public_key pk, const ed25519_signature RS); -void ed25519_sign_blake2b(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_public_key pk, ed25519_signature RS); +void ed25519_sign_blake2b(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, ed25519_signature RS); int ed25519_scalarmult_blake2b(ed25519_public_key res, const ed25519_secret_key sk, const ed25519_public_key pk); diff --git a/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-donna-portable.h b/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-donna-portable.h index a169fa41779..33dad64dcf1 100644 --- a/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-donna-portable.h +++ b/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-donna-portable.h @@ -10,12 +10,14 @@ extern "C" { #define DONNA_INLINE #undef ALIGN + #ifdef _MSC_VER #define ALIGN(x) __declspec(align(x)) #else #define ALIGN(x) __attribute__((aligned(x))) #endif + static inline void U32TO8_LE(unsigned char *p, const uint32_t v) { p[0] = (unsigned char)(v ); p[1] = (unsigned char)(v >> 8); diff --git a/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-keccak.h b/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-keccak.h index fa63770e92b..58fd8355ae4 100644 --- a/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-keccak.h +++ b/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-keccak.h @@ -10,7 +10,7 @@ extern "C" { void ed25519_publickey_keccak(const ed25519_secret_key sk, ed25519_public_key pk); int ed25519_sign_open_keccak(const unsigned char *m, size_t mlen, const ed25519_public_key pk, const ed25519_signature RS); -void ed25519_sign_keccak(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_public_key pk, ed25519_signature RS); +void ed25519_sign_keccak(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, ed25519_signature RS); int ed25519_scalarmult_keccak(ed25519_public_key res, const ed25519_secret_key sk, const ed25519_public_key pk); diff --git a/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-sha3.h b/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-sha3.h index e8a62d0dca3..3adcf84891f 100644 --- a/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-sha3.h +++ b/trezor-crypto/include/TrezorCrypto/ed25519-donna/ed25519-sha3.h @@ -10,7 +10,7 @@ extern "C" { void ed25519_publickey_sha3(const ed25519_secret_key sk, ed25519_public_key pk); int ed25519_sign_open_sha3(const unsigned char *m, size_t mlen, const ed25519_public_key pk, const ed25519_signature RS); -void ed25519_sign_sha3(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_public_key pk, ed25519_signature RS); +void ed25519_sign_sha3(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, ed25519_signature RS); int ed25519_scalarmult_sha3(ed25519_public_key res, const ed25519_secret_key sk, const ed25519_public_key pk); diff --git a/trezor-crypto/include/TrezorCrypto/ed25519.h b/trezor-crypto/include/TrezorCrypto/ed25519.h index 78a27a279ed..6b4c098b963 100644 --- a/trezor-crypto/include/TrezorCrypto/ed25519.h +++ b/trezor-crypto/include/TrezorCrypto/ed25519.h @@ -16,15 +16,11 @@ typedef unsigned char curve25519_key[32]; typedef unsigned char ed25519_cosi_signature[32]; void ed25519_publickey(const ed25519_secret_key sk, ed25519_public_key pk); -#if USE_CARDANO -void ed25519_publickey_ext(const ed25519_secret_key sk, const ed25519_secret_key skext, ed25519_public_key pk); -#endif +void ed25519_publickey_ext(const ed25519_secret_key extsk, ed25519_public_key pk); int ed25519_sign_open(const unsigned char *m, size_t mlen, const ed25519_public_key pk, const ed25519_signature RS); -void ed25519_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_public_key pk, ed25519_signature RS); -#if USE_CARDANO -void ed25519_sign_ext(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_secret_key skext, const ed25519_public_key pk, ed25519_signature RS); -#endif +void ed25519_sign(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, ed25519_signature RS); +void ed25519_sign_ext(const unsigned char *m, size_t mlen, const ed25519_secret_key sk, const ed25519_secret_key skext, ed25519_signature RS); int ed25519_scalarmult(ed25519_public_key res, const ed25519_secret_key sk, const ed25519_public_key pk); diff --git a/trezor-crypto/include/TrezorCrypto/groestl_internal.h b/trezor-crypto/include/TrezorCrypto/groestl_internal.h index a6cd8df4ba5..84587358e4a 100644 --- a/trezor-crypto/include/TrezorCrypto/groestl_internal.h +++ b/trezor-crypto/include/TrezorCrypto/groestl_internal.h @@ -58,8 +58,8 @@ #error This code requires 8-bit bytes #endif -#if (defined __STDC__ && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) - +//win #if defined __STDC__ && __STDC_VERSION__ >= 199901L +#if (defined __STDC__ && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER) // win #include typedef uint32_t sph_u32; @@ -116,6 +116,7 @@ typedef int64_t sph_s64; #endif +#define SPH_LITTLE_ENDIAN 1 // [wallet-core] #if defined SPH_DETECT_LITTLE_ENDIAN && !defined SPH_LITTLE_ENDIAN #define SPH_LITTLE_ENDIAN SPH_DETECT_LITTLE_ENDIAN diff --git a/trezor-crypto/include/TrezorCrypto/nist256p1.h b/trezor-crypto/include/TrezorCrypto/nist256p1.h index 9464ca5b0a5..0c0a743d566 100644 --- a/trezor-crypto/include/TrezorCrypto/nist256p1.h +++ b/trezor-crypto/include/TrezorCrypto/nist256p1.h @@ -29,6 +29,9 @@ #include "bip32.h" #include "ecdsa.h" +//extern const ecdsa_curve nist256p1; +//extern const curve_info nist256p1_info; +//win #ifdef __cplusplus extern "C" { #endif @@ -39,5 +42,4 @@ extern const curve_info nist256p1_info; #ifdef __cplusplus } /* extern "C" */ #endif - #endif diff --git a/trezor-crypto/include/TrezorCrypto/rand.h b/trezor-crypto/include/TrezorCrypto/rand.h index 40844b6848c..7171a9ad860 100644 --- a/trezor-crypto/include/TrezorCrypto/rand.h +++ b/trezor-crypto/include/TrezorCrypto/rand.h @@ -31,10 +31,12 @@ extern "C" { #endif +//win // [wallet-core] Reference counted init and release void *random_init(void); void random_release(void); + uint32_t random32(void); void random_buffer(uint8_t *buf, size_t len); diff --git a/trezor-crypto/include/TrezorCrypto/rfc6979.h b/trezor-crypto/include/TrezorCrypto/rfc6979.h index 3e409535093..e4cb9ff049f 100644 --- a/trezor-crypto/include/TrezorCrypto/rfc6979.h +++ b/trezor-crypto/include/TrezorCrypto/rfc6979.h @@ -27,13 +27,14 @@ #include #include "bignum.h" +#include "ecdsa.h" #include "hmac_drbg.h" // rfc6979 pseudo random number generator state typedef HMAC_DRBG_CTX rfc6979_state; void init_rfc6979(const uint8_t *priv_key, const uint8_t *hash, - rfc6979_state *rng); + const ecdsa_curve *curve, rfc6979_state *rng); void generate_rfc6979(uint8_t rnd[32], rfc6979_state *rng); void generate_k_rfc6979(bignum256 *k, rfc6979_state *rng); diff --git a/trezor-crypto/include/TrezorCrypto/schnorr.h b/trezor-crypto/include/TrezorCrypto/schnorr.h deleted file mode 100644 index 4091c807446..00000000000 --- a/trezor-crypto/include/TrezorCrypto/schnorr.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2019 Anatolii Kurotych - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES - * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef __SCHNORR_H__ -#define __SCHNORR_H__ - -#include - -#if defined(__cplusplus) -extern "C" -{ -#endif - -// result of sign operation -typedef struct { - uint8_t r[32]; - uint8_t s[32]; -} schnorr_sign_pair; - -// sign/verify returns 0 if operation succeeded - -// k is a random from [1, ..., order-1] -int schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key, - const bignum256 *k, const uint8_t *msg, const uint32_t msg_len, - schnorr_sign_pair *result); -int schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key, - const uint8_t *msg, const uint32_t msg_len, - const schnorr_sign_pair *sign); -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/trezor-crypto/include/TrezorCrypto/slip39.h b/trezor-crypto/include/TrezorCrypto/slip39.h new file mode 100644 index 00000000000..08883edf2b5 --- /dev/null +++ b/trezor-crypto/include/TrezorCrypto/slip39.h @@ -0,0 +1,47 @@ +/** + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SLIP39_H__ +#define __SLIP39_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +const char* get_word(uint16_t index); + +bool word_index(uint16_t* index, const char* word, uint8_t word_length); + +uint16_t slip39_word_completion_mask(uint16_t prefix); + +const char* button_sequence_to_word(uint16_t prefix); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/trezor-crypto/include/TrezorCrypto/slip39_wordlist.h b/trezor-crypto/include/TrezorCrypto/slip39_wordlist.h new file mode 100644 index 00000000000..3464aae9412 --- /dev/null +++ b/trezor-crypto/include/TrezorCrypto/slip39_wordlist.h @@ -0,0 +1,1246 @@ +/** + * This file is part of the TREZOR project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __SLIP39_WORDLIST_H__ +#define __SLIP39_WORDLIST_H__ + +#include + +#define WORDS_COUNT 1024 + +static const char* const slip39_wordlist[WORDS_COUNT] = { + "academic", "acid", "acne", "acquire", "acrobat", "activity", + "actress", "adapt", "adequate", "adjust", "admit", "adorn", + "adult", "advance", "advocate", "afraid", "again", "agency", + "agree", "aide", "aircraft", "airline", "airport", "ajar", + "alarm", "album", "alcohol", "alien", "alive", "alpha", + "already", "alto", "aluminum", "always", "amazing", "ambition", + "amount", "amuse", "analysis", "anatomy", "ancestor", "ancient", + "angel", "angry", "animal", "answer", "antenna", "anxiety", + "apart", "aquatic", "arcade", "arena", "argue", "armed", + "artist", "artwork", "aspect", "auction", "august", "aunt", + "average", "aviation", "avoid", "award", "away", "axis", + "axle", "beam", "beard", "beaver", "become", "bedroom", + "behavior", "being", "believe", "belong", "benefit", "best", + "beyond", "bike", "biology", "birthday", "bishop", "black", + "blanket", "blessing", "blimp", "blind", "blue", "body", + "bolt", "boring", "born", "both", "boundary", "bracelet", + "branch", "brave", "breathe", "briefing", "broken", "brother", + "browser", "bucket", "budget", "building", "bulb", "bulge", + "bumpy", "bundle", "burden", "burning", "busy", "buyer", + "cage", "calcium", "camera", "campus", "canyon", "capacity", + "capital", "capture", "carbon", "cards", "careful", "cargo", + "carpet", "carve", "category", "cause", "ceiling", "center", + "ceramic", "champion", "change", "charity", "check", "chemical", + "chest", "chew", "chubby", "cinema", "civil", "class", + "clay", "cleanup", "client", "climate", "clinic", "clock", + "clogs", "closet", "clothes", "club", "cluster", "coal", + "coastal", "coding", "column", "company", "corner", "costume", + "counter", "course", "cover", "cowboy", "cradle", "craft", + "crazy", "credit", "cricket", "criminal", "crisis", "critical", + "crowd", "crucial", "crunch", "crush", "crystal", "cubic", + "cultural", "curious", "curly", "custody", "cylinder", "daisy", + "damage", "dance", "darkness", "database", "daughter", "deadline", + "deal", "debris", "debut", "decent", "decision", "declare", + "decorate", "decrease", "deliver", "demand", "density", "deny", + "depart", "depend", "depict", "deploy", "describe", "desert", + "desire", "desktop", "destroy", "detailed", "detect", "device", + "devote", "diagnose", "dictate", "diet", "dilemma", "diminish", + "dining", "diploma", "disaster", "discuss", "disease", "dish", + "dismiss", "display", "distance", "dive", "divorce", "document", + "domain", "domestic", "dominant", "dough", "downtown", "dragon", + "dramatic", "dream", "dress", "drift", "drink", "drove", + "drug", "dryer", "duckling", "duke", "duration", "dwarf", + "dynamic", "early", "earth", "easel", "easy", "echo", + "eclipse", "ecology", "edge", "editor", "educate", "either", + "elbow", "elder", "election", "elegant", "element", "elephant", + "elevator", "elite", "else", "email", "emerald", "emission", + "emperor", "emphasis", "employer", "empty", "ending", "endless", + "endorse", "enemy", "energy", "enforce", "engage", "enjoy", + "enlarge", "entrance", "envelope", "envy", "epidemic", "episode", + "equation", "equip", "eraser", "erode", "escape", "estate", + "estimate", "evaluate", "evening", "evidence", "evil", "evoke", + "exact", "example", "exceed", "exchange", "exclude", "excuse", + "execute", "exercise", "exhaust", "exotic", "expand", "expect", + "explain", "express", "extend", "extra", "eyebrow", "facility", + "fact", "failure", "faint", "fake", "false", "family", + "famous", "fancy", "fangs", "fantasy", "fatal", "fatigue", + "favorite", "fawn", "fiber", "fiction", "filter", "finance", + "findings", "finger", "firefly", "firm", "fiscal", "fishing", + "fitness", "flame", "flash", "flavor", "flea", "flexible", + "flip", "float", "floral", "fluff", "focus", "forbid", + "force", "forecast", "forget", "formal", "fortune", "forward", + "founder", "fraction", "fragment", "frequent", "freshman", "friar", + "fridge", "friendly", "frost", "froth", "frozen", "fumes", + "funding", "furl", "fused", "galaxy", "game", "garbage", + "garden", "garlic", "gasoline", "gather", "general", "genius", + "genre", "genuine", "geology", "gesture", "glad", "glance", + "glasses", "glen", "glimpse", "goat", "golden", "graduate", + "grant", "grasp", "gravity", "gray", "greatest", "grief", + "grill", "grin", "grocery", "gross", "group", "grownup", + "grumpy", "guard", "guest", "guilt", "guitar", "gums", + "hairy", "hamster", "hand", "hanger", "harvest", "have", + "havoc", "hawk", "hazard", "headset", "health", "hearing", + "heat", "helpful", "herald", "herd", "hesitate", "hobo", + "holiday", "holy", "home", "hormone", "hospital", "hour", + "huge", "human", "humidity", "hunting", "husband", "hush", + "husky", "hybrid", "idea", "identify", "idle", "image", + "impact", "imply", "improve", "impulse", "include", "income", + "increase", "index", "indicate", "industry", "infant", "inform", + "inherit", "injury", "inmate", "insect", "inside", "install", + "intend", "intimate", "invasion", "involve", "iris", "island", + "isolate", "item", "ivory", "jacket", "jerky", "jewelry", + "join", "judicial", "juice", "jump", "junction", "junior", + "junk", "jury", "justice", "kernel", "keyboard", "kidney", + "kind", "kitchen", "knife", "knit", "laden", "ladle", + "ladybug", "lair", "lamp", "language", "large", "laser", + "laundry", "lawsuit", "leader", "leaf", "learn", "leaves", + "lecture", "legal", "legend", "legs", "lend", "length", + "level", "liberty", "library", "license", "lift", "likely", + "lilac", "lily", "lips", "liquid", "listen", "literary", + "living", "lizard", "loan", "lobe", "location", "losing", + "loud", "loyalty", "luck", "lunar", "lunch", "lungs", + "luxury", "lying", "lyrics", "machine", "magazine", "maiden", + "mailman", "main", "makeup", "making", "mama", "manager", + "mandate", "mansion", "manual", "marathon", "march", "market", + "marvel", "mason", "material", "math", "maximum", "mayor", + "meaning", "medal", "medical", "member", "memory", "mental", + "merchant", "merit", "method", "metric", "midst", "mild", + "military", "mineral", "minister", "miracle", "mixed", "mixture", + "mobile", "modern", "modify", "moisture", "moment", "morning", + "mortgage", "mother", "mountain", "mouse", "move", "much", + "mule", "multiple", "muscle", "museum", "music", "mustang", + "nail", "national", "necklace", "negative", "nervous", "network", + "news", "nuclear", "numb", "numerous", "nylon", "oasis", + "obesity", "object", "observe", "obtain", "ocean", "often", + "olympic", "omit", "oral", "orange", "orbit", "order", + "ordinary", "organize", "ounce", "oven", "overall", "owner", + "paces", "pacific", "package", "paid", "painting", "pajamas", + "pancake", "pants", "papa", "paper", "parcel", "parking", + "party", "patent", "patrol", "payment", "payroll", "peaceful", + "peanut", "peasant", "pecan", "penalty", "pencil", "percent", + "perfect", "permit", "petition", "phantom", "pharmacy", "photo", + "phrase", "physics", "pickup", "picture", "piece", "pile", + "pink", "pipeline", "pistol", "pitch", "plains", "plan", + "plastic", "platform", "playoff", "pleasure", "plot", "plunge", + "practice", "prayer", "preach", "predator", "pregnant", "premium", + "prepare", "presence", "prevent", "priest", "primary", "priority", + "prisoner", "privacy", "prize", "problem", "process", "profile", + "program", "promise", "prospect", "provide", "prune", "public", + "pulse", "pumps", "punish", "puny", "pupal", "purchase", + "purple", "python", "quantity", "quarter", "quick", "quiet", + "race", "racism", "radar", "railroad", "rainbow", "raisin", + "random", "ranked", "rapids", "raspy", "reaction", "realize", + "rebound", "rebuild", "recall", "receiver", "recover", "regret", + "regular", "reject", "relate", "remember", "remind", "remove", + "render", "repair", "repeat", "replace", "require", "rescue", + "research", "resident", "response", "result", "retailer", "retreat", + "reunion", "revenue", "review", "reward", "rhyme", "rhythm", + "rich", "rival", "river", "robin", "rocky", "romantic", + "romp", "roster", "round", "royal", "ruin", "ruler", + "rumor", "sack", "safari", "salary", "salon", "salt", + "satisfy", "satoshi", "saver", "says", "scandal", "scared", + "scatter", "scene", "scholar", "science", "scout", "scramble", + "screw", "script", "scroll", "seafood", "season", "secret", + "security", "segment", "senior", "shadow", "shaft", "shame", + "shaped", "sharp", "shelter", "sheriff", "short", "should", + "shrimp", "sidewalk", "silent", "silver", "similar", "simple", + "single", "sister", "skin", "skunk", "slap", "slavery", + "sled", "slice", "slim", "slow", "slush", "smart", + "smear", "smell", "smirk", "smith", "smoking", "smug", + "snake", "snapshot", "sniff", "society", "software", "soldier", + "solution", "soul", "source", "space", "spark", "speak", + "species", "spelling", "spend", "spew", "spider", "spill", + "spine", "spirit", "spit", "spray", "sprinkle", "square", + "squeeze", "stadium", "staff", "standard", "starting", "station", + "stay", "steady", "step", "stick", "stilt", "story", + "strategy", "strike", "style", "subject", "submit", "sugar", + "suitable", "sunlight", "superior", "surface", "surprise", "survive", + "sweater", "swimming", "swing", "switch", "symbolic", "sympathy", + "syndrome", "system", "tackle", "tactics", "tadpole", "talent", + "task", "taste", "taught", "taxi", "teacher", "teammate", + "teaspoon", "temple", "tenant", "tendency", "tension", "terminal", + "testify", "texture", "thank", "that", "theater", "theory", + "therapy", "thorn", "threaten", "thumb", "thunder", "ticket", + "tidy", "timber", "timely", "ting", "tofu", "together", + "tolerate", "total", "toxic", "tracks", "traffic", "training", + "transfer", "trash", "traveler", "treat", "trend", "trial", + "tricycle", "trip", "triumph", "trouble", "true", "trust", + "twice", "twin", "type", "typical", "ugly", "ultimate", + "umbrella", "uncover", "undergo", "unfair", "unfold", "unhappy", + "union", "universe", "unkind", "unknown", "unusual", "unwrap", + "upgrade", "upstairs", "username", "usher", "usual", "valid", + "valuable", "vampire", "vanish", "various", "vegan", "velvet", + "venture", "verdict", "verify", "very", "veteran", "vexed", + "victim", "video", "view", "vintage", "violence", "viral", + "visitor", "visual", "vitamins", "vocal", "voice", "volume", + "voter", "voting", "walnut", "warmth", "warn", "watch", + "wavy", "wealthy", "weapon", "webcam", "welcome", "welfare", + "western", "width", "wildlife", "window", "wine", "wireless", + "wisdom", "withdraw", "wits", "wolf", "woman", "work", + "worthy", "wrap", "wrist", "writing", "wrote", "year", + "yelp", "yield", "yoga", "zero", +}; + +/** + * This array contains number representations of SLIP-39 words. + * These numbers are determined how the words were entered on a + * T9 keyboard with the following layout: + * ab (1) cd (2) ef (3) + * ghij (4) klm (5) nopq (6) + * rs (7) tuv (8) wxyz (9) + * + * Each word is uniquely defined by four buttons. + */ +static const struct { + uint16_t sequence; + uint16_t index; +} words_button_seq[WORDS_COUNT] = { + {1212, 0}, // academic + {1216, 7}, // adapt + {1236, 8}, // adequate + {1242, 1}, // acid + {1248, 9}, // adjust + {1254, 10}, // admit + {1263, 2}, // acne + {1267, 11}, // adorn + {1268, 3}, // acquire + {1276, 4}, // acrobat + {1281, 13}, // advance + {1284, 5}, // activity + {1285, 12}, // adult + {1286, 14}, // advocate + {1287, 6}, // actress + {1315, 67}, // beam + {1317, 68}, // beard + {1318, 69}, // beaver + {1326, 70}, // become + {1327, 71}, // bedroom + {1341, 72}, // behavior + {1346, 73}, // being + {1354, 74}, // believe + {1356, 75}, // belong + {1363, 76}, // benefit + {1371, 15}, // afraid + {1378, 77}, // best + {1396, 78}, // beyond + {1414, 16}, // again + {1417, 23}, // ajar + {1423, 19}, // aide + {1436, 17}, // agency + {1453, 79}, // bike + {1465, 80}, // biology + {1472, 20}, // aircraft + {1473, 18}, // agree + {1474, 82}, // bishop + {1475, 21}, // airline + {1476, 22}, // airport + {1478, 81}, // birthday + {1512, 83}, // black + {1514, 35}, // ambition + {1516, 84}, // blanket + {1517, 24}, // alarm + {1518, 25}, // album + {1519, 34}, // amazing + {1526, 26}, // alcohol + {1537, 85}, // blessing + {1543, 27}, // alien + {1545, 86}, // blimp + {1546, 87}, // blind + {1548, 28}, // alive + {1564, 29}, // alpha + {1568, 36}, // amount + {1573, 30}, // already + {1583, 88}, // blue + {1585, 32}, // aluminum + {1586, 31}, // alto + {1587, 37}, // amuse + {1591, 33}, // always + {1615, 38}, // analysis + {1617, 48}, // apart + {1618, 39}, // anatomy + {1623, 40}, // ancestor + {1624, 41}, // ancient + {1629, 89}, // body + {1643, 42}, // angel + {1645, 44}, // animal + {1647, 43}, // angry + {1658, 90}, // bolt + {1674, 91}, // boring + {1676, 92}, // born + {1679, 45}, // answer + {1681, 49}, // aquatic + {1683, 46}, // antenna + {1684, 93}, // both + {1686, 94}, // boundary + {1694, 47}, // anxiety + {1712, 95}, // bracelet + {1716, 96}, // branch + {1718, 97}, // brave + {1721, 50}, // arcade + {1731, 98}, // breathe + {1736, 51}, // arena + {1743, 99}, // briefing + {1748, 52}, // argue + {1753, 53}, // armed + {1763, 56}, // aspect + {1765, 100}, // broken + {1768, 101}, // brother + {1769, 102}, // browser + {1784, 54}, // artist + {1789, 55}, // artwork + {1824, 104}, // budget + {1825, 103}, // bucket + {1828, 57}, // auction + {1837, 60}, // average + {1841, 61}, // aviation + {1845, 105}, // building + {1848, 58}, // august + {1851, 106}, // bulb + {1854, 107}, // bulge + {1856, 108}, // bumpy + {1862, 109}, // bundle + {1864, 62}, // avoid + {1868, 59}, // aunt + {1872, 110}, // burden + {1876, 111}, // burning + {1879, 112}, // busy + {1893, 113}, // buyer + {1917, 63}, // award + {1919, 64}, // away + {1947, 65}, // axis + {1953, 66}, // axle + {2143, 114}, // cage + {2147, 185}, // daisy + {2151, 186}, // damage + {2152, 115}, // calcium + {2153, 116}, // camera + {2156, 117}, // campus + {2161, 119}, // capacity + {2162, 187}, // dance + {2164, 120}, // capital + {2168, 121}, // capture + {2169, 118}, // canyon + {2171, 122}, // carbon + {2172, 123}, // cards + {2173, 124}, // careful + {2174, 125}, // cargo + {2175, 188}, // darkness + {2176, 126}, // carpet + {2178, 127}, // carve + {2181, 189}, // database + {2183, 128}, // category + {2184, 190}, // daughter + {2187, 129}, // cause + {2312, 191}, // deadline + {2315, 192}, // deal + {2317, 193}, // debris + {2318, 194}, // debut + {2323, 195}, // decent + {2324, 196}, // decision + {2325, 197}, // declare + {2326, 198}, // decorate + {2327, 199}, // decrease + {2345, 130}, // ceiling + {2351, 201}, // demand + {2354, 200}, // deliver + {2361, 204}, // depart + {2363, 205}, // depend + {2364, 206}, // depict + {2365, 207}, // deploy + {2367, 202}, // density + {2368, 131}, // center + {2369, 203}, // deny + {2371, 132}, // ceramic + {2372, 208}, // describe + {2373, 209}, // desert + {2374, 210}, // desire + {2375, 211}, // desktop + {2378, 212}, // destroy + {2381, 213}, // detailed + {2383, 214}, // detect + {2384, 215}, // device + {2386, 216}, // devote + {2414, 217}, // diagnose + {2415, 133}, // champion + {2416, 134}, // change + {2417, 135}, // charity + {2428, 218}, // dictate + {2432, 136}, // check + {2435, 137}, // chemical + {2437, 138}, // chest + {2438, 219}, // diet + {2439, 139}, // chew + {2453, 220}, // dilemma + {2454, 221}, // diminish + {2463, 141}, // cinema + {2464, 222}, // dining + {2465, 223}, // diploma + {2471, 224}, // disaster + {2472, 225}, // discuss + {2473, 226}, // disease + {2474, 227}, // dish + {2475, 228}, // dismiss + {2476, 229}, // display + {2478, 230}, // distance + {2481, 140}, // chubby + {2483, 231}, // dive + {2484, 142}, // civil + {2486, 232}, // divorce + {2517, 143}, // class + {2519, 144}, // clay + {2531, 145}, // cleanup + {2543, 146}, // client + {2545, 147}, // climate + {2546, 148}, // clinic + {2562, 149}, // clock + {2564, 150}, // clogs + {2567, 151}, // closet + {2568, 152}, // clothes + {2581, 153}, // club + {2587, 154}, // cluster + {2615, 155}, // coal + {2617, 156}, // coastal + {2624, 157}, // coding + {2628, 233}, // document + {2651, 234}, // domain + {2653, 235}, // domestic + {2654, 236}, // dominant + {2656, 159}, // company + {2658, 158}, // column + {2676, 160}, // corner + {2678, 161}, // costume + {2683, 164}, // cover + {2684, 237}, // dough + {2686, 162}, // counter + {2687, 163}, // course + {2691, 165}, // cowboy + {2696, 238}, // downtown + {2712, 166}, // cradle + {2713, 167}, // craft + {2714, 239}, // dragon + {2715, 240}, // dramatic + {2719, 168}, // crazy + {2731, 241}, // dream + {2732, 169}, // credit + {2737, 242}, // dress + {2742, 170}, // cricket + {2743, 243}, // drift + {2745, 171}, // criminal + {2746, 244}, // drink + {2747, 172}, // crisis + {2748, 173}, // critical + {2768, 245}, // drove + {2769, 174}, // crowd + {2782, 175}, // crucial + {2784, 246}, // drug + {2786, 176}, // crunch + {2787, 177}, // crush + {2793, 247}, // dryer + {2797, 178}, // crystal + {2814, 179}, // cubic + {2825, 248}, // duckling + {2853, 249}, // duke + {2858, 180}, // cultural + {2871, 250}, // duration + {2874, 181}, // curious + {2875, 182}, // curly + {2878, 183}, // custody + {2917, 251}, // dwarf + {2954, 184}, // cylinder + {2961, 252}, // dynamic + {3124, 323}, // facility + {3128, 324}, // fact + {3145, 325}, // failure + {3146, 326}, // faint + {3153, 327}, // fake + {3154, 329}, // family + {3156, 330}, // famous + {3157, 328}, // false + {3162, 331}, // fancy + {3164, 332}, // fangs + {3168, 333}, // fantasy + {3173, 255}, // easel + {3175, 253}, // early + {3178, 254}, // earth + {3179, 256}, // easy + {3181, 334}, // fatal + {3184, 335}, // fatigue + {3186, 336}, // favorite + {3196, 337}, // fawn + {3243, 260}, // edge + {3246, 257}, // echo + {3248, 261}, // editor + {3254, 258}, // eclipse + {3265, 259}, // ecology + {3282, 262}, // educate + {3413, 338}, // fiber + {3428, 339}, // fiction + {3458, 340}, // filter + {3461, 341}, // finance + {3462, 342}, // findings + {3464, 343}, // finger + {3472, 346}, // fiscal + {3473, 344}, // firefly + {3474, 347}, // fishing + {3475, 345}, // firm + {3484, 263}, // either + {3486, 348}, // fitness + {3514, 273}, // email + {3515, 349}, // flame + {3516, 264}, // elbow + {3517, 350}, // flash + {3518, 351}, // flavor + {3523, 265}, // elder + {3531, 352}, // flea + {3532, 266}, // election + {3534, 267}, // elegant + {3535, 268}, // element + {3536, 269}, // elephant + {3537, 274}, // emerald + {3538, 270}, // elevator + {3539, 353}, // flexible + {3546, 354}, // flip + {3547, 275}, // emission + {3548, 271}, // elite + {3561, 355}, // float + {3563, 276}, // emperor + {3564, 277}, // emphasis + {3565, 278}, // employer + {3567, 356}, // floral + {3568, 279}, // empty + {3573, 272}, // else + {3583, 357}, // fluff + {3624, 280}, // ending + {3625, 281}, // endless + {3626, 282}, // endorse + {3628, 358}, // focus + {3635, 283}, // enemy + {3636, 285}, // enforce + {3637, 284}, // energy + {3641, 286}, // engage + {3642, 292}, // epidemic + {3646, 287}, // enjoy + {3647, 293}, // episode + {3651, 288}, // enlarge + {3671, 359}, // forbid + {3672, 360}, // force + {3673, 361}, // forecast + {3674, 362}, // forget + {3675, 363}, // formal + {3678, 364}, // fortune + {3679, 365}, // forward + {3681, 294}, // equation + {3683, 290}, // envelope + {3684, 295}, // equip + {3686, 366}, // founder + {3687, 289}, // entrance + {3689, 291}, // envy + {3712, 367}, // fraction + {3714, 368}, // fragment + {3717, 296}, // eraser + {3721, 298}, // escape + {3736, 369}, // frequent + {3737, 370}, // freshman + {3741, 371}, // friar + {3742, 372}, // fridge + {3743, 373}, // friendly + {3762, 297}, // erode + {3767, 374}, // frost + {3768, 375}, // froth + {3769, 376}, // frozen + {3781, 299}, // estate + {3784, 300}, // estimate + {3815, 301}, // evaluate + {3836, 302}, // evening + {3842, 303}, // evidence + {3845, 304}, // evil + {3853, 377}, // fumes + {3862, 378}, // funding + {3865, 305}, // evoke + {3873, 380}, // fused + {3875, 379}, // furl + {3912, 306}, // exact + {3915, 307}, // example + {3923, 308}, // exceed + {3924, 309}, // exchange + {3925, 310}, // exclude + {3928, 311}, // excuse + {3931, 322}, // eyebrow + {3932, 312}, // execute + {3937, 313}, // exercise + {3941, 314}, // exhaust + {3961, 316}, // expand + {3963, 317}, // expect + {3965, 318}, // explain + {3967, 319}, // express + {3968, 315}, // exotic + {3983, 320}, // extend + {3987, 321}, // extra + {4125, 483}, // jacket + {4147, 420}, // hairy + {4151, 381}, // galaxy + {4153, 382}, // game + {4157, 421}, // hamster + {4162, 422}, // hand + {4164, 423}, // hanger + {4171, 383}, // garbage + {4172, 384}, // garden + {4175, 385}, // garlic + {4176, 386}, // gasoline + {4178, 424}, // harvest + {4183, 425}, // have + {4184, 387}, // gather + {4186, 426}, // havoc + {4191, 428}, // hazard + {4195, 427}, // hawk + {4231, 452}, // idea + {4236, 453}, // identify + {4253, 454}, // idle + {4312, 429}, // headset + {4315, 430}, // health + {4317, 431}, // hearing + {4318, 432}, // heat + {4356, 433}, // helpful + {4363, 388}, // general + {4364, 389}, // genius + {4365, 392}, // geology + {4367, 390}, // genre + {4368, 391}, // genuine + {4371, 434}, // herald + {4372, 435}, // herd + {4374, 436}, // hesitate + {4375, 484}, // jerky + {4378, 393}, // gesture + {4393, 485}, // jewelry + {4512, 394}, // glad + {4514, 455}, // image + {4516, 395}, // glance + {4517, 396}, // glasses + {4536, 397}, // glen + {4545, 398}, // glimpse + {4561, 456}, // impact + {4565, 457}, // imply + {4567, 458}, // improve + {4568, 459}, // impulse + {4616, 437}, // hobo + {4618, 399}, // goat + {4623, 463}, // index + {4624, 464}, // indicate + {4625, 460}, // include + {4626, 461}, // income + {4627, 462}, // increase + {4628, 465}, // industry + {4631, 466}, // infant + {4636, 467}, // inform + {4643, 468}, // inherit + {4646, 486}, // join + {4648, 469}, // injury + {4651, 470}, // inmate + {4652, 400}, // golden + {4653, 440}, // home + {4654, 438}, // holiday + {4659, 439}, // holy + {4673, 471}, // insect + {4674, 472}, // inside + {4675, 441}, // hormone + {4676, 442}, // hospital + {4678, 473}, // install + {4681, 476}, // invasion + {4683, 474}, // intend + {4684, 475}, // intimate + {4686, 477}, // involve + {4687, 443}, // hour + {4712, 401}, // graduate + {4716, 402}, // grant + {4717, 403}, // grasp + {4718, 404}, // gravity + {4719, 405}, // gray + {4731, 406}, // greatest + {4743, 407}, // grief + {4745, 408}, // grill + {4746, 409}, // grin + {4747, 478}, // iris + {4751, 479}, // island + {4762, 410}, // grocery + {4765, 480}, // isolate + {4767, 411}, // gross + {4768, 412}, // group + {4769, 413}, // grownup + {4785, 414}, // grumpy + {4817, 415}, // guard + {4824, 487}, // judicial + {4835, 481}, // item + {4837, 416}, // guest + {4842, 488}, // juice + {4843, 444}, // huge + {4845, 417}, // guilt + {4848, 418}, // guitar + {4851, 445}, // human + {4854, 446}, // humidity + {4856, 489}, // jump + {4857, 419}, // gums + {4862, 490}, // junction + {4864, 491}, // junior + {4865, 492}, // junk + {4867, 482}, // ivory + {4868, 447}, // hunting + {4871, 448}, // husband + {4874, 449}, // hush + {4875, 450}, // husky + {4878, 494}, // justice + {4879, 493}, // jury + {4917, 451}, // hybrid + {5123, 502}, // laden + {5124, 549}, // machine + {5125, 503}, // ladle + {5129, 504}, // ladybug + {5141, 550}, // magazine + {5142, 551}, // maiden + {5145, 552}, // mailman + {5146, 553}, // main + {5147, 505}, // lair + {5151, 556}, // mama + {5153, 554}, // makeup + {5154, 555}, // making + {5156, 506}, // lamp + {5161, 557}, // manager + {5162, 558}, // mandate + {5164, 507}, // language + {5167, 559}, // mansion + {5168, 560}, // manual + {5171, 561}, // marathon + {5172, 562}, // march + {5173, 509}, // laser + {5174, 508}, // large + {5175, 563}, // market + {5176, 565}, // mason + {5178, 564}, // marvel + {5183, 566}, // material + {5184, 567}, // math + {5186, 510}, // laundry + {5194, 568}, // maximum + {5196, 569}, // mayor + {5197, 511}, // lawsuit + {5312, 512}, // leader + {5313, 513}, // leaf + {5316, 570}, // meaning + {5317, 514}, // learn + {5318, 515}, // leaves + {5321, 571}, // medal + {5324, 572}, // medical + {5328, 516}, // lecture + {5341, 517}, // legal + {5343, 518}, // legend + {5347, 519}, // legs + {5351, 573}, // member + {5356, 574}, // memory + {5362, 520}, // lend + {5364, 521}, // length + {5368, 575}, // mental + {5372, 576}, // merchant + {5374, 577}, // merit + {5376, 495}, // kernel + {5383, 522}, // level + {5384, 578}, // method + {5387, 579}, // metric + {5391, 496}, // keyboard + {5413, 523}, // liberty + {5417, 524}, // library + {5423, 525}, // license + {5426, 497}, // kidney + {5427, 580}, // midst + {5438, 526}, // lift + {5451, 528}, // lilac + {5452, 581}, // mild + {5453, 527}, // likely + {5454, 582}, // military + {5459, 529}, // lily + {5462, 498}, // kind + {5463, 583}, // mineral + {5464, 584}, // minister + {5467, 530}, // lips + {5468, 531}, // liquid + {5471, 585}, // miracle + {5478, 532}, // listen + {5482, 499}, // kitchen + {5483, 533}, // literary + {5484, 534}, // living + {5491, 535}, // lizard + {5493, 586}, // mixed + {5498, 587}, // mixture + {5613, 537}, // lobe + {5614, 588}, // mobile + {5616, 536}, // loan + {5621, 538}, // location + {5623, 589}, // modern + {5624, 590}, // modify + {5643, 500}, // knife + {5647, 591}, // moisture + {5648, 501}, // knit + {5653, 592}, // moment + {5674, 539}, // losing + {5676, 593}, // morning + {5678, 594}, // mortgage + {5682, 540}, // loud + {5683, 598}, // move + {5684, 595}, // mother + {5686, 596}, // mountain + {5687, 597}, // mouse + {5691, 541}, // loyalty + {5824, 599}, // much + {5825, 542}, // luck + {5853, 600}, // mule + {5858, 601}, // multiple + {5861, 543}, // lunar + {5862, 544}, // lunch + {5864, 545}, // lungs + {5872, 602}, // muscle + {5873, 603}, // museum + {5874, 604}, // music + {5878, 605}, // mustang + {5898, 546}, // luxury + {5946, 547}, // lying + {5974, 548}, // lyrics + {6123, 636}, // paces + {6124, 637}, // pacific + {6125, 638}, // package + {6137, 618}, // obesity + {6141, 641}, // pajamas + {6142, 639}, // paid + {6143, 619}, // object + {6145, 606}, // nail + {6146, 640}, // painting + {6161, 644}, // papa + {6162, 642}, // pancake + {6163, 645}, // paper + {6168, 643}, // pants + {6172, 646}, // parcel + {6173, 620}, // observe + {6174, 617}, // oasis + {6175, 647}, // parking + {6178, 648}, // party + {6181, 621}, // obtain + {6183, 649}, // patent + {6184, 607}, // national + {6187, 650}, // patrol + {6195, 651}, // payment + {6197, 652}, // payroll + {6231, 622}, // ocean + {6312, 653}, // peaceful + {6316, 654}, // peanut + {6317, 655}, // peasant + {6321, 656}, // pecan + {6325, 608}, // necklace + {6341, 609}, // negative + {6361, 657}, // penalty + {6362, 658}, // pencil + {6372, 659}, // percent + {6373, 660}, // perfect + {6375, 661}, // permit + {6378, 610}, // nervous + {6383, 623}, // often + {6384, 662}, // petition + {6389, 611}, // network + {6397, 612}, // news + {6416, 663}, // phantom + {6417, 664}, // pharmacy + {6425, 668}, // pickup + {6428, 669}, // picture + {6432, 670}, // piece + {6453, 671}, // pile + {6463, 673}, // pipeline + {6465, 672}, // pink + {6468, 665}, // photo + {6471, 666}, // phrase + {6478, 674}, // pistol + {6482, 675}, // pitch + {6497, 667}, // physics + {6514, 676}, // plains + {6516, 677}, // plan + {6517, 678}, // plastic + {6518, 679}, // platform + {6519, 680}, // playoff + {6531, 681}, // pleasure + {6548, 625}, // omit + {6568, 682}, // plot + {6586, 683}, // plunge + {6595, 624}, // olympic + {6712, 684}, // practice + {6714, 628}, // orbit + {6715, 626}, // oral + {6716, 627}, // orange + {6719, 685}, // prayer + {6723, 629}, // order + {6724, 630}, // ordinary + {6731, 686}, // preach + {6732, 687}, // predator + {6734, 688}, // pregnant + {6735, 689}, // premium + {6736, 690}, // prepare + {6737, 691}, // presence + {6738, 692}, // prevent + {6741, 631}, // organize + {6743, 693}, // priest + {6745, 694}, // primary + {6746, 695}, // priority + {6747, 696}, // prisoner + {6748, 697}, // privacy + {6749, 698}, // prize + {6761, 699}, // problem + {6762, 700}, // process + {6763, 701}, // profile + {6764, 702}, // program + {6765, 703}, // promise + {6767, 704}, // prospect + {6768, 705}, // provide + {6786, 706}, // prune + {6815, 707}, // public + {6816, 716}, // quantity + {6817, 717}, // quarter + {6825, 613}, // nuclear + {6836, 633}, // oven + {6837, 634}, // overall + {6842, 718}, // quick + {6843, 719}, // quiet + {6851, 614}, // numb + {6853, 615}, // numerous + {6856, 709}, // pumps + {6857, 708}, // pulse + {6861, 712}, // pupal + {6862, 632}, // ounce + {6864, 710}, // punish + {6869, 711}, // puny + {6872, 713}, // purchase + {6876, 714}, // purple + {6956, 616}, // nylon + {6963, 635}, // owner + {6984, 715}, // python + {7121, 722}, // radar + {7123, 720}, // race + {7124, 721}, // racism + {7125, 775}, // sack + {7131, 776}, // safari + {7145, 723}, // railroad + {7146, 724}, // rainbow + {7147, 725}, // raisin + {7151, 777}, // salary + {7156, 778}, // salon + {7158, 779}, // salt + {7162, 726}, // random + {7164, 728}, // rapids + {7165, 727}, // ranked + {7176, 729}, // raspy + {7183, 782}, // saver + {7184, 780}, // satisfy + {7186, 781}, // satoshi + {7197, 783}, // says + {7216, 784}, // scandal + {7217, 785}, // scared + {7218, 786}, // scatter + {7236, 787}, // scene + {7243, 789}, // science + {7246, 788}, // scholar + {7268, 790}, // scout + {7271, 791}, // scramble + {7273, 792}, // screw + {7274, 793}, // script + {7276, 794}, // scroll + {7312, 730}, // reaction + {7313, 795}, // seafood + {7315, 731}, // realize + {7316, 732}, // rebound + {7317, 796}, // season + {7318, 733}, // rebuild + {7321, 734}, // recall + {7323, 735}, // receiver + {7326, 736}, // recover + {7327, 797}, // secret + {7328, 798}, // security + {7343, 739}, // reject + {7345, 799}, // segment + {7347, 737}, // regret + {7348, 738}, // regular + {7351, 740}, // relate + {7353, 741}, // remember + {7354, 742}, // remind + {7356, 743}, // remove + {7361, 745}, // repair + {7362, 744}, // render + {7363, 746}, // repeat + {7364, 800}, // senior + {7365, 747}, // replace + {7368, 748}, // require + {7372, 749}, // rescue + {7373, 750}, // research + {7374, 751}, // resident + {7376, 752}, // response + {7378, 753}, // result + {7381, 754}, // retailer + {7383, 757}, // revenue + {7384, 758}, // review + {7386, 756}, // reunion + {7387, 755}, // retreat + {7391, 759}, // reward + {7412, 801}, // shadow + {7413, 802}, // shaft + {7415, 803}, // shame + {7416, 804}, // shaped + {7417, 805}, // sharp + {7423, 811}, // sidewalk + {7424, 762}, // rich + {7435, 806}, // shelter + {7437, 807}, // sheriff + {7453, 812}, // silent + {7454, 814}, // similar + {7456, 815}, // simple + {7458, 813}, // silver + {7464, 816}, // single + {7467, 808}, // short + {7468, 809}, // should + {7474, 810}, // shrimp + {7478, 817}, // sister + {7481, 763}, // rival + {7483, 764}, // river + {7495, 760}, // rhyme + {7498, 761}, // rhythm + {7516, 820}, // slap + {7517, 827}, // smart + {7518, 821}, // slavery + {7531, 828}, // smear + {7532, 822}, // sled + {7535, 829}, // smell + {7542, 823}, // slice + {7545, 824}, // slim + {7546, 818}, // skin + {7547, 830}, // smirk + {7548, 831}, // smith + {7565, 832}, // smoking + {7569, 825}, // slow + {7584, 833}, // smug + {7586, 819}, // skunk + {7587, 826}, // slush + {7612, 843}, // space + {7614, 765}, // robin + {7615, 834}, // snake + {7616, 835}, // snapshot + {7617, 844}, // spark + {7624, 837}, // society + {7625, 766}, // rocky + {7631, 845}, // speak + {7632, 846}, // species + {7635, 847}, // spelling + {7636, 848}, // spend + {7638, 838}, // software + {7639, 849}, // spew + {7642, 850}, // spider + {7643, 836}, // sniff + {7645, 851}, // spill + {7646, 852}, // spine + {7647, 853}, // spirit + {7648, 854}, // spit + {7651, 767}, // romantic + {7652, 839}, // soldier + {7656, 768}, // romp + {7658, 840}, // solution + {7671, 855}, // spray + {7674, 856}, // sprinkle + {7678, 769}, // roster + {7681, 857}, // square + {7683, 858}, // squeeze + {7685, 841}, // soul + {7686, 770}, // round + {7687, 842}, // source + {7691, 771}, // royal + {7812, 859}, // stadium + {7813, 860}, // staff + {7814, 873}, // subject + {7815, 874}, // submit + {7816, 861}, // standard + {7817, 862}, // starting + {7818, 863}, // station + {7819, 864}, // stay + {7831, 865}, // steady + {7836, 866}, // step + {7841, 875}, // sugar + {7842, 867}, // stick + {7845, 868}, // stilt + {7846, 772}, // ruin + {7848, 876}, // suitable + {7853, 773}, // ruler + {7856, 774}, // rumor + {7863, 878}, // superior + {7865, 877}, // sunlight + {7867, 869}, // story + {7871, 870}, // strategy + {7873, 879}, // surface + {7874, 871}, // strike + {7876, 880}, // surprise + {7878, 881}, // survive + {7895, 872}, // style + {7931, 882}, // sweater + {7945, 883}, // swimming + {7946, 884}, // swing + {7948, 885}, // switch + {7951, 886}, // symbolic + {7956, 887}, // sympathy + {7962, 888}, // syndrome + {7978, 889}, // system + {8125, 890}, // tackle + {8126, 892}, // tadpole + {8128, 891}, // tactics + {8153, 893}, // talent + {8154, 965}, // valid + {8156, 967}, // vampire + {8158, 966}, // valuable + {8164, 968}, // vanish + {8174, 969}, // various + {8175, 894}, // task + {8178, 895}, // taste + {8184, 896}, // taught + {8194, 897}, // taxi + {8312, 898}, // teacher + {8315, 899}, // teammate + {8317, 900}, // teaspoon + {8341, 970}, // vegan + {8356, 901}, // temple + {8358, 971}, // velvet + {8361, 902}, // tenant + {8362, 903}, // tendency + {8367, 904}, // tension + {8368, 972}, // venture + {8372, 973}, // verdict + {8374, 974}, // verify + {8375, 905}, // terminal + {8378, 906}, // testify + {8379, 975}, // very + {8383, 976}, // veteran + {8393, 977}, // vexed + {8398, 907}, // texture + {8416, 908}, // thank + {8418, 909}, // that + {8423, 979}, // video + {8425, 917}, // ticket + {8428, 978}, // victim + {8429, 918}, // tidy + {8431, 910}, // theater + {8436, 911}, // theory + {8437, 912}, // therapy + {8439, 980}, // view + {8451, 919}, // timber + {8453, 920}, // timely + {8459, 946}, // ugly + {8464, 921}, // ting + {8465, 982}, // violence + {8467, 913}, // thorn + {8468, 981}, // vintage + {8471, 983}, // viral + {8473, 914}, // threaten + {8474, 984}, // visitor + {8478, 985}, // visual + {8481, 986}, // vitamins + {8485, 915}, // thumb + {8486, 916}, // thunder + {8517, 948}, // umbrella + {8584, 947}, // ultimate + {8621, 987}, // vocal + {8623, 950}, // undergo + {8626, 949}, // uncover + {8631, 951}, // unfair + {8636, 952}, // unfold + {8638, 922}, // tofu + {8641, 953}, // unhappy + {8642, 988}, // voice + {8643, 923}, // together + {8646, 954}, // union + {8647, 960}, // upgrade + {8648, 955}, // universe + {8653, 924}, // tolerate + {8654, 956}, // unkind + {8656, 957}, // unknown + {8658, 989}, // volume + {8678, 961}, // upstairs + {8681, 925}, // total + {8683, 990}, // voter + {8684, 991}, // voting + {8687, 958}, // unusual + {8694, 926}, // toxic + {8697, 959}, // unwrap + {8712, 927}, // tracks + {8713, 928}, // traffic + {8714, 929}, // training + {8716, 930}, // transfer + {8717, 931}, // trash + {8718, 932}, // traveler + {8731, 933}, // treat + {8736, 934}, // trend + {8737, 962}, // username + {8741, 935}, // trial + {8742, 936}, // tricycle + {8743, 963}, // usher + {8746, 937}, // trip + {8748, 938}, // triumph + {8768, 939}, // trouble + {8781, 964}, // usual + {8783, 940}, // true + {8787, 941}, // trust + {8942, 942}, // twice + {8946, 943}, // twin + {8963, 944}, // type + {8964, 945}, // typical + {9156, 992}, // walnut + {9175, 993}, // warmth + {9176, 994}, // warn + {9182, 995}, // watch + {9189, 996}, // wavy + {9312, 999}, // webcam + {9315, 997}, // wealthy + {9316, 998}, // weapon + {9317, 1019}, // year + {9352, 1000}, // welcome + {9353, 1001}, // welfare + {9356, 1020}, // yelp + {9376, 1023}, // zero + {9378, 1002}, // western + {9428, 1003}, // width + {9435, 1021}, // yield + {9452, 1004}, // wildlife + {9462, 1005}, // window + {9463, 1006}, // wine + {9472, 1008}, // wisdom + {9473, 1007}, // wireless + {9484, 1009}, // withdraw + {9487, 1010}, // wits + {9641, 1022}, // yoga + {9651, 1012}, // woman + {9653, 1011}, // wolf + {9675, 1013}, // work + {9678, 1014}, // worthy + {9716, 1015}, // wrap + {9747, 1016}, // wrist + {9748, 1017}, // writing + {9768, 1018}, // wrote +}; + +#endif diff --git a/trezor-crypto/include/TrezorCrypto/zilliqa.h b/trezor-crypto/include/TrezorCrypto/zilliqa.h new file mode 100644 index 00000000000..46ada660b06 --- /dev/null +++ b/trezor-crypto/include/TrezorCrypto/zilliqa.h @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2019 Anatolii Kurotych + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES + * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __ZILLIQA_H__ +#define __ZILLIQA_H__ + +#include + +#if defined(__cplusplus) +extern "C" +{ +#endif + +// result of sign operation +typedef struct { + uint8_t r[32]; + uint8_t s[32]; +} schnorr_sign_pair; + +// sign/verify returns 0 if operation succeeded + +int zil_schnorr_sign(const ecdsa_curve *curve, const uint8_t *priv_key, + const uint8_t *msg, const uint32_t msg_len, uint8_t *sig); +int zil_schnorr_verify(const ecdsa_curve *curve, const uint8_t *pub_key, + const uint8_t *sig, const uint8_t *msg, const uint32_t msg_len); + +// k is a random from [1, ..., order-1] +int zil_schnorr_sign_k(const ecdsa_curve *curve, const uint8_t *priv_key, + const bignum256 *k, const uint8_t *msg, const uint32_t msg_len, + schnorr_sign_pair *result); +int zil_schnorr_verify_pair(const ecdsa_curve *curve, const uint8_t *pub_key, + const uint8_t *msg, const uint32_t msg_len, + const schnorr_sign_pair *sign); +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/walletconsole/CMakeLists.txt b/walletconsole/CMakeLists.txt index 16f80e331f3..1c6bba462d5 100644 --- a/walletconsole/CMakeLists.txt +++ b/walletconsole/CMakeLists.txt @@ -1,17 +1,16 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + # walletconsole executable file(GLOB walletconsole_sources *.cpp) add_executable(walletconsole ${walletconsole_sources}) -#target_link_libraries(tests gtest_main TrezorCrypto TrustWalletCore ${Protobuf_LIBRARIES} Boost::boost) + + target_link_libraries(walletconsole walletconsolelib TrezorCrypto TrustWalletCore ${Protobuf_LIBRARIES} Boost::boost) -target_include_directories(walletconsole PRIVATE ${CMAKE_SOURCE_DIR}/walletconsole/lib ${CMAKE_SOURCE_DIR}/src) -if(NOT ("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC")) - target_compile_options(walletconsole PRIVATE "-Wall") -endif() -set_target_properties(walletconsole - PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON -) +target_include_directories(walletconsole PRIVATE ${CMAKE_SOURCE_DIR}/walletconsole/lib ${CMAKE_SOURCE_DIR}/src) -INSTALL(TARGETS walletconsole DESTINATION ${CMAKE_INSTALL_BINDIR}) \ No newline at end of file +INSTALL(TARGETS walletconsole DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/walletconsole/lib/Address.cpp b/walletconsole/lib/Address.cpp index 48f6aa9e65c..7a082f7413c 100644 --- a/walletconsole/lib/Address.cpp +++ b/walletconsole/lib/Address.cpp @@ -53,7 +53,7 @@ bool Address::addrPri(const string& coinid, const string& prikey_in, string& res return true; } -bool Address::addr(const string& coinid, const string& addrStr, string& res) { +bool Address::addr(const string& coinid, const string& addrStr, [[maybe_unused]] string& res) { Coin coin; if (!_coins.findCoin(coinid, coin)) { return false; } auto ctype = (TWCoinType)coin.c; diff --git a/walletconsole/lib/Buffer.cpp b/walletconsole/lib/Buffer.cpp index 3c79030c7b2..27f40a7e596 100644 --- a/walletconsole/lib/Buffer.cpp +++ b/walletconsole/lib/Buffer.cpp @@ -43,7 +43,7 @@ bool Buffer::prepareInput(const string& in, string& in_out) { int n = std::stoi(in2.substr(1)); // of the form #n int idx = n - 1; - if (idx < 0 || idx >= _prev.size()) { + if (idx < 0 || idx >= static_cast(_prev.size())) { _out << "Requested " << in2 << ", but out of range of buffers (n=" << _prev.size() << ")." << endl; return false; } @@ -58,7 +58,7 @@ bool Buffer::prepareInput(const string& in, string& in_out) { void Buffer::buffer() const { _out << "Last value: " << _last.get() << endl; _out << _prev.size() << " previous values:" << endl; - for (int i = 0; i < _prev.size(); ++i) { + for (auto i = 0ul; i < _prev.size(); ++i) { _out << " #" << i + 1 << " " << _prev[i].get() << endl; } } diff --git a/walletconsole/lib/CMakeLists.txt b/walletconsole/lib/CMakeLists.txt index 0dec5ad513b..b14eb51ec08 100644 --- a/walletconsole/lib/CMakeLists.txt +++ b/walletconsole/lib/CMakeLists.txt @@ -1,13 +1,12 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + # walletconsolelib library file(GLOB_RECURSE walletconsolelib_sources *.cpp) add_library(walletconsolelib ${walletconsolelib_sources}) #target_link_libraries(tests gtest_main TrezorCrypto TrustWalletCore ${Protobuf_LIBRARIES} Boost::boost) target_link_libraries(walletconsolelib TrezorCrypto TrustWalletCore ${Protobuf_LIBRARIES} Boost::boost) target_include_directories(walletconsolelib PRIVATE ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/src) -target_compile_options(walletconsolelib PRIVATE "-Wall") - -set_target_properties(walletconsolelib - PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON -) diff --git a/walletconsole/lib/CommandExecutor.cpp b/walletconsole/lib/CommandExecutor.cpp index e20be755518..973c2cf4dd7 100644 --- a/walletconsole/lib/CommandExecutor.cpp +++ b/walletconsole/lib/CommandExecutor.cpp @@ -189,7 +189,7 @@ string CommandExecutor::parseLine(const string& line, vector& params) { return cmd; } -bool CommandExecutor::checkMinParams(const vector& params, int n) const { +bool CommandExecutor::checkMinParams(const vector& params, std::size_t n) const { if (params.size() - 1 >= n) { return true; } diff --git a/walletconsole/lib/CommandExecutor.h b/walletconsole/lib/CommandExecutor.h index 1177d987266..a722e47c1fc 100644 --- a/walletconsole/lib/CommandExecutor.h +++ b/walletconsole/lib/CommandExecutor.h @@ -46,7 +46,7 @@ class CommandExecutor { static string parseLine(const string& line, vector& params); bool prepareInputs(const vector& p_in, vector& p_out); bool setCoin(const string& coin, bool force); - bool checkMinParams(const vector& params, int n) const; + bool checkMinParams(const vector& params, std::size_t n) const; void help() const; }; diff --git a/walletconsole/lib/Keys.cpp b/walletconsole/lib/Keys.cpp index 2c6158e4139..974e3f46e38 100644 --- a/walletconsole/lib/Keys.cpp +++ b/walletconsole/lib/Keys.cpp @@ -31,10 +31,6 @@ Keys::Keys(ostream& out, const Coins& coins) : _out(out), _coins(coins) { void privateKeyToResult(const PrivateKey& priKey, string& res_out) { // take the key, but may need to take extension as well res_out = hex(priKey.bytes); - if (priKey.extensionBytes.size() > 0) { - res_out += hex(priKey.extensionBytes); - res_out += hex(priKey.chainCodeBytes); - } } bool Keys::newKey(const string& coinid, string& res) { @@ -69,7 +65,7 @@ bool Keys::pubPri(const string& coinid, const string& p, string& res) { } } -bool Keys::priPub(const string& p, string& res) { +bool Keys::priPub([[maybe_unused]] const string& p, [[maybe_unused]] string& res) { _out << "Not yet implemented! :)" << endl; return false; } @@ -81,7 +77,7 @@ void Keys::setMnemonic(const vector& param) { } // concatenate string mnem = ""; - for (int i = 1; i < param.size(); ++i) { + for (auto i = 1ul; i < param.size(); ++i) { if (i > 1) mnem += " "; mnem += param[i]; } diff --git a/walletconsole/lib/Util.cpp b/walletconsole/lib/Util.cpp index eea581e34e5..86dd938dc84 100644 --- a/walletconsole/lib/Util.cpp +++ b/walletconsole/lib/Util.cpp @@ -48,7 +48,7 @@ bool Util::base64Decode(const string& p, string& res) { } } -bool Util::fileW(const string& fileName, const string& data, string& res) { +bool Util::fileW(const string& fileName, const string& data, [[maybe_unused]] string& res) { if (fileExists(fileName)) { _out << "Warning: File '" << fileName << "' already exists, not overwriting to be safe." << endl; return false; diff --git a/wasm/.gitignore b/wasm/.gitignore new file mode 100644 index 00000000000..c1327fb6af0 --- /dev/null +++ b/wasm/.gitignore @@ -0,0 +1,5 @@ +dist/ +generated/ +wallet-core.d.ts +lib/wallet-core.js +lib/wallet-core.wasm diff --git a/wasm/.mocharc.json b/wasm/.mocharc.json new file mode 100644 index 00000000000..6a7296561e4 --- /dev/null +++ b/wasm/.mocharc.json @@ -0,0 +1,8 @@ +{ + "require": "ts-node/register", + "extensions": ["ts", "tsx"], + "spec":[ + "tests/*.test.ts", + "tests/**/*.test.*" + ] +} diff --git a/wasm/CMakeLists.txt b/wasm/CMakeLists.txt new file mode 100644 index 00000000000..379042f09e6 --- /dev/null +++ b/wasm/CMakeLists.txt @@ -0,0 +1,44 @@ +# Copyright © 2017-2022 Trust Wallet. +# +# This file is part of Trust. The full Trust copyright notice, including +# terms governing use, modification, and redistribution, is contained in the +# file LICENSE at the root of the source code distribution tree. + +file(GLOB wasm_sources src/*.cpp src/generated/*.cpp) +file(GLOB wasm_headers src/*.h src/generated/*.h) +set(TARGET_NAME wallet-core) +set(CMAKE_EXECUTABLE_SUFFIX ".js") +add_executable(${TARGET_NAME} ${wasm_sources} ${wasm_headers}) + +target_link_libraries(${TARGET_NAME} TrustWalletCore) +target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/trezor-crypto/include) +target_compile_options(${TARGET_NAME} PRIVATE "-Wall") + +set_target_properties(${TARGET_NAME} + PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED ON +) + +# We use a set of COMPILE_FLAGS and LINK_FLAGS, see below links for more details. +# - https://emscripten.org/docs/optimizing/Optimizing-Code.html#how-to-optimize-code +# - https://github.com/emscripten-core/emscripten/blob/main/src/settings.js +# - https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html?highlight=lembind#embind + +# STRICT: Emscripten 'strict' build mode, disables AUTO_NATIVE_LIBRARIES and AUTO_JS_LIBRARIES etc. +# ASSERTIONS: Enable runtime assertions. + +# MODULARIZE=1: Emit generated JavaScript code wrapped in a function that returns a promise. +# ALLOW_MEMORY_GROWTH=1: Allowing allocating more memory from the system as necessary. +# DYNAMIC_EXECUTION=0: Do not emit eval() and new Function() in the generated JavaScript code. + +# -O2: good old code optimization level for release. +# --bind: Link Embind library. +# --no-entry: Skip main entry point because it's built as a library. +# --closure 1: Enable Emscripten closure compiler to minimize generated JavaScript code size. + +set_target_properties(${TARGET_NAME} + PROPERTIES + COMPILE_FLAGS "-O2 -sSTRICT -sUSE_BOOST_HEADERS=1" + LINK_FLAGS "--bind --no-entry --closure 1 -O2 -sSTRICT -sASSERTIONS -sMODULARIZE=1 -sALLOW_MEMORY_GROWTH=1 -sDYNAMIC_EXECUTION=0" +) diff --git a/wasm/README.md b/wasm/README.md new file mode 100644 index 00000000000..63385904724 --- /dev/null +++ b/wasm/README.md @@ -0,0 +1,7 @@ +Trust Wallet Core is an open source, cross platform and cross blockchain library, it adds beta support for WebAssembly recently, You can try it out now: + +```js +npm install @trustwallet/wallet-core +``` + +Documentation will be added to [developer.trustwallet.com](https://developer.trustwallet.com/wallet-core) later, please check out [tests](https://github.com/trustwallet/wallet-core/tree/master/wasm/tests) here for API usages. diff --git a/wasm/index.ts b/wasm/index.ts new file mode 100644 index 00000000000..2e298c52d53 --- /dev/null +++ b/wasm/index.ts @@ -0,0 +1,15 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import * as Loader from "./lib/wallet-core"; +import { TW } from "./generated/core_proto"; +import { WalletCore } from "./src/wallet-core"; +import * as KeyStore from "./src/keystore"; + +declare function load(): Promise; + +export const initWasm: typeof load = Loader; +export { TW, WalletCore, KeyStore }; diff --git a/wasm/package-lock.json b/wasm/package-lock.json new file mode 100644 index 00000000000..8697fe149e1 --- /dev/null +++ b/wasm/package-lock.json @@ -0,0 +1,2351 @@ +{ + "name": "@trustwallet/wallet-core", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@trustwallet/wallet-core", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "protobufjs": ">=6.11.3" + }, + "devDependencies": { + "@types/chai": "^4.3.0", + "@types/mocha": "^9.1.0", + "@types/node": "^18.7.18", + "@types/webextension-polyfill": "^0.9.0", + "buffer": "^6.0.3", + "chai": "^4.3.6", + "mocha": "^9.2.2", + "ts-node": "^10.7.0", + "typescript": "^4.6.3" + } + }, + "node_modules/@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-consumer": "0.8.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "node_modules/@types/chai": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", + "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", + "dev": true + }, + "node_modules/@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "node_modules/@types/mocha": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" + }, + "node_modules/@types/webextension-polyfill": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@types/webextension-polyfill/-/webextension-polyfill-0.9.0.tgz", + "integrity": "sha512-HG1y1o2hK8ag6Y7dfkrAbfKmMIP+B0E6SwAzUfmQ1dDxEIdLTtMyrStY26suHBPrAL7Xw/chlDW02ugc3uXWtQ==", + "dev": true + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "node_modules/loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/protobufjs": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + }, + "bin": { + "pbjs": "bin/pbjs", + "pbts": "bin/pbts" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-node": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", + "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", + "dev": true, + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", + "dev": true + }, + "@types/chai": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", + "integrity": "sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==", + "dev": true + }, + "@types/long": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", + "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + }, + "@types/mocha": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==", + "dev": true + }, + "@types/node": { + "version": "18.7.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.18.tgz", + "integrity": "sha512-m+6nTEOadJZuTPkKR/SYK3A2d7FZrgElol9UP1Kae90VVU4a6mxnPuLiIW1m4Cq4gZ/nWb9GrdVXJCoCazDAbg==" + }, + "@types/webextension-polyfill": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@types/webextension-polyfill/-/webextension-polyfill-0.9.0.tgz", + "integrity": "sha512-HG1y1o2hK8ag6Y7dfkrAbfKmMIP+B0E6SwAzUfmQ1dDxEIdLTtMyrStY26suHBPrAL7Xw/chlDW02ugc3uXWtQ==", + "dev": true + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "acorn": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz", + "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chai": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "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" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + } + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "protobufjs": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": ">=13.7.0", + "long": "^4.0.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-node": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", + "integrity": "sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "0.7.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.0", + "yn": "3.1.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "typescript": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz", + "integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", + "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/wasm/package.json b/wasm/package.json new file mode 100644 index 00000000000..8ae7e3f0b3b --- /dev/null +++ b/wasm/package.json @@ -0,0 +1,46 @@ +{ + "name": "@trustwallet/wallet-core", + "version": "1.0.0", + "description": "wallet core wasm and protobuf models", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "test": "mocha --trace-warnings", + "generate": "npm run codegen:js && npm run codegen:ts", + "codegen:js": "pbjs -t static-module '../src/proto/*.proto' --no-delimited --force-long -o generated/core_proto.js", + "codegen:js-browser": "pbjs -t static-module '../src/proto/*.proto' -w closure --no-delimited --force-long -o ../samples/wasm/core_proto.js", + "codegen:ts": "pbts -o generated/core_proto.d.ts generated/core_proto.js", + "clean": "rm -rf dist generated && mkdir -p dist/generated generated", + "build": "npm run clean && npm run generate && cp -R generated lib dist && tsc && cp src/wallet-core.d.ts dist/src", + "build-and-test": "npm run copy:wasm && npm run build && npm test", + "copy:wasm": "mkdir -p lib && cp ../wasm-build/wasm/wallet-core.* lib", + "copy:wasm-sample": "cp ../wasm-build/wasm/wallet-core.* ../samples/wasm/" + }, + "repository": { + "type": "git", + "url": "git://github.com/trustwallet/wallet-core.git" + }, + "author": "hewigovens", + "license": "MIT", + "bugs": { + "url": "https://github.com/trustwallet/wallet-core/issues" + }, + "homepage": "https://github.com/trustwallet/wallet-core#readme", + "files": [ + "dist" + ], + "dependencies": { + "protobufjs": ">=6.11.3" + }, + "devDependencies": { + "@types/chai": "^4.3.0", + "@types/mocha": "^9.1.0", + "@types/node": "^18.7.18", + "@types/webextension-polyfill": "^0.9.0", + "buffer": "^6.0.3", + "chai": "^4.3.6", + "mocha": "^9.2.2", + "ts-node": "^10.7.0", + "typescript": "^4.6.3" + } +} diff --git a/wasm/src/AnySigner.cpp b/wasm/src/AnySigner.cpp new file mode 100644 index 00000000000..cbddc1a8bf1 --- /dev/null +++ b/wasm/src/AnySigner.cpp @@ -0,0 +1,44 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include +#include + +#include "WasmData.h" +#include "Coin.h" + +using namespace emscripten; + +namespace TW::Wasm { + +class AnySigner { + public: + static auto sign(const std::string& string, TWCoinType coin) { + Data out; + TW::anyCoinSign(coin, TW::data(string), out); + return DataToVal(out); + } + + static auto supportsJSON(TWCoinType coin) { + return TW::supportsJSONSigning(coin); + } + + static auto plan(const std::string& string, TWCoinType coin) { + Data out; + TW::anyCoinPlan(coin, TW::data(string), out); + return DataToVal(out); + } +}; + +EMSCRIPTEN_BINDINGS(Wasm_TWAnySigner) { + class_("AnySigner") + .class_function("sign", &AnySigner::sign) + .class_function("plan", &AnySigner::plan) + .class_function("supportsJSON", &AnySigner::supportsJSON); +} + +} // namespace TW::Wasm diff --git a/wasm/src/AnySigner.d.ts b/wasm/src/AnySigner.d.ts new file mode 100644 index 00000000000..b188bc2f479 --- /dev/null +++ b/wasm/src/AnySigner.d.ts @@ -0,0 +1,5 @@ +export class AnySigner { + static sign(data: Uint8Array | Buffer, coin: CoinType): Uint8Array; + static plan(data: Uint8Array | Buffer, coin: CoinType): Uint8Array; + static supportsJSON(coin: CoinType): boolean; +} diff --git a/wasm/src/HexCoding.cpp b/wasm/src/HexCoding.cpp new file mode 100644 index 00000000000..1a1c58f57c9 --- /dev/null +++ b/wasm/src/HexCoding.cpp @@ -0,0 +1,35 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include + +#include "WasmData.h" +#include "HexCoding.h" + +using namespace emscripten; + +namespace TW::Wasm { + +class HexCoding { + public: + static auto parseHex(const std::string& string) { + return DataToVal(TW::parse_hex(string, true)); + } + + static auto hexEncoded(const std::string& string) { + auto data = TW::data(string); + return TW::hexEncoded(data); + } +}; + +EMSCRIPTEN_BINDINGS(Wasm_HexCoding) { + class_("HexCoding") + .class_function("decode", &HexCoding::parseHex) + .class_function("encode", &HexCoding::hexEncoded); +} + +} // namespace TW::Wasm diff --git a/wasm/src/HexCoding.d.ts b/wasm/src/HexCoding.d.ts new file mode 100644 index 00000000000..f15943f9d56 --- /dev/null +++ b/wasm/src/HexCoding.d.ts @@ -0,0 +1,4 @@ +export namespace HexCoding { + export function decode(hex: string): Uint8Array; + export function encode(buffer: Uint8Array | Buffer): string; +} diff --git a/wasm/src/Random.cpp b/wasm/src/Random.cpp new file mode 100644 index 00000000000..d0ea0b2557f --- /dev/null +++ b/wasm/src/Random.cpp @@ -0,0 +1,25 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include +#include +#include +#include + +extern "C" { +uint32_t random32(void) { + std::mt19937 rng(std::random_device{}()); + return rng(); +} + +void random_buffer(uint8_t* buf, size_t len) { + std::mt19937 rng(std::random_device{}()); + std::generate_n(buf, len, [&rng]() -> uint8_t { return rng() & 0x000000ff; }); + return; +} + +} // extern "C" diff --git a/wasm/src/WasmData.cpp b/wasm/src/WasmData.cpp new file mode 100644 index 00000000000..7426d443675 --- /dev/null +++ b/wasm/src/WasmData.cpp @@ -0,0 +1,30 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "WasmData.h" +#include "Defer.h" + +using namespace emscripten; + +namespace TW::Wasm { + +auto DataToVal(Data data) -> val { + auto view = val(typed_memory_view(data.size(), data.data())); + auto jsArray = val::global("Uint8Array").new_(data.size()); + jsArray.call("set", view); + return jsArray; +} + +auto TWDataToVal(TWData* _Nonnull data) -> val { + defer { + TWDataDelete(data); + }; + auto* v = reinterpret_cast(data); + return DataToVal(*v); +} + +} // namespace TW::Wasm diff --git a/wasm/src/WasmData.h b/wasm/src/WasmData.h new file mode 100644 index 00000000000..133f9b69f73 --- /dev/null +++ b/wasm/src/WasmData.h @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#pragma once + +#include +#include + +#include "Data.h" + +namespace TW::Wasm { + +auto DataToVal(Data data) -> emscripten::val; + +/// Converts a TWData * to Uint8Array, deleting the TWData * when done. +auto TWDataToVal(TWData *_Nonnull data) -> emscripten::val; + +} // namespace TW::Wasm diff --git a/wasm/src/WasmString.cpp b/wasm/src/WasmString.cpp new file mode 100644 index 00000000000..2ea9f9cec79 --- /dev/null +++ b/wasm/src/WasmString.cpp @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#include "WasmString.h" +#include "Defer.h" + +namespace TW::Wasm { + +auto TWStringToStd(TWString *_Nonnull string) -> std::string { + defer { + TWStringDelete(string); + }; + auto* s = reinterpret_cast(string); + auto result = *s; + return result; +} + +} // namespace TW::Wasm diff --git a/wasm/src/WasmString.h b/wasm/src/WasmString.h new file mode 100644 index 00000000000..ae58e22daa8 --- /dev/null +++ b/wasm/src/WasmString.h @@ -0,0 +1,18 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. +// + +#pragma once + +#include +#include + +namespace TW::Wasm { + +/// Converts a TWString * to std::string, deleting the TWString * when done. +auto TWStringToStd(TWString *_Nonnull string) -> std::string; + +} // namespace TW::Wasm diff --git a/wasm/src/keystore/default-impl.ts b/wasm/src/keystore/default-impl.ts new file mode 100644 index 00000000000..91da9b40f16 --- /dev/null +++ b/wasm/src/keystore/default-impl.ts @@ -0,0 +1,159 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import { WalletCore, CoinType, PrivateKey, StoredKey } from "../wallet-core"; +import * as Types from "./types"; + +export class Default implements Types.IKeyStore { + private readonly core: WalletCore; + private readonly storage: Types.IStorage; + + constructor(core: WalletCore, storage: Types.IStorage) { + this.core = core; + this.storage = storage; + } + + hasWallet(id: string): Promise { + return this.storage + .get(id) + .then((wallet) => true) + .catch((error) => false); + } + + load(id: string): Promise { + return this.storage.get(id); + } + + loadAll(): Promise { + return this.storage.loadAll(); + } + + delete(id: string, password: string): Promise { + return this.storage.delete(id, password); + } + + mapWallet(storedKey: StoredKey): Types.Wallet { + const json = storedKey.exportJSON(); + return JSON.parse(Buffer.from(json).toString()) as Types.Wallet; + } + + mapStoredKey(wallet: Types.Wallet): StoredKey { + const json = Buffer.from(JSON.stringify(wallet)); + return this.core.StoredKey.importJSON(json); + } + + importWallet(wallet: Types.Wallet): Promise { + return this.storage.set(wallet.id, wallet); + } + + import( + mnemonic: string, + name: string, + password: string, + coins: CoinType[] + ): Promise { + return new Promise((resolve, reject) => { + const { Mnemonic, StoredKey, HDWallet } = this.core; + + if (!Mnemonic.isValid(mnemonic)) { + throw Types.Error.InvalidMnemonic; + } + let pass = Buffer.from(password); + let storedKey = StoredKey.importHDWallet(mnemonic, name, pass, coins[0]); + let hdWallet = HDWallet.createWithMnemonic(mnemonic, ""); + coins.forEach((coin) => { + storedKey.accountForCoin(coin, hdWallet); + }); + let wallet = this.mapWallet(storedKey); + + storedKey.delete(); + hdWallet.delete(); + + this.importWallet(wallet) + .then(() => resolve(wallet)) + .catch((error) => reject(error)); + }); + } + + importKey( + key: Uint8Array, + name: string, + password: string, + coin: CoinType + ): Promise { + return new Promise((resolve, reject) => { + const { StoredKey, PrivateKey, Curve } = this.core; + + // FIXME: get curve from coin + if ( + !PrivateKey.isValid(key, Curve.secp256k1) || + !PrivateKey.isValid(key, Curve.ed25519) + ) { + throw Types.Error.InvalidKey; + } + let pass = Buffer.from(password); + let storedKey = StoredKey.importPrivateKey(key, name, pass, coin); + let wallet = this.mapWallet(storedKey); + storedKey.delete(); + this.importWallet(wallet) + .then(() => resolve(wallet)) + .catch((error) => reject(error)); + }); + } + + addAccounts( + id: string, + password: string, + coins: CoinType[] + ): Promise { + return this.load(id).then((wallet) => { + let storedKey = this.mapStoredKey(wallet); + let hdWallet = storedKey.wallet(Buffer.from(password)); + coins.forEach((coin) => { + storedKey.accountForCoin(coin, hdWallet); + }); + let newWallet = this.mapWallet(storedKey); + storedKey.delete(); + hdWallet.delete(); + return this.importWallet(newWallet).then(() => newWallet); + }); + } + + getKey( + id: string, + password: string, + account: Types.ActiveAccount + ): Promise { + return this.load(id).then((wallet) => { + let storedKey = this.mapStoredKey(wallet); + let hdWallet = storedKey.wallet(Buffer.from(password)); + let coin = (this.core.CoinType as any).values["" + account.coin]; + let privateKey = hdWallet.getKey(coin, account.derivationPath); + storedKey.delete(); + hdWallet.delete(); + return privateKey; + }); + } + + export(id: string, password: string): Promise { + return this.load(id).then((wallet) => { + let storedKey = this.mapStoredKey(wallet); + let value: string | Uint8Array; + switch (wallet.type) { + case Types.WalletType.Mnemonic: + value = storedKey.decryptMnemonic(Buffer.from(password)); + break; + case Types.WalletType.PrivateKey: + value = storedKey.decryptPrivateKey(Buffer.from(password)); + break; + default: + throw Types.Error.InvalidJSON; + } + storedKey.delete(); + return value; + }); + } +} diff --git a/wasm/src/keystore/extension-storage.ts b/wasm/src/keystore/extension-storage.ts new file mode 100644 index 00000000000..3056a4b8681 --- /dev/null +++ b/wasm/src/keystore/extension-storage.ts @@ -0,0 +1,72 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import { Storage } from "webextension-polyfill"; +import * as Types from "./types"; + +// Extension KeyStore +export class ExtensionStorage implements Types.IStorage { + readonly storage: Storage.StorageArea; + readonly walletIdsKey: string; + + constructor(walletIdsKey: string, storage: Storage.StorageArea) { + this.walletIdsKey = walletIdsKey; + this.storage = storage; + } + + get(id: string): Promise { + return this.storage.get(id).then((object) => { + let wallet = object[id]; + if (wallet === undefined) { + throw Types.Error.WalletNotFound; + } + return wallet as Types.Wallet; + }); + } + + set(id: string, wallet: Types.Wallet): Promise { + return this.getWalletIds().then((ids) => { + if (ids.indexOf(id) === -1) { + ids.push(id); + } + return this.storage.set({ + [id]: wallet, + [this.walletIdsKey]: ids, + }); + }); + } + + loadAll(): Promise { + return this.getWalletIds().then((ids) => { + if (ids.length === 0) { + return []; + } + return this.storage + .get(ids) + .then((wallets) => Object.keys(wallets).map((key) => wallets[key])); + }); + } + + delete(id: string, password: string): Promise { + return this.getWalletIds().then((ids) => { + let index = ids.indexOf(id); + if (index === -1) { + return; + } + ids.splice(index, 1); + return this.storage + .remove(id) + .then(() => this.storage.set({ [this.walletIdsKey]: ids })); + }); + } + + private getWalletIds(): Promise { + return this.storage.get(this.walletIdsKey).then((object) => { + let ids = object[this.walletIdsKey] as string[]; + return ids === undefined ? [] : ids; + }); + } +} diff --git a/wasm/src/keystore/fs-storage.ts b/wasm/src/keystore/fs-storage.ts new file mode 100644 index 00000000000..3689be747e3 --- /dev/null +++ b/wasm/src/keystore/fs-storage.ts @@ -0,0 +1,45 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import * as Types from "./types"; +import * as fs from "fs/promises"; + +// FileSystem Storage +export class FileSystemStorage implements Types.IStorage { + private readonly directory: string; + + constructor(directory: string) { + this.directory = directory.endsWith("/") ? directory : directory + "/"; + } + + getFilename(id): string { + return this.directory + id + ".json"; + } + + get(id: string): Promise { + return fs + .readFile(this.getFilename(id)) + .then((data) => JSON.parse(data.toString()) as Types.Wallet); + } + + set(id: string, wallet: Types.Wallet): Promise { + return fs.writeFile(this.getFilename(id), JSON.stringify(wallet)); + } + + loadAll(): Promise { + return fs.readdir(this.directory).then((files) => { + return Promise.all( + files + .filter((file) => file.endsWith(".json")) + .map((file) => this.get(file.replace(".json", ""))) + ); + }); + } + + delete(id: string, password: string): Promise { + return fs.unlink(this.getFilename(id)); + } +} diff --git a/wasm/src/keystore/index.ts b/wasm/src/keystore/index.ts new file mode 100644 index 00000000000..68931378db0 --- /dev/null +++ b/wasm/src/keystore/index.ts @@ -0,0 +1,10 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +export { Default } from "./default-impl"; +export * from "./types"; +export { FileSystemStorage } from "./fs-storage"; +export { ExtensionStorage } from "./extension-storage"; diff --git a/wasm/src/keystore/types.ts b/wasm/src/keystore/types.ts new file mode 100644 index 00000000000..cc0ce8a21fd --- /dev/null +++ b/wasm/src/keystore/types.ts @@ -0,0 +1,93 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import { CoinType, PrivateKey } from "../wallet-core"; + +export enum WalletType { + Mnemonic = "mnemonic", + PrivateKey = "privateKey", + WatchOnly = "watchOnly", + Hardware = "hardware", +} + +export enum Error { + WalletNotFound = "wallet not found", + AccountNotFound = "account not found", + InvalidPassword = "invalid password", + InvalidMnemonic = "invalid mnemonic", + InvalidJSON = "invalid JSON", + InvalidKey = "invalid key", +} + +export interface ActiveAccount { + address: string; + coin: number; + publicKey: string; + derivationPath: string; + extendedPublicKey?: string; +} + +export interface Wallet { + id: string; + + type: WalletType; + name: string; + version: number; + activeAccounts: ActiveAccount[]; +} + +export interface IKeyStore { + // Check if wallet id exists + hasWallet(id: string): Promise; + + // Load a wallet by wallet id + load(id: string): Promise; + + // Load all wallets + loadAll(): Promise; + + // Import a wallet by mnemonic, name, password and initial active accounts (from coinTypes) + import( + mnemonic: string, + name: string, + password: string, + coins: CoinType[] + ): Promise; + + // Import a wallet by private key, name and password + importKey( + key: Uint8Array, + name: string, + password: string, + coin: CoinType + ): Promise; + + // Import a Wallet object directly + importWallet(wallet: Wallet): Promise; + + // Add active accounts to a wallet by wallet id, password, coin + addAccounts(id: string, password: string, coins: CoinType[]): Promise; + + // Get private key of an account by wallet id, password, coin and derivation path + getKey( + id: string, + password: string, + account: ActiveAccount + ): Promise; + + // Delete a wallet by wallet id and password.aq1aq + delete(id: string, password: string): Promise; + + // Export a wallet by wallet id and password, returns mnemonic or private key + export(id: string, password: string): Promise; +} + +export interface IStorage { + get(id: string): Promise; + set(id: string, wallet: Wallet): Promise; + loadAll(): Promise; + delete(id: string, password: string): Promise; +} diff --git a/wasm/tests/AES.test.ts b/wasm/tests/AES.test.ts new file mode 100644 index 00000000000..9a44fb1b337 --- /dev/null +++ b/wasm/tests/AES.test.ts @@ -0,0 +1,46 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; + +describe("AES", () => { + + it("test decrypting", () => { + const { AES, HexCoding, AESPaddingMode } = globalThis.core; + + const key = HexCoding.decode( + "5caa3a74154cee16bd1b570a1330be46e086474ac2f4720530662ef1a469662c" + ); + const iv = HexCoding.decode("89ef1d6728bac2f1dcde2ef9330d2bb8"); + const cipher = HexCoding.decode( + "1b3db3674de082d65455eba0ae61cfe7e681c8ef1132e60c8dbd8e52daf18f4fea42cc76366c83351dab6dca52682ff81f828753f89a21e1cc46587ca51ccd353914ffdd3b0394acfee392be6c22b3db9237d3f717a3777e3577dd70408c089a4c9c85130a68c43b0a8aadb00f1b8a8558798104e67aa4ff027b35d4b989e7fd3988d5dcdd563105767670be735b21c4" + ); + + const decrypted = AES.decryptCBC(key, cipher, iv, AESPaddingMode.pkcs7); + + assert.equal(Buffer.from(decrypted).toString(), + '{"id":1554098597199736,"jsonrpc":"2.0","method":"wc_sessionUpdate","params":[{"approved":false,"chainId":null,"accounts":null}]}' + ); + }); + + it("test encrypting", () => { + const { AES, HexCoding, AESPaddingMode } = globalThis.core; + + const key = HexCoding.decode( + "bbc82a01ebdb14698faee4a9e5038de72c995a9f6bcdb21903d62408b0c5ca96" + ); + const iv = HexCoding.decode("5b3a1a561e395d7ad7fe9c92abdacd17"); + const plain = Buffer.from('{"jsonrpc":"2.0","id":1554343834752446,"error":{"code":-32000,"message":"Session Rejected"}}'); + + const encrypted = AES.encryptCBC(key, plain, iv, AESPaddingMode.pkcs7); + + assert.equal(HexCoding.encode(encrypted), + '0x6a93788fcd6d266d06ccff35d1ed328d634605a7f2734f1519256b9950d86d6ca34fe4a13ff0ed513025b49427e6fe15268c84d6dfad6c0c8a21abc9306a5308f545b08d946a2a24b7cd18526bcefd6d9740db9b8e21f4511df148d9b9b03ad9' + ); + }); +}); diff --git a/wasm/tests/AnyAddress.test.ts b/wasm/tests/AnyAddress.test.ts new file mode 100644 index 00000000000..8d0de564223 --- /dev/null +++ b/wasm/tests/AnyAddress.test.ts @@ -0,0 +1,30 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; + +describe("AnyAddress", () => { + it("test validating Solana address", () => { + const { AnyAddress, HexCoding, CoinType } = globalThis.core; + + var address = AnyAddress.createWithString( + "7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q", + CoinType.solana + ); + + assert.equal(address.coin().value, 501); + assert.equal(address.description(), "7v91N7iZ9mNicL8WfG6cgSCKyRXydQjLh6UYBWwm6y1Q"); + + const data = address.data(); + + assert.equal(data.length, 32); + assert.typeOf(data, "Uint8Array"); + assert.equal(HexCoding.encode(data), "0x66c2f508c9c555cacc9fb26d88e88dd54e210bb5a8bce5687f60d7e75c4cd07f"); + + address.delete(); + }).timeout(5000); +}); diff --git a/wasm/tests/Base32.test.ts b/wasm/tests/Base32.test.ts new file mode 100644 index 00000000000..36915d1d41b --- /dev/null +++ b/wasm/tests/Base32.test.ts @@ -0,0 +1,55 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import { assert } from "chai"; +import { Buffer } from "buffer"; + +describe("Base32", () => { + + it("test decrypting", () => { + const { Base32 } = globalThis.core; + + const decoded = Base32.decode("JBSWY3DPK5XXE3DE"); + + assert.equal(Buffer.from(decoded).toString(), "HelloWorld"); + }); + + it("test decrypting with alphabet", () => { + const { Base32 } = globalThis.core; + + const decoded = Base32.decodeWithAlphabet( + "g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i", + "abcdefghijklmnopqrstuvwxyz234567" + ); + + assert.equal( + Buffer.from(decoded).toString(), + "7uoq6tp427uzv7fztkbsnn64iwotfrristwpryy" + ); + }); + + it("test encrypting", () => { + const { Base32 } = globalThis.core; + + const encoded = Base32.encode(Buffer.from("HelloWorld")); + + assert.equal(encoded, "JBSWY3DPK5XXE3DE"); + }); + + it("test encrypting with alphabet", () => { + const { Base32 } = globalThis.core; + + const encoded = Base32.encodeWithAlphabet( + Buffer.from("7uoq6tp427uzv7fztkbsnn64iwotfrristwpryy"), + "abcdefghijklmnopqrstuvwxyz234567" + ); + + assert.equal( + encoded, + "g52w64jworydimrxov5hmn3gpj2gwyttnzxdmndjo5xxiztsojuxg5dxobzhs6i" + ); + }); +}); diff --git a/wasm/tests/Base64.test.ts b/wasm/tests/Base64.test.ts new file mode 100644 index 00000000000..b2f6297627b --- /dev/null +++ b/wasm/tests/Base64.test.ts @@ -0,0 +1,42 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import { assert } from "chai"; +import { Buffer } from "buffer"; + +describe("Base64", () => { + + it("test decoding", () => { + const { Base64 } = globalThis.core; + + const decoded = Base64.decode("SGVsbG9Xb3JsZA=="); + + assert.equal(Buffer.from(decoded).toString(), "HelloWorld"); + }); + + it("test encoding", () => { + const { Base64 } = globalThis.core; + + const encoded = Base64.encode(Buffer.from("HelloWorld")); + + assert.equal(encoded, "SGVsbG9Xb3JsZA=="); + }); + + it("test encoding (URL-safe)", () => { + const { Base64 } = globalThis.core; + + const encoded = Base64.encodeUrl(Buffer.from("==?=")); + + assert.equal(encoded, "PT0_PQ=="); + }); + + it("test decoding (URL-safe)", () => { + const { Base64 } = globalThis.core; + const decoded = Base64.decodeUrl("PT0_PQ=="); + + assert.equal(Buffer.from(decoded).toString(), "==?="); + }); +}); diff --git a/wasm/tests/Blockchain/Bitcoin.test.ts b/wasm/tests/Blockchain/Bitcoin.test.ts new file mode 100644 index 00000000000..4cad6e61e5d --- /dev/null +++ b/wasm/tests/Blockchain/Bitcoin.test.ts @@ -0,0 +1,16 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import { TW } from "../../dist"; + +describe("Bitcoin", () => { + it("test Bitcoin SigningInput / SigningOutput", () => { + assert.isNotNull(TW.Bitcoin.Proto.SigningInput); + assert.isNotNull(TW.Binance.Proto.SigningOutput); + }); +}); diff --git a/wasm/tests/Blockchain/Ethereum.test.ts b/wasm/tests/Blockchain/Ethereum.test.ts new file mode 100644 index 00000000000..660b6e43afc --- /dev/null +++ b/wasm/tests/Blockchain/Ethereum.test.ts @@ -0,0 +1,168 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; +import { TW } from "../../dist"; + +describe("Ethereum", () => { + + it("test address", () => { + const { PrivateKey, HexCoding, AnyAddress, CoinType, Curve } = globalThis.core; + + const data = HexCoding.decode("727f677b390c151caf9c206fd77f77918f56904b5504243db9b21e51182c4c06"); + + assert.isTrue(PrivateKey.isValid(data, Curve.secp256k1)); + + const key = PrivateKey.createWithData(data); + const pubKey = key.getPublicKeySecp256k1(false); + + assert.equal( + HexCoding.encode(pubKey.data()), + "0x043182a24fdefe5711d735a434e983bf32a63fd99d214d63936b312643c325c6e33545c4aaff6b923544044d363d73668ec8724b7e62b54d17d49879405cf20648" + ); + + const address = AnyAddress.createWithPublicKey(pubKey, CoinType.smartChain); + + assert.equal(address.description(), "0xf3d468DBb386aaD46E92FF222adDdf872C8CC064"); + + key.delete(); + pubKey.delete(); + address.delete(); + }); + + it("test signing transfer tx", () => { + const { HexCoding, AnySigner, CoinType } = globalThis.core;; + const input = TW.Ethereum.Proto.SigningInput.create({ + toAddress: "0x3535353535353535353535353535353535353535", + chainId: Buffer.from("01", "hex"), + nonce: Buffer.from("09", "hex"), + gasPrice: Buffer.from("04a817c800", "hex"), + gasLimit: Buffer.from("5208", "hex"), + transaction: TW.Ethereum.Proto.Transaction.create({ + transfer: TW.Ethereum.Proto.Transaction.Transfer.create({ + amount: Buffer.from("0de0b6b3a7640000", "hex"), + }), + }), + privateKey: HexCoding.decode( + "4646464646464646464646464646464646464646464646464646464646464646" + ), + }); + + const encoded = TW.Ethereum.Proto.SigningInput.encode(input).finish(); + assert.equal( + HexCoding.encode(encoded), + "0x0a0101120109220504a817c8002a025208422a3078333533353335333533353335333533353335333533353335333533353335333533353335333533354a204646464646464646464646464646464646464646464646464646464646464646520c0a0a0a080de0b6b3a7640000" + ); + + const outputData = AnySigner.sign(encoded, CoinType.ethereum); + const output = TW.Ethereum.Proto.SigningOutput.decode(outputData); + assert.equal( + HexCoding.encode(output.encoded), + "0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83" + ); + }); + + it("test signing eip1559 erc20 transfer tx", () => { + const { HexCoding, AnySigner, CoinType } = globalThis.core;; + + const input = TW.Ethereum.Proto.SigningInput.create({ + toAddress: "0x6b175474e89094c44da98b954eedeac495271d0f", + chainId: Buffer.from("01", "hex"), + nonce: Buffer.from("00", "hex"), + txMode: TW.Ethereum.Proto.TransactionMode.Enveloped, + maxInclusionFeePerGas: Buffer.from("0077359400", "hex"), + maxFeePerGas: Buffer.from("00b2d05e00", "hex"), + gasLimit: Buffer.from("0130B9", "hex"), + transaction: TW.Ethereum.Proto.Transaction.create({ + erc20Transfer: TW.Ethereum.Proto.Transaction.ERC20Transfer.create({ + to: "0x5322b34c88ed0691971bf52a7047448f0f4efc84", + amount: Buffer.from("1bc16d674ec80000", "hex"), + }), + }), + privateKey: HexCoding.decode( + "0x608dcb1742bb3fb7aec002074e3420e4fab7d00cced79ccdac53ed5b27138151" + ), + }); + + const encoded = TW.Ethereum.Proto.SigningInput.encode(input).finish(); + const outputData = AnySigner.sign(encoded, CoinType.ethereum); + const output = TW.Ethereum.Proto.SigningOutput.decode(outputData); + assert.equal( + HexCoding.encode(output.encoded), + "0x02f8b00180847735940084b2d05e00830130b9946b175474e89094c44da98b954eedeac495271d0f80b844a9059cbb0000000000000000000000005322b34c88ed0691971bf52a7047448f0f4efc840000000000000000000000000000000000000000000000001bc16d674ec80000c080a0adfcfdf98d4ed35a8967a0c1d78b42adb7c5d831cf5a3272654ec8f8bcd7be2ea011641e065684f6aa476f4fd250aa46cd0b44eccdb0a6e1650d658d1998684cdf" + ); + }); + + it("test signing personal message", () => { + const { HexCoding, Hash, PrivateKey, Curve } = globalThis.core;; + const message = Buffer.from("Some data"); + const prefix = Buffer.from("\x19Ethereum Signed Message:\n" + message.length); + const hash = Hash.keccak256(Buffer.concat([prefix, message])); + + assert.equal(HexCoding.encode(hash), "0x1da44b586eb0729ff70a73c326926f6ed5a25f5b056e7f47fbc6e58d86871655"); + + var key = PrivateKey.createWithData(HexCoding.decode("1fcb84974220eb76e619d7208e1446ae9c0f755e97fb220a8f61c7dc03a0dfce")); + + const signature = key.sign(hash, Curve.secp256k1); + + assert.equal(HexCoding.encode(signature), "0x58156c371347613642e94b66abc4ced8e36011fb3233f5372371aa5ad321671b1a10c0b88f47ce543fd4c455761f5fbf8f61d050f57dcba986640011da794a9000"); + + key.delete(); + }); + + it("test signing EIP712 message", () => { + const { EthereumAbi, HexCoding, Hash, PrivateKey, Curve } = globalThis.core;; + + const key = PrivateKey.createWithData(Hash.keccak256(Buffer.from("cow"))); + const message = { + types: { + EIP712Domain: [ + { name: "name", type: "string" }, + { name: "version", type: "string" }, + { name: "chainId", type: "uint256" }, + { name: "verifyingContract", type: "address" }, + ], + Person: [ + { name: "name", type: "string" }, + { name: "wallet", type: "address" }, + ], + Mail: [ + { name: "from", type: "Person" }, + { name: "to", type: "Person" }, + { name: "contents", type: "string" }, + ], + }, + primaryType: "Mail", + domain: { + name: "Ether Mail", + version: "1", + chainId: "0x01", + verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + }, + message: { + from: { + name: "Cow", + wallet: "CD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + }, + to: { + name: "Bob", + wallet: "bBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + }, + contents: "Hello, Bob!", + }, + }; + + const hash = EthereumAbi.encodeTyped(JSON.stringify(message)); + const signature = key.sign(hash, Curve.secp256k1); + + assert.equal(HexCoding.encode(hash), "0xbe609aee343fb3c4b28e1df9e632fca64fcfaede20f02e86244efddf30957bd2"); + assert.equal(HexCoding.encode(signature), "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b9156201"); + + key.delete(); + }); +}); diff --git a/wasm/tests/CoinType.test.ts b/wasm/tests/CoinType.test.ts new file mode 100644 index 00000000000..d18fdf4c60e --- /dev/null +++ b/wasm/tests/CoinType.test.ts @@ -0,0 +1,22 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; + +describe("CoinType", () => { + it("test raw value", () => { + const { CoinType } = globalThis.core; + + assert.equal(CoinType.bitcoin.value, 0); + assert.equal(CoinType.litecoin.value, 2); + assert.equal(CoinType.dogecoin.value, 3); + assert.equal(CoinType.ethereum.value, 60); + assert.equal(CoinType.binance.value, 714); + assert.equal(CoinType.cosmos.value, 118); + assert.equal(CoinType.solana.value, 501); + }); +}); diff --git a/wasm/tests/HDWallet.test.ts b/wasm/tests/HDWallet.test.ts new file mode 100644 index 00000000000..567517cc74d --- /dev/null +++ b/wasm/tests/HDWallet.test.ts @@ -0,0 +1,34 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; + +describe("HDWallet", () => { + + it("test creating 24 words", () => { + const { HDWallet, Mnemonic } = globalThis.core; + + var wallet = HDWallet.create(256, "password"); + const mnemonic = wallet.mnemonic(); + + assert.equal(mnemonic.split(" ").length, 24); + assert.isTrue(Mnemonic.isValid(mnemonic)); + + wallet.delete(); + }); + + it("test deriving Ethereum address", () => { + const { HDWallet, CoinType } = globalThis.core; + + var wallet = HDWallet.createWithMnemonic("ripple scissors kick mammal hire column oak again sun offer wealth tomorrow wagon turn fatal", "TREZOR"); + const address = wallet.getAddressForCoin(CoinType.ethereum); + + assert.equal(address, "0x27Ef5cDBe01777D62438AfFeb695e33fC2335979"); + + wallet.delete(); + }); +}); diff --git a/wasm/tests/HRP.test.ts b/wasm/tests/HRP.test.ts new file mode 100644 index 00000000000..99d596e5f98 --- /dev/null +++ b/wasm/tests/HRP.test.ts @@ -0,0 +1,18 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; + +describe("HRP", () => { + it("test string value", () => { + const { HRP, describeHRP } = globalThis.core; + + assert.equal(describeHRP(HRP.bitcoin), "bc"); + assert.equal(describeHRP(HRP.binance), "bnb"); + + }); +}); diff --git a/wasm/tests/Hash.test.ts b/wasm/tests/Hash.test.ts new file mode 100644 index 00000000000..47c26e87e81 --- /dev/null +++ b/wasm/tests/Hash.test.ts @@ -0,0 +1,42 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; + +describe("Hash", () => { + it("test keccak256", () => { + const { Hash, HexCoding } = globalThis.core; + + const sha3Hash = Hash.keccak256(Buffer.from("Test keccak-256")); + + assert.equal( + HexCoding.encode(sha3Hash), + "0x9aeb50f48121c80b2ff73ad48b5f197d940f748d936d35c992367370c1abfb18" + ); + }); + + it("test sha256", () => { + const { Hash, HexCoding } = globalThis.core; + + const sha256Hash = Hash.sha256(Buffer.from("Test hash")); + assert.equal( + HexCoding.encode(sha256Hash), + "0xf250fc8f40aeea3297c0158ec1bfa07b503805f2a822530bd63c50625bb9376b" + ); + }); + + it("test sha512_256", () => { + const { Hash, HexCoding } = globalThis.core; + + const hash = Hash.sha512_256(Buffer.from("hello")); + assert.equal( + HexCoding.encode(hash), + "0xe30d87cfa2a75db545eac4d61baf970366a8357c7f72fa95b52d0accb698f13a" + ); + }); +}); diff --git a/wasm/tests/HexCoding.test.ts b/wasm/tests/HexCoding.test.ts new file mode 100644 index 00000000000..0bacf5d67ce --- /dev/null +++ b/wasm/tests/HexCoding.test.ts @@ -0,0 +1,23 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; + +describe("HexCoding", () => { + it("test encoding / decoding hex string", () => { + const { HexCoding } = globalThis.core; + + const expected = new Uint8Array([0x52, 0x8]); + const decoded = HexCoding.decode("0x5208"); + + assert.deepEqual(decoded, expected); + + const encoded = HexCoding.encode(expected); + + assert.equal(encoded, "0x5208"); + }); +}); diff --git a/wasm/tests/KeyStore+extension.test.ts b/wasm/tests/KeyStore+extension.test.ts new file mode 100644 index 00000000000..fe22c5b7091 --- /dev/null +++ b/wasm/tests/KeyStore+extension.test.ts @@ -0,0 +1,68 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import { KeyStore } from "../dist"; +import { ChromeStorageMock } from "./mock"; + +describe("KeyStore", async () => { + it("test ExtensionStorage", async () => { + const { CoinType, HexCoding } = globalThis.core; + const mnemonic = globalThis.mnemonic as string; + const password = globalThis.password as string; + + const walletIdsKey = "all-wallet-ids"; + const storage = new KeyStore.ExtensionStorage( + walletIdsKey, + new ChromeStorageMock() + ); + const keystore = new KeyStore.Default(globalThis.core, storage); + + var wallet = await keystore.import(mnemonic, "Coolw", password, [ + CoinType.bitcoin, + ]); + + assert.equal(wallet.name, "Coolw"); + assert.equal(wallet.type, "mnemonic"); + assert.equal(wallet.version, 3); + + const account = wallet.activeAccounts[0]; + const key = await keystore.getKey(wallet.id, password, account); + + assert.equal( + HexCoding.encode(key.data()), + "0xd2568511baea8dc347f14c4e0479eb8ebe29eb5f664ed796e755896250ffd11f" + ); + assert.equal(account.address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + assert.equal( + account.extendedPublicKey, + "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn" + ); + assert.equal( + account.publicKey, + "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006" + ); + + wallet = await keystore.addAccounts(wallet.id, password, [ + CoinType.ethereum, + CoinType.binance, + ]); + + assert.equal(wallet.activeAccounts.length, 3); + assert.isTrue(await keystore.hasWallet(wallet.id)); + assert.isFalse(await keystore.hasWallet("invalid-id")); + + const exported = await keystore.export(wallet.id, password); + assert.equal(exported, mnemonic); + + const wallets = await keystore.loadAll(); + + await wallets.forEach((w) => { + keystore.delete(w.id, password); + }); + }).timeout(10000); +}); diff --git a/wasm/tests/KeyStore+fs.test.ts b/wasm/tests/KeyStore+fs.test.ts new file mode 100644 index 00000000000..743a9a8e4b4 --- /dev/null +++ b/wasm/tests/KeyStore+fs.test.ts @@ -0,0 +1,70 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import * as fs from "fs"; +import { KeyStore } from "../dist"; + +describe("KeyStore", async () => { + it("test FileSystemStorage", async () => { + const { CoinType, HexCoding } = globalThis.core; + const mnemonic = globalThis.mnemonic as string; + const password = globalThis.password as string; + const testDir = "/tmp/wasm-test"; + + fs.mkdirSync(testDir, { recursive: true }); + + const storage = new KeyStore.FileSystemStorage(testDir); + const keystore = new KeyStore.Default(globalThis.core, storage); + + var wallet = await keystore.import(mnemonic, "Coolw", password, [ + CoinType.bitcoin, + ]); + const stats = fs.statSync(storage.getFilename(wallet.id)); + + assert.isTrue(stats.isFile()); + assert.isTrue(stats.size > 0); + assert.equal(wallet.name, "Coolw"); + assert.equal(wallet.type, "mnemonic"); + assert.equal(wallet.version, 3); + + const account = wallet.activeAccounts[0]; + const key = await keystore.getKey(wallet.id, password, account); + + assert.equal( + HexCoding.encode(key.data()), + "0xd2568511baea8dc347f14c4e0479eb8ebe29eb5f664ed796e755896250ffd11f" + ); + assert.equal(account.address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + assert.equal( + account.extendedPublicKey, + "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn" + ); + assert.equal( + account.publicKey, + "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006" + ); + + wallet = await keystore.addAccounts(wallet.id, password, [ + CoinType.ethereum, + CoinType.binance, + ]); + + assert.equal(wallet.activeAccounts.length, 3); + assert.isTrue(await keystore.hasWallet(wallet.id)); + assert.isFalse(await keystore.hasWallet("invalid-id")); + + const exported = await keystore.export(wallet.id, password); + assert.equal(exported, mnemonic); + + const wallets = await keystore.loadAll(); + + await wallets.forEach((w) => { + keystore.delete(w.id, password); + }); + }).timeout(10000); +}); diff --git a/wasm/tests/Mnemonic.test.ts b/wasm/tests/Mnemonic.test.ts new file mode 100644 index 00000000000..690c614aa95 --- /dev/null +++ b/wasm/tests/Mnemonic.test.ts @@ -0,0 +1,43 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; + +describe("Mnemonic", () => { + + it("test isValid", () => { + const { Mnemonic } = globalThis.core; + + assert.isTrue( + Mnemonic.isValid( + "credit expect life fade cover suit response wash pear what skull force" + ) + ); + assert.isFalse( + Mnemonic.isValid( + "ripple scissors hisc mammal hire column oak again sun offer wealth tomorrow" + ) + ); + }); + + it("test isValidWord", () => { + const { Mnemonic } = globalThis.core; + + assert.isTrue(Mnemonic.isValidWord("credit")); + + assert.isFalse(Mnemonic.isValidWord("di")); + assert.isFalse(Mnemonic.isValidWord("cr")); + assert.isFalse(Mnemonic.isValidWord("hybridous")); + }); + + it("test suggest", () => { + const { Mnemonic } = globalThis.core; + + assert.equal(Mnemonic.suggest("air"), "air airport"); + assert.equal(Mnemonic.suggest("rob"), "robot robust"); + }); +}); diff --git a/wasm/tests/PBKDF2.test.ts b/wasm/tests/PBKDF2.test.ts new file mode 100644 index 00000000000..138ff504963 --- /dev/null +++ b/wasm/tests/PBKDF2.test.ts @@ -0,0 +1,24 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; + +describe("PBKDF2", () => { + it("test sha256 hmac", () => { + const { PBKDF2, HexCoding } = globalThis.core; + + const password = Buffer.from("password"); + const salt = Buffer.from("salt"); + + const key = PBKDF2.hmacSha256(password, salt, 1, 20); + const key2 = PBKDF2.hmacSha256(password, salt, 4096, 20); + + assert.equal(HexCoding.encode(key), "0x120fb6cffcf8b32c43e7225256c4f837a86548c9"); + assert.equal(HexCoding.encode(key2), "0xc5e478d59288c841aa530db6845c4c8d962893a0"); + }); +}); diff --git a/wasm/tests/StoredKey.test.ts b/wasm/tests/StoredKey.test.ts new file mode 100644 index 00000000000..a24f8d8479f --- /dev/null +++ b/wasm/tests/StoredKey.test.ts @@ -0,0 +1,40 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import { Buffer } from "buffer"; + +describe("StoredKey", () => { + it("test importing mnemonic", () => { + const { StoredKey, CoinType } = globalThis.core; + + const mnemonic = "team engine square letter hero song dizzy scrub tornado fabric divert saddle"; + const password = Buffer.from("password"); + const storedKey = StoredKey.importHDWallet( + mnemonic, + "test wallet", + password, + CoinType.bitcoin + ); + + assert.isTrue(storedKey.isMnemonic()); + + const jsonData = storedKey.exportJSON(); + const json = JSON.parse(Buffer.from(jsonData).toString()); + + assert.equal(json.type, "mnemonic"); + assert.equal(json.name, "test wallet"); + + const account = json.activeAccounts[0]; + + assert.equal(account.address, "bc1qturc268v0f2srjh4r2zu4t6zk4gdutqd5a6zny"); + assert.equal(account.extendedPublicKey, "zpub6qbsWdbcKW9sC6shTKK4VEhfWvDCoWpfLnnVfYKHLHt31wKYUwH3aFDz4WLjZvjHZ5W4qVEyk37cRwzTbfrrT1Gnu8SgXawASnkdQ994atn"); + assert.equal(account.publicKey, "02df6fc590ab3101bbe0bb5765cbaeab9b5dcfe09ac9315d707047cbd13bc7e006"); + + storedKey.delete(); + }).timeout(5000); +}); diff --git a/wasm/tests/initWasm.test.ts b/wasm/tests/initWasm.test.ts new file mode 100644 index 00000000000..0fb9dd84fef --- /dev/null +++ b/wasm/tests/initWasm.test.ts @@ -0,0 +1,19 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import "mocha"; +import { assert } from "chai"; +import { initWasm } from "../dist"; + +describe("Module", () => { + it("load test", (done) => { + initWasm().then((WalletCore) => { + assert.isDefined(WalletCore); + assert.isNotNull(WalletCore); + done(); + }); + }).timeout(5000); +}); diff --git a/wasm/tests/mock.ts b/wasm/tests/mock.ts new file mode 100644 index 00000000000..99451f70d76 --- /dev/null +++ b/wasm/tests/mock.ts @@ -0,0 +1,53 @@ +// Copyright © 2017-2022 Trust Wallet. +// +// This file is part of Trust. The full Trust copyright notice, including +// terms governing use, modification, and redistribution, is contained in the +// file LICENSE at the root of the source code distribution tree. + +import { Events, Storage } from "webextension-polyfill"; + +export class ChromeStorageMock implements Storage.StorageArea { + object = {}; + + get( + keys?: string | string[] | Record | null | undefined + ): Promise> { + var ids: string[] = []; + if (typeof keys === "string") { + ids.push(keys); + } else if (keys instanceof Array) { + ids = ids.concat(keys); + } + + var result: Record = {}; + ids.forEach((id) => { + result[id] = this.object[id]; + }); + return Promise.resolve(result); + } + + set(items: Record): Promise { + Object.keys(items).forEach((key) => { + this.object[key] = items[key]; + }); + return Promise.resolve(); + } + + remove(keys: string | string[]): Promise { + var ids: string[] = []; + if (typeof keys === "string") { + ids.push(keys); + } + ids = ids.concat(keys); + ids.forEach((id) => delete this.object[id]); + return Promise.resolve(); + } + + clear(): Promise { + throw new Error("Method not implemented."); + } + + onChanged: Events.Event< + (changes: Storage.StorageAreaOnChangedChangesType) => void + > = {} as any; +} diff --git a/wasm/tests/setup.test.ts b/wasm/tests/setup.test.ts new file mode 100644 index 00000000000..54d6664beee --- /dev/null +++ b/wasm/tests/setup.test.ts @@ -0,0 +1,9 @@ +import "mocha"; +import { initWasm } from "../dist"; + +before(async () => { + globalThis.mnemonic = + "team engine square letter hero song dizzy scrub tornado fabric divert saddle"; + globalThis.password = "password"; + globalThis.core = await initWasm(); +}); diff --git a/wasm/tsconfig.json b/wasm/tsconfig.json new file mode 100644 index 00000000000..bcca79e9e1b --- /dev/null +++ b/wasm/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "strict": true, + "skipLibCheck": true, + "typeRoots": [ + "./node_modules/@types" + ], + "types": [ + "node", + "mocha" + ], + "noImplicitAny": false + }, + "exclude": [ + "node_modules", + "./tests/**/*.ts" + ], +} diff --git a/windows-bootstrap.ps1 b/windows-bootstrap.ps1 new file mode 100644 index 00000000000..0b29e2d3d1c --- /dev/null +++ b/windows-bootstrap.ps1 @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +# > powershell .\windows-bootstrap + +# Ported from the `bootstrap.sh` bash script +# Initializes the workspace with dependencies, then performs full build + +# Fail if any commands fails +$ErrorActionPreference = "Stop" + +#Set the position of the toolchain = $toolsPath +$toolsPath = Join-Path $PSScriptRoot "tools" + +Write-Host "#### Initializing workspace with dependencies ... ####" +& $toolsPath\windows-dependencies.ps1 + +Write-Host "#### Building and running tests ... ####" +& $toolsPath\windows-build-and-test.ps1 + +