From 28eec6ce1b606adff043885cd1f9d85124baa4a5 Mon Sep 17 00:00:00 2001 From: "Elliot." Date: Wed, 13 Aug 2025 11:00:22 -0700 Subject: [PATCH 01/66] Update .git-blame-ignore-revs for #5657 (#5675) Now that #5657 has been squashed and merged, we can add its commit hash to .git-blame-ignore-revs. --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index a72fc4afd8..a9805e705c 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -11,3 +11,4 @@ b9d007813378ad0ff45660dc07285b823c7e9855 fe9a5365b8a52d4acc42eb27369247e6f238a4f9 9a93577314e6a8d4b4a8368cc9d2b15a5d8303e8 552377c76f55b403a1c876df873a23d780fcc81c +97f0747e103f13e26e45b731731059b32f7679ac From de33a6a241ef76b049b440afbfce20a31101c186 Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 14 Aug 2025 06:07:09 -0400 Subject: [PATCH 02/66] fix: Add -Wno-deprecated-declarations for Clang only (#5680) This change adds `-Wno-deprecated-declarations` for Clang only (not for GCC) builds in `cmake/RippledCompiler.cmake`. --- cmake/RippledCompiler.cmake | 1 + include/xrpl/basics/Expected.h | 10 ---------- include/xrpl/beast/hash/hash_append.h | 27 +-------------------------- 3 files changed, 2 insertions(+), 36 deletions(-) diff --git a/cmake/RippledCompiler.cmake b/cmake/RippledCompiler.cmake index 30058fd503..bc3a62a48c 100644 --- a/cmake/RippledCompiler.cmake +++ b/cmake/RippledCompiler.cmake @@ -94,6 +94,7 @@ else () INTERFACE -Wall -Wdeprecated + $<$:-Wno-deprecated-declarations> $<$:-Wextra -Wno-unused-parameter> $<$:-Werror> -fstack-protector diff --git a/include/xrpl/basics/Expected.h b/include/xrpl/basics/Expected.h index d2440f63ab..9afb160d9d 100644 --- a/include/xrpl/basics/Expected.h +++ b/include/xrpl/basics/Expected.h @@ -22,18 +22,8 @@ #include -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - #include -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - #include namespace ripple { diff --git a/include/xrpl/beast/hash/hash_append.h b/include/xrpl/beast/hash/hash_append.h index e113567ab1..a4ffeaf30c 100644 --- a/include/xrpl/beast/hash/hash_append.h +++ b/include/xrpl/beast/hash/hash_append.h @@ -24,35 +24,10 @@ #include #include -/* - -Workaround for overzealous clang warning, which trips on libstdc++ headers - - In file included from - /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_algo.h:61: - /usr/lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/stl_tempbuf.h:263:8: - error: 'get_temporary_buffer> *>>' is deprecated - [-Werror,-Wdeprecated-declarations] 263 | - std::get_temporary_buffer(_M_original_len)); - ^ -*/ - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated" -#pragma clang diagnostic ignored "-Wdeprecated-declarations" -#endif - -#include - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - #include #include #include +#include #include #include #include From a14551b1517382ae0626471fb852ee55333edf4e Mon Sep 17 00:00:00 2001 From: "Elliot." Date: Thu, 14 Aug 2025 09:28:01 -0700 Subject: [PATCH 03/66] fix: Change log to debug level for AMM offer retrieval and IOU payment check (#5686) Reduce log noise by changing two log statements from error/warn level to debug level. These logs occur during normal operation when AMM offers are not available or when IOU authorization checks fail, which are expected scenarios that don't require an elevated log level. --- src/xrpld/app/paths/detail/AMMLiquidity.cpp | 2 +- src/xrpld/app/paths/detail/DirectStep.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xrpld/app/paths/detail/AMMLiquidity.cpp b/src/xrpld/app/paths/detail/AMMLiquidity.cpp index 83894b2e76..f24e67c7e1 100644 --- a/src/xrpld/app/paths/detail/AMMLiquidity.cpp +++ b/src/xrpld/app/paths/detail/AMMLiquidity.cpp @@ -248,7 +248,7 @@ AMMLiquidity::getOffer( return offer; } - JLOG(j_.error()) << "AMMLiquidity::getOffer, failed " + JLOG(j_.debug()) << "AMMLiquidity::getOffer, no valid offer " << ammContext_.multiPath() << " " << ammContext_.curIters() << " " << (clobQuality ? clobQuality->rate() : STAmount{}) diff --git a/src/xrpld/app/paths/detail/DirectStep.cpp b/src/xrpld/app/paths/detail/DirectStep.cpp index 4dc9cbf20d..5e62a289a3 100644 --- a/src/xrpld/app/paths/detail/DirectStep.cpp +++ b/src/xrpld/app/paths/detail/DirectStep.cpp @@ -423,7 +423,7 @@ DirectIPaymentStep::check( !((*sleLine)[sfFlags] & authField) && (*sleLine)[sfBalance] == beast::zero) { - JLOG(j_.warn()) + JLOG(j_.debug()) << "DirectStepI: can't receive IOUs from issuer without auth." << " src: " << src_; return terNO_AUTH; From d8628d481d0c439635c6ae7d26ca24f7e9c115b9 Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 14 Aug 2025 16:17:37 -0400 Subject: [PATCH 04/66] docs: Updates list of maintainers and reviewers (#5687) --- CONTRIBUTING.md | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fb29de5b7e..b0ae72ae54 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -384,9 +384,8 @@ Maintainers are users with maintain or admin access to the repo. - [bthomee](https://github.com/bthomee) (Ripple) - [intelliot](https://github.com/intelliot) (Ripple) - [JoelKatz](https://github.com/JoelKatz) (Ripple) -- [nixer89](https://github.com/nixer89) (XRP Ledger Foundation) -- [RichardAH](https://github.com/RichardAH) (XRP Ledger Foundation) -- [Silkjaer](https://github.com/Silkjaer) (XRP Ledger Foundation) +- [legleux](https://github.com/legleux) (Ripple) +- [mankins](https://github.com/mankins) (XRP Ledger Foundation) - [WietseWind](https://github.com/WietseWind) (XRPL Labs + XRP Ledger Foundation) - [ximinez](https://github.com/ximinez) (Ripple) @@ -395,27 +394,24 @@ Maintainers are users with maintain or admin access to the repo. Code Reviewers are developers who have the ability to review, approve, and in some cases merge source code changes. -- [HowardHinnant](https://github.com/HowardHinnant) (Ripple) -- [scottschurr](https://github.com/scottschurr) (Ripple) -- [seelabs](https://github.com/seelabs) (Ripple) -- [Ed Hennis](https://github.com/ximinez) (Ripple) -- [mvadari](https://github.com/mvadari) (Ripple) -- [thejohnfreeman](https://github.com/thejohnfreeman) (Ripple) +- [a1q123456](https://github.com/a1q123456) (Ripple) - [Bronek](https://github.com/Bronek) (Ripple) -- [manojsdoshi](https://github.com/manojsdoshi) (Ripple) -- [godexsoft](https://github.com/godexsoft) (Ripple) -- [mDuo13](https://github.com/mDuo13) (Ripple) -- [ckniffen](https://github.com/ckniffen) (Ripple) -- [arihantkothari](https://github.com/arihantkothari) (Ripple) -- [pwang200](https://github.com/pwang200) (Ripple) -- [sophiax851](https://github.com/sophiax851) (Ripple) -- [shawnxie999](https://github.com/shawnxie999) (Ripple) -- [gregtatcam](https://github.com/gregtatcam) (Ripple) -- [mtrippled](https://github.com/mtrippled) (Ripple) +- [bthomee](https://github.com/bthomee) (Ripple) - [ckeshava](https://github.com/ckeshava) (Ripple) -- [nbougalis](https://github.com/nbougalis) None -- [RichardAH](https://github.com/RichardAH) (XRPL Labs + XRP Ledger Foundation) - [dangell7](https://github.com/dangell7) (XRPL Labs) +- [godexsoft](https://github.com/godexsoft) (Ripple) +- [gregtatcam](https://github.com/gregtatcam) (Ripple) +- [kuznetsss](https://github.com/kuznetsss) (Ripple) +- [lmaisons](https://github.com/lmaisons) (Ripple) +- [mathbunnyru](https://github.com/mathbunnyru) (Ripple) +- [mvadari](https://github.com/mvadari) (Ripple) +- [oleks-rip](https://github.com/oleks-rip) (Ripple) +- [PeterChen13579](https://github.com/PeterChen13579) (Ripple) +- [pwang200](https://github.com/pwang200) (Ripple) +- [q73zhao](https://github.com/q73zhao) (Ripple) +- [shawnxie999](https://github.com/shawnxie999) (Ripple) +- [Tapanito](https://github.com/Tapanito) (Ripple) +- [ximinez](https://github.com/ximinez) (Ripple) Developers not on this list are able and encouraged to submit feedback on pending code changes (open pull requests). From fb89213d4db171c4bb88703d9f4dee4ea98360a5 Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Fri, 15 Aug 2025 14:50:35 -0700 Subject: [PATCH 05/66] Set version to 2.6.0-rc2 --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index fb4bc086f6..0d7ea1a7ca 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -36,7 +36,7 @@ namespace BuildInfo { // and follow the format described at http://semver.org/ //------------------------------------------------------------------------------ // clang-format off -char const* const versionString = "2.6.0-rc1" +char const* const versionString = "2.6.0-rc2" // clang-format on #if defined(DEBUG) || defined(SANITIZER) From ceb0ce5634e57eaa5207bd13da8705cb0117ebc3 Mon Sep 17 00:00:00 2001 From: Jingchen Date: Sat, 16 Aug 2025 00:27:13 +0100 Subject: [PATCH 06/66] refactor: Decouple net from xrpld and move rpc-related classes to the rpc folder (#5477) As a step of modularisation, this change moves code from `xrpld` to `libxrpl`. --- Builds/levelization/results/loops.txt | 9 -------- Builds/levelization/results/ordering.txt | 13 ++++++----- cmake/RippledCore.cmake | 10 +++++++++ cmake/RippledInstall.cmake | 1 + {src/xrpld => include/xrpl}/net/AutoSocket.h | 0 {src/xrpld => include/xrpl}/net/HTTPClient.h | 9 +++++--- .../xrpl}/net/HTTPClientSSLContext.h | 21 +++++++++--------- .../xrpl}/net/RegisterSSLCerts.h | 0 .../net/detail => libxrpl/net}/HTTPClient.cpp | 15 ++++++++----- .../net}/RegisterSSLCerts.cpp | 2 +- .../net/images/interrupt_sequence.png | Bin src/{xrpld => libxrpl}/net/images/states.png | Bin src/test/jtx/impl/Env.cpp | 10 ++++++--- src/test/jtx/impl/utility.cpp | 2 +- src/test/rpc/RPCCall_test.cpp | 2 +- src/xrpld/app/ledger/BookListeners.h | 2 +- src/xrpld/app/main/GRPCServer.h | 2 +- src/xrpld/app/main/Main.cpp | 2 +- src/xrpld/app/misc/NetworkOPs.h | 2 +- src/xrpld/app/misc/detail/WorkSSL.cpp | 7 +++++- src/xrpld/app/misc/detail/WorkSSL.h | 2 +- src/xrpld/app/paths/PathRequest.h | 2 +- src/xrpld/core/detail/Config.cpp | 5 +++-- src/xrpld/rpc/Context.h | 2 +- src/xrpld/{net => rpc}/InfoSub.h | 0 src/xrpld/{net => rpc}/RPCCall.h | 0 src/xrpld/{net => rpc}/RPCSub.h | 2 +- src/xrpld/{net => rpc}/detail/InfoSub.cpp | 2 +- src/xrpld/{net => rpc}/detail/RPCCall.cpp | 13 +++++------ src/xrpld/rpc/detail/RPCHandler.cpp | 2 +- src/xrpld/{net => rpc}/detail/RPCSub.cpp | 4 ++-- src/xrpld/rpc/detail/WSInfoSub.h | 2 +- src/xrpld/rpc/handlers/Subscribe.cpp | 2 +- 33 files changed, 83 insertions(+), 64 deletions(-) rename {src/xrpld => include/xrpl}/net/AutoSocket.h (100%) rename {src/xrpld => include/xrpl}/net/HTTPClient.h (93%) rename {src/xrpld => include/xrpl}/net/HTTPClientSSLContext.h (92%) rename {src/xrpld => include/xrpl}/net/RegisterSSLCerts.h (100%) rename src/{xrpld/net/detail => libxrpl/net}/HTTPClient.cpp (98%) rename src/{xrpld/net/detail => libxrpl/net}/RegisterSSLCerts.cpp (98%) rename src/{xrpld => libxrpl}/net/images/interrupt_sequence.png (100%) rename src/{xrpld => libxrpl}/net/images/states.png (100%) rename src/xrpld/{net => rpc}/InfoSub.h (100%) rename src/xrpld/{net => rpc}/RPCCall.h (100%) rename src/xrpld/{net => rpc}/RPCSub.h (98%) rename src/xrpld/{net => rpc}/detail/InfoSub.cpp (99%) rename src/xrpld/{net => rpc}/detail/RPCCall.cpp (99%) rename src/xrpld/{net => rpc}/detail/RPCSub.cpp (99%) diff --git a/Builds/levelization/results/loops.txt b/Builds/levelization/results/loops.txt index df1d273f93..0bbd65a9e4 100644 --- a/Builds/levelization/results/loops.txt +++ b/Builds/levelization/results/loops.txt @@ -10,9 +10,6 @@ Loop: xrpld.app xrpld.core Loop: xrpld.app xrpld.ledger xrpld.app > xrpld.ledger -Loop: xrpld.app xrpld.net - xrpld.app > xrpld.net - Loop: xrpld.app xrpld.overlay xrpld.overlay > xrpld.app @@ -25,15 +22,9 @@ Loop: xrpld.app xrpld.rpc Loop: xrpld.app xrpld.shamap xrpld.app > xrpld.shamap -Loop: xrpld.core xrpld.net - xrpld.net > xrpld.core - Loop: xrpld.core xrpld.perflog xrpld.perflog == xrpld.core -Loop: xrpld.net xrpld.rpc - xrpld.rpc ~= xrpld.net - Loop: xrpld.overlay xrpld.rpc xrpld.rpc ~= xrpld.overlay diff --git a/Builds/levelization/results/ordering.txt b/Builds/levelization/results/ordering.txt index ce22d8edb0..bf2d1db693 100644 --- a/Builds/levelization/results/ordering.txt +++ b/Builds/levelization/results/ordering.txt @@ -2,6 +2,8 @@ libxrpl.basics > xrpl.basics libxrpl.crypto > xrpl.basics libxrpl.json > xrpl.basics libxrpl.json > xrpl.json +libxrpl.net > xrpl.basics +libxrpl.net > xrpl.net libxrpl.protocol > xrpl.basics libxrpl.protocol > xrpl.json libxrpl.protocol > xrpl.protocol @@ -62,9 +64,9 @@ test.jtx > xrpl.basics test.jtx > xrpld.app test.jtx > xrpld.core test.jtx > xrpld.ledger -test.jtx > xrpld.net test.jtx > xrpld.rpc test.jtx > xrpl.json +test.jtx > xrpl.net test.jtx > xrpl.protocol test.jtx > xrpl.resource test.jtx > xrpl.server @@ -109,7 +111,6 @@ test.rpc > test.toplevel test.rpc > xrpl.basics test.rpc > xrpld.app test.rpc > xrpld.core -test.rpc > xrpld.net test.rpc > xrpld.overlay test.rpc > xrpld.rpc test.rpc > xrpl.json @@ -134,6 +135,7 @@ test.toplevel > xrpl.json test.unit_test > xrpl.basics tests.libxrpl > xrpl.basics xrpl.json > xrpl.basics +xrpl.net > xrpl.basics xrpl.protocol > xrpl.basics xrpl.protocol > xrpl.json xrpl.resource > xrpl.basics @@ -149,6 +151,7 @@ xrpld.app > xrpld.consensus xrpld.app > xrpld.nodestore xrpld.app > xrpld.perflog xrpld.app > xrpl.json +xrpld.app > xrpl.net xrpld.app > xrpl.protocol xrpld.app > xrpl.resource xrpld.conditions > xrpl.basics @@ -158,14 +161,11 @@ xrpld.consensus > xrpl.json xrpld.consensus > xrpl.protocol xrpld.core > xrpl.basics xrpld.core > xrpl.json +xrpld.core > xrpl.net xrpld.core > xrpl.protocol xrpld.ledger > xrpl.basics xrpld.ledger > xrpl.json xrpld.ledger > xrpl.protocol -xrpld.net > xrpl.basics -xrpld.net > xrpl.json -xrpld.net > xrpl.protocol -xrpld.net > xrpl.resource xrpld.nodestore > xrpl.basics xrpld.nodestore > xrpld.core xrpld.nodestore > xrpld.unity @@ -189,6 +189,7 @@ xrpld.rpc > xrpld.core xrpld.rpc > xrpld.ledger xrpld.rpc > xrpld.nodestore xrpld.rpc > xrpl.json +xrpld.rpc > xrpl.net xrpld.rpc > xrpl.protocol xrpld.rpc > xrpl.resource xrpld.rpc > xrpl.server diff --git a/cmake/RippledCore.cmake b/cmake/RippledCore.cmake index 1ef5a4ad68..83b27e6c4f 100644 --- a/cmake/RippledCore.cmake +++ b/cmake/RippledCore.cmake @@ -99,6 +99,15 @@ target_link_libraries(xrpl.libxrpl.protocol PUBLIC add_module(xrpl resource) target_link_libraries(xrpl.libxrpl.resource PUBLIC xrpl.libxrpl.protocol) +# Level 06 +add_module(xrpl net) +target_link_libraries(xrpl.libxrpl.net PUBLIC + xrpl.libxrpl.basics + xrpl.libxrpl.json + xrpl.libxrpl.protocol + xrpl.libxrpl.resource +) + add_module(xrpl server) target_link_libraries(xrpl.libxrpl.server PUBLIC xrpl.libxrpl.protocol) @@ -121,6 +130,7 @@ target_link_modules(xrpl PUBLIC protocol resource server + net ) # All headers in libxrpl are in modules. diff --git a/cmake/RippledInstall.cmake b/cmake/RippledInstall.cmake index 9ce288d785..f32781f596 100644 --- a/cmake/RippledInstall.cmake +++ b/cmake/RippledInstall.cmake @@ -19,6 +19,7 @@ install ( xrpl.libxrpl.protocol xrpl.libxrpl.resource xrpl.libxrpl.server + xrpl.libxrpl.net xrpl.libxrpl antithesis-sdk-cpp EXPORT RippleExports diff --git a/src/xrpld/net/AutoSocket.h b/include/xrpl/net/AutoSocket.h similarity index 100% rename from src/xrpld/net/AutoSocket.h rename to include/xrpl/net/AutoSocket.h diff --git a/src/xrpld/net/HTTPClient.h b/include/xrpl/net/HTTPClient.h similarity index 93% rename from src/xrpld/net/HTTPClient.h rename to include/xrpl/net/HTTPClient.h index a11b885290..ef295e8e5a 100644 --- a/src/xrpld/net/HTTPClient.h +++ b/include/xrpl/net/HTTPClient.h @@ -20,9 +20,8 @@ #ifndef RIPPLE_NET_HTTPCLIENT_H_INCLUDED #define RIPPLE_NET_HTTPCLIENT_H_INCLUDED -#include - #include +#include #include #include @@ -44,7 +43,11 @@ public: static constexpr auto maxClientHeaderBytes = kilobytes(32); static void - initializeSSLContext(Config const& config, beast::Journal j); + initializeSSLContext( + std::string const& sslVerifyDir, + std::string const& sslVerifyFile, + bool sslVerify, + beast::Journal j); static void get(bool bSSL, diff --git a/src/xrpld/net/HTTPClientSSLContext.h b/include/xrpl/net/HTTPClientSSLContext.h similarity index 92% rename from src/xrpld/net/HTTPClientSSLContext.h rename to include/xrpl/net/HTTPClientSSLContext.h index 68f91b18b0..2f7d6c005e 100644 --- a/src/xrpld/net/HTTPClientSSLContext.h +++ b/include/xrpl/net/HTTPClientSSLContext.h @@ -20,11 +20,10 @@ #ifndef RIPPLE_NET_HTTPCLIENTSSLCONTEXT_H_INCLUDED #define RIPPLE_NET_HTTPCLIENTSSLCONTEXT_H_INCLUDED -#include -#include - #include #include +#include +#include #include #include @@ -37,31 +36,33 @@ class HTTPClientSSLContext { public: explicit HTTPClientSSLContext( - Config const& config, + std::string const& sslVerifyDir, + std::string const& sslVerifyFile, + bool sslVerify, beast::Journal j, boost::asio::ssl::context_base::method method = boost::asio::ssl::context::sslv23) - : ssl_context_{method}, j_(j), verify_{config.SSL_VERIFY} + : ssl_context_{method}, j_(j), verify_{sslVerify} { boost::system::error_code ec; - if (config.SSL_VERIFY_FILE.empty()) + if (sslVerifyFile.empty()) { registerSSLCerts(ssl_context_, ec, j_); - if (ec && config.SSL_VERIFY_DIR.empty()) + if (ec && sslVerifyDir.empty()) Throw(boost::str( boost::format("Failed to set_default_verify_paths: %s") % ec.message())); } else { - ssl_context_.load_verify_file(config.SSL_VERIFY_FILE); + ssl_context_.load_verify_file(sslVerifyFile); } - if (!config.SSL_VERIFY_DIR.empty()) + if (!sslVerifyDir.empty()) { - ssl_context_.add_verify_path(config.SSL_VERIFY_DIR, ec); + ssl_context_.add_verify_path(sslVerifyDir, ec); if (ec) Throw(boost::str( diff --git a/src/xrpld/net/RegisterSSLCerts.h b/include/xrpl/net/RegisterSSLCerts.h similarity index 100% rename from src/xrpld/net/RegisterSSLCerts.h rename to include/xrpl/net/RegisterSSLCerts.h diff --git a/src/xrpld/net/detail/HTTPClient.cpp b/src/libxrpl/net/HTTPClient.cpp similarity index 98% rename from src/xrpld/net/detail/HTTPClient.cpp rename to src/libxrpl/net/HTTPClient.cpp index 901237e1e3..f7d540750a 100644 --- a/src/xrpld/net/detail/HTTPClient.cpp +++ b/src/libxrpl/net/HTTPClient.cpp @@ -17,12 +17,11 @@ */ //============================================================================== -#include -#include -#include - #include #include +#include +#include +#include #include #include @@ -36,9 +35,13 @@ namespace ripple { static std::optional httpClientSSLContext; void -HTTPClient::initializeSSLContext(Config const& config, beast::Journal j) +HTTPClient::initializeSSLContext( + std::string const& sslVerifyDir, + std::string const& sslVerifyFile, + bool sslVerify, + beast::Journal j) { - httpClientSSLContext.emplace(config, j); + httpClientSSLContext.emplace(sslVerifyDir, sslVerifyFile, sslVerify, j); } //------------------------------------------------------------------------------ diff --git a/src/xrpld/net/detail/RegisterSSLCerts.cpp b/src/libxrpl/net/RegisterSSLCerts.cpp similarity index 98% rename from src/xrpld/net/detail/RegisterSSLCerts.cpp rename to src/libxrpl/net/RegisterSSLCerts.cpp index 5a710323ad..cd5bd631aa 100644 --- a/src/xrpld/net/detail/RegisterSSLCerts.cpp +++ b/src/libxrpl/net/RegisterSSLCerts.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include +#include #if BOOST_OS_WINDOWS #include diff --git a/src/xrpld/net/images/interrupt_sequence.png b/src/libxrpl/net/images/interrupt_sequence.png similarity index 100% rename from src/xrpld/net/images/interrupt_sequence.png rename to src/libxrpl/net/images/interrupt_sequence.png diff --git a/src/xrpld/net/images/states.png b/src/libxrpl/net/images/states.png similarity index 100% rename from src/xrpld/net/images/states.png rename to src/libxrpl/net/images/states.png diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 7c17687eee..46558a188a 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -30,12 +30,12 @@ #include #include -#include -#include +#include #include #include #include +#include #include #include #include @@ -74,7 +74,11 @@ Env::AppBundle::AppBundle( auto timeKeeper_ = std::make_unique(); timeKeeper = timeKeeper_.get(); // Hack so we don't have to call Config::setup - HTTPClient::initializeSSLContext(*config, debugLog()); + HTTPClient::initializeSSLContext( + config->SSL_VERIFY_DIR, + config->SSL_VERIFY_FILE, + config->SSL_VERIFY, + debugLog()); owned = make_Application( std::move(config), std::move(logs), std::move(timeKeeper_)); app = owned.get(); diff --git a/src/test/jtx/impl/utility.cpp b/src/test/jtx/impl/utility.cpp index afa7ee8f35..27b45a32cb 100644 --- a/src/test/jtx/impl/utility.cpp +++ b/src/test/jtx/impl/utility.cpp @@ -19,7 +19,7 @@ #include -#include +#include #include #include diff --git a/src/test/rpc/RPCCall_test.cpp b/src/test/rpc/RPCCall_test.cpp index b73f2e11a0..d22896388d 100644 --- a/src/test/rpc/RPCCall_test.cpp +++ b/src/test/rpc/RPCCall_test.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include diff --git a/src/xrpld/app/ledger/BookListeners.h b/src/xrpld/app/ledger/BookListeners.h index ca58bf3058..5522ad3ec0 100644 --- a/src/xrpld/app/ledger/BookListeners.h +++ b/src/xrpld/app/ledger/BookListeners.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_APP_LEDGER_BOOKLISTENERS_H_INCLUDED #define RIPPLE_APP_LEDGER_BOOKLISTENERS_H_INCLUDED -#include +#include #include diff --git a/src/xrpld/app/main/GRPCServer.h b/src/xrpld/app/main/GRPCServer.h index 5ed4ba8454..c48138cd92 100644 --- a/src/xrpld/app/main/GRPCServer.h +++ b/src/xrpld/app/main/GRPCServer.h @@ -22,9 +22,9 @@ #include #include -#include #include #include +#include #include #include #include diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index 19c8c9910d..3fdf362dd9 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/xrpld/app/misc/NetworkOPs.h b/src/xrpld/app/misc/NetworkOPs.h index b8da7d7dc7..639cd782b7 100644 --- a/src/xrpld/app/misc/NetworkOPs.h +++ b/src/xrpld/app/misc/NetworkOPs.h @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/xrpld/app/misc/detail/WorkSSL.cpp b/src/xrpld/app/misc/detail/WorkSSL.cpp index 0285f43502..0d6801ab84 100644 --- a/src/xrpld/app/misc/detail/WorkSSL.cpp +++ b/src/xrpld/app/misc/detail/WorkSSL.cpp @@ -33,7 +33,12 @@ WorkSSL::WorkSSL( bool lastStatus, callback_type cb) : WorkBase(host, path, port, ios, lastEndpoint, lastStatus, cb) - , context_(config, j, boost::asio::ssl::context::tlsv12_client) + , context_( + config.SSL_VERIFY_DIR, + config.SSL_VERIFY_FILE, + config.SSL_VERIFY, + j, + boost::asio::ssl::context::tlsv12_client) , stream_(socket_, context_.context()) { auto ec = context_.preConnectVerify(stream_, host_); diff --git a/src/xrpld/app/misc/detail/WorkSSL.h b/src/xrpld/app/misc/detail/WorkSSL.h index 2d423a9e50..6a310986e7 100644 --- a/src/xrpld/app/misc/detail/WorkSSL.h +++ b/src/xrpld/app/misc/detail/WorkSSL.h @@ -22,9 +22,9 @@ #include #include -#include #include +#include #include #include diff --git a/src/xrpld/app/paths/PathRequest.h b/src/xrpld/app/paths/PathRequest.h index aea0e564fb..854a0f6129 100644 --- a/src/xrpld/app/paths/PathRequest.h +++ b/src/xrpld/app/paths/PathRequest.h @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index 1a07109b74..95147e23d5 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include @@ -27,6 +26,7 @@ #include #include #include +#include #include #include @@ -409,7 +409,8 @@ Config::setup( legacy("database_path", boost::filesystem::absolute(dataDir).string()); } - HTTPClient::initializeSSLContext(*this, j_); + HTTPClient::initializeSSLContext( + this->SSL_VERIFY_DIR, this->SSL_VERIFY_FILE, this->SSL_VERIFY, j_); if (RUN_STANDALONE) LEDGER_HISTORY = 0; diff --git a/src/xrpld/rpc/Context.h b/src/xrpld/rpc/Context.h index 32a7cca653..0b1a8dfbf5 100644 --- a/src/xrpld/rpc/Context.h +++ b/src/xrpld/rpc/Context.h @@ -21,7 +21,7 @@ #define RIPPLE_RPC_CONTEXT_H_INCLUDED #include -#include +#include #include #include diff --git a/src/xrpld/net/InfoSub.h b/src/xrpld/rpc/InfoSub.h similarity index 100% rename from src/xrpld/net/InfoSub.h rename to src/xrpld/rpc/InfoSub.h diff --git a/src/xrpld/net/RPCCall.h b/src/xrpld/rpc/RPCCall.h similarity index 100% rename from src/xrpld/net/RPCCall.h rename to src/xrpld/rpc/RPCCall.h diff --git a/src/xrpld/net/RPCSub.h b/src/xrpld/rpc/RPCSub.h similarity index 98% rename from src/xrpld/net/RPCSub.h rename to src/xrpld/rpc/RPCSub.h index 9730ca2dec..0f106be018 100644 --- a/src/xrpld/net/RPCSub.h +++ b/src/xrpld/rpc/RPCSub.h @@ -21,7 +21,7 @@ #define RIPPLE_NET_RPCSUB_H_INCLUDED #include -#include +#include #include diff --git a/src/xrpld/net/detail/InfoSub.cpp b/src/xrpld/rpc/detail/InfoSub.cpp similarity index 99% rename from src/xrpld/net/detail/InfoSub.cpp rename to src/xrpld/rpc/detail/InfoSub.cpp index 9f394cf08e..de00f518a5 100644 --- a/src/xrpld/net/detail/InfoSub.cpp +++ b/src/xrpld/rpc/detail/InfoSub.cpp @@ -17,7 +17,7 @@ */ //============================================================================== -#include +#include namespace ripple { diff --git a/src/xrpld/net/detail/RPCCall.cpp b/src/xrpld/rpc/detail/RPCCall.cpp similarity index 99% rename from src/xrpld/net/detail/RPCCall.cpp rename to src/xrpld/rpc/detail/RPCCall.cpp index 0cc3cb6618..aa8c80fff7 100644 --- a/src/xrpld/net/detail/RPCCall.cpp +++ b/src/xrpld/rpc/detail/RPCCall.cpp @@ -17,12 +17,8 @@ */ //============================================================================== -#include -#include -#include -#include +#include #include -#include #include #include @@ -33,7 +29,10 @@ #include #include #include +#include +#include #include +#include #include #include #include @@ -160,7 +159,7 @@ private: std::string const& strPk, TokenType type = TokenType::AccountPublic) { - if (parseBase58(type, strPk)) + if (parseBase58(type, strPk)) return true; auto pkHex = strUnHex(strPk); @@ -1508,7 +1507,7 @@ rpcClient( } else { - ServerHandler::Setup setup; + ripple::ServerHandler::Setup setup; try { setup = setup_ServerHandler( diff --git a/src/xrpld/rpc/detail/RPCHandler.cpp b/src/xrpld/rpc/detail/RPCHandler.cpp index c261666eb9..b2e4c2c440 100644 --- a/src/xrpld/rpc/detail/RPCHandler.cpp +++ b/src/xrpld/rpc/detail/RPCHandler.cpp @@ -24,9 +24,9 @@ #include #include #include -#include #include #include +#include #include #include #include diff --git a/src/xrpld/net/detail/RPCSub.cpp b/src/xrpld/rpc/detail/RPCSub.cpp similarity index 99% rename from src/xrpld/net/detail/RPCSub.cpp rename to src/xrpld/rpc/detail/RPCSub.cpp index 3f0c923e13..966ad6df4b 100644 --- a/src/xrpld/net/detail/RPCSub.cpp +++ b/src/xrpld/rpc/detail/RPCSub.cpp @@ -17,8 +17,8 @@ */ //============================================================================== -#include -#include +#include +#include #include #include diff --git a/src/xrpld/rpc/detail/WSInfoSub.h b/src/xrpld/rpc/detail/WSInfoSub.h index 1652617455..030eac318e 100644 --- a/src/xrpld/rpc/detail/WSInfoSub.h +++ b/src/xrpld/rpc/detail/WSInfoSub.h @@ -20,7 +20,7 @@ #ifndef RIPPLE_RPC_WSINFOSUB_H #define RIPPLE_RPC_WSINFOSUB_H -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/Subscribe.cpp b/src/xrpld/rpc/handlers/Subscribe.cpp index e71d973b7b..c089f0255d 100644 --- a/src/xrpld/rpc/handlers/Subscribe.cpp +++ b/src/xrpld/rpc/handlers/Subscribe.cpp @@ -21,8 +21,8 @@ #include #include #include -#include #include +#include #include #include From dc1caa41b2577f5bfd4601aa3590a324f1be8a34 Mon Sep 17 00:00:00 2001 From: Bart Date: Mon, 18 Aug 2025 10:21:43 -0400 Subject: [PATCH 07/66] refactor: Revamp CI workflows (#5661) This change refactors the CI workflows to leverage the new CI Docker images for Debian, Red Hat, and Ubuntu. --- .github/actions/build-deps/action.yml | 71 +++ .github/actions/build-test/action.yml | 102 +++++ .github/actions/build/action.yml | 34 -- .github/actions/dependencies/action.yml | 38 -- .../scripts}/levelization/README.md | 10 +- .../scripts/levelization/generate.sh | 4 +- .../scripts}/levelization/results/loops.txt | 0 .../levelization/results/ordering.txt | 0 .github/scripts/strategy-matrix/generate.py | 153 +++++++ .github/scripts/strategy-matrix/linux.json | 170 +++++++ .github/scripts/strategy-matrix/macos.json | 29 ++ .github/scripts/strategy-matrix/windows.json | 26 ++ .github/workflows/build-test.yml | 191 ++++++++ .github/workflows/check-format.yml | 112 +++++ .github/workflows/check-levelization.yml | 46 ++ .github/workflows/check-missing-commits.yml | 62 +++ .github/workflows/clang-format.yml | 64 --- .github/workflows/doxygen.yml | 37 -- .github/workflows/levelization.yml | 53 --- .github/workflows/libxrpl.yml | 91 ---- .github/workflows/macos.yml | 112 ----- .github/workflows/missing-commits.yml | 60 --- .github/workflows/nix.yml | 422 ------------------ .github/workflows/notify-clio.yml | 80 ++++ .github/workflows/on-pr.yml | 133 ++++++ .github/workflows/on-trigger.yml | 140 ++++++ .github/workflows/publish-docs.yml | 58 +++ .github/workflows/windows.yml | 106 ----- .gitignore | 7 +- .prettierignore | 2 + CONTRIBUTING.md | 2 +- README.md | 2 +- conan/global.conf | 9 + external/README.md | 3 +- 34 files changed, 1397 insertions(+), 1032 deletions(-) create mode 100644 .github/actions/build-deps/action.yml create mode 100644 .github/actions/build-test/action.yml delete mode 100644 .github/actions/build/action.yml delete mode 100644 .github/actions/dependencies/action.yml rename {Builds => .github/scripts}/levelization/README.md (95%) rename Builds/levelization/levelization.sh => .github/scripts/levelization/generate.sh (98%) rename {Builds => .github/scripts}/levelization/results/loops.txt (100%) rename {Builds => .github/scripts}/levelization/results/ordering.txt (100%) create mode 100644 .github/scripts/strategy-matrix/generate.py create mode 100644 .github/scripts/strategy-matrix/linux.json create mode 100644 .github/scripts/strategy-matrix/macos.json create mode 100644 .github/scripts/strategy-matrix/windows.json create mode 100644 .github/workflows/build-test.yml create mode 100644 .github/workflows/check-format.yml create mode 100644 .github/workflows/check-levelization.yml create mode 100644 .github/workflows/check-missing-commits.yml delete mode 100644 .github/workflows/clang-format.yml delete mode 100644 .github/workflows/doxygen.yml delete mode 100644 .github/workflows/levelization.yml delete mode 100644 .github/workflows/libxrpl.yml delete mode 100644 .github/workflows/macos.yml delete mode 100644 .github/workflows/missing-commits.yml delete mode 100644 .github/workflows/nix.yml create mode 100644 .github/workflows/notify-clio.yml create mode 100644 .github/workflows/on-pr.yml create mode 100644 .github/workflows/on-trigger.yml create mode 100644 .github/workflows/publish-docs.yml delete mode 100644 .github/workflows/windows.yml create mode 100644 .prettierignore create mode 100644 conan/global.conf diff --git a/.github/actions/build-deps/action.yml b/.github/actions/build-deps/action.yml new file mode 100644 index 0000000000..12d80e859c --- /dev/null +++ b/.github/actions/build-deps/action.yml @@ -0,0 +1,71 @@ +# This action installs and optionally uploads Conan dependencies to a remote +# repository. The dependencies will only be uploaded if the credentials are +# provided. +name: Build Conan dependencies + +inputs: + build_dir: + description: 'The directory where to build.' + required: true + type: string + build_type: + description: 'The build type to use.' + required: true + type: choice + options: + - 'Debug' + - 'Release' + conan_remote_name: + description: 'The name of the Conan remote to use.' + required: true + type: string + conan_remote_url: + description: 'The URL of the Conan endpoint to use.' + required: true + type: string + conan_remote_username: + description: 'The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' + required: false + type: string + default: '' + conan_remote_password: + description: 'The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' + required: false + type: string + default: '' + force_build: + description: 'Force building of all dependencies.' + required: false + type: boolean + default: false + force_upload: + description: 'Force uploading of all dependencies.' + required: false + type: boolean + default: false + +runs: + using: composite + steps: + - name: Install Conan dependencies + shell: bash + run: | + echo 'Installing dependencies.' + mkdir -p ${{ inputs.build_dir }} + cd ${{ inputs.build_dir }} + conan install \ + --output-folder . \ + --build ${{ inputs.force_build && '"*"' || 'missing' }} \ + --options:host '&:tests=True' \ + --options:host '&:xrpld=True' \ + --settings:all build_type=${{ inputs.build_type }} \ + --format=json .. + - name: Upload Conan dependencies + if: ${{ inputs.conan_remote_username && inputs.conan_remote_password }} + shell: bash + working-directory: ${{ inputs.build_dir }} + run: | + echo "Logging into Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." + conan remote login ${{ inputs.conan_remote_name }} "${{ inputs.conan_remote_username }}" --password "${{ inputs.conan_remote_password }}" + echo 'Uploading dependencies.' + conan upload '*' --confirm --check ${{ inputs.force_upload && '--force' || '' }} --remote=${{ inputs.conan_remote_name }} diff --git a/.github/actions/build-test/action.yml b/.github/actions/build-test/action.yml new file mode 100644 index 0000000000..30337ddb98 --- /dev/null +++ b/.github/actions/build-test/action.yml @@ -0,0 +1,102 @@ +# This action build and tests the binary. The Conan dependencies must have +# already been installed (see the build-deps action). +name: Build and Test + +inputs: + build_dir: + description: 'The directory where to build.' + required: true + type: string + build_type: + description: 'The build type to use.' + required: true + type: choice + options: + - 'Debug' + - 'Release' + cmake_args: + description: 'Additional arguments to pass to CMake.' + required: false + type: string + default: '' + cmake_target: + description: 'The CMake target to build.' + required: true + type: string + codecov_token: + description: 'The Codecov token to use for uploading coverage reports.' + required: false + type: string + default: '' + os: + description: 'The operating system to use for the build (linux, macos, or windows).' + required: true + type: choice + options: + - 'linux' + - 'macos' + - 'windows' + +runs: + using: composite + steps: + - name: Configure CMake + shell: bash + working-directory: ${{ inputs.build_dir }} + run: | + echo 'Configuring CMake.' + cmake \ + -G '${{ inputs.os == 'windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ + -DCMAKE_BUILD_TYPE=${{ inputs.build_type }} \ + ${{ inputs.cmake_args }} \ + .. + - name: Build the binary + shell: bash + working-directory: ${{ inputs.build_dir }} + run: | + echo 'Building binary.' + cmake \ + --build . \ + --config ${{ inputs.build_type }} \ + --parallel $(nproc) \ + --target ${{ inputs.cmake_target }} + - name: Check linking + if: ${{ inputs.os == 'linux' }} + shell: bash + working-directory: ${{ inputs.build_dir }} + run: | + echo 'Checking linking.' + ldd ./rippled + if [ "$(ldd ./rippled | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then + echo 'The binary is statically linked.' + else + echo 'The binary is dynamically linked.' + exit 1 + fi + - name: Verify voidstar + if: ${{ contains(inputs.cmake_args, '-Dvoidstar=ON') }} + shell: bash + working-directory: ${{ inputs.build_dir }} + run: | + echo 'Verifying presence of instrumentation.' + ./rippled --version | grep libvoidstar + - name: Test the binary + if: ${{ inputs.cmake_target != 'coverage' }} + shell: bash + working-directory: ${{ inputs.build_dir }}/${{ inputs.os == 'windows' && inputs.build_type || '' }} + run: | + echo 'Testing binary.' + ./rippled --unittest --unittest-jobs $(nproc) + ctest -j $(nproc) --output-on-failure + - name: Upload coverage report + if: ${{ inputs.cmake_target == 'coverage' && inputs.codecov_token }} + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 + with: + disable_search: true + disable_telem: true + fail_ci_if_error: true + files: ${{ inputs.build_dir }}/coverage.xml + plugins: noop + token: ${{ inputs.codecov_token }} + verbose: true diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml deleted file mode 100644 index 6714369155..0000000000 --- a/.github/actions/build/action.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: build -inputs: - generator: - default: null - configuration: - required: true - cmake-args: - default: null - cmake-target: - default: all -# An implicit input is the environment variable `build_dir`. -runs: - using: composite - steps: - - name: configure - shell: bash - run: | - cd ${build_dir} - cmake \ - ${{ inputs.generator && format('-G "{0}"', inputs.generator) || '' }} \ - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ - -DCMAKE_BUILD_TYPE=${{ inputs.configuration }} \ - -Dtests=TRUE \ - -Dxrpld=TRUE \ - ${{ inputs.cmake-args }} \ - .. - - name: build - shell: bash - run: | - cmake \ - --build ${build_dir} \ - --config ${{ inputs.configuration }} \ - --parallel ${NUM_PROCESSORS:-$(nproc)} \ - --target ${{ inputs.cmake-target }} diff --git a/.github/actions/dependencies/action.yml b/.github/actions/dependencies/action.yml deleted file mode 100644 index 0bd28f15dd..0000000000 --- a/.github/actions/dependencies/action.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: dependencies -inputs: - configuration: - required: true -# Implicit inputs are the environment variables `build_dir`, CONAN_REMOTE_URL, -# CONAN_REMOTE_USERNAME, and CONAN_REMOTE_PASSWORD. The latter two are only -# used to upload newly built dependencies to the Conan remote. -runs: - using: composite - steps: - - name: add Conan remote - if: ${{ env.CONAN_REMOTE_URL != '' }} - shell: bash - run: | - echo "Adding Conan remote 'xrplf' at ${{ env.CONAN_REMOTE_URL }}." - conan remote add --index 0 --force xrplf ${{ env.CONAN_REMOTE_URL }} - echo "Listing Conan remotes." - conan remote list - - name: install dependencies - shell: bash - run: | - mkdir -p ${{ env.build_dir }} - cd ${{ env.build_dir }} - conan install \ - --output-folder . \ - --build missing \ - --options:host "&:tests=True" \ - --options:host "&:xrpld=True" \ - --settings:all build_type=${{ inputs.configuration }} \ - .. - - name: upload dependencies - if: ${{ env.CONAN_REMOTE_URL != '' && env.CONAN_REMOTE_USERNAME != '' && env.CONAN_REMOTE_PASSWORD != '' && github.ref_type == 'branch' && github.ref_name == github.event.repository.default_branch }} - shell: bash - run: | - echo "Logging into Conan remote 'xrplf' at ${{ env.CONAN_REMOTE_URL }}." - conan remote login xrplf "${{ env.CONAN_REMOTE_USERNAME }}" --password "${{ env.CONAN_REMOTE_PASSWORD }}" - echo "Uploading dependencies." - conan upload '*' --confirm --check --remote xrplf diff --git a/Builds/levelization/README.md b/.github/scripts/levelization/README.md similarity index 95% rename from Builds/levelization/README.md rename to .github/scripts/levelization/README.md index 93aa316b61..ec41a021cc 100644 --- a/Builds/levelization/README.md +++ b/.github/scripts/levelization/README.md @@ -50,7 +50,7 @@ that `test` code should _never_ be included in `ripple` code.) ## Validation -The [levelization.sh](levelization.sh) script takes no parameters, +The [levelization](generate.sh) script takes no parameters, reads no environment variables, and can be run from any directory, as long as it is in the expected location in the rippled repo. It can be run at any time from within a checked out repo, and will @@ -72,15 +72,15 @@ It generates many files of [results](results): desired as described above. In a perfect repo, this file will be empty. This file is committed to the repo, and is used by the [levelization - Github workflow](../../.github/workflows/levelization.yml) to validate + Github workflow](../../workflows/check-levelization.yml) to validate that nothing changed. - [`ordering.txt`](results/ordering.txt): A list showing relationships between modules where there are no loops as they actually exist, as opposed to how they are desired as described above. This file is committed to the repo, and is used by the [levelization - Github workflow](../../.github/workflows/levelization.yml) to validate + Github workflow](../../workflows/check-levelization.yml) to validate that nothing changed. -- [`levelization.yml`](../../.github/workflows/levelization.yml) +- [`levelization.yml`](../../workflows/check-levelization.yml) Github Actions workflow to test that levelization loops haven't changed. Unfortunately, if changes are detected, it can't tell if they are improvements or not, so if you have resolved any issues or @@ -111,4 +111,4 @@ get those details locally. 1. Run `levelization.sh` 2. Grep the modules in `paths.txt`. - For example, if a cycle is found `A ~= B`, simply `grep -w -A Builds/levelization/results/paths.txt | grep -w B` + A .github/scripts/levelization/results/paths.txt | grep -w B` diff --git a/Builds/levelization/levelization.sh b/.github/scripts/levelization/generate.sh similarity index 98% rename from Builds/levelization/levelization.sh rename to .github/scripts/levelization/generate.sh index c18ca703f7..775ddf789f 100755 --- a/Builds/levelization/levelization.sh +++ b/.github/scripts/levelization/generate.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Usage: levelization.sh +# Usage: generate.sh # This script takes no parameters, reads no environment variables, # and can be run from any directory, as long as it is in the expected # location in the repo. @@ -19,7 +19,7 @@ export LANG=C rm -rfv results mkdir results includes="$( pwd )/results/rawincludes.txt" -pushd ../.. +pushd ../../.. echo Raw includes: grep -r '^[ ]*#include.*/.*\.h' include src | \ grep -v boost | tee ${includes} diff --git a/Builds/levelization/results/loops.txt b/.github/scripts/levelization/results/loops.txt similarity index 100% rename from Builds/levelization/results/loops.txt rename to .github/scripts/levelization/results/loops.txt diff --git a/Builds/levelization/results/ordering.txt b/.github/scripts/levelization/results/ordering.txt similarity index 100% rename from Builds/levelization/results/ordering.txt rename to .github/scripts/levelization/results/ordering.txt diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py new file mode 100644 index 0000000000..a5180c942d --- /dev/null +++ b/.github/scripts/strategy-matrix/generate.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +import argparse +import itertools +import json +import re + +''' +Generate a strategy matrix for GitHub Actions CI. + +On each PR commit we will build a selection of Debian, RHEL, Ubuntu, MacOS, and +Windows configurations, while upon merge into the develop, release, or master +branches, we will build all configurations. + +We will further set additional CMake arguments as follows: +- All builds will have the `tests`, `werr`, and `xrpld` options. +- All builds will have the `wextra` option except for GCC 12 and Clang 16. +- All release builds will have the `assert` option. +- Certain Debian Bookworm configurations will change the reference fee, enable + codecov, and enable voidstar in PRs. +''' +def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict], build_type: list[str], cmake_args: list[str]) -> dict: + configurations = [] + for architecture, os, build_type, cmake_args in itertools.product(architecture, os, build_type, cmake_args): + # The default CMake target is 'all' for Linux and MacOS and 'install' + # for Windows, but it can get overridden for certain configurations. + cmake_target = 'install' if os["distro_name"] == 'windows' else 'all' + + # Only generate a subset of configurations in PRs. + if not all: + # Debian: + # - Bookworm using GCC 13: Release and Unity on linux/arm64, set + # the reference fee to 500. + # - Bookworm using GCC 15: Debug and no Unity on linux/amd64, enable + # code coverage. + # - Bookworm using Clang 16: Debug and no Unity on linux/arm64, + # enable voidstar. + # - Bookworm using Clang 17: Release and no Unity on linux/amd64, + # set the reference fee to 1000. + # - Bookworm using Clang 20: Debug and Unity on linux/amd64. + if os['distro_name'] == 'debian': + skip = True + if os['distro_version'] == 'bookworm': + if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-13' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/arm64': + cmake_args = f'{cmake_args} -DUNIT_TEST_REFERENCE_FEE=500' + skip = False + if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64': + cmake_args = f'{cmake_args} -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0' + cmake_target = 'coverage' + skip = False + if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-16' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/arm64': + cmake_args = f'{cmake_args} -Dvoidstar=ON' + skip = False + if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-17' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/amd64': + cmake_args = f'{cmake_args} -DUNIT_TEST_REFERENCE_FEE=1000' + skip = False + if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-20' and build_type == 'Debug' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/amd64': + skip = False + if skip: + continue + + # RHEL: + # - 9.4 using GCC 12: Debug and Unity on linux/amd64. + # - 9.6 using Clang: Release and no Unity on linux/amd64. + if os['distro_name'] == 'rhel': + skip = True + if os['distro_version'] == '9.4': + if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-12' and build_type == 'Debug' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/amd64': + skip = False + elif os['distro_version'] == '9.6': + if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-any' and build_type == 'Release' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64': + skip = False + if skip: + continue + + # Ubuntu: + # - Jammy using GCC 12: Debug and no Unity on linux/arm64. + # - Noble using GCC 14: Release and Unity on linux/amd64. + # - Noble using Clang 18: Debug and no Unity on linux/amd64. + # - Noble using Clang 19: Release and Unity on linux/arm64. + if os['distro_name'] == 'ubuntu': + skip = True + if os['distro_version'] == 'jammy': + if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-12' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/arm64': + skip = False + elif os['distro_version'] == 'noble': + if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-14' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/amd64': + skip = False + if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-18' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64': + skip = False + if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-19' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/arm64': + skip = False + if skip: + continue + + # MacOS: + # - Debug and no Unity on macos/arm64. + if os['distro_name'] == 'macos' and not (build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'macos/arm64'): + continue + + # Windows: + # - Release and Unity on windows/amd64. + if os['distro_name'] == 'windows' and not (build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'windows/amd64'): + continue + + + # Additional CMake arguments. + cmake_args = f'{cmake_args} -Dtests=ON -Dwerr=ON -Dxrpld=ON' + if not f'{os['compiler_name']}-{os['compiler_version']}' in ['gcc-12', 'clang-16']: + cmake_args = f'{cmake_args} -Dwextra=ON' + if build_type == 'Release': + cmake_args = f'{cmake_args} -Dassert=ON' + + # Generate a unique name for the configuration, e.g. macos-arm64-debug + # or debian-bookworm-gcc-12-amd64-release-unity. + config_name = os['distro_name'] + if (n := os['distro_version']) != '': + config_name += f'-{n}' + if (n := os['compiler_name']) != '': + config_name += f'-{n}' + if (n := os['compiler_version']) != '': + config_name += f'-{n}' + config_name += f'-{architecture['platform'][architecture['platform'].find('/')+1:]}' + config_name += f'-{build_type.lower()}' + if '-Dunity=ON' in cmake_args: + config_name += '-unity' + + configurations.append({ + 'architecture': architecture, + 'os': os, + 'build_type': build_type, + 'cmake_args': cmake_args, + 'cmake_target': cmake_target, + 'config_name': config_name, + }) + + return {'include': configurations} + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-a', '--all', help='Set to generate all configurations (generally used when merging a PR) or leave unset to generate a subset of configurations (generally used when committing to a PR).', action="store_true") + parser.add_argument('-c', '--config', help='Path to the JSON file containing the strategy matrix configurations.', required=True, type=str) + args = parser.parse_args() + + # Load the JSON configuration file. + config = None + with open(args.config, 'r') as f: + config = json.load(f) + if config['architecture'] is None or config['os'] is None or config['build_type'] is None or config['cmake_args'] is None: + raise Exception('Invalid configuration file.') + + # Generate the strategy matrix. + print(f'matrix={json.dumps(generate_strategy_matrix(args.all, config['architecture'], config['os'], config['build_type'], config['cmake_args']))}') diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json new file mode 100644 index 0000000000..d8f176273d --- /dev/null +++ b/.github/scripts/strategy-matrix/linux.json @@ -0,0 +1,170 @@ +{ + "architecture": [ + { + "platform": "linux/amd64", + "runner": [ + "self-hosted", + "Linux", + "X64", + "heavy" + ] + }, + { + "platform": "linux/arm64", + "runner": [ + "self-hosted", + "Linux", + "ARM64", + "heavy-arm64" + ] + } + ], + "os": [ + { + "distro_name": "debian", + "distro_version": "bookworm", + "compiler_name": "gcc", + "compiler_version": "12" + }, + { + "distro_name": "debian", + "distro_version": "bookworm", + "compiler_name": "gcc", + "compiler_version": "13" + }, + { + "distro_name": "debian", + "distro_version": "bookworm", + "compiler_name": "gcc", + "compiler_version": "14" + }, + { + "distro_name": "debian", + "distro_version": "bookworm", + "compiler_name": "gcc", + "compiler_version": "15" + }, + { + "distro_name": "debian", + "distro_version": "bookworm", + "compiler_name": "clang", + "compiler_version": "16" + }, + { + "distro_name": "debian", + "distro_version": "bookworm", + "compiler_name": "clang", + "compiler_version": "17" + }, + { + "distro_name": "debian", + "distro_version": "bookworm", + "compiler_name": "clang", + "compiler_version": "18" + }, + { + "distro_name": "debian", + "distro_version": "bookworm", + "compiler_name": "clang", + "compiler_version": "19" + }, + { + "distro_name": "debian", + "distro_version": "bookworm", + "compiler_name": "clang", + "compiler_version": "20" + }, + { + "distro_name": "rhel", + "distro_version": "9.4", + "compiler_name": "gcc", + "compiler_version": "12" + }, + { + "distro_name": "rhel", + "distro_version": "9.4", + "compiler_name": "gcc", + "compiler_version": "13" + }, + { + "distro_name": "rhel", + "distro_version": "9.4", + "compiler_name": "gcc", + "compiler_version": "14" + }, + { + "distro_name": "rhel", + "distro_version": "9.6", + "compiler_name": "gcc", + "compiler_version": "13" + }, + { + "distro_name": "rhel", + "distro_version": "9.6", + "compiler_name": "gcc", + "compiler_version": "14" + }, + { + "distro_name": "rhel", + "distro_version": "9.4", + "compiler_name": "clang", + "compiler_version": "any" + }, + { + "distro_name": "rhel", + "distro_version": "9.6", + "compiler_name": "clang", + "compiler_version": "any" + }, + { + "distro_name": "ubuntu", + "distro_version": "jammy", + "compiler_name": "gcc", + "compiler_version": "12" + }, + { + "distro_name": "ubuntu", + "distro_version": "noble", + "compiler_name": "gcc", + "compiler_version": "13" + }, + { + "distro_name": "ubuntu", + "distro_version": "noble", + "compiler_name": "gcc", + "compiler_version": "14" + }, + { + "distro_name": "ubuntu", + "distro_version": "noble", + "compiler_name": "clang", + "compiler_version": "16" + }, + { + "distro_name": "ubuntu", + "distro_version": "noble", + "compiler_name": "clang", + "compiler_version": "17" + }, + { + "distro_name": "ubuntu", + "distro_version": "noble", + "compiler_name": "clang", + "compiler_version": "18" + }, + { + "distro_name": "ubuntu", + "distro_version": "noble", + "compiler_name": "clang", + "compiler_version": "19" + } + ], + "build_type": [ + "Debug", + "Release" + ], + "cmake_args": [ + "-Dunity=OFF", + "-Dunity=ON" + ] +} diff --git a/.github/scripts/strategy-matrix/macos.json b/.github/scripts/strategy-matrix/macos.json new file mode 100644 index 0000000000..a6ffdf14b7 --- /dev/null +++ b/.github/scripts/strategy-matrix/macos.json @@ -0,0 +1,29 @@ +{ + "architecture": [ + { + "platform": "macos/arm64", + "runner": [ + "self-hosted", + "macOS", + "ARM64", + "mac-runner-m1" + ] + } + ], + "os": [ + { + "distro_name": "macos", + "distro_version": "", + "compiler_name": "", + "compiler_version": "" + } + ], + "build_type": [ + "Debug", + "Release" + ], + "cmake_args": [ + "-Dunity=OFF -DCMAKE_POLICY_VERSION_MINIMUM=3.5", + "-Dunity=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5" + ] +} diff --git a/.github/scripts/strategy-matrix/windows.json b/.github/scripts/strategy-matrix/windows.json new file mode 100644 index 0000000000..aaa8c94411 --- /dev/null +++ b/.github/scripts/strategy-matrix/windows.json @@ -0,0 +1,26 @@ +{ + "architecture": [ + { + "platform": "windows/amd64", + "runner": [ + "windows-latest" + ] + } + ], + "os": [ + { + "distro_name": "windows", + "distro_version": "", + "compiler_name": "", + "compiler_version": "" + } + ], + "build_type": [ + "Debug", + "Release" + ], + "cmake_args": [ + "-Dunity=OFF", + "-Dunity=ON" + ] +} diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 0000000000..f3b3374090 --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,191 @@ +# This workflow builds and tests the binary for various configurations. +name: Build and test + +# This workflow can only be triggered by other workflows. +on: + workflow_call: + inputs: + build_dir: + description: 'The directory where to build.' + required: false + type: string + default: '.build' + conan_remote_name: + description: 'The name of the Conan remote to use.' + required: true + type: string + conan_remote_url: + description: 'The URL of the Conan endpoint to use.' + required: true + type: string + dependencies_force_build: + description: 'Force building of all dependencies.' + required: false + type: boolean + default: false + dependencies_force_upload: + description: 'Force uploading of all dependencies.' + required: false + type: boolean + default: false + os: + description: 'The operating system to use for the build (linux, macos, or windows).' + required: true + type: string + strategy_matrix_all: + description: 'Generate a strategy matrix containing all configurations.' + required: false + type: boolean + default: false + secrets: + codecov_token: + description: 'The Codecov token to use for uploading coverage reports.' + required: false + conan_remote_username: + description: 'The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' + required: false + conan_remote_password: + description: 'The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' + required: false + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.os }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + # Generate the strategy matrix to be used by the following job. + generate-matrix: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: 3.13 + - name: Generate strategy matrix + working-directory: .github/scripts/strategy-matrix + id: generate + run: python generate.py ${{ inputs.strategy_matrix_all && '--all' || '' }} --config=${{ inputs.os }}.json | tee "${GITHUB_OUTPUT}" + outputs: + matrix: ${{ steps.generate.outputs.matrix }} + + # Build and test the binary. + build-test: + needs: + - generate-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} + runs-on: ${{ matrix.architecture.runner }} + container: ${{ inputs.os == 'linux' && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }} + steps: + - name: Clean workspace (MacOS) + if: ${{ inputs.os == 'macos' }} + run: | + WORKSPACE=${{ github.workspace }} + echo "Cleaning workspace '${WORKSPACE}'." + if [ -z "${WORKSPACE}" ] || [ "${WORKSPACE}" = "/" ]; then + echo "Invalid working directory '${WORKSPACE}'." + exit 1 + fi + find "${WORKSPACE}" -depth 1 | xargs rm -rfv + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Set up Python (Windows) + if: ${{ inputs.os == 'windows' }} + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: 3.13 + - name: Install Conan (Windows) + if: ${{ inputs.os == 'windows' }} + run: pip install wheel conan + - name: Check configuration (Windows) + if: ${{ inputs.os == 'windows' }} + run: | + echo 'Checking environment variables.' + set + + echo 'Checking CMake version.' + cmake --version + + echo 'Checking Conan version.' + conan --version + - name: Check configuration (Linux and MacOS) + if: ${{ inputs.os == 'linux' || inputs.os == 'macos' }} + run: | + echo 'Checking path.' + echo ${PATH} | tr ':' '\n' + + echo 'Checking environment variables.' + env | sort + + echo 'Checking CMake version.' + cmake --version + + echo 'Checking compiler version.' + ${{ inputs.os == 'linux' && '${CC}' || 'clang' }} --version + + echo 'Checking Conan version.' + conan --version + + echo 'Checking Ninja version.' + ninja --version + - name: Set up Conan home directory (MacOS) + if: ${{ inputs.os == 'macos' }} + run: | + echo 'Setting up Conan home directory.' + export CONAN_HOME=${{ github.workspace }}/.conan + mkdir -p ${CONAN_HOME} + - name: Set up Conan home directory (Windows) + if: ${{ inputs.os == 'windows' }} + run: | + echo 'Setting up Conan home directory.' + set CONAN_HOME=${{ github.workspace }}\.conan + mkdir -p %CONAN_HOME% + - name: Set up Conan configuration + run: | + echo 'Installing configuration.' + cat conan/global.conf ${{ inputs.os == 'linux' && '>>' || '>' }} $(conan config home)/global.conf + + echo 'Conan configuration:' + conan config show '*' + - name: Set up Conan profile + run: | + echo 'Installing profile.' + conan config install conan/profiles/default -tf $(conan config home)/profiles/ + + echo 'Conan profile:' + conan profile show + - name: Set up Conan remote + shell: bash + run: | + echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." + conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }} + + echo 'Listing Conan remotes.' + conan remote list + - name: Build dependencies + uses: ./.github/actions/build-deps + with: + build_dir: ${{ inputs.build_dir }} + build_type: ${{ matrix.build_type }} + conan_remote_name: ${{ inputs.conan_remote_name }} + conan_remote_url: ${{ inputs.conan_remote_url }} + conan_remote_username: ${{ secrets.conan_remote_username }} + conan_remote_password: ${{ secrets.conan_remote_password }} + force_build: ${{ inputs.dependencies_force_build }} + force_upload: ${{ inputs.dependencies_force_upload }} + - name: Build and test binary + uses: ./.github/actions/build-test + with: + build_dir: ${{ inputs.build_dir }} + build_type: ${{ matrix.build_type }} + cmake_args: ${{ matrix.cmake_args }} + cmake_target: ${{ matrix.cmake_target }} + codecov_token: ${{ secrets.codecov_token }} + os: ${{ inputs.os }} diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml new file mode 100644 index 0000000000..5e3da10028 --- /dev/null +++ b/.github/workflows/check-format.yml @@ -0,0 +1,112 @@ +# This workflow checks if the code is properly formatted. +name: Check format + +# This workflow can only be triggered by other workflows. +on: workflow_call + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-format + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + clang-format: + runs-on: ubuntu-latest + container: ghcr.io/xrplf/ci/tools-rippled-clang-format + steps: + # The $GITHUB_WORKSPACE and ${{ github.workspace }} might not point to the + # same directory for jobs running in containers. The actions/checkout step + # is *supposed* to checkout into $GITHUB_WORKSPACE and then add it to + # safe.directory (see instructions at https://github.com/actions/checkout) + # but that is apparently not happening for some container images. We + # therefore preemptively add both directories to safe.directory. See also + # https://github.com/actions/runner/issues/2058 for more details. + - name: Configure git safe.directory + run: | + git config --global --add safe.directory $GITHUB_WORKSPACE + git config --global --add safe.directory ${{ github.workspace }} + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Check configuration + run: | + echo 'Checking path.' + echo ${PATH} | tr ':' '\n' + + echo 'Checking environment variables.' + env | sort + + echo 'Checking clang-format version.' + clang-format --version + - name: Format code + run: find include src tests -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format -i {} + + - name: Check for differences + env: + MESSAGE: | + One or more files did not conform to the formatting specified in + .clang-format. Maybe you did not run 'git-clang-format' or + 'clang-format' before committing, or your version of clang-format + has an incompatibility with the one used here (see the "Check + configuration" step above). + + Run 'git-clang-format --extensions cpp,h,hpp,ipp develop' in your + repo, and then commit and push the changes. + run: | + DIFF=$(git status --porcelain) + if [ -n "${DIFF}" ]; then + # Print the files that changed to give the contributor a hint about + # what to expect when running git-clang-format on their own machine. + git status + echo "${MESSAGE}" + exit 1 + fi + + prettier: + runs-on: ubuntu-latest + container: ghcr.io/xrplf/ci/tools-rippled-prettier + steps: + - name: Configure git safe.directory + run: | + git config --global --add safe.directory $GITHUB_WORKSPACE + git config --global --add safe.directory ${{ github.workspace }} + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - name: Check configuration + run: | + echo 'Checking path.' + echo ${PATH} | tr ':' '\n' + + echo 'Checking environment variables.' + env | sort + + echo 'Checking NPM version.' + npm --version + + echo 'Checking Node.js version.' + node --version + + echo 'Checking prettier version.' + prettier --version + - name: Format code + run: prettier --check . + - name: Check for differences + env: + MESSAGE: | + One or more files did not conform to the formatting rules specified + by Prettier. Maybe you did not run 'prettier' before committing, or + your version of prettier has an incompatibility with the one used + here (see the "Check configuration" step above). + + Run 'prettier --check .' in your repo, and then commit and push the + changes. + run: | + DIFF=$(git status --porcelain) + if [ -n "${DIFF}" ]; then + # Print the files that changed to give the contributor a hint about + # what to expect when running prettier on their own machine. + git status + echo "${MESSAGE}" + exit 1 + fi diff --git a/.github/workflows/check-levelization.yml b/.github/workflows/check-levelization.yml new file mode 100644 index 0000000000..3430ca28a2 --- /dev/null +++ b/.github/workflows/check-levelization.yml @@ -0,0 +1,46 @@ +# This workflow checks if the dependencies between the modules are correctly +# indexed. +name: Check levelization + +# This workflow can only be triggered by other workflows. +on: workflow_call + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-levelization + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + levelization: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Check levelization + run: .github/scripts/levelization/generate.sh + - name: Check for differences + env: + MESSAGE: | + + The dependency relationships between the modules in rippled have + changed, which may be an improvement or a regression. + + A rule of thumb is that if your changes caused something to be + removed from loops.txt, it's probably an improvement, while if + something was added, it's probably a regression. + + Run '.github/scripts/levelization/generate.sh' in your repo, commit + and push the changes. See .github/scripts/levelization/README.md for + more info. + run: | + DIFF=$(git status --porcelain) + if [ -n "${DIFF}" ]; then + # Print the differences to give the contributor a hint about what to + # expect when running levelization on their own machine. + git diff + echo "${MESSAGE}" + exit 1 + fi diff --git a/.github/workflows/check-missing-commits.yml b/.github/workflows/check-missing-commits.yml new file mode 100644 index 0000000000..da0e296e70 --- /dev/null +++ b/.github/workflows/check-missing-commits.yml @@ -0,0 +1,62 @@ +# This workflow checks that all commits in the "master" branch are also in the +# "release" and "develop" branches, and that all commits in the "release" branch +# are also in the "develop" branch. +name: Check for missing commits + +# This workflow can only be triggered by other workflows. +on: workflow_call + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-missing-commits + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + check: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 0 + - name: Check for missing commits + env: + MESSAGE: | + + If you are reading this, then the commits indicated above are missing + from the "develop" and/or "release" branch. Do a reverse-merge as soon + as possible. See CONTRIBUTING.md for instructions. + run: | + set -o pipefail + # Branches are ordered by how "canonical" they are. Every commit in one + # branch should be in all the branches behind it. + order=(master release develop) + branches=() + for branch in "${order[@]}"; do + # Check that the branches exist so that this job will work on forked + # repos, which don't necessarily have master and release branches. + echo "Checking if ${branch} exists." + if git ls-remote --exit-code --heads origin \ + refs/heads/${branch} > /dev/null; then + branches+=(origin/${branch}) + fi + done + + prior=() + for branch in "${branches[@]}"; do + if [[ ${#prior[@]} -ne 0 ]]; then + echo "Checking ${prior[@]} for commits missing from ${branch}." + git log --oneline --no-merges "${prior[@]}" \ + ^$branch | tee -a "missing-commits.txt" + echo + fi + prior+=("${branch}") + done + + if [[ $(cat missing-commits.txt | wc -l) -ne 0 ]]; then + echo "${MESSAGE}" + exit 1 + fi diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml deleted file mode 100644 index 0d81f87791..0000000000 --- a/.github/workflows/clang-format.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: clang-format - -on: - push: - pull_request: - types: [opened, reopened, synchronize, ready_for_review] - -jobs: - check: - if: ${{ github.event_name == 'push' || github.event.pull_request.draft != true || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }} - runs-on: ubuntu-24.04 - container: ghcr.io/xrplf/ci/tools-rippled-clang-format - steps: - # For jobs running in containers, $GITHUB_WORKSPACE and ${{ github.workspace }} might not be the - # same directory. The actions/checkout step is *supposed* to checkout into $GITHUB_WORKSPACE and - # then add it to safe.directory (see instructions at https://github.com/actions/checkout) - # but that's apparently not happening for some container images. We can't be sure what is actually - # happening, so let's pre-emptively add both directories to safe.directory. There's a - # Github issue opened in 2022 and not resolved in 2025 https://github.com/actions/runner/issues/2058 ¯\_(ツ)_/¯ - - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git config --global --add safe.directory ${{ github.workspace }} - - uses: actions/checkout@v4 - - name: Format first-party sources - run: | - clang-format --version - find include src tests -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format -i {} + - - name: Check for differences - id: assert - shell: bash - run: | - set -o pipefail - git diff --exit-code | tee "clang-format.patch" - - name: Upload patch - if: failure() && steps.assert.outcome == 'failure' - uses: actions/upload-artifact@v4 - continue-on-error: true - with: - name: clang-format.patch - if-no-files-found: ignore - path: clang-format.patch - - name: What happened? - if: failure() && steps.assert.outcome == 'failure' - env: - PREAMBLE: | - If you are reading this, you are looking at a failed Github Actions - job. That means you pushed one or more files that did not conform - to the formatting specified in .clang-format. That may be because - you neglected to run 'git clang-format' or 'clang-format' before - committing, or that your version of clang-format has an - incompatibility with the one on this - machine, which is: - SUGGESTION: | - - To fix it, you can do one of two things: - 1. Download and apply the patch generated as an artifact of this - job to your repo, commit, and push. - 2. Run 'git-clang-format --extensions cpp,h,hpp,ipp develop' - in your repo, commit, and push. - run: | - echo "${PREAMBLE}" - clang-format --version - echo "${SUGGESTION}" - exit 1 diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml deleted file mode 100644 index 01e04a3f5a..0000000000 --- a/.github/workflows/doxygen.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Build and publish Doxygen documentation -# To test this workflow, push your changes to your fork's `develop` branch. -on: - push: - branches: - - develop - - doxygen -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - documentation: - runs-on: ubuntu-latest - permissions: - contents: write - container: ghcr.io/xrplf/rippled-build-ubuntu:aaf5e3e - steps: - - name: checkout - uses: actions/checkout@v4 - - name: check environment - run: | - echo ${PATH} | tr ':' '\n' - cmake --version - doxygen --version - env | sort - - name: build - run: | - mkdir build - cd build - cmake -Donly_docs=TRUE .. - cmake --build . --target docs --parallel $(nproc) - - name: publish - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: build/docs/html diff --git a/.github/workflows/levelization.yml b/.github/workflows/levelization.yml deleted file mode 100644 index 979049d630..0000000000 --- a/.github/workflows/levelization.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: levelization - -on: - push: - pull_request: - types: [opened, reopened, synchronize, ready_for_review] - -jobs: - check: - if: ${{ github.event_name == 'push' || github.event.pull_request.draft != true || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }} - runs-on: ubuntu-latest - env: - CLANG_VERSION: 10 - steps: - - uses: actions/checkout@v4 - - name: Check levelization - run: Builds/levelization/levelization.sh - - name: Check for differences - id: assert - run: | - set -o pipefail - git diff --exit-code | tee "levelization.patch" - - name: Upload patch - if: failure() && steps.assert.outcome == 'failure' - uses: actions/upload-artifact@v4 - continue-on-error: true - with: - name: levelization.patch - if-no-files-found: ignore - path: levelization.patch - - name: What happened? - if: failure() && steps.assert.outcome == 'failure' - env: - MESSAGE: | - If you are reading this, you are looking at a failed Github - Actions job. That means you changed the dependency relationships - between the modules in rippled. That may be an improvement or a - regression. This check doesn't judge. - - A rule of thumb, though, is that if your changes caused - something to be removed from loops.txt, that's probably an - improvement. If something was added, it's probably a regression. - - To fix it, you can do one of two things: - 1. Download and apply the patch generated as an artifact of this - job to your repo, commit, and push. - 2. Run './Builds/levelization/levelization.sh' in your repo, - commit, and push. - - See Builds/levelization/README.md for more info. - run: | - echo "${MESSAGE}" - exit 1 diff --git a/.github/workflows/libxrpl.yml b/.github/workflows/libxrpl.yml deleted file mode 100644 index 5880c03d71..0000000000 --- a/.github/workflows/libxrpl.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Check libXRPL compatibility with Clio -env: - CONAN_REMOTE_URL: https://conan.ripplex.io - CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.CONAN_REMOTE_USERNAME }} - CONAN_PASSWORD_XRPLF: ${{ secrets.CONAN_REMOTE_PASSWORD }} -on: - pull_request: - paths: - - "src/libxrpl/protocol/BuildInfo.cpp" - - ".github/workflows/libxrpl.yml" - types: [opened, reopened, synchronize, ready_for_review] -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - publish: - if: ${{ github.event_name == 'push' || github.event.pull_request.draft != true || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }} - name: Publish libXRPL - outputs: - outcome: ${{ steps.upload.outputs.outcome }} - version: ${{ steps.version.outputs.version }} - channel: ${{ steps.channel.outputs.channel }} - runs-on: [self-hosted, heavy] - container: ghcr.io/xrplf/rippled-build-ubuntu:aaf5e3e - steps: - - name: Wait for essential checks to succeed - uses: lewagon/wait-on-check-action@v1.3.4 - with: - ref: ${{ github.event.pull_request.head.sha || github.sha }} - running-workflow-name: wait-for-check-regexp - check-regexp: "(dependencies|test).*linux.*" # Ignore windows and mac tests but make sure linux passes - repo-token: ${{ secrets.GITHUB_TOKEN }} - wait-interval: 10 - - name: Checkout - uses: actions/checkout@v4 - - name: Generate channel - id: channel - shell: bash - run: | - echo channel="clio/pr_${{ github.event.pull_request.number }}" | tee ${GITHUB_OUTPUT} - - name: Export new package - shell: bash - run: | - conan export . ${{ steps.channel.outputs.channel }} - - name: Add Conan remote - shell: bash - run: | - echo "Adding Conan remote 'xrplf' at ${{ env.CONAN_REMOTE_URL }}." - conan remote add xrplf ${{ env.CONAN_REMOTE_URL }} --insert 0 --force - echo "Listing Conan remotes." - conan remote list - - name: Parse new version - id: version - shell: bash - run: | - echo version="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" \ - | awk -F '"' '{print $2}')" | tee ${GITHUB_OUTPUT} - - name: Try to authenticate to Conan remote - id: remote - shell: bash - run: | - # `conan user` implicitly uses the environment variables CONAN_LOGIN_USERNAME_ and CONAN_PASSWORD_. - # https://docs.conan.io/1/reference/commands/misc/user.html#using-environment-variables - # https://docs.conan.io/1/reference/env_vars.html#conan-login-username-conan-login-username-remote-name - # https://docs.conan.io/1/reference/env_vars.html#conan-password-conan-password-remote-name - echo outcome=$(conan user --remote xrplf --password >&2 \ - && echo success || echo failure) | tee ${GITHUB_OUTPUT} - - name: Upload new package - id: upload - if: (steps.remote.outputs.outcome == 'success') - shell: bash - run: | - echo "conan upload version ${{ steps.version.outputs.version }} on channel ${{ steps.channel.outputs.channel }}" - echo outcome=$(conan upload xrpl/${{ steps.version.outputs.version }}@${{ steps.channel.outputs.channel }} --remote ripple --confirm >&2 \ - && echo success || echo failure) | tee ${GITHUB_OUTPUT} - notify_clio: - name: Notify Clio - runs-on: ubuntu-latest - needs: publish - env: - GH_TOKEN: ${{ secrets.CLIO_NOTIFY_TOKEN }} - steps: - - name: Notify Clio about new version - if: (needs.publish.outputs.outcome == 'success') - shell: bash - run: | - gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ - /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ - -F "client_payload[version]=${{ needs.publish.outputs.version }}@${{ needs.publish.outputs.channel }}" \ - -F "client_payload[pr]=${{ github.event.pull_request.number }}" diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml deleted file mode 100644 index 73e25c357f..0000000000 --- a/.github/workflows/macos.yml +++ /dev/null @@ -1,112 +0,0 @@ -name: macos -on: - pull_request: - types: [opened, reopened, synchronize, ready_for_review] - push: - # If the branches list is ever changed, be sure to change it on all - # build/test jobs (nix, macos, windows, instrumentation) - branches: - # Always build the package branches - - develop - - release - - master - # Branches that opt-in to running - - "ci/**" -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true -# This part of Conan configuration is specific to this workflow only; we do not want -# to pollute conan/profiles directory with settings which might not work for others -env: - CONAN_REMOTE_URL: https://conan.ripplex.io - CONAN_REMOTE_USERNAME: ${{ secrets.CONAN_REMOTE_USERNAME }} - CONAN_REMOTE_PASSWORD: ${{ secrets.CONAN_REMOTE_PASSWORD }} - # This part of the Conan configuration is specific to this workflow only; we - # do not want to pollute the 'conan/profiles' directory with settings that - # might not work for other workflows. - CONAN_GLOBAL_CONF: | - core.download:parallel={{os.cpu_count()}} - core.upload:parallel={{os.cpu_count()}} - tools.build:jobs={{ (os.cpu_count() * 4/5) | int }} - tools.build:verbosity=verbose - tools.compilation:verbosity=verbose - -jobs: - test: - if: ${{ github.event_name == 'push' || github.event.pull_request.draft != true || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }} - strategy: - matrix: - platform: - - macos - generator: - - Ninja - configuration: - - Release - runs-on: [self-hosted, macOS, mac-runner-m1] - env: - # The `build` action requires these variables. - build_dir: .build - NUM_PROCESSORS: 12 - steps: - - name: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - name: install Conan - run: | - brew install conan - - name: install Ninja - if: matrix.generator == 'Ninja' - run: brew install ninja - - name: install python - run: | - if which python > /dev/null 2>&1; then - echo "Python executable exists" - else - brew install python@3.13 - ln -s /opt/homebrew/bin/python3 /opt/homebrew/bin/python - fi - - name: install cmake - run: | - if which cmake > /dev/null 2>&1; then - echo "cmake executable exists" - else - brew install cmake - fi - - name: install nproc - run: | - brew install coreutils - - name: check environment - run: | - env | sort - echo ${PATH} | tr ':' '\n' - python --version - conan --version - cmake --version - nproc --version - echo -n "nproc returns: " - nproc - system_profiler SPHardwareDataType - sysctl -n hw.logicalcpu - clang --version - - name: configure Conan - run: | - echo "${CONAN_GLOBAL_CONF}" > $(conan config home)/global.conf - conan config install conan/profiles/ -tf $(conan config home)/profiles/ - conan profile show - - name: build dependencies - uses: ./.github/actions/dependencies - with: - configuration: ${{ matrix.configuration }} - - name: build - uses: ./.github/actions/build - with: - generator: ${{ matrix.generator }} - configuration: ${{ matrix.configuration }} - cmake-args: "-Dassert=TRUE -Dwerr=TRUE ${{ matrix.cmake-args }}" - - name: test - run: | - n=$(nproc) - echo "Using $n test jobs" - - cd ${build_dir} - ./rippled --unittest --unittest-jobs $n - ctest -j $n --output-on-failure diff --git a/.github/workflows/missing-commits.yml b/.github/workflows/missing-commits.yml deleted file mode 100644 index ed478a2327..0000000000 --- a/.github/workflows/missing-commits.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: missing-commits - -on: - push: - branches: - # Only check that the branches are up to date when updating the - # relevant branches. - - develop - - release - -jobs: - up_to_date: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Check for missing commits - id: commits - env: - SUGGESTION: | - - If you are reading this, then the commits indicated above are - missing from "develop" and/or "release". Do a reverse-merge - as soon as possible. See CONTRIBUTING.md for instructions. - run: | - set -o pipefail - # Branches ordered by how "canonical" they are. Every commit in - # one branch should be in all the branches behind it - order=( master release develop ) - branches=() - for branch in "${order[@]}" - do - # Check that the branches exist so that this job will work on - # forked repos, which don't necessarily have master and - # release branches. - if git ls-remote --exit-code --heads origin \ - refs/heads/${branch} > /dev/null - then - branches+=( origin/${branch} ) - fi - done - - prior=() - for branch in "${branches[@]}" - do - if [[ ${#prior[@]} -ne 0 ]] - then - echo "Checking ${prior[@]} for commits missing from ${branch}" - git log --oneline --no-merges "${prior[@]}" \ - ^$branch | tee -a "missing-commits.txt" - echo - fi - prior+=( "${branch}" ) - done - if [[ $( cat missing-commits.txt | wc -l ) -ne 0 ]] - then - echo "${SUGGESTION}" - exit 1 - fi diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml deleted file mode 100644 index 395bd72b8d..0000000000 --- a/.github/workflows/nix.yml +++ /dev/null @@ -1,422 +0,0 @@ -name: nix -on: - pull_request: - types: [opened, reopened, synchronize, ready_for_review] - push: - # If the branches list is ever changed, be sure to change it on all - # build/test jobs (nix, macos, windows) - branches: - # Always build the package branches - - develop - - release - - master - # Branches that opt-in to running - - "ci/**" -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - CONAN_REMOTE_URL: https://conan.ripplex.io - CONAN_REMOTE_USERNAME: ${{ secrets.CONAN_REMOTE_USERNAME }} - CONAN_REMOTE_PASSWORD: ${{ secrets.CONAN_REMOTE_PASSWORD }} - # This part of the Conan configuration is specific to this workflow only; we - # do not want to pollute the 'conan/profiles' directory with settings that - # might not work for other workflows. - CONAN_GLOBAL_CONF: | - core.download:parallel={{ os.cpu_count() }} - core.upload:parallel={{ os.cpu_count() }} - tools.build:jobs={{ (os.cpu_count() * 4/5) | int }} - tools.build:verbosity=verbose - tools.compilation:verbosity=verbose - -# This workflow has multiple job matrixes. -# They can be considered phases because most of the matrices ("test", -# "coverage", "conan", ) depend on the first ("dependencies"). -# -# The first phase has a job in the matrix for each combination of -# variables that affects dependency ABI: -# platform, compiler, and configuration. -# It creates a GitHub artifact holding the Conan profile, -# and builds and caches binaries for all the dependencies. -# If an Artifactory remote is configured, they are cached there. -# If not, they are added to the GitHub artifact. -# GitHub's "cache" action has a size limit (10 GB) that is too small -# to hold the binaries if they are built locally. -# We must use the "{upload,download}-artifact" actions instead. -# -# The remaining phases have a job in the matrix for each test -# configuration. They install dependency binaries from the cache, -# whichever was used, and build and test rippled. -# -# "instrumentation" is independent, but is included here because it also -# builds on linux in the same "on:" conditions. - -jobs: - dependencies: - if: ${{ github.event_name == 'push' || github.event.pull_request.draft != true || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }} - strategy: - fail-fast: false - matrix: - platform: - - linux - compiler: - - gcc - - clang - configuration: - - Debug - - Release - include: - - compiler: gcc - compiler_version: 12 - distro: ubuntu - codename: jammy - - compiler: clang - compiler_version: 16 - distro: debian - codename: bookworm - runs-on: [self-hosted, heavy] - container: ghcr.io/xrplf/ci/${{ matrix.distro }}-${{ matrix.codename }}:${{ matrix.compiler }}-${{ matrix.compiler_version }} - env: - build_dir: .build - steps: - - name: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - name: check environment - run: | - echo ${PATH} | tr ':' '\n' - lsb_release -a || true - ${{ matrix.compiler }}-${{ matrix.compiler_version }} --version - conan --version - cmake --version - env | sort - - name: configure Conan - run: | - echo "${CONAN_GLOBAL_CONF}" >> $(conan config home)/global.conf - conan config install conan/profiles/ -tf $(conan config home)/profiles/ - conan profile show - - name: archive profile - # Create this archive before dependencies are added to the local cache. - run: tar -czf conan.tar.gz -C ${CONAN_HOME} . - - name: build dependencies - uses: ./.github/actions/dependencies - with: - configuration: ${{ matrix.configuration }} - - name: upload archive - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 - with: - name: ${{ matrix.platform }}-${{ matrix.compiler }}-${{ matrix.configuration }} - path: conan.tar.gz - if-no-files-found: error - - test: - strategy: - fail-fast: false - matrix: - platform: - - linux - compiler: - - gcc - - clang - configuration: - - Debug - - Release - include: - - compiler: gcc - compiler_version: 12 - distro: ubuntu - codename: jammy - - compiler: clang - compiler_version: 16 - distro: debian - codename: bookworm - cmake-args: - - - - "-Dunity=ON" - needs: dependencies - runs-on: [self-hosted, heavy] - container: ghcr.io/xrplf/ci/${{ matrix.distro }}-${{ matrix.codename }}:${{ matrix.compiler }}-${{ matrix.compiler_version }} - env: - build_dir: .build - steps: - - name: download cache - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 - with: - name: ${{ matrix.platform }}-${{ matrix.compiler }}-${{ matrix.configuration }} - - name: extract cache - run: | - mkdir -p ${CONAN_HOME} - tar -xzf conan.tar.gz -C ${CONAN_HOME} - - name: check environment - run: | - env | sort - echo ${PATH} | tr ':' '\n' - conan --version - cmake --version - - name: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - name: dependencies - uses: ./.github/actions/dependencies - with: - configuration: ${{ matrix.configuration }} - - name: build - uses: ./.github/actions/build - with: - generator: Ninja - configuration: ${{ matrix.configuration }} - cmake-args: "-Dassert=TRUE -Dwerr=TRUE ${{ matrix.cmake-args }}" - - name: check linking - run: | - cd ${build_dir} - ldd ./rippled - if [ "$(ldd ./rippled | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then - echo 'The binary is statically linked.' - else - echo 'The binary is dynamically linked.' - exit 1 - fi - - name: test - run: | - cd ${build_dir} - ./rippled --unittest --unittest-jobs $(nproc) - ctest -j $(nproc) --output-on-failure - - reference-fee-test: - strategy: - fail-fast: false - matrix: - platform: - - linux - compiler: - - gcc - configuration: - - Debug - cmake-args: - - "-DUNIT_TEST_REFERENCE_FEE=200" - - "-DUNIT_TEST_REFERENCE_FEE=1000" - needs: dependencies - runs-on: [self-hosted, heavy] - container: ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12 - env: - build_dir: .build - steps: - - name: download cache - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 - with: - name: ${{ matrix.platform }}-${{ matrix.compiler }}-${{ matrix.configuration }} - - name: extract cache - run: | - mkdir -p ${CONAN_HOME} - tar -xzf conan.tar.gz -C ${CONAN_HOME} - - name: check environment - run: | - env | sort - echo ${PATH} | tr ':' '\n' - conan --version - cmake --version - - name: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - name: dependencies - uses: ./.github/actions/dependencies - with: - configuration: ${{ matrix.configuration }} - - name: build - uses: ./.github/actions/build - with: - generator: Ninja - configuration: ${{ matrix.configuration }} - cmake-args: "-Dassert=TRUE -Dwerr=TRUE ${{ matrix.cmake-args }}" - - name: test - run: | - cd ${build_dir} - ./rippled --unittest --unittest-jobs $(nproc) - ctest -j $(nproc) --output-on-failure - - coverage: - strategy: - fail-fast: false - matrix: - platform: - - linux - compiler: - - gcc - configuration: - - Debug - needs: dependencies - runs-on: [self-hosted, heavy] - container: ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12 - env: - build_dir: .build - steps: - - name: download cache - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 - with: - name: ${{ matrix.platform }}-${{ matrix.compiler }}-${{ matrix.configuration }} - - name: extract cache - run: | - mkdir -p ${CONAN_HOME} - tar -xzf conan.tar.gz -C ${CONAN_HOME} - - name: check environment - run: | - echo ${PATH} | tr ':' '\n' - conan --version - cmake --version - gcovr --version - env | sort - ls ${CONAN_HOME} - - name: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - name: dependencies - uses: ./.github/actions/dependencies - with: - configuration: ${{ matrix.configuration }} - - name: build - uses: ./.github/actions/build - with: - generator: Ninja - configuration: ${{ matrix.configuration }} - cmake-args: >- - -Dassert=TRUE - -Dwerr=TRUE - -Dcoverage=ON - -Dcoverage_format=xml - -DCODE_COVERAGE_VERBOSE=ON - -DCMAKE_CXX_FLAGS="-O0" - -DCMAKE_C_FLAGS="-O0" - cmake-target: coverage - - name: move coverage report - shell: bash - run: | - mv "${build_dir}/coverage.xml" ./ - - name: archive coverage report - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 - with: - name: coverage.xml - path: coverage.xml - retention-days: 30 - - name: upload coverage report - uses: wandalen/wretry.action@v1.4.10 - with: - action: codecov/codecov-action@v4.5.0 - with: | - files: coverage.xml - fail_ci_if_error: true - disable_search: true - verbose: true - plugin: noop - token: ${{ secrets.CODECOV_TOKEN }} - attempt_limit: 5 - attempt_delay: 210000 # in milliseconds - - conan: - needs: dependencies - runs-on: [self-hosted, heavy] - container: - image: ghcr.io/xrplf/ci/ubuntu-jammy:gcc-12 - env: - build_dir: .build - platform: linux - compiler: gcc - compiler_version: 12 - configuration: Release - steps: - - name: download cache - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 - with: - name: ${{ env.platform }}-${{ env.compiler }}-${{ env.configuration }} - - name: extract cache - run: | - mkdir -p ${CONAN_HOME} - tar -xzf conan.tar.gz -C ${CONAN_HOME} - - name: check environment - run: | - env | sort - echo ${PATH} | tr ':' '\n' - conan --version - cmake --version - - name: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - name: dependencies - uses: ./.github/actions/dependencies - with: - configuration: ${{ env.configuration }} - - name: export - run: | - conan export . --version head - - name: build - run: | - cd tests/conan - mkdir ${build_dir} && cd ${build_dir} - conan install .. \ - --settings:all build_type=${configuration} \ - --output-folder . \ - --build missing - cmake .. \ - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=./build/${configuration}/generators/conan_toolchain.cmake \ - -DCMAKE_BUILD_TYPE=${configuration} - cmake --build . - ./example | grep '^[[:digit:]]\+\.[[:digit:]]\+\.[[:digit:]]\+' - - instrumentation-build: - needs: dependencies - runs-on: [self-hosted, heavy] - container: ghcr.io/xrplf/ci/debian-bookworm:clang-16 - env: - build_dir: .build - steps: - - name: download cache - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 - with: - name: linux-clang-Debug - - - name: extract cache - run: | - mkdir -p ${CONAN_HOME} - tar -xzf conan.tar.gz -C ${CONAN_HOME} - - - name: check environment - run: | - echo ${PATH} | tr ':' '\n' - conan --version - cmake --version - env | sort - ls ${CONAN_HOME} - - - name: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - - name: dependencies - uses: ./.github/actions/dependencies - with: - configuration: Debug - - - name: prepare environment - run: | - mkdir -p ${build_dir} - echo "SOURCE_DIR=$(pwd)" >> $GITHUB_ENV - echo "BUILD_DIR=$(pwd)/${build_dir}" >> $GITHUB_ENV - - - name: build with instrumentation - run: | - cd ${BUILD_DIR} - cmake -S ${SOURCE_DIR} -B ${BUILD_DIR} \ - -Dvoidstar=ON \ - -Dtests=ON \ - -Dxrpld=ON \ - -DCMAKE_BUILD_TYPE=Debug \ - -DSECP256K1_BUILD_BENCHMARK=OFF \ - -DSECP256K1_BUILD_TESTS=OFF \ - -DSECP256K1_BUILD_EXHAUSTIVE_TESTS=OFF \ - -DCMAKE_TOOLCHAIN_FILE=${BUILD_DIR}/build/generators/conan_toolchain.cmake - cmake --build . --parallel $(nproc) - - - name: verify instrumentation enabled - run: | - cd ${BUILD_DIR} - ./rippled --version | grep libvoidstar - - - name: run unit tests - run: | - cd ${BUILD_DIR} - ./rippled -u --unittest-jobs $(( $(nproc)/4 )) - ctest -j $(nproc) --output-on-failure diff --git a/.github/workflows/notify-clio.yml b/.github/workflows/notify-clio.yml new file mode 100644 index 0000000000..32ac01d61a --- /dev/null +++ b/.github/workflows/notify-clio.yml @@ -0,0 +1,80 @@ +# This workflow exports the built libxrpl package to the Conan remote on a +# a channel named after the pull request, and notifies the Clio repository about +# the new version so it can check for compatibility. +name: Notify Clio + +# This workflow can only be triggered by other workflows. +on: + workflow_call: + inputs: + conan_remote_name: + description: 'The name of the Conan remote to use.' + required: true + type: string + conan_remote_url: + description: 'The URL of the Conan endpoint to use.' + required: true + type: string + secrets: + clio_notify_token: + description: 'The GitHub token to notify Clio about new versions.' + required: true + conan_remote_username: + description: 'The username for logging into the Conan remote.' + required: true + conan_remote_password: + description: 'The password for logging into the Conan remote.' + required: true + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-clio + cancel-in-progress: true + +defaults: + run: + shell: bash + +jobs: + upload: + runs-on: ubuntu-latest + container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13 + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Generate outputs + id: generate + run: | + echo 'Generating channel.' + echo channel="clio/pr_${{ github.event.pull_request.number }}" | tee "${GITHUB_OUTPUT}" + echo 'Extracting version.' + echo version="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" | tee "${GITHUB_OUTPUT}" + - name: Add Conan remote + run: | + echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." + conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }} + echo 'Listing Conan remotes.' + conan remote list + - name: Log into Conan remote + run: conan remote login ${{ inputs.conan_remote_name }} "${{ secrets.conan_remote_username }}" --password "${{ secrets.conan_remote_password }}" + - name: Upload package + run: | + echo 'Exporting package to channel ${{ steps.generate.outputs.channel }}.' + conan export --channel=${{ steps.generate.outputs.channel }} . + echo 'Uploading package version ${{ steps.generate.outputs.version }} on channel ${{ steps.generate.outputs.channel }}.' + conan upload --confirm --check --remote=${{ inputs.conan_remote_name }} xrpl/${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.channel }} + outputs: + channel: ${{ steps.generate.outputs.channel }} + version: ${{ steps.generate.outputs.version }} + + notify: + needs: upload + runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.clio_notify_token }} + steps: + - name: Notify Clio + run: | + gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ + -F "client_payload[version]=${{ needs.upload.outputs.version }}@${{ needs.upload.outputs.channel }}" \ + -F "client_payload[pr]=${{ github.event.pull_request.number }}" diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml new file mode 100644 index 0000000000..10bc61797a --- /dev/null +++ b/.github/workflows/on-pr.yml @@ -0,0 +1,133 @@ +# This workflow runs all workflows to check, build and test the project on +# various Linux flavors, as well as on MacOS and Windows, on every push to a +# user branch. However, it will not run if the pull request is a draft unless it +# has the 'DraftRunCI' label. +name: PR + +on: + pull_request: + paths: + - '.github/actions/build-deps/**' + - '.github/actions/build-test/**' + - '.github/scripts/levelization/**' + - '.github/scripts/strategy-matrix/**' + - '.github/workflows/build-test.yml' + - '.github/workflows/check-format.yml' + - '.github/workflows/check-levelization.yml' + - '.github/workflows/notify-clio.yml' + - '.github/workflows/on-pr.yml' + # Keep the list of paths below in sync with those in the `on-trigger.yml` + # file. + - 'cmake/**' + - 'conan/**' + - 'external/**' + - 'include/**' + - 'src/**' + - 'tests/**' + - '.clang-format' + - '.codecov.yml' + - '.pre-commit-config.yaml' + - 'CMakeLists.txt' + - 'conanfile.py' + types: + - opened + - synchronize + - labeled + - unlabeled + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +env: + CONAN_REMOTE_NAME: xrplf + CONAN_REMOTE_URL: https://conan.ripplex.io + +jobs: + # This job determines whether the workflow should run. It runs when: + # * Opened as a non-draft PR. + # * A commit is added to a non-draft PR or the PR has the 'DraftRunCI' label. + # * A draft PR has the 'DraftRunCI' label added. + # * A non-draft PR has the 'DraftRunCI' label removed. + # These checks are in part to ensure the workflow won't run needlessly while + # also allowing it to be triggered without having to add a no-op commit. A new + # workflow execution can be triggered by adding and then removing the label on + # a non-draft PR, or conversely by removing it and then adding it back on a + # draft PR; this can be useful in certain cases. + should-run: + if: >- + ${{ + (github.event.action == 'opened' && !github.event.pull_request.draft) || + (github.event.action == 'synchronize' && (!github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'DraftRunCI'))) || + (github.event.action == 'labeled' && github.event.pull_request.draft && github.event.label.name == 'DraftRunCI') || + (github.event.action == 'unlabeled' && !github.event.pull_request.draft && github.event.label.name == 'DraftRunCI') + }} + runs-on: ubuntu-latest + steps: + - name: No-op + run: echo '' + + check-clang-format: + needs: should-run + uses: ./.github/workflows/check-format.yml + + check-levelization: + needs: should-run + uses: ./.github/workflows/check-levelization.yml + + # This job works around the limitation that GitHub Actions does not support + # using environment variables as inputs for reusable workflows. + generate-outputs: + needs: should-run + runs-on: ubuntu-latest + steps: + - name: No-op + run: echo '' + outputs: + conan_remote_name: ${{ env.CONAN_REMOTE_NAME }} + conan_remote_url: ${{ env.CONAN_REMOTE_URL }} + + build-linux: + needs: generate-outputs + uses: ./.github/workflows/build-test.yml + with: + conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} + conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} + os: 'linux' + secrets: + codecov_token: ${{ secrets.CODECOV_TOKEN }} + + build-macos: + needs: generate-outputs + uses: ./.github/workflows/build-test.yml + with: + conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} + conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} + os: 'macos' + + build-windows: + needs: generate-outputs + uses: ./.github/workflows/build-test.yml + with: + conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} + conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} + os: 'windows' + + notify-clio: + needs: + - generate-outputs + - build-linux + - build-macos + - build-windows + uses: ./.github/workflows/notify-clio.yml + with: + conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} + conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} + secrets: + clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }} + conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} + conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml new file mode 100644 index 0000000000..a6dded7fd8 --- /dev/null +++ b/.github/workflows/on-trigger.yml @@ -0,0 +1,140 @@ +# This workflow runs all workflows to build the dependencies required for the +# project on various Linux flavors, as well as on MacOS and Windows, on a +# scheduled basis, on merge into the 'develop', 'release', or 'master' branches, +# or manually. The missing commits check is only run when the code is merged +# into the 'develop' or 'release' branches, and the documentation is built when +# the code is merged into the 'develop' branch. +name: Trigger + +on: + push: + branches: + - develop + - release + - master + paths: + - '.github/actions/build-deps/**' + - '.github/actions/build-test/**' + - '.github/scripts/strategy-matrix/**' + - '.github/workflows/build-test.yml' + - '.github/workflows/check-missing-commits.yml' + - '.github/workflows/on-trigger.yml' + - '.github/workflows/publish-docs.yml' + # Keep the list of paths below in sync with those in `on-pr.yml`. + - 'cmake/**' + - 'conan/**' + - 'external/**' + - 'include/**' + - 'src/**' + - 'tests/**' + - '.clang-format' + - '.codecov.yml' + - '.pre-commit-config.yaml' + - 'CMakeLists.txt' + - 'conanfile.py' + # Run at 06:32 UTC on every day of the week from Monday through Friday. This + # will force all dependencies to be rebuilt, which is useful to verify that + # all dependencies can be built successfully. Only the dependencies that + # are actually missing from the remote will be uploaded. + schedule: + - cron: '32 6 * * 1-5' + # Run when manually triggered via the GitHub UI or API. If `force_upload` is + # true, then the dependencies that were missing (`force_rebuild` is false) or + # rebuilt (`force_rebuild` is true) will be uploaded, overwriting existing + # dependencies if needed. + workflow_dispatch: + inputs: + dependencies_force_build: + description: 'Force building of all dependencies.' + required: false + type: boolean + default: false + dependencies_force_upload: + description: 'Force uploading of all dependencies.' + required: false + type: boolean + default: false + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +env: + CONAN_REMOTE_NAME: xrplf + CONAN_REMOTE_URL: https://conan.ripplex.io + +jobs: + check-missing-commits: + if: ${{ github.event_name == 'push' && github.ref_type == 'branch' && contains(fromJSON('["develop", "release"]'), github.ref_name) }} + uses: ./.github/workflows/check-missing-commits.yml + + # This job works around the limitation that GitHub Actions does not support + # using environment variables as inputs for reusable workflows. It also sets + # outputs that depend on the event that triggered the workflow. + generate-outputs: + runs-on: ubuntu-latest + steps: + - name: Check inputs and set outputs + id: generate + run: | + if [[ "${{ github.event_name }}" == 'push' ]]; then + echo 'dependencies_force_build=false' | tee "${GITHUB_OUTPUT}" + echo 'dependencies_force_upload=false' | tee "${GITHUB_OUTPUT}" + elif [[ "${{ github.event_name }}" == 'schedule' ]]; then + echo 'dependencies_force_build=true' | tee "${GITHUB_OUTPUT}" + echo 'dependencies_force_upload=false' | tee "${GITHUB_OUTPUT}" + else + echo 'dependencies_force_build=${{ inputs.dependencies_force_build }}' | tee "${GITHUB_OUTPUT}" + echo 'dependencies_force_upload=${{ inputs.dependencies_force_upload }}' | tee "${GITHUB_OUTPUT}" + fi + outputs: + conan_remote_name: ${{ env.CONAN_REMOTE_NAME }} + conan_remote_url: ${{ env.CONAN_REMOTE_URL }} + dependencies_force_build: ${{ steps.generate.outputs.dependencies_force_build }} + dependencies_force_upload: ${{ steps.generate.outputs.dependencies_force_upload }} + + build-linux: + needs: generate-outputs + uses: ./.github/workflows/build-test.yml + with: + conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} + conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} + dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build }} + dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload }} + os: 'linux' + strategy_matrix_all: true + secrets: + conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} + conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} + + build-macos: + needs: generate-outputs + uses: ./.github/workflows/build-test.yml + with: + conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} + conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} + dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build }} + dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload }} + os: 'macos' + strategy_matrix_all: true + secrets: + conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} + conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} + + build-windows: + needs: generate-outputs + uses: ./.github/workflows/build-test.yml + with: + conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} + conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} + dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build }} + dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload }} + os: 'windows' + strategy_matrix_all: true + secrets: + conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} + conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml new file mode 100644 index 0000000000..a2c24bd076 --- /dev/null +++ b/.github/workflows/publish-docs.yml @@ -0,0 +1,58 @@ +# This workflow builds the documentation for the repository, and publishes it to +# GitHub Pages when changes are merged into the default branch. +name: Build and publish documentation + +on: + push: + paths: + - '.github/workflows/publish-docs.yml' + - '*.md' + - '**/*.md' + - 'docs/**' + - 'include/**' + - 'src/libxrpl/**' + - 'src/xrpld/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +defaults: + run: + shell: bash + +env: + BUILD_DIR: .build + +jobs: + publish: + runs-on: ubuntu-latest + container: ghcr.io/xrplf/ci/tools-rippled-documentation + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Check configuration + run: | + echo 'Checking path.' + echo ${PATH} | tr ':' '\n' + + echo 'Checking environment variables.' + env | sort + + echo 'Checking CMake version.' + cmake --version + + echo 'Checking Doxygen version.' + doxygen --version + - name: Build documentation + run: | + mkdir -p ${{ env.BUILD_DIR }} + cd ${{ env.BUILD_DIR }} + cmake -Donly_docs=ON .. + cmake --build . --target docs --parallel $(nproc) + - name: Publish documentation + if: ${{ github.ref_type == 'branch' && github.ref_name == github.event.repository.default_branch }} + uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ${{ env.BUILD_DIR }}/docs/html diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml deleted file mode 100644 index b81ffc8d3a..0000000000 --- a/.github/workflows/windows.yml +++ /dev/null @@ -1,106 +0,0 @@ -name: windows - -on: - pull_request: - types: [opened, reopened, synchronize, ready_for_review] - push: - # If the branches list is ever changed, be sure to change it on all - # build/test jobs (nix, macos, windows, instrumentation) - branches: - # Always build the package branches - - develop - - release - - master - # Branches that opt-in to running - - "ci/**" - -# https://docs.github.com/en/actions/using-jobs/using-concurrency -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true -env: - CONAN_REMOTE_URL: https://conan.ripplex.io - CONAN_REMOTE_USERNAME: ${{ secrets.CONAN_REMOTE_USERNAME }} - CONAN_REMOTE_PASSWORD: ${{ secrets.CONAN_REMOTE_PASSWORD }} - # This part of the Conan configuration is specific to this workflow only; we - # do not want to pollute the 'conan/profiles' directory with settings that - # might not work for other workflows. - CONAN_GLOBAL_CONF: | - core.download:parallel={{os.cpu_count()}} - core.upload:parallel={{os.cpu_count()}} - tools.build:jobs=24 - tools.build:verbosity=verbose - tools.compilation:verbosity=verbose - -jobs: - test: - if: ${{ github.event_name == 'push' || github.event.pull_request.draft != true || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }} - strategy: - fail-fast: false - matrix: - version: - - generator: Visual Studio 17 2022 - runs-on: windows-2022 - configuration: - - type: Release - tests: true - - type: Debug - # Skip running unit tests on debug builds, because they - # take an unreasonable amount of time - tests: false - runtime: d - runs-on: ${{ matrix.version.runs-on }} - env: - build_dir: .build - steps: - - name: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - name: choose Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 - with: - python-version: 3.13 - - name: learn Python cache directory - id: pip-cache - shell: bash - run: | - python -m pip install --upgrade pip - echo "dir=$(pip cache dir)" | tee ${GITHUB_OUTPUT} - - name: restore Python cache directory - uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: ${{ runner.os }}-${{ hashFiles('.github/workflows/windows.yml') }} - - name: install Conan - run: pip install wheel conan - - name: check environment - run: | - dir env: - $env:PATH -split ';' - python --version - conan --version - cmake --version - - name: configure Conan - shell: bash - run: | - echo "${CONAN_GLOBAL_CONF}" > $(conan config home)/global.conf - conan config install conan/profiles/ -tf $(conan config home)/profiles/ - conan profile show - - name: build dependencies - uses: ./.github/actions/dependencies - with: - configuration: ${{ matrix.configuration.type }} - - name: build - uses: ./.github/actions/build - with: - generator: "${{ matrix.version.generator }}" - configuration: ${{ matrix.configuration.type }} - # Hard code for now. Move to the matrix if varied options are needed - cmake-args: "-Dassert=TRUE -Dwerr=TRUE -Dreporting=OFF -Dunity=ON" - cmake-target: install - - name: test - shell: bash - if: ${{ matrix.configuration.tests }} - run: | - cd ${build_dir}/${{ matrix.configuration.type }} - ./rippled --unittest --unittest-jobs $(nproc) - ctest -j $(nproc) --output-on-failure diff --git a/.gitignore b/.gitignore index e5952e0de1..ab54adba74 100644 --- a/.gitignore +++ b/.gitignore @@ -37,10 +37,9 @@ Release/*.* *.gcov # Levelization checking -Builds/levelization/results/rawincludes.txt -Builds/levelization/results/paths.txt -Builds/levelization/results/includes/ -Builds/levelization/results/includedby/ +.github/scripts/levelization/results/* +!.github/scripts/levelization/results/loops.txt +!.github/scripts/levelization/results/ordering.txt # Ignore tmp directory. tmp diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..477120ade1 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +external +.* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b0ae72ae54..a5e0933d00 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,7 +81,7 @@ If you create new source files, they must be organized as follows: The source must be formatted according to the style guide below. -Header includes must be [levelized](./Builds/levelization). +Header includes must be [levelized](.github/scripts/levelization). Changes should be usually squashed down into a single commit. Some larger or more complicated change sets make more sense, diff --git a/README.md b/README.md index 4fdb89dffa..fe7daa38bc 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ If you are interested in running an **API Server** (including a **Full History S Here are some good places to start learning the source code: - Read the markdown files in the source tree: `src/ripple/**/*.md`. -- Read [the levelization document](./Builds/levelization) to get an idea of the internal dependency graph. +- Read [the levelization document](.github/scripts/levelization) to get an idea of the internal dependency graph. - In the big picture, the `main` function constructs an `ApplicationImp` object, which implements the `Application` virtual interface. Almost every component in the application takes an `Application&` parameter in its constructor, typically named `app` and stored as a member variable `app_`. This allows most components to depend on any other component. ### Repository Contents diff --git a/conan/global.conf b/conan/global.conf new file mode 100644 index 0000000000..ae03818232 --- /dev/null +++ b/conan/global.conf @@ -0,0 +1,9 @@ +# Global configuration for Conan. This is used to set the number of parallel +# downloads, uploads, and build jobs. The verbosity is set to verbose to +# provide more information during the build process. +core:non_interactive=True +core.download:parallel={{ os.cpu_count() }} +core.upload:parallel={{ os.cpu_count() }} +tools.build:jobs={{ (os.cpu_count() * 4/5) | int }} +tools.build:verbosity=verbose +tools.compilation:verbosity=verbose diff --git a/external/README.md b/external/README.md index 99ce2c337e..7de1fd25a0 100644 --- a/external/README.md +++ b/external/README.md @@ -1,7 +1,6 @@ # External Conan recipes -The subdirectories in this directory contain copies of external libraries used -by rippled. +The subdirectories in this directory contain external libraries used by rippled. | Folder | Upstream | Description | | :--------------- | :------------------------------------------------------------- | :------------------------------------------------------------------------------------------- | From b04d239926f265f2c7b5e1fd4a86b76d2479f322 Mon Sep 17 00:00:00 2001 From: Bart Date: Mon, 18 Aug 2025 10:49:55 -0400 Subject: [PATCH 08/66] fix: Modify jobs to use '>>' instead of 'tee' for GITHUB_OUTPUT (#5699) --- .github/workflows/build-test.yml | 2 +- .github/workflows/notify-clio.yml | 4 ++-- .github/workflows/on-trigger.yml | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index f3b3374090..3fb8f057a2 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -70,7 +70,7 @@ jobs: - name: Generate strategy matrix working-directory: .github/scripts/strategy-matrix id: generate - run: python generate.py ${{ inputs.strategy_matrix_all && '--all' || '' }} --config=${{ inputs.os }}.json | tee "${GITHUB_OUTPUT}" + run: python generate.py ${{ inputs.strategy_matrix_all && '--all' || '' }} --config=${{ inputs.os }}.json >> "${GITHUB_OUTPUT}" outputs: matrix: ${{ steps.generate.outputs.matrix }} diff --git a/.github/workflows/notify-clio.yml b/.github/workflows/notify-clio.yml index 32ac01d61a..ae95d04db0 100644 --- a/.github/workflows/notify-clio.yml +++ b/.github/workflows/notify-clio.yml @@ -45,9 +45,9 @@ jobs: id: generate run: | echo 'Generating channel.' - echo channel="clio/pr_${{ github.event.pull_request.number }}" | tee "${GITHUB_OUTPUT}" + echo channel="clio/pr_${{ github.event.pull_request.number }}" >> "${GITHUB_OUTPUT}" echo 'Extracting version.' - echo version="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" | tee "${GITHUB_OUTPUT}" + echo version="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" >> "${GITHUB_OUTPUT}" - name: Add Conan remote run: | echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index a6dded7fd8..004e44c1fb 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -82,14 +82,14 @@ jobs: id: generate run: | if [[ "${{ github.event_name }}" == 'push' ]]; then - echo 'dependencies_force_build=false' | tee "${GITHUB_OUTPUT}" - echo 'dependencies_force_upload=false' | tee "${GITHUB_OUTPUT}" + echo 'dependencies_force_build=false' >> "${GITHUB_OUTPUT}" + echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}" elif [[ "${{ github.event_name }}" == 'schedule' ]]; then - echo 'dependencies_force_build=true' | tee "${GITHUB_OUTPUT}" - echo 'dependencies_force_upload=false' | tee "${GITHUB_OUTPUT}" + echo 'dependencies_force_build=true' >> "${GITHUB_OUTPUT}" + echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}" else - echo 'dependencies_force_build=${{ inputs.dependencies_force_build }}' | tee "${GITHUB_OUTPUT}" - echo 'dependencies_force_upload=${{ inputs.dependencies_force_upload }}' | tee "${GITHUB_OUTPUT}" + echo 'dependencies_force_build=${{ inputs.dependencies_force_build }}' >> "${GITHUB_OUTPUT}" + echo 'dependencies_force_upload=${{ inputs.dependencies_force_upload }}' >> "${GITHUB_OUTPUT}" fi outputs: conan_remote_name: ${{ env.CONAN_REMOTE_NAME }} From afc05659edbc22d04b13402f157832778556f7b7 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 19 Aug 2025 12:46:38 -0400 Subject: [PATCH 09/66] fix: Adjust the CI workflows (#5700) --- .github/actions/build-deps/action.yml | 29 +++++-------- .github/actions/build-test/action.yml | 25 +++++------- .github/scripts/strategy-matrix/generate.py | 16 +++++++- .github/workflows/build-test.yml | 33 ++++++++++----- .github/workflows/notify-clio.yml | 15 ++++--- .github/workflows/on-pr.yml | 29 ++++--------- .github/workflows/on-trigger.yml | 45 +++++---------------- .github/workflows/publish-docs.yml | 2 + 8 files changed, 84 insertions(+), 110 deletions(-) diff --git a/.github/actions/build-deps/action.yml b/.github/actions/build-deps/action.yml index 12d80e859c..272af0f97d 100644 --- a/.github/actions/build-deps/action.yml +++ b/.github/actions/build-deps/action.yml @@ -3,46 +3,37 @@ # provided. name: Build Conan dependencies +# Note that actions do not support 'type' and all inputs are strings, see +# https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs. inputs: build_dir: description: 'The directory where to build.' required: true - type: string build_type: - description: 'The build type to use.' + description: 'The build type to use ("Debug", "Release").' required: true - type: choice - options: - - 'Debug' - - 'Release' conan_remote_name: description: 'The name of the Conan remote to use.' required: true - type: string conan_remote_url: description: 'The URL of the Conan endpoint to use.' required: true - type: string conan_remote_username: description: 'The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' required: false - type: string default: '' conan_remote_password: description: 'The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' required: false - type: string default: '' force_build: - description: 'Force building of all dependencies.' + description: 'Force building of all dependencies ("true", "false").' required: false - type: boolean - default: false + default: 'false' force_upload: - description: 'Force uploading of all dependencies.' + description: 'Force uploading of all dependencies ("true", "false").' required: false - type: boolean - default: false + default: 'false' runs: using: composite @@ -55,17 +46,17 @@ runs: cd ${{ inputs.build_dir }} conan install \ --output-folder . \ - --build ${{ inputs.force_build && '"*"' || 'missing' }} \ + --build ${{ inputs.force_build == 'true' && '"*"' || 'missing' }} \ --options:host '&:tests=True' \ --options:host '&:xrpld=True' \ --settings:all build_type=${{ inputs.build_type }} \ --format=json .. - name: Upload Conan dependencies - if: ${{ inputs.conan_remote_username && inputs.conan_remote_password }} + if: ${{ inputs.conan_remote_username != '' && inputs.conan_remote_password != '' }} shell: bash working-directory: ${{ inputs.build_dir }} run: | echo "Logging into Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." conan remote login ${{ inputs.conan_remote_name }} "${{ inputs.conan_remote_username }}" --password "${{ inputs.conan_remote_password }}" echo 'Uploading dependencies.' - conan upload '*' --confirm --check ${{ inputs.force_upload && '--force' || '' }} --remote=${{ inputs.conan_remote_name }} + conan upload '*' --confirm --check ${{ inputs.force_upload == 'true' && '--force' || '' }} --remote=${{ inputs.conan_remote_name }} diff --git a/.github/actions/build-test/action.yml b/.github/actions/build-test/action.yml index 30337ddb98..fc01d81091 100644 --- a/.github/actions/build-test/action.yml +++ b/.github/actions/build-test/action.yml @@ -2,40 +2,33 @@ # already been installed (see the build-deps action). name: Build and Test +# Note that actions do not support 'type' and all inputs are strings, see +# https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs. inputs: build_dir: description: 'The directory where to build.' required: true - type: string + build_only: + description: 'Whether to only build or to build and test the code ("true", "false").' + required: false + default: 'false' build_type: - description: 'The build type to use.' + description: 'The build type to use ("Debug", "Release").' required: true - type: choice - options: - - 'Debug' - - 'Release' cmake_args: description: 'Additional arguments to pass to CMake.' required: false - type: string default: '' cmake_target: description: 'The CMake target to build.' required: true - type: string codecov_token: description: 'The Codecov token to use for uploading coverage reports.' required: false - type: string default: '' os: - description: 'The operating system to use for the build (linux, macos, or windows).' + description: 'The operating system to use for the build ("linux", "macos", "windows").' required: true - type: choice - options: - - 'linux' - - 'macos' - - 'windows' runs: using: composite @@ -82,7 +75,7 @@ runs: echo 'Verifying presence of instrumentation.' ./rippled --version | grep libvoidstar - name: Test the binary - if: ${{ inputs.cmake_target != 'coverage' }} + if: ${{ inputs.build_only == 'true' }} shell: bash working-directory: ${{ inputs.build_dir }}/${{ inputs.os == 'windows' && inputs.build_type || '' }} run: | diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index a5180c942d..42927b5ccd 100644 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -9,7 +9,7 @@ Generate a strategy matrix for GitHub Actions CI. On each PR commit we will build a selection of Debian, RHEL, Ubuntu, MacOS, and Windows configurations, while upon merge into the develop, release, or master -branches, we will build all configurations. +branches, we will build all configurations, and test most of them. We will further set additional CMake arguments as follows: - All builds will have the `tests`, `werr`, and `xrpld` options. @@ -25,6 +25,13 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] # for Windows, but it can get overridden for certain configurations. cmake_target = 'install' if os["distro_name"] == 'windows' else 'all' + # We build and test all configurations by default, except for Windows in + # Debug, because it is too slow, as well as when code coverage is + # enabled as that mode already runs the tests. + build_only = False + if os['distro_name'] == 'windows' and build_type == 'Debug': + build_only = True + # Only generate a subset of configurations in PRs. if not all: # Debian: @@ -46,6 +53,7 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64': cmake_args = f'{cmake_args} -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0' cmake_target = 'coverage' + build_only = True skip = False if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-16' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/arm64': cmake_args = f'{cmake_args} -Dvoidstar=ON' @@ -110,6 +118,11 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] if build_type == 'Release': cmake_args = f'{cmake_args} -Dassert=ON' + # We skip all RHEL on arm64 due to a build failure that needs further + # investigation. + if os['distro_name'] == 'rhel' and architecture['platform'] == 'linux/arm64': + continue + # Generate a unique name for the configuration, e.g. macos-arm64-debug # or debian-bookworm-gcc-12-amd64-release-unity. config_name = os['distro_name'] @@ -128,6 +141,7 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] 'architecture': architecture, 'os': os, 'build_type': build_type, + 'build_only': 'true' if build_only else 'false', 'cmake_args': cmake_args, 'cmake_target': cmake_target, 'config_name': config_name, diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 3fb8f057a2..a330f5decb 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -1,7 +1,10 @@ # This workflow builds and tests the binary for various configurations. name: Build and test -# This workflow can only be triggered by other workflows. +# This workflow can only be triggered by other workflows. Note that the +# workflow_call event does not support the 'choice' input type, see +# https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#onworkflow_callinputsinput_idtype, +# so we use 'string' instead. on: workflow_call: inputs: @@ -29,14 +32,15 @@ on: type: boolean default: false os: - description: 'The operating system to use for the build (linux, macos, or windows).' + description: 'The operating system to use for the build ("linux", "macos", "windows").' required: true type: string - strategy_matrix_all: - description: 'Generate a strategy matrix containing all configurations.' + strategy_matrix: + # TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations. + description: 'The strategy matrix to use for generating the configurations ("minimal", "all").' required: false - type: boolean - default: false + type: string + default: 'minimal' secrets: codecov_token: description: 'The Codecov token to use for uploading coverage reports.' @@ -70,7 +74,7 @@ jobs: - name: Generate strategy matrix working-directory: .github/scripts/strategy-matrix id: generate - run: python generate.py ${{ inputs.strategy_matrix_all && '--all' || '' }} --config=${{ inputs.os }}.json >> "${GITHUB_OUTPUT}" + run: python generate.py ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} --config=${{ inputs.os }}.json >> "${GITHUB_OUTPUT}" outputs: matrix: ${{ steps.generate.outputs.matrix }} @@ -101,9 +105,11 @@ jobs: uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: 3.13 - - name: Install Conan (Windows) + - name: Install build tools (Windows) if: ${{ inputs.os == 'windows' }} - run: pip install wheel conan + run: | + echo 'Installing build tools.' + pip install wheel conan - name: Check configuration (Windows) if: ${{ inputs.os == 'windows' }} run: | @@ -115,6 +121,11 @@ jobs: echo 'Checking Conan version.' conan --version + - name: Install build tools (MacOS) + if: ${{ inputs.os == 'macos' }} + run: | + echo 'Installing build tools.' + brew install cmake conan ninja coreutils - name: Check configuration (Linux and MacOS) if: ${{ inputs.os == 'linux' || inputs.os == 'macos' }} run: | @@ -135,6 +146,9 @@ jobs: echo 'Checking Ninja version.' ninja --version + + echo 'Checking nproc version.' + nproc --version - name: Set up Conan home directory (MacOS) if: ${{ inputs.os == 'macos' }} run: | @@ -184,6 +198,7 @@ jobs: uses: ./.github/actions/build-test with: build_dir: ${{ inputs.build_dir }} + build_only: ${{ matrix.build_only }} build_type: ${{ matrix.build_type }} cmake_args: ${{ matrix.cmake_args }} cmake_target: ${{ matrix.cmake_target }} diff --git a/.github/workflows/notify-clio.yml b/.github/workflows/notify-clio.yml index ae95d04db0..6ccf527ea6 100644 --- a/.github/workflows/notify-clio.yml +++ b/.github/workflows/notify-clio.yml @@ -44,10 +44,11 @@ jobs: - name: Generate outputs id: generate run: | - echo 'Generating channel.' - echo channel="clio/pr_${{ github.event.pull_request.number }}" >> "${GITHUB_OUTPUT}" + echo 'Generating user and channel.' + echo "user=clio" >> "${GITHUB_OUTPUT}" + echo "channel=pr_${{ github.event.pull_request.number }}" >> "${GITHUB_OUTPUT}" echo 'Extracting version.' - echo version="$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" >> "${GITHUB_OUTPUT}" + echo "version=$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" >> "${GITHUB_OUTPUT}" - name: Add Conan remote run: | echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." @@ -58,10 +59,8 @@ jobs: run: conan remote login ${{ inputs.conan_remote_name }} "${{ secrets.conan_remote_username }}" --password "${{ secrets.conan_remote_password }}" - name: Upload package run: | - echo 'Exporting package to channel ${{ steps.generate.outputs.channel }}.' - conan export --channel=${{ steps.generate.outputs.channel }} . - echo 'Uploading package version ${{ steps.generate.outputs.version }} on channel ${{ steps.generate.outputs.channel }}.' - conan upload --confirm --check --remote=${{ inputs.conan_remote_name }} xrpl/${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.channel }} + conan export --user=${{ steps.generate.outputs.user }} --channel=${{ steps.generate.outputs.channel }} . + conan upload --confirm --check --remote=${{ inputs.conan_remote_name }} xrpl/${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }} outputs: channel: ${{ steps.generate.outputs.channel }} version: ${{ steps.generate.outputs.version }} @@ -76,5 +75,5 @@ jobs: run: | gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ - -F "client_payload[version]=${{ needs.upload.outputs.version }}@${{ needs.upload.outputs.channel }}" \ + -F "client_payload[version]=${{ needs.upload.outputs.version }}@${{ needs.upload.outputs.user }}/${{ needs.upload.outputs.channel }}" \ -F "client_payload[pr]=${{ github.event.pull_request.number }}" diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 10bc61797a..9d7bbbf89c 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -71,7 +71,7 @@ jobs: - name: No-op run: echo '' - check-clang-format: + check-format: needs: should-run uses: ./.github/workflows/check-format.yml @@ -91,38 +91,23 @@ jobs: conan_remote_name: ${{ env.CONAN_REMOTE_NAME }} conan_remote_url: ${{ env.CONAN_REMOTE_URL }} - build-linux: + build-test: needs: generate-outputs uses: ./.github/workflows/build-test.yml + strategy: + matrix: + os: [linux, macos, windows] with: conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} - os: 'linux' + os: ${{ matrix.os }} secrets: codecov_token: ${{ secrets.CODECOV_TOKEN }} - build-macos: - needs: generate-outputs - uses: ./.github/workflows/build-test.yml - with: - conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} - conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} - os: 'macos' - - build-windows: - needs: generate-outputs - uses: ./.github/workflows/build-test.yml - with: - conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} - conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} - os: 'windows' - notify-clio: needs: - generate-outputs - - build-linux - - build-macos - - build-windows + - build-test uses: ./.github/workflows/notify-clio.yml with: conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index 004e44c1fb..ed9a794985 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -81,10 +81,10 @@ jobs: - name: Check inputs and set outputs id: generate run: | - if [[ "${{ github.event_name }}" == 'push' ]]; then + if [[ '${{ github.event_name }}' == 'push' ]]; then echo 'dependencies_force_build=false' >> "${GITHUB_OUTPUT}" echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}" - elif [[ "${{ github.event_name }}" == 'schedule' ]]; then + elif [[ '${{ github.event_name }}' == 'schedule' ]]; then echo 'dependencies_force_build=true' >> "${GITHUB_OUTPUT}" echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}" else @@ -97,44 +97,19 @@ jobs: dependencies_force_build: ${{ steps.generate.outputs.dependencies_force_build }} dependencies_force_upload: ${{ steps.generate.outputs.dependencies_force_upload }} - build-linux: + build-test: needs: generate-outputs uses: ./.github/workflows/build-test.yml + strategy: + matrix: + os: [linux, macos, windows] with: conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} - dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build }} - dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload }} - os: 'linux' - strategy_matrix_all: true - secrets: - conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} - conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} - - build-macos: - needs: generate-outputs - uses: ./.github/workflows/build-test.yml - with: - conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} - conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} - dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build }} - dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload }} - os: 'macos' - strategy_matrix_all: true - secrets: - conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} - conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} - - build-windows: - needs: generate-outputs - uses: ./.github/workflows/build-test.yml - with: - conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} - conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} - dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build }} - dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload }} - os: 'windows' - strategy_matrix_all: true + dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build == 'true' }} + dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload == 'true' }} + os: ${{ matrix.os }} + strategy_matrix: 'all' secrets: conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index a2c24bd076..509644e6b5 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -28,6 +28,8 @@ jobs: publish: runs-on: ubuntu-latest container: ghcr.io/xrplf/ci/tools-rippled-documentation + permissions: + contents: write steps: - name: Checkout repository uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 From 56c1e078f2367da8d5c42b6ef1b7ffe0d0da6bec Mon Sep 17 00:00:00 2001 From: Bart Date: Wed, 20 Aug 2025 19:25:40 -0400 Subject: [PATCH 10/66] fix: Correctly check for build_only when deciding whether to run tests (#5708) This change modifies the `build_only` check used to determine whether to run tests. For easier debugging in the future it also prints out the contents of the strategy matrix. --- .github/actions/build-test/action.yml | 2 +- .github/workflows/build-test.yml | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/actions/build-test/action.yml b/.github/actions/build-test/action.yml index fc01d81091..44292e7318 100644 --- a/.github/actions/build-test/action.yml +++ b/.github/actions/build-test/action.yml @@ -75,7 +75,7 @@ runs: echo 'Verifying presence of instrumentation.' ./rippled --version | grep libvoidstar - name: Test the binary - if: ${{ inputs.build_only == 'true' }} + if: ${{ inputs.build_only == 'false' }} shell: bash working-directory: ${{ inputs.build_dir }}/${{ inputs.os == 'windows' && inputs.build_type || '' }} run: | diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index a330f5decb..2fa557f671 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -88,6 +88,19 @@ jobs: runs-on: ${{ matrix.architecture.runner }} container: ${{ inputs.os == 'linux' && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }} steps: + - name: Check strategy matrix + run: | + echo 'Operating system distro name: ${{ matrix.os.distro_name }}' + echo 'Operating system distro version: ${{ matrix.os.distro_version }}' + echo 'Operating system compiler name: ${{ matrix.os.compiler_name }}' + echo 'Operating system compiler version: ${{ matrix.os.compiler_version }}' + echo 'Architecture platform: ${{ matrix.architecture.platform }}' + echo 'Architecture runner: ${{ toJson(matrix.architecture.runner) }}' + echo 'Build type: ${{ matrix.build_type }}' + echo 'Build only: ${{ matrix.build_only }}' + echo 'CMake arguments: ${{ matrix.cmake_args }}' + echo 'CMake target: ${{ matrix.cmake_target }}' + echo 'Config name: ${{ matrix.config_name }}' - name: Clean workspace (MacOS) if: ${{ inputs.os == 'macos' }} run: | From f847e3287cf50a6d31d6b31e8e7db3e57bf3aa8d Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 21 Aug 2025 07:41:00 -0400 Subject: [PATCH 11/66] Update Conan dependencies: OpenSSL (#5617) This change updates OpenSSL from 1.1.1w to 3.5.2. The code works as-is, but many functions have been marked as deprecated and thus will need to be rewritten. For now we explicitly add the `-DOPENSSL_SUPPRESS_DEPRECATED` to give us time to do so, while providing us with the benefits of the updated version. --- cmake/RippledCompiler.cmake | 17 ++++++++++------- conanfile.py | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/cmake/RippledCompiler.cmake b/cmake/RippledCompiler.cmake index bc3a62a48c..4d16222cbe 100644 --- a/cmake/RippledCompiler.cmake +++ b/cmake/RippledCompiler.cmake @@ -16,13 +16,16 @@ set(CMAKE_CXX_EXTENSIONS OFF) target_compile_definitions (common INTERFACE $<$:DEBUG _DEBUG> - $<$,$>>:NDEBUG>) - # ^^^^ NOTE: CMAKE release builds already have NDEBUG - # defined, so no need to add it explicitly except for - # this special case of (profile ON) and (assert OFF) - # -- presumably this is because we don't want profile - # builds asserting unless asserts were specifically - # requested + #[===[ + NOTE: CMAKE release builds already have NDEBUG defined, so no need to add it + explicitly except for the special case of (profile ON) and (assert OFF). + Presumably this is because we don't want profile builds asserting unless + asserts were specifically requested. + ]===] + $<$,$>>:NDEBUG> + # TODO: Remove once we have migrated functions from OpenSSL 1.x to 3.x. + OPENSSL_SUPPRESS_DEPRECATED +) if (MSVC) # remove existing exception flag since we set it to -EHa diff --git a/conanfile.py b/conanfile.py index ab4657277c..da99836157 100644 --- a/conanfile.py +++ b/conanfile.py @@ -27,7 +27,7 @@ class Xrpl(ConanFile): 'grpc/1.50.1', 'libarchive/3.8.1', 'nudb/2.0.9', - 'openssl/1.1.1w', + 'openssl/3.5.2', 'soci/4.0.3', 'zlib/1.3.1', ] From b13370ac0d207217354f1fc1c29aef87769fb8a1 Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 21 Aug 2025 15:22:25 -0400 Subject: [PATCH 12/66] chore: Reverts formatting changes to external files, adds formatting changes to proto files (#5711) This change reverts the formatting applied to external files and adds formatting of proto files. As clang-format will complain if a proto file is modified or moved, since the .clang-format file does not explicitly contain a section for proto files, the change has been included in this PR as well. --- .clang-format | 5 + external/{antithesis-sdk => }/.clang-format | 2 +- external/ed25519-donna/README.md | 97 +- external/ed25519-donna/fuzz/README.md | 99 +- external/secp256k1/CHANGELOG.md | 144 +- external/secp256k1/CMakePresets.json | 6 +- external/secp256k1/CONTRIBUTING.md | 74 +- external/secp256k1/README.md | 130 +- external/secp256k1/SECURITY.md | 10 +- external/secp256k1/doc/ellswift.md | 410 +- external/secp256k1/doc/musig.md | 3 +- external/secp256k1/doc/release-process.md | 40 +- .../secp256k1/doc/safegcd_implementation.md | 301 +- .../ecdsa_secp256k1_sha256_bitcoin_test.json | 8086 ++++++++--------- .../proto/org/xrpl/rpc/v1/get_ledger.proto | 114 +- .../org/xrpl/rpc/v1/get_ledger_data.proto | 63 +- .../org/xrpl/rpc/v1/get_ledger_diff.proto | 30 +- .../org/xrpl/rpc/v1/get_ledger_entry.proto | 32 +- .../xrpl/proto/org/xrpl/rpc/v1/ledger.proto | 88 +- .../proto/org/xrpl/rpc/v1/xrp_ledger.proto | 3 - include/xrpl/proto/ripple.proto | 500 +- 21 files changed, 5056 insertions(+), 5181 deletions(-) rename external/{antithesis-sdk => }/.clang-format (54%) diff --git a/.clang-format b/.clang-format index cfd991e64b..9c3820a6bf 100644 --- a/.clang-format +++ b/.clang-format @@ -100,3 +100,8 @@ Language: JavaScript --- Language: Json IndentWidth: 2 +--- +Language: Proto +BasedOnStyle: Google +ColumnLimit: 0 +IndentWidth: 2 diff --git a/external/antithesis-sdk/.clang-format b/external/.clang-format similarity index 54% rename from external/antithesis-sdk/.clang-format rename to external/.clang-format index e871ed18b4..a5121ff074 100644 --- a/external/antithesis-sdk/.clang-format +++ b/external/.clang-format @@ -1,3 +1,3 @@ --- DisableFormat: true -SortIncludes: false +SortIncludes: Never diff --git a/external/ed25519-donna/README.md b/external/ed25519-donna/README.md index 31b2431632..e09fc27e31 100644 --- a/external/ed25519-donna/README.md +++ b/external/ed25519-donna/README.md @@ -1,12 +1,12 @@ -[ed25519](http://ed25519.cr.yp.to/) is an -[Elliptic Curve Digital Signature Algortithm](http://en.wikipedia.org/wiki/Elliptic_Curve_DSA), -developed by [Dan Bernstein](http://cr.yp.to/djb.html), -[Niels Duif](http://www.nielsduif.nl/), -[Tanja Lange](http://hyperelliptic.org/tanja), -[Peter Schwabe](http://www.cryptojedi.org/users/peter/), +[ed25519](http://ed25519.cr.yp.to/) is an +[Elliptic Curve Digital Signature Algortithm](http://en.wikipedia.org/wiki/Elliptic_Curve_DSA), +developed by [Dan Bernstein](http://cr.yp.to/djb.html), +[Niels Duif](http://www.nielsduif.nl/), +[Tanja Lange](http://hyperelliptic.org/tanja), +[Peter Schwabe](http://www.cryptojedi.org/users/peter/), and [Bo-Yin Yang](http://www.iis.sinica.edu.tw/pages/byyang/). -This project provides performant, portable 32-bit & 64-bit implementations. All implementations are +This project provides performant, portable 32-bit & 64-bit implementations. All implementations are of course constant time in regard to secret data. #### Performance @@ -52,35 +52,35 @@ are made. #### Compilation -No configuration is needed **if you are compiling against OpenSSL**. +No configuration is needed **if you are compiling against OpenSSL**. ##### Hash Options If you are not compiling aginst OpenSSL, you will need a hash function. -To use a simple/**slow** implementation of SHA-512, use `-DED25519_REFHASH` when compiling `ed25519.c`. +To use a simple/**slow** implementation of SHA-512, use `-DED25519_REFHASH` when compiling `ed25519.c`. This should never be used except to verify the code works when OpenSSL is not available. -To use a custom hash function, use `-DED25519_CUSTOMHASH` when compiling `ed25519.c` and put your +To use a custom hash function, use `-DED25519_CUSTOMHASH` when compiling `ed25519.c` and put your custom hash implementation in ed25519-hash-custom.h. The hash must have a 512bit digest and implement - struct ed25519_hash_context; + struct ed25519_hash_context; - void ed25519_hash_init(ed25519_hash_context *ctx); - void ed25519_hash_update(ed25519_hash_context *ctx, const uint8_t *in, size_t inlen); - void ed25519_hash_final(ed25519_hash_context *ctx, uint8_t *hash); - void ed25519_hash(uint8_t *hash, const uint8_t *in, size_t inlen); + void ed25519_hash_init(ed25519_hash_context *ctx); + void ed25519_hash_update(ed25519_hash_context *ctx, const uint8_t *in, size_t inlen); + void ed25519_hash_final(ed25519_hash_context *ctx, uint8_t *hash); + void ed25519_hash(uint8_t *hash, const uint8_t *in, size_t inlen); ##### Random Options If you are not compiling aginst OpenSSL, you will need a random function for batch verification. -To use a custom random function, use `-DED25519_CUSTOMRANDOM` when compiling `ed25519.c` and put your +To use a custom random function, use `-DED25519_CUSTOMRANDOM` when compiling `ed25519.c` and put your custom hash implementation in ed25519-randombytes-custom.h. The random function must implement: - void ED25519_FN(ed25519_randombytes_unsafe) (void *p, size_t len); + void ED25519_FN(ed25519_randombytes_unsafe) (void *p, size_t len); -Use `-DED25519_TEST` when compiling `ed25519.c` to use a deterministically seeded, non-thread safe CSPRNG +Use `-DED25519_TEST` when compiling `ed25519.c` to use a deterministically seeded, non-thread safe CSPRNG variant of Bob Jenkins [ISAAC](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29) ##### Minor options @@ -91,79 +91,80 @@ Use `-DED25519_FORCE_32BIT` to force the use of 32 bit routines even when compil ##### 32-bit - gcc ed25519.c -m32 -O3 -c + gcc ed25519.c -m32 -O3 -c ##### 64-bit - gcc ed25519.c -m64 -O3 -c + gcc ed25519.c -m64 -O3 -c ##### SSE2 - gcc ed25519.c -m32 -O3 -c -DED25519_SSE2 -msse2 - gcc ed25519.c -m64 -O3 -c -DED25519_SSE2 + gcc ed25519.c -m32 -O3 -c -DED25519_SSE2 -msse2 + gcc ed25519.c -m64 -O3 -c -DED25519_SSE2 clang and icc are also supported + #### Usage To use the code, link against `ed25519.o -mbits` and: - #include "ed25519.h" + #include "ed25519.h" Add `-lssl -lcrypto` when using OpenSSL (Some systems don't need -lcrypto? It might be trial and error). To generate a private key, simply generate 32 bytes from a secure cryptographic source: - ed25519_secret_key sk; - randombytes(sk, sizeof(ed25519_secret_key)); + ed25519_secret_key sk; + randombytes(sk, sizeof(ed25519_secret_key)); To generate a public key: - ed25519_public_key pk; - ed25519_publickey(sk, pk); + ed25519_public_key pk; + ed25519_publickey(sk, pk); To sign a message: - ed25519_signature sig; - ed25519_sign(message, message_len, sk, pk, signature); + ed25519_signature sig; + ed25519_sign(message, message_len, sk, pk, signature); To verify a signature: - int valid = ed25519_sign_open(message, message_len, pk, signature) == 0; + int valid = ed25519_sign_open(message, message_len, pk, signature) == 0; To batch verify signatures: - const unsigned char *mp[num] = {message1, message2..} - size_t ml[num] = {message_len1, message_len2..} - const unsigned char *pkp[num] = {pk1, pk2..} - const unsigned char *sigp[num] = {signature1, signature2..} - int valid[num] + const unsigned char *mp[num] = {message1, message2..} + size_t ml[num] = {message_len1, message_len2..} + const unsigned char *pkp[num] = {pk1, pk2..} + const unsigned char *sigp[num] = {signature1, signature2..} + int valid[num] - /* valid[i] will be set to 1 if the individual signature was valid, 0 otherwise */ - int all_valid = ed25519_sign_open_batch(mp, ml, pkp, sigp, num, valid) == 0; + /* valid[i] will be set to 1 if the individual signature was valid, 0 otherwise */ + int all_valid = ed25519_sign_open_batch(mp, ml, pkp, sigp, num, valid) == 0; -**Note**: Batch verification uses `ed25519_randombytes_unsafe`, implemented in -`ed25519-randombytes.h`, to generate random scalars for the verification code. +**Note**: Batch verification uses `ed25519_randombytes_unsafe`, implemented in +`ed25519-randombytes.h`, to generate random scalars for the verification code. The default implementation now uses OpenSSLs `RAND_bytes`. Unlike the [SUPERCOP](http://bench.cr.yp.to/supercop.html) version, signatures are -not appended to messages, and there is no need for padding in front of messages. -Additionally, the secret key does not contain a copy of the public key, so it is +not appended to messages, and there is no need for padding in front of messages. +Additionally, the secret key does not contain a copy of the public key, so it is 32 bytes instead of 64 bytes, and the public key must be provided to the signing function. ##### Curve25519 -Curve25519 public keys can be generated thanks to -[Adam Langley](http://www.imperialviolet.org/2013/05/10/fastercurve25519.html) +Curve25519 public keys can be generated thanks to +[Adam Langley](http://www.imperialviolet.org/2013/05/10/fastercurve25519.html) leveraging Ed25519's precomputed basepoint scalar multiplication. - curved25519_key sk, pk; - randombytes(sk, sizeof(curved25519_key)); - curved25519_scalarmult_basepoint(pk, sk); + curved25519_key sk, pk; + randombytes(sk, sizeof(curved25519_key)); + curved25519_scalarmult_basepoint(pk, sk); -Note the name is curved25519, a combination of curve and ed25519, to prevent +Note the name is curved25519, a combination of curve and ed25519, to prevent name clashes. Performance is slightly faster than short message ed25519 signing due to both using the same code for the scalar multiply. @@ -179,4 +180,4 @@ with extreme values to ensure they function correctly. SSE2 is now supported. #### Papers -[Available on the Ed25519 website](http://ed25519.cr.yp.to/papers.html) +[Available on the Ed25519 website](http://ed25519.cr.yp.to/papers.html) \ No newline at end of file diff --git a/external/ed25519-donna/fuzz/README.md b/external/ed25519-donna/fuzz/README.md index 0a5cd49177..306ddfe08c 100644 --- a/external/ed25519-donna/fuzz/README.md +++ b/external/ed25519-donna/fuzz/README.md @@ -1,78 +1,78 @@ This code fuzzes ed25519-donna (and optionally ed25519-donna-sse2) against the ref10 implementations of -[curve25519](https://github.com/floodyberry/supercop/tree/master/crypto_scalarmult/curve25519/ref10) and +[curve25519](https://github.com/floodyberry/supercop/tree/master/crypto_scalarmult/curve25519/ref10) and [ed25519](https://github.com/floodyberry/supercop/tree/master/crypto_sign/ed25519/ref10). Curve25519 tests that generating a public key from a secret key # Building -## \*nix + PHP +## *nix + PHP `php build-nix.php (required parameters) (optional parameters)` Required parameters: -- `--function=[curve25519,ed25519]` -- `--bits=[32,64]` +* `--function=[curve25519,ed25519]` +* `--bits=[32,64]` Optional parameters: -- `--with-sse2` +* `--with-sse2` - Also fuzz against ed25519-donna-sse2 + Also fuzz against ed25519-donna-sse2 +* `--with-openssl` -- `--with-openssl` + Build with OpenSSL's SHA-512. - Build with OpenSSL's SHA-512. + Default: Reference SHA-512 implementation (slow!) - Default: Reference SHA-512 implementation (slow!) +* `--compiler=[gcc,clang,icc]` -- `--compiler=[gcc,clang,icc]` + Default: gcc - Default: gcc +* `--no-asm` -- `--no-asm` + Do not use platform specific assembler - Do not use platform specific assembler example: - - php build-nix.php --bits=64 --function=ed25519 --with-sse2 --compiler=icc + + php build-nix.php --bits=64 --function=ed25519 --with-sse2 --compiler=icc ## Windows Create a project with access to the ed25519 files. -If you are not using OpenSSL, add the `ED25519_REFHASH` define to the projects +If you are not using OpenSSL, add the `ED25519_REFHASH` define to the projects "Properties/Preprocessor/Preprocessor Definitions" option Add the following files to the project: -- `fuzz/curve25519-ref10.c` -- `fuzz/ed25519-ref10.c` -- `fuzz/ed25519-donna.c` -- `fuzz/ed25519-donna-sse2.c` (optional) -- `fuzz-[curve25519/ed25519].c` (depending on which you want to fuzz) +* `fuzz/curve25519-ref10.c` +* `fuzz/ed25519-ref10.c` +* `fuzz/ed25519-donna.c` +* `fuzz/ed25519-donna-sse2.c` (optional) +* `fuzz-[curve25519/ed25519].c` (depending on which you want to fuzz) -If you are also fuzzing against ed25519-donna-sse2, add the `ED25519_SSE2` define for `fuzz-[curve25519/ed25519].c` under +If you are also fuzzing against ed25519-donna-sse2, add the `ED25519_SSE2` define for `fuzz-[curve25519/ed25519].c` under its "Properties/Preprocessor/Preprocessor Definitions" option. # Running -If everything agrees, the program will only output occasional status dots (every 0x1000 passes) +If everything agrees, the program will only output occasional status dots (every 0x1000 passes) and a 64bit progress count (every 0x20000 passes): fuzzing: ref10 curved25519 curved25519-sse2 - + ................................ [0000000000020000] ................................ [0000000000040000] ................................ [0000000000060000] ................................ [0000000000080000] ................................ [00000000000a0000] ................................ [00000000000c0000] - + If any of the implementations do not agree with the ref10 implementation, the program will dump -the random data that was used, the data generated by the ref10 implementation, and diffs of the +the random data that was used, the data generated by the ref10 implementation, and diffs of the ed25519-donna data against the ref10 data. ## Example errors @@ -83,21 +83,21 @@ These are example error dumps (with intentionally introduced errors). Random data: -- sk, or Secret Key -- m, or Message +* sk, or Secret Key +* m, or Message Generated data: -- pk, or Public Key -- sig, or Signature -- valid, or if the signature of the message is valid with the public key +* pk, or Public Key +* sig, or Signature +* valid, or if the signature of the message is valid with the public key Dump: sk: 0x3b,0xb7,0x17,0x7a,0x66,0xdc,0xb7,0x9a,0x90,0x25,0x07,0x99,0x96,0xf3,0x92,0xef, 0x78,0xf8,0xad,0x6c,0x35,0x87,0x81,0x67,0x03,0xe6,0x95,0xba,0x06,0x18,0x7c,0x9c, - + m: 0x7c,0x8d,0x3d,0xe1,0x92,0xee,0x7a,0xb8,0x4d,0xc9,0xfb,0x02,0x34,0x1e,0x5a,0x91, 0xee,0x01,0xa6,0xb8,0xab,0x37,0x3f,0x3d,0x6d,0xa2,0x47,0xe3,0x27,0x93,0x7c,0xb7, @@ -107,66 +107,67 @@ Dump: 0x63,0x14,0xe0,0x81,0x52,0xec,0xcd,0xcf,0x70,0x54,0x7d,0xa3,0x49,0x8b,0xf0,0x89, 0x70,0x07,0x12,0x2a,0xd9,0xaa,0x16,0x01,0xb2,0x16,0x3a,0xbb,0xfc,0xfa,0x13,0x5b, 0x69,0x83,0x92,0x70,0x95,0x76,0xa0,0x8e,0x16,0x79,0xcc,0xaa,0xb5,0x7c,0xf8,0x7a, - + ref10: pk: 0x71,0xb0,0x5e,0x62,0x1b,0xe3,0xe7,0x36,0x91,0x8b,0xc0,0x13,0x36,0x0c,0xc9,0x04, 0x16,0xf5,0xff,0x48,0x0c,0x83,0x6b,0x88,0x53,0xa2,0xc6,0x0f,0xf7,0xac,0x42,0x04, - + sig: 0x3e,0x05,0xc5,0x37,0x16,0x0b,0x29,0x30,0x89,0xa3,0xe7,0x83,0x08,0x16,0xdd,0x96, 0x02,0xfa,0x0d,0x44,0x2c,0x43,0xaa,0x80,0x93,0x04,0x58,0x22,0x09,0xbf,0x11,0xa5, 0xcc,0xa5,0x3c,0x9f,0xa0,0xa4,0x64,0x5a,0x4a,0xdb,0x20,0xfb,0xc7,0x9b,0xfd,0x3f, 0x08,0xae,0xc4,0x3c,0x1e,0xd8,0xb6,0xb4,0xd2,0x6d,0x80,0x92,0xcb,0x71,0xf3,0x02, - + valid: yes - + ed25519-donna: pk diff: ____,____,____,____,____,____,____,____,____,____,____,____,____,____,____,____, ____,____,____,____,____,____,____,____,____,____,____,____,____,____,____,____, - + sig diff: 0x2c,0xb9,0x25,0x14,0xd0,0x94,0xeb,0xfe,0x46,0x02,0xc2,0xe8,0xa3,0xeb,0xbf,0xb5, 0x72,0x84,0xbf,0xc1,0x8a,0x32,0x30,0x99,0xf7,0x58,0xfe,0x06,0xa8,0xdc,0xdc,0xab, 0xb5,0x57,0x03,0x33,0x87,0xce,0x54,0x55,0x6a,0x69,0x8a,0xc4,0xb7,0x2a,0xed,0x97, 0xb4,0x68,0xe7,0x52,0x7a,0x07,0x55,0x3b,0xa2,0x94,0xd6,0x5e,0xa1,0x61,0x80,0x08, - + valid: no -In this case, the generated public key matches, but the generated signature is completely +In this case, the generated public key matches, but the generated signature is completely different and does not validate. ### Curve25519 Random data: -- sk, or Secret Key +* sk, or Secret Key Generated data: -- pk, or Public Key +* pk, or Public Key Dump: sk: 0x44,0xec,0x0b,0x0e,0xa2,0x0e,0x9c,0x5b,0x8c,0xce,0x7b,0x1d,0x68,0xae,0x0f,0x9e, 0x81,0xe2,0x04,0x76,0xda,0x87,0xa4,0x9e,0xc9,0x4f,0x3b,0xf9,0xc3,0x89,0x63,0x70, - - + + ref10: 0x24,0x55,0x55,0xc0,0xf9,0x80,0xaf,0x02,0x43,0xee,0x8c,0x7f,0xc1,0xad,0x90,0x95, 0x57,0x91,0x14,0x2e,0xf2,0x14,0x22,0x80,0xdd,0x4e,0x3c,0x85,0x71,0x84,0x8c,0x62, - - + + curved25519 diff: 0x12,0xd1,0x61,0x2b,0x16,0xb3,0xd8,0x29,0xf8,0xa3,0xba,0x70,0x4e,0x49,0x4f,0x43, 0xa1,0x3c,0x6b,0x42,0x11,0x61,0xcc,0x30,0x87,0x73,0x46,0xfb,0x85,0xc7,0x9a,0x35, - - + + curved25519-sse2 diff: ____,____,____,____,____,____,____,____,____,____,____,____,____,____,____,____, ____,____,____,____,____,____,____,____,____,____,____,____,____,____,____,____, -In this case, curved25519 is totally wrong, while curved25519-sse2 matches the reference -implementation. + +In this case, curved25519 is totally wrong, while curved25519-sse2 matches the reference +implementation. \ No newline at end of file diff --git a/external/secp256k1/CHANGELOG.md b/external/secp256k1/CHANGELOG.md index a000672887..ee447c0c1c 100644 --- a/external/secp256k1/CHANGELOG.md +++ b/external/secp256k1/CHANGELOG.md @@ -8,189 +8,153 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.6.0] - 2024-11-04 #### Added - -- New module `musig` implements the MuSig2 multisignature scheme according to the [BIP 327 specification](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). See: - - Header file `include/secp256k1_musig.h` which defines the new API. - - Document `doc/musig.md` for further notes on API usage. - - Usage example `examples/musig.c`. -- New CMake variable `SECP256K1_APPEND_LDFLAGS` for appending linker flags to the build command. + - New module `musig` implements the MuSig2 multisignature scheme according to the [BIP 327 specification](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). See: + - Header file `include/secp256k1_musig.h` which defines the new API. + - Document `doc/musig.md` for further notes on API usage. + - Usage example `examples/musig.c`. + - New CMake variable `SECP256K1_APPEND_LDFLAGS` for appending linker flags to the build command. #### Changed - -- API functions now use a significantly more robust method to clear secrets from the stack before returning. However, secret clearing remains a best-effort security measure and cannot guarantee complete removal. -- Any type `secp256k1_foo` can now be forward-declared using `typedef struct secp256k1_foo secp256k1_foo;` (or also `struct secp256k1_foo;` in C++). -- Organized CMake build artifacts into dedicated directories (`bin/` for executables, `lib/` for libraries) to improve build output structure and Windows shared library compatibility. + - API functions now use a significantly more robust method to clear secrets from the stack before returning. However, secret clearing remains a best-effort security measure and cannot guarantee complete removal. + - Any type `secp256k1_foo` can now be forward-declared using `typedef struct secp256k1_foo secp256k1_foo;` (or also `struct secp256k1_foo;` in C++). + - Organized CMake build artifacts into dedicated directories (`bin/` for executables, `lib/` for libraries) to improve build output structure and Windows shared library compatibility. #### Removed - -- Removed the `secp256k1_scratch_space` struct and its associated functions `secp256k1_scratch_space_create` and `secp256k1_scratch_space_destroy` because the scratch space was unused in the API. + - Removed the `secp256k1_scratch_space` struct and its associated functions `secp256k1_scratch_space_create` and `secp256k1_scratch_space_destroy` because the scratch space was unused in the API. #### ABI Compatibility - The symbols `secp256k1_scratch_space_create` and `secp256k1_scratch_space_destroy` were removed. Otherwise, the library maintains backward compatibility with versions 0.3.x through 0.5.x. ## [0.5.1] - 2024-08-01 #### Added - -- Added usage example for an ElligatorSwift key exchange. + - Added usage example for an ElligatorSwift key exchange. #### Changed - -- The default size of the precomputed table for signing was changed from 22 KiB to 86 KiB. The size can be changed with the configure option `--ecmult-gen-kb` (`SECP256K1_ECMULT_GEN_KB` for CMake). -- "auto" is no longer an accepted value for the `--with-ecmult-window` and `--with-ecmult-gen-kb` configure options (this also applies to `SECP256K1_ECMULT_WINDOW_SIZE` and `SECP256K1_ECMULT_GEN_KB` in CMake). To achieve the same configuration as previously provided by the "auto" value, omit setting the configure option explicitly. + - The default size of the precomputed table for signing was changed from 22 KiB to 86 KiB. The size can be changed with the configure option `--ecmult-gen-kb` (`SECP256K1_ECMULT_GEN_KB` for CMake). + - "auto" is no longer an accepted value for the `--with-ecmult-window` and `--with-ecmult-gen-kb` configure options (this also applies to `SECP256K1_ECMULT_WINDOW_SIZE` and `SECP256K1_ECMULT_GEN_KB` in CMake). To achieve the same configuration as previously provided by the "auto" value, omit setting the configure option explicitly. #### Fixed - -- Fixed compilation when the extrakeys module is disabled. + - Fixed compilation when the extrakeys module is disabled. #### ABI Compatibility - The ABI is backward compatible with versions 0.5.0, 0.4.x and 0.3.x. ## [0.5.0] - 2024-05-06 #### Added - -- New function `secp256k1_ec_pubkey_sort` that sorts public keys using lexicographic (of compressed serialization) order. + - New function `secp256k1_ec_pubkey_sort` that sorts public keys using lexicographic (of compressed serialization) order. #### Changed - -- The implementation of the point multiplication algorithm used for signing and public key generation was changed, resulting in improved performance for those operations. - - The related configure option `--ecmult-gen-precision` was replaced with `--ecmult-gen-kb` (`SECP256K1_ECMULT_GEN_KB` for CMake). - - This changes the supported precomputed table sizes for these operations. The new supported sizes are 2 KiB, 22 KiB, or 86 KiB (while the old supported sizes were 32 KiB, 64 KiB, or 512 KiB). + - The implementation of the point multiplication algorithm used for signing and public key generation was changed, resulting in improved performance for those operations. + - The related configure option `--ecmult-gen-precision` was replaced with `--ecmult-gen-kb` (`SECP256K1_ECMULT_GEN_KB` for CMake). + - This changes the supported precomputed table sizes for these operations. The new supported sizes are 2 KiB, 22 KiB, or 86 KiB (while the old supported sizes were 32 KiB, 64 KiB, or 512 KiB). #### ABI Compatibility - The ABI is backward compatible with versions 0.4.x and 0.3.x. ## [0.4.1] - 2023-12-21 #### Changed - -- The point multiplication algorithm used for ECDH operations (module `ecdh`) was replaced with a slightly faster one. -- Optional handwritten x86_64 assembly for field operations was removed because modern C compilers are able to output more efficient assembly. This change results in a significant speedup of some library functions when handwritten x86_64 assembly is enabled (`--with-asm=x86_64` in GNU Autotools, `-DSECP256K1_ASM=x86_64` in CMake), which is the default on x86_64. Benchmarks with GCC 10.5.0 show a 10% speedup for `secp256k1_ecdsa_verify` and `secp256k1_schnorrsig_verify`. + - The point multiplication algorithm used for ECDH operations (module `ecdh`) was replaced with a slightly faster one. + - Optional handwritten x86_64 assembly for field operations was removed because modern C compilers are able to output more efficient assembly. This change results in a significant speedup of some library functions when handwritten x86_64 assembly is enabled (`--with-asm=x86_64` in GNU Autotools, `-DSECP256K1_ASM=x86_64` in CMake), which is the default on x86_64. Benchmarks with GCC 10.5.0 show a 10% speedup for `secp256k1_ecdsa_verify` and `secp256k1_schnorrsig_verify`. #### ABI Compatibility - The ABI is backward compatible with versions 0.4.0 and 0.3.x. ## [0.4.0] - 2023-09-04 #### Added - -- New module `ellswift` implements ElligatorSwift encoding for public keys and x-only Diffie-Hellman key exchange for them. - ElligatorSwift permits representing secp256k1 public keys as 64-byte arrays which cannot be distinguished from uniformly random. See: - - Header file `include/secp256k1_ellswift.h` which defines the new API. - - Document `doc/ellswift.md` which explains the mathematical background of the scheme. - - The [paper](https://eprint.iacr.org/2022/759) on which the scheme is based. -- We now test the library with unreleased development snapshots of GCC and Clang. This gives us an early chance to catch miscompilations and constant-time issues introduced by the compiler (such as those that led to the previous two releases). + - New module `ellswift` implements ElligatorSwift encoding for public keys and x-only Diffie-Hellman key exchange for them. + ElligatorSwift permits representing secp256k1 public keys as 64-byte arrays which cannot be distinguished from uniformly random. See: + - Header file `include/secp256k1_ellswift.h` which defines the new API. + - Document `doc/ellswift.md` which explains the mathematical background of the scheme. + - The [paper](https://eprint.iacr.org/2022/759) on which the scheme is based. + - We now test the library with unreleased development snapshots of GCC and Clang. This gives us an early chance to catch miscompilations and constant-time issues introduced by the compiler (such as those that led to the previous two releases). #### Fixed - -- Fixed symbol visibility in Windows DLL builds, where three internal library symbols were wrongly exported. + - Fixed symbol visibility in Windows DLL builds, where three internal library symbols were wrongly exported. #### Changed - -- When consuming libsecp256k1 as a static library on Windows, the user must now define the `SECP256K1_STATIC` macro before including `secp256k1.h`. + - When consuming libsecp256k1 as a static library on Windows, the user must now define the `SECP256K1_STATIC` macro before including `secp256k1.h`. #### ABI Compatibility - This release is backward compatible with the ABI of 0.3.0, 0.3.1, and 0.3.2. Symbol visibility is now believed to be handled properly on supported platforms and is now considered to be part of the ABI. Please report any improperly exported symbols as a bug. ## [0.3.2] - 2023-05-13 - We strongly recommend updating to 0.3.2 if you use or plan to use GCC >=13 to compile libsecp256k1. When in doubt, check the GCC version using `gcc -v`. #### Security - -- Module `ecdh`: Fix "constant-timeness" issue with GCC 13.1 (and potentially future versions of GCC) that could leave applications using libsecp256k1's ECDH module vulnerable to a timing side-channel attack. The fix avoids secret-dependent control flow during ECDH computations when libsecp256k1 is compiled with GCC 13.1. + - Module `ecdh`: Fix "constant-timeness" issue with GCC 13.1 (and potentially future versions of GCC) that could leave applications using libsecp256k1's ECDH module vulnerable to a timing side-channel attack. The fix avoids secret-dependent control flow during ECDH computations when libsecp256k1 is compiled with GCC 13.1. #### Fixed - -- Fixed an old bug that permitted compilers to potentially output bad assembly code on x86_64. In theory, it could lead to a crash or a read of unrelated memory, but this has never been observed on any compilers so far. + - Fixed an old bug that permitted compilers to potentially output bad assembly code on x86_64. In theory, it could lead to a crash or a read of unrelated memory, but this has never been observed on any compilers so far. #### Changed - -- Various improvements and changes to CMake builds. CMake builds remain experimental. - - Made API versioning consistent with GNU Autotools builds. - - Switched to `BUILD_SHARED_LIBS` variable for controlling whether to build a static or a shared library. - - Added `SECP256K1_INSTALL` variable for the controlling whether to install the build artefacts. -- Renamed asm build option `arm` to `arm32`. Use `--with-asm=arm32` instead of `--with-asm=arm` (GNU Autotools), and `-DSECP256K1_ASM=arm32` instead of `-DSECP256K1_ASM=arm` (CMake). + - Various improvements and changes to CMake builds. CMake builds remain experimental. + - Made API versioning consistent with GNU Autotools builds. + - Switched to `BUILD_SHARED_LIBS` variable for controlling whether to build a static or a shared library. + - Added `SECP256K1_INSTALL` variable for the controlling whether to install the build artefacts. + - Renamed asm build option `arm` to `arm32`. Use `--with-asm=arm32` instead of `--with-asm=arm` (GNU Autotools), and `-DSECP256K1_ASM=arm32` instead of `-DSECP256K1_ASM=arm` (CMake). #### ABI Compatibility - The ABI is compatible with versions 0.3.0 and 0.3.1. ## [0.3.1] - 2023-04-10 - We strongly recommend updating to 0.3.1 if you use or plan to use Clang >=14 to compile libsecp256k1, e.g., Xcode >=14 on macOS has Clang >=14. When in doubt, check the Clang version using `clang -v`. #### Security - -- Fix "constant-timeness" issue with Clang >=14 that could leave applications using libsecp256k1 vulnerable to a timing side-channel attack. The fix avoids secret-dependent control flow and secret-dependent memory accesses in conditional moves of memory objects when libsecp256k1 is compiled with Clang >=14. + - Fix "constant-timeness" issue with Clang >=14 that could leave applications using libsecp256k1 vulnerable to a timing side-channel attack. The fix avoids secret-dependent control flow and secret-dependent memory accesses in conditional moves of memory objects when libsecp256k1 is compiled with Clang >=14. #### Added - -- Added tests against [Project Wycheproof's](https://github.com/google/wycheproof/) set of ECDSA test vectors (Bitcoin "low-S" variant), a fixed set of test cases designed to trigger various edge cases. + - Added tests against [Project Wycheproof's](https://github.com/google/wycheproof/) set of ECDSA test vectors (Bitcoin "low-S" variant), a fixed set of test cases designed to trigger various edge cases. #### Changed - -- Increased minimum required CMake version to 3.13. CMake builds remain experimental. + - Increased minimum required CMake version to 3.13. CMake builds remain experimental. #### ABI Compatibility - The ABI is compatible with version 0.3.0. ## [0.3.0] - 2023-03-08 #### Added - -- Added experimental support for CMake builds. Traditional GNU Autotools builds (`./configure` and `make`) remain fully supported. -- Usage examples: Added a recommended method for securely clearing sensitive data, e.g., secret keys, from memory. -- Tests: Added a new test binary `noverify_tests`. This binary runs the tests without some additional checks present in the ordinary `tests` binary and is thereby closer to production binaries. The `noverify_tests` binary is automatically run as part of the `make check` target. + - Added experimental support for CMake builds. Traditional GNU Autotools builds (`./configure` and `make`) remain fully supported. + - Usage examples: Added a recommended method for securely clearing sensitive data, e.g., secret keys, from memory. + - Tests: Added a new test binary `noverify_tests`. This binary runs the tests without some additional checks present in the ordinary `tests` binary and is thereby closer to production binaries. The `noverify_tests` binary is automatically run as part of the `make check` target. #### Fixed - -- Fixed declarations of API variables for MSVC (`__declspec(dllimport)`). This fixes MSVC builds of programs which link against a libsecp256k1 DLL dynamically and use API variables (and not only API functions). Unfortunately, the MSVC linker now will emit warning `LNK4217` when trying to link against libsecp256k1 statically. Pass `/ignore:4217` to the linker to suppress this warning. + - Fixed declarations of API variables for MSVC (`__declspec(dllimport)`). This fixes MSVC builds of programs which link against a libsecp256k1 DLL dynamically and use API variables (and not only API functions). Unfortunately, the MSVC linker now will emit warning `LNK4217` when trying to link against libsecp256k1 statically. Pass `/ignore:4217` to the linker to suppress this warning. #### Changed - -- Forbade cloning or destroying `secp256k1_context_static`. Create a new context instead of cloning the static context. (If this change breaks your code, your code is probably wrong.) -- Forbade randomizing (copies of) `secp256k1_context_static`. Randomizing a copy of `secp256k1_context_static` did not have any effect and did not provide defense-in-depth protection against side-channel attacks. Create a new context if you want to benefit from randomization. + - Forbade cloning or destroying `secp256k1_context_static`. Create a new context instead of cloning the static context. (If this change breaks your code, your code is probably wrong.) + - Forbade randomizing (copies of) `secp256k1_context_static`. Randomizing a copy of `secp256k1_context_static` did not have any effect and did not provide defense-in-depth protection against side-channel attacks. Create a new context if you want to benefit from randomization. #### Removed - -- Removed the configuration header `src/libsecp256k1-config.h`. We recommend passing flags to `./configure` or `cmake` to set configuration options (see `./configure --help` or `cmake -LH`). If you cannot or do not want to use one of the supported build systems, pass configuration flags such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG` manually to the compiler (see the file `configure.ac` for supported flags). + - Removed the configuration header `src/libsecp256k1-config.h`. We recommend passing flags to `./configure` or `cmake` to set configuration options (see `./configure --help` or `cmake -LH`). If you cannot or do not want to use one of the supported build systems, pass configuration flags such as `-DSECP256K1_ENABLE_MODULE_SCHNORRSIG` manually to the compiler (see the file `configure.ac` for supported flags). #### ABI Compatibility - -Due to changes in the API regarding `secp256k1_context_static` described above, the ABI is _not_ compatible with previous versions. +Due to changes in the API regarding `secp256k1_context_static` described above, the ABI is *not* compatible with previous versions. ## [0.2.0] - 2022-12-12 #### Added - -- Added usage examples for common use cases in a new `examples/` directory. -- Added `secp256k1_selftest`, to be used in conjunction with `secp256k1_context_static`. -- Added support for 128-bit wide multiplication on MSVC for x86_64 and arm64, giving roughly a 20% speedup on those platforms. + - Added usage examples for common use cases in a new `examples/` directory. + - Added `secp256k1_selftest`, to be used in conjunction with `secp256k1_context_static`. + - Added support for 128-bit wide multiplication on MSVC for x86_64 and arm64, giving roughly a 20% speedup on those platforms. #### Changed - -- Enabled modules `schnorrsig`, `extrakeys` and `ecdh` by default in `./configure`. -- The `secp256k1_nonce_function_rfc6979` nonce function, used by default by `secp256k1_ecdsa_sign`, now reduces the message hash modulo the group order to match the specification. This only affects improper use of ECDSA signing API. + - Enabled modules `schnorrsig`, `extrakeys` and `ecdh` by default in `./configure`. + - The `secp256k1_nonce_function_rfc6979` nonce function, used by default by `secp256k1_ecdsa_sign`, now reduces the message hash modulo the group order to match the specification. This only affects improper use of ECDSA signing API. #### Deprecated - -- Deprecated context flags `SECP256K1_CONTEXT_VERIFY` and `SECP256K1_CONTEXT_SIGN`. Use `SECP256K1_CONTEXT_NONE` instead. -- Renamed `secp256k1_context_no_precomp` to `secp256k1_context_static`. -- Module `schnorrsig`: renamed `secp256k1_schnorrsig_sign` to `secp256k1_schnorrsig_sign32`. + - Deprecated context flags `SECP256K1_CONTEXT_VERIFY` and `SECP256K1_CONTEXT_SIGN`. Use `SECP256K1_CONTEXT_NONE` instead. + - Renamed `secp256k1_context_no_precomp` to `secp256k1_context_static`. + - Module `schnorrsig`: renamed `secp256k1_schnorrsig_sign` to `secp256k1_schnorrsig_sign32`. #### ABI Compatibility - Since this is the first release, we do not compare application binary interfaces. -However, there are earlier unreleased versions of libsecp256k1 that are _not_ ABI compatible with this version. +However, there are earlier unreleased versions of libsecp256k1 that are *not* ABI compatible with this version. ## [0.1.0] - 2013-03-05 to 2021-12-25 diff --git a/external/secp256k1/CMakePresets.json b/external/secp256k1/CMakePresets.json index 60138c16bf..b35cd80579 100644 --- a/external/secp256k1/CMakePresets.json +++ b/external/secp256k1/CMakePresets.json @@ -1,9 +1,5 @@ { - "cmakeMinimumRequired": { - "major": 3, - "minor": 21, - "patch": 0 - }, + "cmakeMinimumRequired": {"major": 3, "minor": 21, "patch": 0}, "version": 3, "configurePresets": [ { diff --git a/external/secp256k1/CONTRIBUTING.md b/external/secp256k1/CONTRIBUTING.md index 88c22af02b..a366d38b0e 100644 --- a/external/secp256k1/CONTRIBUTING.md +++ b/external/secp256k1/CONTRIBUTING.md @@ -12,15 +12,15 @@ The libsecp256k1 project welcomes contributions in the form of new functionality It is the responsibility of the contributors to convince the maintainers that the proposed functionality is within the project's scope, high-quality and maintainable. Contributors are recommended to provide the following in addition to the new code: -- **Specification:** - A specification can help significantly in reviewing the new code as it provides documentation and context. - It may justify various design decisions, give a motivation and outline security goals. - If the specification contains pseudocode, a reference implementation or test vectors, these can be used to compare with the proposed libsecp256k1 code. -- **Security Arguments:** - In addition to a defining the security goals, it should be argued that the new functionality meets these goals. - Depending on the nature of the new functionality, a wide range of security arguments are acceptable, ranging from being "obviously secure" to rigorous proofs of security. -- **Relevance Arguments:** - The relevance of the new functionality for the Bitcoin ecosystem should be argued by outlining clear use cases. +* **Specification:** + A specification can help significantly in reviewing the new code as it provides documentation and context. + It may justify various design decisions, give a motivation and outline security goals. + If the specification contains pseudocode, a reference implementation or test vectors, these can be used to compare with the proposed libsecp256k1 code. +* **Security Arguments:** + In addition to a defining the security goals, it should be argued that the new functionality meets these goals. + Depending on the nature of the new functionality, a wide range of security arguments are acceptable, ranging from being "obviously secure" to rigorous proofs of security. +* **Relevance Arguments:** + The relevance of the new functionality for the Bitcoin ecosystem should be argued by outlining clear use cases. These are not the only factors taken into account when considering to add new functionality. The proposed new libsecp256k1 code must be of high quality, including API documentation and tests, as well as featuring a misuse-resistant API design. @@ -44,36 +44,36 @@ The Contributor Workflow & Peer Review in libsecp256k1 are similar to Bitcoin Co In addition, libsecp256k1 tries to maintain the following coding conventions: -- No runtime heap allocation (e.g., no `malloc`) unless explicitly requested by the caller (via `secp256k1_context_create` or `secp256k1_scratch_space_create`, for example). Moreover, it should be possible to use the library without any heap allocations. -- The tests should cover all lines and branches of the library (see [Test coverage](#coverage)). -- Operations involving secret data should be tested for being constant time with respect to the secrets (see [src/ctime_tests.c](src/ctime_tests.c)). -- Local variables containing secret data should be cleared explicitly to try to delete secrets from memory. -- Use `secp256k1_memcmp_var` instead of `memcmp` (see [#823](https://github.com/bitcoin-core/secp256k1/issues/823)). -- As a rule of thumb, the default values for configuration options should target standard desktop machines and align with Bitcoin Core's defaults, and the tests should mostly exercise the default configuration (see [#1549](https://github.com/bitcoin-core/secp256k1/issues/1549#issuecomment-2200559257)). +* No runtime heap allocation (e.g., no `malloc`) unless explicitly requested by the caller (via `secp256k1_context_create` or `secp256k1_scratch_space_create`, for example). Moreover, it should be possible to use the library without any heap allocations. +* The tests should cover all lines and branches of the library (see [Test coverage](#coverage)). +* Operations involving secret data should be tested for being constant time with respect to the secrets (see [src/ctime_tests.c](src/ctime_tests.c)). +* Local variables containing secret data should be cleared explicitly to try to delete secrets from memory. +* Use `secp256k1_memcmp_var` instead of `memcmp` (see [#823](https://github.com/bitcoin-core/secp256k1/issues/823)). +* As a rule of thumb, the default values for configuration options should target standard desktop machines and align with Bitcoin Core's defaults, and the tests should mostly exercise the default configuration (see [#1549](https://github.com/bitcoin-core/secp256k1/issues/1549#issuecomment-2200559257)). #### Style conventions -- Commits should be atomic and diffs should be easy to read. For this reason, do not mix any formatting fixes or code moves with actual code changes. Make sure each individual commit is hygienic: that it builds successfully on its own without warnings, errors, regressions, or test failures. -- New code should adhere to the style of existing, in particular surrounding, code. Other than that, we do not enforce strict rules for code formatting. -- The code conforms to C89. Most notably, that means that only `/* ... */` comments are allowed (no `//` line comments). Moreover, any declarations in a `{ ... }` block (e.g., a function) must appear at the beginning of the block before any statements. When you would like to declare a variable in the middle of a block, you can open a new block: - ```C - void secp256k_foo(void) { - unsigned int x; /* declaration */ - int y = 2*x; /* declaration */ - x = 17; /* statement */ - { - int a, b; /* declaration */ - a = x + y; /* statement */ - secp256k_bar(x, &b); /* statement */ - } - } - ``` -- Use `unsigned int` instead of just `unsigned`. -- Use `void *ptr` instead of `void* ptr`. -- Arguments of the publicly-facing API must have a specific order defined in [include/secp256k1.h](include/secp256k1.h). -- User-facing comment lines in headers should be limited to 80 chars if possible. -- All identifiers in file scope should start with `secp256k1_`. -- Avoid trailing whitespace. +* Commits should be atomic and diffs should be easy to read. For this reason, do not mix any formatting fixes or code moves with actual code changes. Make sure each individual commit is hygienic: that it builds successfully on its own without warnings, errors, regressions, or test failures. +* New code should adhere to the style of existing, in particular surrounding, code. Other than that, we do not enforce strict rules for code formatting. +* The code conforms to C89. Most notably, that means that only `/* ... */` comments are allowed (no `//` line comments). Moreover, any declarations in a `{ ... }` block (e.g., a function) must appear at the beginning of the block before any statements. When you would like to declare a variable in the middle of a block, you can open a new block: + ```C + void secp256k_foo(void) { + unsigned int x; /* declaration */ + int y = 2*x; /* declaration */ + x = 17; /* statement */ + { + int a, b; /* declaration */ + a = x + y; /* statement */ + secp256k_bar(x, &b); /* statement */ + } + } + ``` +* Use `unsigned int` instead of just `unsigned`. +* Use `void *ptr` instead of `void* ptr`. +* Arguments of the publicly-facing API must have a specific order defined in [include/secp256k1.h](include/secp256k1.h). +* User-facing comment lines in headers should be limited to 80 chars if possible. +* All identifiers in file scope should start with `secp256k1_`. +* Avoid trailing whitespace. ### Tests @@ -101,7 +101,7 @@ To create a HTML report with coloured and annotated source code: #### Exhaustive tests There are tests of several functions in which a small group replaces secp256k1. -These tests are _exhaustive_ since they provide all elements and scalars of the small group as input arguments (see [src/tests_exhaustive.c](src/tests_exhaustive.c)). +These tests are *exhaustive* since they provide all elements and scalars of the small group as input arguments (see [src/tests_exhaustive.c](src/tests_exhaustive.c)). ### Benchmarks diff --git a/external/secp256k1/README.md b/external/secp256k1/README.md index 4cd64c7fee..222e5fb768 100644 --- a/external/secp256k1/README.md +++ b/external/secp256k1/README.md @@ -1,4 +1,5 @@ -# libsecp256k1 +libsecp256k1 +============ ![Dependencies: None](https://img.shields.io/badge/dependencies-none-success) [![irc.libera.chat #secp256k1](https://img.shields.io/badge/irc.libera.chat-%23secp256k1-success)](https://web.libera.chat/#secp256k1) @@ -8,59 +9,60 @@ High-performance high-assurance C library for digital signatures and other crypt This library is intended to be the highest quality publicly available library for cryptography on the secp256k1 curve. However, the primary focus of its development has been for usage in the Bitcoin system and usage unlike Bitcoin's may be less well tested, verified, or suffer from a less well thought out interface. Correct usage requires some care and consideration that the library is fit for your application's purpose. Features: +* secp256k1 ECDSA signing/verification and key generation. +* Additive and multiplicative tweaking of secret/public keys. +* Serialization/parsing of secret keys, public keys, signatures. +* Constant time, constant memory access signing and public key generation. +* Derandomized ECDSA (via RFC6979 or with a caller provided function.) +* Very efficient implementation. +* Suitable for embedded systems. +* No runtime dependencies. +* Optional module for public key recovery. +* Optional module for ECDH key exchange. +* Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). +* Optional module for ElligatorSwift key exchange according to [BIP-324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki). +* Optional module for MuSig2 Schnorr multi-signatures according to [BIP-327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). -- secp256k1 ECDSA signing/verification and key generation. -- Additive and multiplicative tweaking of secret/public keys. -- Serialization/parsing of secret keys, public keys, signatures. -- Constant time, constant memory access signing and public key generation. -- Derandomized ECDSA (via RFC6979 or with a caller provided function.) -- Very efficient implementation. -- Suitable for embedded systems. -- No runtime dependencies. -- Optional module for public key recovery. -- Optional module for ECDH key exchange. -- Optional module for Schnorr signatures according to [BIP-340](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki). -- Optional module for ElligatorSwift key exchange according to [BIP-324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki). -- Optional module for MuSig2 Schnorr multi-signatures according to [BIP-327](https://github.com/bitcoin/bips/blob/master/bip-0327.mediawiki). +Implementation details +---------------------- -## Implementation details +* General + * No runtime heap allocation. + * Extensive testing infrastructure. + * Structured to facilitate review and analysis. + * Intended to be portable to any system with a C89 compiler and uint64_t support. + * No use of floating types. + * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") +* Field operations + * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). + * Using 5 52-bit limbs + * Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan). + * This is an experimental feature that has not received enough scrutiny to satisfy the standard of quality of this library but is made available for testing and review by the community. +* Scalar operations + * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. + * Using 4 64-bit limbs (relying on __int128 support in the compiler). + * Using 8 32-bit limbs. +* Modular inverses (both field elements and scalars) based on [safegcd](https://gcd.cr.yp.to/index.html) with some modifications, and a variable-time variant (by Peter Dettman). +* Group operations + * Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). + * Use addition between points in Jacobian and affine coordinates where possible. + * Use a unified addition/doubling formula where necessary to avoid data-dependent branches. + * Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. +* Point multiplication for verification (a*P + b*G). + * Use wNAF notation for point multiplicands. + * Use a much larger window for multiples of G, using precomputed multiples. + * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. + * Use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. +* Point multiplication for signing + * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. + * Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains) + * Access the table with branch-free conditional moves so memory access is uniform. + * No data-dependent branches + * Optional runtime blinding which attempts to frustrate differential power analysis. + * The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally. -- General - - No runtime heap allocation. - - Extensive testing infrastructure. - - Structured to facilitate review and analysis. - - Intended to be portable to any system with a C89 compiler and uint64_t support. - - No use of floating types. - - Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") -- Field operations - - Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). - - Using 5 52-bit limbs - - Using 10 26-bit limbs (including hand-optimized assembly for 32-bit ARM, by Wladimir J. van der Laan). - - This is an experimental feature that has not received enough scrutiny to satisfy the standard of quality of this library but is made available for testing and review by the community. -- Scalar operations - - Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. - - Using 4 64-bit limbs (relying on \_\_int128 support in the compiler). - - Using 8 32-bit limbs. -- Modular inverses (both field elements and scalars) based on [safegcd](https://gcd.cr.yp.to/index.html) with some modifications, and a variable-time variant (by Peter Dettman). -- Group operations - - Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). - - Use addition between points in Jacobian and affine coordinates where possible. - - Use a unified addition/doubling formula where necessary to avoid data-dependent branches. - - Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. -- Point multiplication for verification (a*P + b*G). - - Use wNAF notation for point multiplicands. - - Use a much larger window for multiples of G, using precomputed multiples. - - Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. - - Use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. -- Point multiplication for signing - - Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. - - Intended to be completely free of timing sidechannels for secret-key operations (on reasonable hardware/toolchains) - - Access the table with branch-free conditional moves so memory access is uniform. - - No data-dependent branches - - Optional runtime blinding which attempts to frustrate differential power analysis. - - The precomputed tables add and eventually subtract points for which no known scalar (secret key) is known, preventing even an attacker with control over the secret key used to control the data internally. - -## Building with Autotools +Building with Autotools +----------------------- $ ./autogen.sh $ ./configure @@ -70,7 +72,8 @@ Features: To compile optional modules (such as Schnorr signatures), you need to run `./configure` with additional flags (such as `--enable-module-schnorrsig`). Run `./configure --help` to see the full list of available flags. -## Building with CMake (experimental) +Building with CMake (experimental) +---------------------------------- To maintain a pristine source tree, CMake encourages to perform an out-of-source build by using a separate dedicated build tree. @@ -106,19 +109,18 @@ In "Developer Command Prompt for VS 2022": >cmake -G "Visual Studio 17 2022" -A x64 -S . -B build >cmake --build build --config RelWithDebInfo -## Usage examples - +Usage examples +----------- Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`. - -- [ECDSA example](examples/ecdsa.c) -- [Schnorr signatures example](examples/schnorr.c) -- [Deriving a shared secret (ECDH) example](examples/ecdh.c) -- [ElligatorSwift key exchange example](examples/ellswift.c) + * [ECDSA example](examples/ecdsa.c) + * [Schnorr signatures example](examples/schnorr.c) + * [Deriving a shared secret (ECDH) example](examples/ecdh.c) + * [ElligatorSwift key exchange example](examples/ellswift.c) To compile the Schnorr signature and ECDH examples, you also need to configure with `--enable-module-schnorrsig` and `--enable-module-ecdh`. -## Benchmark - +Benchmark +------------ If configured with `--enable-benchmark` (which is the default), binaries for benchmarking the libsecp256k1 functions will be present in the root directory after the build. To print the benchmark result to the command line: @@ -129,10 +131,12 @@ To create a CSV file for the benchmark result : $ ./bench_name | sed '2d;s/ \{1,\}//g' > bench_name.csv -## Reporting a vulnerability +Reporting a vulnerability +------------ See [SECURITY.md](SECURITY.md) -## Contributing to libsecp256k1 +Contributing to libsecp256k1 +------------ See [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/external/secp256k1/SECURITY.md b/external/secp256k1/SECURITY.md index cb438707ce..b515cc1c8e 100644 --- a/external/secp256k1/SECURITY.md +++ b/external/secp256k1/SECURITY.md @@ -6,10 +6,10 @@ To report security issues send an email to secp256k1-security@bitcoincore.org (n The following keys may be used to communicate sensitive information to developers: -| Name | Fingerprint | -| ------------- | ------------------------------------------------- | -| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 | -| Jonas Nick | 36C7 1A37 C9D9 88BD E825 08D9 B1A7 0E4F 8DCD 0366 | -| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 | +| Name | Fingerprint | +|------|-------------| +| Pieter Wuille | 133E AC17 9436 F14A 5CF1 B794 860F EB80 4E66 9320 | +| Jonas Nick | 36C7 1A37 C9D9 88BD E825 08D9 B1A7 0E4F 8DCD 0366 | +| Tim Ruffing | 09E0 3F87 1092 E40E 106E 902B 33BC 86AB 80FF 5516 | You can import a key by running the following command with that individual’s fingerprint: `gpg --keyserver hkps://keys.openpgp.org --recv-keys ""` Ensure that you put quotes around fingerprints containing spaces. diff --git a/external/secp256k1/doc/ellswift.md b/external/secp256k1/doc/ellswift.md index ffbe9d02ac..9d60e6be0b 100644 --- a/external/secp256k1/doc/ellswift.md +++ b/external/secp256k1/doc/ellswift.md @@ -5,17 +5,17 @@ construction in the ["SwiftEC: Shallue–van de Woestijne Indifferentiable Function To Elliptic Curves"](https://eprint.iacr.org/2022/759) paper by Jorge Chávez-Saab, Francisco Rodríguez-Henríquez, and Mehdi Tibouchi. -- [1. Introduction](#1-introduction) -- [2. The decoding function](#2-the-decoding-function) - - [2.1 Decoding for `secp256k1`](#21-decoding-for-secp256k1) -- [3. The encoding function](#3-the-encoding-function) - - [3.1 Switching to _v, w_ coordinates](#31-switching-to-v-w-coordinates) - - [3.2 Avoiding computing all inverses](#32-avoiding-computing-all-inverses) - - [3.3 Finding the inverse](#33-finding-the-inverse) - - [3.4 Dealing with special cases](#34-dealing-with-special-cases) - - [3.5 Encoding for `secp256k1`](#35-encoding-for-secp256k1) -- [4. Encoding and decoding full _(x, y)_ coordinates](#4-encoding-and-decoding-full-x-y-coordinates) - - [4.1 Full _(x, y)_ coordinates for `secp256k1`](#41-full-x-y-coordinates-for-secp256k1) +* [1. Introduction](#1-introduction) +* [2. The decoding function](#2-the-decoding-function) + + [2.1 Decoding for `secp256k1`](#21-decoding-for-secp256k1) +* [3. The encoding function](#3-the-encoding-function) + + [3.1 Switching to *v, w* coordinates](#31-switching-to-v-w-coordinates) + + [3.2 Avoiding computing all inverses](#32-avoiding-computing-all-inverses) + + [3.3 Finding the inverse](#33-finding-the-inverse) + + [3.4 Dealing with special cases](#34-dealing-with-special-cases) + + [3.5 Encoding for `secp256k1`](#35-encoding-for-secp256k1) +* [4. Encoding and decoding full *(x, y)* coordinates](#4-encoding-and-decoding-full-x-y-coordinates) + + [4.1 Full *(x, y)* coordinates for `secp256k1`](#41-full-x-y-coordinates-for-secp256k1) ## 1. Introduction @@ -34,14 +34,13 @@ are taken modulo $p$), and then evaluating $F_u(t)$, which for every $u$ and $t$ x-coordinate on the curve. The functions $F_u$ will be defined in [Section 2](#2-the-decoding-function). **Encoding** a given $x$ coordinate is conceptually done as follows: +* Loop: + * Pick a uniformly random field element $u.$ + * Compute the set $L = F_u^{-1}(x)$ of $t$ values for which $F_u(t) = x$, which may have up to *8* elements. + * With probability $1 - \dfrac{\\#L}{8}$, restart the loop. + * Select a uniformly random $t \in L$ and return $(u, t).$ -- Loop: - - Pick a uniformly random field element $u.$ - - Compute the set $L = F_u^{-1}(x)$ of $t$ values for which $F_u(t) = x$, which may have up to _8_ elements. - - With probability $1 - \dfrac{\\#L}{8}$, restart the loop. - - Select a uniformly random $t \in L$ and return $(u, t).$ - -This is the _ElligatorSwift_ algorithm, here given for just x-coordinates. An extension to full +This is the *ElligatorSwift* algorithm, here given for just x-coordinates. An extension to full $(x, y)$ points will be given in [Section 4](#4-encoding-and-decoding-full-x-y-coordinates). The algorithm finds a uniformly random $(u, t)$ among (almost all) those for which $F_u(t) = x.$ Section 3.2 in the paper proves that the number of such encodings for @@ -51,40 +50,37 @@ almost all x-coordinates on the curve (all but at most 39) is close to two times ## 2. The decoding function First some definitions: - -- $\mathbb{F}$ is the finite field of size $q$, of characteristic 5 or more, and $q \equiv 1 \mod 3.$ - - For `secp256k1`, $q = 2^{256} - 2^{32} - 977$, which satisfies that requirement. -- Let $E$ be the elliptic curve of points $(x, y) \in \mathbb{F}^2$ for which $y^2 = x^3 + ax + b$, with $a$ and $b$ +* $\mathbb{F}$ is the finite field of size $q$, of characteristic 5 or more, and $q \equiv 1 \mod 3.$ + * For `secp256k1`, $q = 2^{256} - 2^{32} - 977$, which satisfies that requirement. +* Let $E$ be the elliptic curve of points $(x, y) \in \mathbb{F}^2$ for which $y^2 = x^3 + ax + b$, with $a$ and $b$ public constants, for which $\Delta_E = -16(4a^3 + 27b^2)$ is a square, and at least one of $(-b \pm \sqrt{-3 \Delta_E} / 36)/2$ is a square. - This implies that the order of $E$ is either odd, or a multiple of _4_. + This implies that the order of $E$ is either odd, or a multiple of *4*. If $a=0$, this condition is always fulfilled. - - For `secp256k1`, $a=0$ and $b=7.$ -- Let the function $g(x) = x^3 + ax + b$, so the $E$ curve equation is also $y^2 = g(x).$ -- Let the function $h(x) = 3x^3 + 4a.$ -- Define $V$ as the set of solutions $(x_1, x_2, x_3, z)$ to $z^2 = g(x_1)g(x_2)g(x_3).$ -- Define $S_u$ as the set of solutions $(X, Y)$ to $X^2 + h(u)Y^2 = -g(u)$ and $Y \neq 0.$ -- $P_u$ is a function from $\mathbb{F}$ to $S_u$ that will be defined below. -- $\psi_u$ is a function from $S_u$ to $V$ that will be defined below. + * For `secp256k1`, $a=0$ and $b=7.$ +* Let the function $g(x) = x^3 + ax + b$, so the $E$ curve equation is also $y^2 = g(x).$ +* Let the function $h(x) = 3x^3 + 4a.$ +* Define $V$ as the set of solutions $(x_1, x_2, x_3, z)$ to $z^2 = g(x_1)g(x_2)g(x_3).$ +* Define $S_u$ as the set of solutions $(X, Y)$ to $X^2 + h(u)Y^2 = -g(u)$ and $Y \neq 0.$ +* $P_u$ is a function from $\mathbb{F}$ to $S_u$ that will be defined below. +* $\psi_u$ is a function from $S_u$ to $V$ that will be defined below. **Note**: In the paper: - -- $F_u$ corresponds to $F_{0,u}$ there. -- $P_u(t)$ is called $P$ there. -- All $S_u$ sets together correspond to $S$ there. -- All $\psi_u$ functions together (operating on elements of $S$) correspond to $\psi$ there. +* $F_u$ corresponds to $F_{0,u}$ there. +* $P_u(t)$ is called $P$ there. +* All $S_u$ sets together correspond to $S$ there. +* All $\psi_u$ functions together (operating on elements of $S$) correspond to $\psi$ there. Note that for $V$, the left hand side of the equation $z^2$ is square, and thus the right hand must also be square. As multiplying non-squares results in a square in $\mathbb{F}$, out of the three right-hand side factors an even number must be non-squares. -This implies that exactly _1_ or exactly _3_ out of +This implies that exactly *1* or exactly *3* out of $\\{g(x_1), g(x_2), g(x_3)\\}$ must be square, and thus that for any $(x_1,x_2,x_3,z) \in V$, at least one of $\\{x_1, x_2, x_3\\}$ must be a valid x-coordinate on $E.$ There is one exception to this, namely when $z=0$, but even then one of the three values is a valid x-coordinate. **Define** the decoding function $F_u(t)$ as: - -- Let $(x_1, x_2, x_3, z) = \psi_u(P_u(t)).$ -- Return the first element $x$ of $(x_3, x_2, x_1)$ which is a valid x-coordinate on $E$ (i.e., $g(x)$ is square). +* Let $(x_1, x_2, x_3, z) = \psi_u(P_u(t)).$ +* Return the first element $x$ of $(x_3, x_2, x_1)$ which is a valid x-coordinate on $E$ (i.e., $g(x)$ is square). $P_u(t) = (X(u, t), Y(u, t))$, where: @@ -102,13 +98,12 @@ Y(u, t) & = & \left\\{\begin{array}{ll} $$ $P_u(t)$ is defined: - -- For $a=0$, unless: - - $u = 0$ or $t = 0$ (division by zero) - - $g(u) = -t^2$ (would give $Y=0$). -- For $a \neq 0$, unless: - - $X_0(u) = 0$ or $h(u)t^2 = -1$ (division by zero) - - $Y_0(u) (1 - h(u)t^2) = 2X_0(u)t$ (would give $Y=0$). +* For $a=0$, unless: + * $u = 0$ or $t = 0$ (division by zero) + * $g(u) = -t^2$ (would give $Y=0$). +* For $a \neq 0$, unless: + * $X_0(u) = 0$ or $h(u)t^2 = -1$ (division by zero) + * $Y_0(u) (1 - h(u)t^2) = 2X_0(u)t$ (would give $Y=0$). The functions $X_0(u)$ and $Y_0(u)$ are defined in Appendix A of the paper, and depend on various properties of $E.$ @@ -128,22 +123,20 @@ $$ Put together and specialized for $a=0$ curves, decoding $(u, t)$ to an x-coordinate is: **Define** $F_u(t)$ as: - -- Let $X = \dfrac{u^3 + b - t^2}{2t}.$ -- Let $Y = \dfrac{X + t}{u\sqrt{-3}}.$ -- Return the first $x$ in $(u + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u}{2}, \dfrac{X}{2Y} - \dfrac{u}{2})$ for which $g(x)$ is square. +* Let $X = \dfrac{u^3 + b - t^2}{2t}.$ +* Let $Y = \dfrac{X + t}{u\sqrt{-3}}.$ +* Return the first $x$ in $(u + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u}{2}, \dfrac{X}{2Y} - \dfrac{u}{2})$ for which $g(x)$ is square. To make sure that every input decodes to a valid x-coordinate, we remap the inputs in case $P_u$ is not defined (when $u=0$, $t=0$, or $g(u) = -t^2$): **Define** $F_u(t)$ as: - -- Let $u'=u$ if $u \neq 0$; $1$ otherwise (guaranteeing $u' \neq 0$). -- Let $t'=t$ if $t \neq 0$; $1$ otherwise (guaranteeing $t' \neq 0$). -- Let $t''=t'$ if $g(u') \neq -t'^2$; $2t'$ otherwise (guaranteeing $t'' \neq 0$ and $g(u') \neq -t''^2$). -- Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$ -- Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$ -- Return the first $x$ in $(u' + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u'}{2}, \dfrac{X}{2Y} - \dfrac{u'}{2})$ for which $x^3 + b$ is square. +* Let $u'=u$ if $u \neq 0$; $1$ otherwise (guaranteeing $u' \neq 0$). +* Let $t'=t$ if $t \neq 0$; $1$ otherwise (guaranteeing $t' \neq 0$). +* Let $t''=t'$ if $g(u') \neq -t'^2$; $2t'$ otherwise (guaranteeing $t'' \neq 0$ and $g(u') \neq -t''^2$). +* Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$ +* Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$ +* Return the first $x$ in $(u' + 4Y^2, \dfrac{-X}{2Y} - \dfrac{u'}{2}, \dfrac{X}{2Y} - \dfrac{u'}{2})$ for which $x^3 + b$ is square. The choices here are not strictly necessary. Just returning a fixed constant in any of the undefined cases would suffice, but the approach here is simple enough and gives fairly uniform output even in these cases. @@ -157,11 +150,10 @@ in `secp256k1_ellswift_xswiftec_var` (which outputs the actual x-coordinate). ## 3. The encoding function To implement $F_u^{-1}(x)$, the function to find the set of inverses $t$ for which $F_u(t) = x$, we have to reverse the process: - -- Find all the $(X, Y) \in S_u$ that could have given rise to $x$, through the $x_1$, $x_2$, or $x_3$ formulas in $\psi_u.$ -- Map those $(X, Y)$ solutions to $t$ values using $P_u^{-1}(X, Y).$ -- For each of the found $t$ values, verify that $F_u(t) = x.$ -- Return the remaining $t$ values. +* Find all the $(X, Y) \in S_u$ that could have given rise to $x$, through the $x_1$, $x_2$, or $x_3$ formulas in $\psi_u.$ +* Map those $(X, Y)$ solutions to $t$ values using $P_u^{-1}(X, Y).$ +* For each of the found $t$ values, verify that $F_u(t) = x.$ +* Return the remaining $t$ values. The function $P_u^{-1}$, which finds $t$ given $(X, Y) \in S_u$, is significantly simpler than $P_u:$ @@ -193,14 +185,13 @@ precedence over both. Because of this, the $g(-u-x)$ being square test for $x_1$ values round-trip back to the input $x$ correctly. This is the reason for choosing the $(x_3, x_2, x_1)$ precedence order in the decoder; any order which does not place $x_3$ first requires more complicated round-trip checks in the encoder. -### 3.1 Switching to _v, w_ coordinates +### 3.1 Switching to *v, w* coordinates Before working out the formulas for all this, we switch to different variables for $S_u.$ Let $v = (X/Y - u)/2$, and $w = 2Y.$ Or in the other direction, $X = w(u/2 + v)$ and $Y = w/2:$ - -- $S_u'$ becomes the set of $(v, w)$ for which $w^2 (u^2 + uv + v^2 + a) = -g(u)$ and $w \neq 0.$ -- For $a=0$ curves, $P_u^{-1}$ can be stated for $(v,w)$ as $P_u^{'-1}(v, w) = w\left(\frac{\sqrt{-3}-1}{2}u - v\right).$ -- $\psi_u$ can be stated for $(v, w)$ as $\psi_u'(v, w) = (x_1, x_2, x_3, z)$, where +* $S_u'$ becomes the set of $(v, w)$ for which $w^2 (u^2 + uv + v^2 + a) = -g(u)$ and $w \neq 0.$ +* For $a=0$ curves, $P_u^{-1}$ can be stated for $(v,w)$ as $P_u^{'-1}(v, w) = w\left(\frac{\sqrt{-3}-1}{2}u - v\right).$ +* $\psi_u$ can be stated for $(v, w)$ as $\psi_u'(v, w) = (x_1, x_2, x_3, z)$, where $$ \begin{array}{lcl} @@ -213,37 +204,34 @@ $$ We can now write the expressions for finding $(v, w)$ given $x$ explicitly, by solving each of the $\\{x_1, x_2, x_3\\}$ expressions for $v$ or $w$, and using the $S_u'$ equation to find the other variable: - -- Assuming $x = x_1$, we find $v = x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions). -- Assuming $x = x_2$, we find $v = -u-x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions). -- Assuming $x = x_3$, we find $w = \pm\sqrt{x-u}$ and $v = -u/2 \pm \sqrt{-w^2(4g(u) + w^2h(u))}/(2w^2)$ (four solutions). +* Assuming $x = x_1$, we find $v = x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions). +* Assuming $x = x_2$, we find $v = -u-x$ and $w = \pm\sqrt{-g(u)/(u^2 + uv + v^2 + a)}$ (two solutions). +* Assuming $x = x_3$, we find $w = \pm\sqrt{x-u}$ and $v = -u/2 \pm \sqrt{-w^2(4g(u) + w^2h(u))}/(2w^2)$ (four solutions). ### 3.2 Avoiding computing all inverses -The _ElligatorSwift_ algorithm as stated in Section 1 requires the computation of $L = F_u^{-1}(x)$ (the +The *ElligatorSwift* algorithm as stated in Section 1 requires the computation of $L = F_u^{-1}(x)$ (the set of all $t$ such that $(u, t)$ decode to $x$) in full. This is unnecessary. Observe that the procedure of restarting with probability $(1 - \frac{\\#L}{8})$ and otherwise returning a uniformly random element from $L$ is actually equivalent to always padding $L$ with $\bot$ values up to length 8, picking a uniformly random element from that, restarting whenever $\bot$ is picked: -**Define** _ElligatorSwift(x)_ as: - -- Loop: - - Pick a uniformly random field element $u.$ - - Compute the set $L = F_u^{-1}(x).$ - - Let $T$ be the 8-element vector consisting of the elements of $L$, plus $8 - \\#L$ times $\\{\bot\\}.$ - - Select a uniformly random $t \in T.$ - - If $t \neq \bot$, return $(u, t)$; restart loop otherwise. +**Define** *ElligatorSwift(x)* as: +* Loop: + * Pick a uniformly random field element $u.$ + * Compute the set $L = F_u^{-1}(x).$ + * Let $T$ be the 8-element vector consisting of the elements of $L$, plus $8 - \\#L$ times $\\{\bot\\}.$ + * Select a uniformly random $t \in T.$ + * If $t \neq \bot$, return $(u, t)$; restart loop otherwise. Now notice that the order of elements in $T$ does not matter, as all we do is pick a uniformly random element in it, so we do not need to have all $\bot$ values at the end. As we have 8 distinct formulas for finding $(v, w)$ (taking the variants due to $\pm$ into account), we can associate every index in $T$ with exactly one of those formulas, making sure that: - -- Formulas that yield no solutions (due to division by zero or non-existing square roots) or invalid solutions are made to return $\bot.$ -- For the $x_1$ and $x_2$ cases, if $g(-u-x)$ is a square, $\bot$ is returned instead (the round-trip check). -- In case multiple formulas would return the same non- $\bot$ result, all but one of those must be turned into $\bot$ to avoid biasing those. +* Formulas that yield no solutions (due to division by zero or non-existing square roots) or invalid solutions are made to return $\bot.$ +* For the $x_1$ and $x_2$ cases, if $g(-u-x)$ is a square, $\bot$ is returned instead (the round-trip check). +* In case multiple formulas would return the same non- $\bot$ result, all but one of those must be turned into $\bot$ to avoid biasing those. The last condition above only occurs with negligible probability for cryptographically-sized curves, but is interesting to take into account as it allows exhaustive testing in small groups. See [Section 3.4](#34-dealing-with-special-cases) @@ -252,13 +240,12 @@ for an analysis of all the negligible cases. If we define $T = (G_{0,u}(x), G_{1,u}(x), \ldots, G_{7,u}(x))$, with each $G_{i,u}$ matching one of the formulas, the loop can be simplified to only compute one of the inverses instead of all of them: -**Define** _ElligatorSwift(x)_ as: - -- Loop: - - Pick a uniformly random field element $u.$ - - Pick a uniformly random integer $c$ in $[0,8).$ - - Let $t = G_{c,u}(x).$ - - If $t \neq \bot$, return $(u, t)$; restart loop otherwise. +**Define** *ElligatorSwift(x)* as: +* Loop: + * Pick a uniformly random field element $u.$ + * Pick a uniformly random integer $c$ in $[0,8).$ + * Let $t = G_{c,u}(x).$ + * If $t \neq \bot$, return $(u, t)$; restart loop otherwise. This is implemented in `secp256k1_ellswift_xelligatorswift_var`. @@ -269,19 +256,18 @@ Those are then repeated as $c=4$ through $c=7$ for the other sign of $w$ (noting Ignoring the negligible cases, we get: **Define** $G_{c,u}(x)$ as: - -- If $c \in \\{0, 1, 4, 5\\}$ (for $x_1$ and $x_2$ formulas): - - If $g(-u-x)$ is square, return $\bot$ (as $x_3$ would be valid and take precedence). - - If $c \in \\{0, 4\\}$ (the $x_1$ formula) let $v = x$, otherwise let $v = -u-x$ (the $x_2$ formula) - - Let $s = -g(u)/(u^2 + uv + v^2 + a)$ (using $s = w^2$ in what follows). -- Otherwise, when $c \in \\{2, 3, 6, 7\\}$ (for $x_3$ formulas): - - Let $s = x-u.$ - - Let $r = \sqrt{-s(4g(u) + sh(u))}.$ - - Let $v = (r/s - u)/2$ if $c \in \\{3, 7\\}$; $(-r/s - u)/2$ otherwise. -- Let $w = \sqrt{s}.$ -- Depending on $c:$ - - If $c \in \\{0, 1, 2, 3\\}:$ return $P_u^{'-1}(v, w).$ - - If $c \in \\{4, 5, 6, 7\\}:$ return $P_u^{'-1}(v, -w).$ +* If $c \in \\{0, 1, 4, 5\\}$ (for $x_1$ and $x_2$ formulas): + * If $g(-u-x)$ is square, return $\bot$ (as $x_3$ would be valid and take precedence). + * If $c \in \\{0, 4\\}$ (the $x_1$ formula) let $v = x$, otherwise let $v = -u-x$ (the $x_2$ formula) + * Let $s = -g(u)/(u^2 + uv + v^2 + a)$ (using $s = w^2$ in what follows). +* Otherwise, when $c \in \\{2, 3, 6, 7\\}$ (for $x_3$ formulas): + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}.$ + * Let $v = (r/s - u)/2$ if $c \in \\{3, 7\\}$; $(-r/s - u)/2$ otherwise. +* Let $w = \sqrt{s}.$ +* Depending on $c:$ + * If $c \in \\{0, 1, 2, 3\\}:$ return $P_u^{'-1}(v, w).$ + * If $c \in \\{4, 5, 6, 7\\}:$ return $P_u^{'-1}(v, -w).$ Whenever a square root of a non-square is taken, $\bot$ is returned; for both square roots this happens with roughly 50% on random inputs. Similarly, when a division by 0 would occur, $\bot$ is returned as well; this will only happen @@ -298,21 +284,20 @@ transformation. Furthermore, that transformation has no effect on $s$ in the fir as $u^2 + ux + x^2 + a = u^2 + u(-u-x) + (-u-x)^2 + a.$ Thus we can extract it out and move it down: **Define** $G_{c,u}(x)$ as: - -- If $c \in \\{0, 1, 4, 5\\}:$ - - If $g(-u-x)$ is square, return $\bot.$ - - Let $s = -g(u)/(u^2 + ux + x^2 + a).$ - - Let $v = x.$ -- Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ - - Let $s = x-u.$ - - Let $r = \sqrt{-s(4g(u) + sh(u))}.$ - - Let $v = (r/s - u)/2.$ -- Let $w = \sqrt{s}.$ -- Depending on $c:$ - - If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w).$ - - If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w).$ - - If $c \in \\{4, 6\\}:$ return $P_u^{'-1}(v, -w).$ - - If $c \in \\{5, 7\\}:$ return $P_u^{'-1}(-u-v, -w).$ +* If $c \in \\{0, 1, 4, 5\\}:$ + * If $g(-u-x)$ is square, return $\bot.$ + * Let $s = -g(u)/(u^2 + ux + x^2 + a).$ + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}.$ +* Depending on $c:$ + * If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w).$ + * If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w).$ + * If $c \in \\{4, 6\\}:$ return $P_u^{'-1}(v, -w).$ + * If $c \in \\{5, 7\\}:$ return $P_u^{'-1}(-u-v, -w).$ This shows there will always be exactly 0, 4, or 8 $t$ values for a given $(u, x)$ input. There can be 0, 1, or 2 $(v, w)$ pairs before invoking $P_u^{'-1}$, and each results in 4 distinct $t$ values. @@ -325,60 +310,58 @@ we analyse them here. They generally fall into two categories: cases in which th do not decode back to $x$ (or at least cannot guarantee that they do), and cases in which the encoder might produce the same $t$ value for multiple $c$ inputs (thereby biasing that encoding): -- In the branch for $x_1$ and $x_2$ (where $c \in \\{0, 1, 4, 5\\}$): - - When $g(u) = 0$, we would have $s=w=Y=0$, which is not on $S_u.$ This is only possible on even-ordered curves. +* In the branch for $x_1$ and $x_2$ (where $c \in \\{0, 1, 4, 5\\}$): + * When $g(u) = 0$, we would have $s=w=Y=0$, which is not on $S_u.$ This is only possible on even-ordered curves. Excluding this also removes the one condition under which the simplified check for $x_3$ on the curve fails (namely when $g(x_1)=g(x_2)=0$ but $g(x_3)$ is not square). This does exclude some valid encodings: when both $g(u)=0$ and $u^2+ux+x^2+a=0$ (also implying $g(x)=0$), the $S_u'$ equation degenerates to $0 = 0$, and many valid $t$ values may exist. Yet, these cannot be targeted uniformly by the encoder anyway as there will generally be more than 8. - - When $g(x) = 0$, the same $t$ would be produced as in the $x_3$ branch (where $c \in \\{2, 3, 6, 7\\}$) which we give precedence + * When $g(x) = 0$, the same $t$ would be produced as in the $x_3$ branch (where $c \in \\{2, 3, 6, 7\\}$) which we give precedence as it can deal with $g(u)=0$. This is again only possible on even-ordered curves. -- In the branch for $x_3$ (where $c \in \\{2, 3, 6, 7\\}$): - - When $s=0$, a division by zero would occur. - - When $v = -u-v$ and $c \in \\{3, 7\\}$, the same $t$ would be returned as in the $c \in \\{2, 6\\}$ cases. +* In the branch for $x_3$ (where $c \in \\{2, 3, 6, 7\\}$): + * When $s=0$, a division by zero would occur. + * When $v = -u-v$ and $c \in \\{3, 7\\}$, the same $t$ would be returned as in the $c \in \\{2, 6\\}$ cases. It is equivalent to checking whether $r=0$. This cannot occur in the $x_1$ or $x_2$ branches, as it would trigger the $g(-u-x)$ is square condition. A similar concern for $w = -w$ does not exist, as $w=0$ is already impossible in both branches: in the first it requires $g(u)=0$ which is already outlawed on even-ordered curves and impossible on others; in the second it would trigger division by zero. -- Curve-specific special cases also exist that need to be rejected, because they result in $(u,t)$ which is invalid to the decoder, or because of division by zero in the encoder: - - For $a=0$ curves, when $u=0$ or when $t=0$. The latter can only be reached by the encoder when $g(u)=0$, which requires an even-ordered curve. - - For $a \neq 0$ curves, when $X_0(u)=0$, when $h(u)t^2 = -1$, or when $w(u + 2v) = 2X_0(u)$ while also either $w \neq 2Y_0(u)$ or $h(u)=0$. +* Curve-specific special cases also exist that need to be rejected, because they result in $(u,t)$ which is invalid to the decoder, or because of division by zero in the encoder: + * For $a=0$ curves, when $u=0$ or when $t=0$. The latter can only be reached by the encoder when $g(u)=0$, which requires an even-ordered curve. + * For $a \neq 0$ curves, when $X_0(u)=0$, when $h(u)t^2 = -1$, or when $w(u + 2v) = 2X_0(u)$ while also either $w \neq 2Y_0(u)$ or $h(u)=0$. **Define** a version of $G_{c,u}(x)$ which deals with all these cases: - -- If $a=0$ and $u=0$, return $\bot.$ -- If $a \neq 0$ and $X_0(u)=0$, return $\bot.$ -- If $c \in \\{0, 1, 4, 5\\}:$ - - If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only). - - If $g(-u-x)$ is square, return $\bot.$ - - Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero). - - Let $v = x.$ -- Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ - - Let $s = x-u.$ - - Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square. - - If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$ - - If $s = 0$, return $\bot.$ - - Let $v = (r/s - u)/2.$ -- Let $w = \sqrt{s}$; return $\bot$ if not square. -- If $a \neq 0$ and $w(u+2v) = 2X_0(u)$ and either $w \neq 2Y_0(u)$ or $h(u) = 0$, return $\bot.$ -- Depending on $c:$ - - If $c \in \\{0, 2\\}$, let $t = P_u^{'-1}(v, w).$ - - If $c \in \\{1, 3\\}$, let $t = P_u^{'-1}(-u-v, w).$ - - If $c \in \\{4, 6\\}$, let $t = P_u^{'-1}(v, -w).$ - - If $c \in \\{5, 7\\}$, let $t = P_u^{'-1}(-u-v, -w).$ -- If $a=0$ and $t=0$, return $\bot$ (even curves only). -- If $a \neq 0$ and $h(u)t^2 = -1$, return $\bot.$ -- Return $t.$ +* If $a=0$ and $u=0$, return $\bot.$ +* If $a \neq 0$ and $X_0(u)=0$, return $\bot.$ +* If $c \in \\{0, 1, 4, 5\\}:$ + * If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only). + * If $g(-u-x)$ is square, return $\bot.$ + * Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero). + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square. + * If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$ + * If $s = 0$, return $\bot.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}$; return $\bot$ if not square. +* If $a \neq 0$ and $w(u+2v) = 2X_0(u)$ and either $w \neq 2Y_0(u)$ or $h(u) = 0$, return $\bot.$ +* Depending on $c:$ + * If $c \in \\{0, 2\\}$, let $t = P_u^{'-1}(v, w).$ + * If $c \in \\{1, 3\\}$, let $t = P_u^{'-1}(-u-v, w).$ + * If $c \in \\{4, 6\\}$, let $t = P_u^{'-1}(v, -w).$ + * If $c \in \\{5, 7\\}$, let $t = P_u^{'-1}(-u-v, -w).$ +* If $a=0$ and $t=0$, return $\bot$ (even curves only). +* If $a \neq 0$ and $h(u)t^2 = -1$, return $\bot.$ +* Return $t.$ Given any $u$, using this algorithm over all $x$ and $c$ values, every $t$ value will be reached exactly once, for an $x$ for which $F_u(t) = x$ holds, except for these cases that will not be reached: - -- All cases where $P_u(t)$ is not defined: - - For $a=0$ curves, when $u=0$, $t=0$, or $g(u) = -t^2.$ - - For $a \neq 0$ curves, when $h(u)t^2 = -1$, $X_0(u) = 0$, or $Y_0(u) (1 - h(u) t^2) = 2X_0(u)t.$ -- When $g(u)=0$, the potentially many $t$ values that decode to an $x$ satisfying $g(x)=0$ using the $x_2$ formula. These were excluded by the $g(u)=0$ condition in the $c \in \\{0, 1, 4, 5\\}$ branch. +* All cases where $P_u(t)$ is not defined: + * For $a=0$ curves, when $u=0$, $t=0$, or $g(u) = -t^2.$ + * For $a \neq 0$ curves, when $h(u)t^2 = -1$, $X_0(u) = 0$, or $Y_0(u) (1 - h(u) t^2) = 2X_0(u)t.$ +* When $g(u)=0$, the potentially many $t$ values that decode to an $x$ satisfying $g(x)=0$ using the $x_2$ formula. These were excluded by the $g(u)=0$ condition in the $c \in \\{0, 1, 4, 5\\}$ branch. These cases form a negligible subset of all $(u, t)$ for cryptographically sized curves. @@ -387,42 +370,40 @@ These cases form a negligible subset of all $(u, t)$ for cryptographically sized Specialized for odd-ordered $a=0$ curves: **Define** $G_{c,u}(x)$ as: - -- If $u=0$, return $\bot.$ -- If $c \in \\{0, 1, 4, 5\\}:$ - - If $(-u-x)^3 + b$ is square, return $\bot$ - - Let $s = -(u^3 + b)/(u^2 + ux + x^2)$ (cannot cause division by 0). - - Let $v = x.$ -- Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ - - Let $s = x-u.$ - - Let $r = \sqrt{-s(4(u^3 + b) + 3su^2)}$; return $\bot$ if not square. - - If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$ - - If $s = 0$, return $\bot.$ - - Let $v = (r/s - u)/2.$ -- Let $w = \sqrt{s}$; return $\bot$ if not square. -- Depending on $c:$ - - If $c \in \\{0, 2\\}:$ return $w(\frac{\sqrt{-3}-1}{2}u - v).$ - - If $c \in \\{1, 3\\}:$ return $w(\frac{\sqrt{-3}+1}{2}u + v).$ - - If $c \in \\{4, 6\\}:$ return $w(\frac{-\sqrt{-3}+1}{2}u + v).$ - - If $c \in \\{5, 7\\}:$ return $w(\frac{-\sqrt{-3}-1}{2}u - v).$ +* If $u=0$, return $\bot.$ +* If $c \in \\{0, 1, 4, 5\\}:$ + * If $(-u-x)^3 + b$ is square, return $\bot$ + * Let $s = -(u^3 + b)/(u^2 + ux + x^2)$ (cannot cause division by 0). + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3, 6, 7\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4(u^3 + b) + 3su^2)}$; return $\bot$ if not square. + * If $c \in \\{3, 7\\}$ and $r=0$, return $\bot.$ + * If $s = 0$, return $\bot.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}$; return $\bot$ if not square. +* Depending on $c:$ + * If $c \in \\{0, 2\\}:$ return $w(\frac{\sqrt{-3}-1}{2}u - v).$ + * If $c \in \\{1, 3\\}:$ return $w(\frac{\sqrt{-3}+1}{2}u + v).$ + * If $c \in \\{4, 6\\}:$ return $w(\frac{-\sqrt{-3}+1}{2}u + v).$ + * If $c \in \\{5, 7\\}:$ return $w(\frac{-\sqrt{-3}-1}{2}u - v).$ This is implemented in `secp256k1_ellswift_xswiftec_inv_var`. And the x-only ElligatorSwift encoding algorithm is still: -**Define** _ElligatorSwift(x)_ as: - -- Loop: - - Pick a uniformly random field element $u.$ - - Pick a uniformly random integer $c$ in $[0,8).$ - - Let $t = G_{c,u}(x).$ - - If $t \neq \bot$, return $(u, t)$; restart loop otherwise. +**Define** *ElligatorSwift(x)* as: +* Loop: + * Pick a uniformly random field element $u.$ + * Pick a uniformly random integer $c$ in $[0,8).$ + * Let $t = G_{c,u}(x).$ + * If $t \neq \bot$, return $(u, t)$; restart loop otherwise. Note that this logic does not take the remapped $u=0$, $t=0$, and $g(u) = -t^2$ cases into account; it just avoids them. While it is not impossible to make the encoder target them, this would increase the maximum number of $t$ values for a given $(u, x)$ combination beyond 8, and thereby slow down the ElligatorSwift loop proportionally, for a negligible gain in uniformity. -## 4. Encoding and decoding full _(x, y)_ coordinates +## 4. Encoding and decoding full *(x, y)* coordinates So far we have only addressed encoding and decoding x-coordinates, but in some cases an encoding for full points with $(x, y)$ coordinates is desirable. It is possible to encode this information @@ -441,32 +422,30 @@ four distinct $P_u^{'-1}$ calls in the definition of $G_{u,c}.$ To encode the sign of $y$ in the sign of $Y:$ -**Define** _Decode(u, t)_ for full $(x, y)$ as: - -- Let $(X, Y) = P_u(t).$ -- Let $x$ be the first value in $(u + 4Y^2, \frac{-X}{2Y} - \frac{u}{2}, \frac{X}{2Y} - \frac{u}{2})$ for which $g(x)$ is square. -- Let $y = \sqrt{g(x)}.$ -- If $sign(y) = sign(Y)$, return $(x, y)$; otherwise return $(x, -y).$ +**Define** *Decode(u, t)* for full $(x, y)$ as: +* Let $(X, Y) = P_u(t).$ +* Let $x$ be the first value in $(u + 4Y^2, \frac{-X}{2Y} - \frac{u}{2}, \frac{X}{2Y} - \frac{u}{2})$ for which $g(x)$ is square. +* Let $y = \sqrt{g(x)}.$ +* If $sign(y) = sign(Y)$, return $(x, y)$; otherwise return $(x, -y).$ And encoding would be done using a $G_{c,u}(x, y)$ function defined as: **Define** $G_{c,u}(x, y)$ as: - -- If $c \in \\{0, 1\\}:$ - - If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only). - - If $g(-u-x)$ is square, return $\bot.$ - - Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero). - - Let $v = x.$ -- Otherwise, when $c \in \\{2, 3\\}:$ - - Let $s = x-u.$ - - Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square. - - If $c = 3$ and $r = 0$, return $\bot.$ - - Let $v = (r/s - u)/2.$ -- Let $w = \sqrt{s}$; return $\bot$ if not square. -- Let $w' = w$ if $sign(w/2) = sign(y)$; $-w$ otherwise. -- Depending on $c:$ - - If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w').$ - - If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w').$ +* If $c \in \\{0, 1\\}:$ + * If $g(u) = 0$ or $g(x) = 0$, return $\bot$ (even curves only). + * If $g(-u-x)$ is square, return $\bot.$ + * Let $s = -g(u)/(u^2 + ux + x^2 + a)$ (cannot cause division by zero). + * Let $v = x.$ +* Otherwise, when $c \in \\{2, 3\\}:$ + * Let $s = x-u.$ + * Let $r = \sqrt{-s(4g(u) + sh(u))}$; return $\bot$ if not square. + * If $c = 3$ and $r = 0$, return $\bot.$ + * Let $v = (r/s - u)/2.$ +* Let $w = \sqrt{s}$; return $\bot$ if not square. +* Let $w' = w$ if $sign(w/2) = sign(y)$; $-w$ otherwise. +* Depending on $c:$ + * If $c \in \\{0, 2\\}:$ return $P_u^{'-1}(v, w').$ + * If $c \in \\{1, 3\\}:$ return $P_u^{'-1}(-u-v, w').$ Note that $c$ now only ranges $[0,4)$, as the sign of $w'$ is decided based on that of $y$, rather than on $c.$ This change makes some valid encodings unreachable: when $y = 0$ and $sign(Y) \neq sign(0)$. @@ -475,23 +454,22 @@ In the above logic, $sign$ can be implemented in several ways, such as parity of of the input field element (for prime-sized fields) or the quadratic residuosity (for fields where $-1$ is not square). The choice does not matter, as long as it only takes on two possible values, and for $x \neq 0$ it holds that $sign(x) \neq sign(-x)$. -### 4.1 Full _(x, y)_ coordinates for `secp256k1` +### 4.1 Full *(x, y)* coordinates for `secp256k1` For $a=0$ curves, there is another option. Note that for those, the $P_u(t)$ function translates negations of $t$ to negations of (both) $X$ and $Y.$ Thus, we can use $sign(t)$ to encode the y-coordinate directly. Combined with the earlier remapping to guarantee all inputs land on the curve, we get as decoder: -**Define** _Decode(u, t)_ as: - -- Let $u'=u$ if $u \neq 0$; $1$ otherwise. -- Let $t'=t$ if $t \neq 0$; $1$ otherwise. -- Let $t''=t'$ if $u'^3 + b + t'^2 \neq 0$; $2t'$ otherwise. -- Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$ -- Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$ -- Let $x$ be the first element of $(u' + 4Y^2, \frac{-X}{2Y} - \frac{u'}{2}, \frac{X}{2Y} - \frac{u'}{2})$ for which $g(x)$ is square. -- Let $y = \sqrt{g(x)}.$ -- Return $(x, y)$ if $sign(y) = sign(t)$; $(x, -y)$ otherwise. +**Define** *Decode(u, t)* as: +* Let $u'=u$ if $u \neq 0$; $1$ otherwise. +* Let $t'=t$ if $t \neq 0$; $1$ otherwise. +* Let $t''=t'$ if $u'^3 + b + t'^2 \neq 0$; $2t'$ otherwise. +* Let $X = \dfrac{u'^3 + b - t''^2}{2t''}.$ +* Let $Y = \dfrac{X + t''}{u'\sqrt{-3}}.$ +* Let $x$ be the first element of $(u' + 4Y^2, \frac{-X}{2Y} - \frac{u'}{2}, \frac{X}{2Y} - \frac{u'}{2})$ for which $g(x)$ is square. +* Let $y = \sqrt{g(x)}.$ +* Return $(x, y)$ if $sign(y) = sign(t)$; $(x, -y)$ otherwise. This is implemented in `secp256k1_ellswift_swiftec_var`. The used $sign(x)$ function is the parity of $x$ when represented as in integer in $[0,q).$ diff --git a/external/secp256k1/doc/musig.md b/external/secp256k1/doc/musig.md index 176b131da6..ae21f9b131 100644 --- a/external/secp256k1/doc/musig.md +++ b/external/secp256k1/doc/musig.md @@ -1,4 +1,5 @@ -# Notes on the musig module API +Notes on the musig module API +=========================== The following sections contain additional notes on the API of the musig module (`include/secp256k1_musig.h`). A usage example can be found in `examples/musig.c`. diff --git a/external/secp256k1/doc/release-process.md b/external/secp256k1/doc/release-process.md index 4ac9ca0d23..a64bae0f0d 100644 --- a/external/secp256k1/doc/release-process.md +++ b/external/secp256k1/doc/release-process.md @@ -2,7 +2,7 @@ This document outlines the process for releasing versions of the form `$MAJOR.$MINOR.$PATCH`. -We distinguish between two types of releases: _regular_ and _maintenance_ releases. +We distinguish between two types of releases: *regular* and *maintenance* releases. Regular releases are releases of a new major or minor version as well as patches of the most recent release. Maintenance releases, on the other hand, are required for patches of older releases. @@ -15,7 +15,6 @@ This process also assumes that there will be no minor releases for old major rel We aim to cut a regular release every 3-4 months, approximately twice as frequent as major Bitcoin Core releases. Every second release should be published one month before the feature freeze of the next major Bitcoin Core release, allowing sufficient time to update the library in Core. ## Sanity checks - Perform these checks when reviewing the release PR (see below): 1. Ensure `make distcheck` doesn't fail. @@ -43,15 +42,15 @@ Perform these checks when reviewing the release PR (see below): ## Regular release 1. Open a PR to the master branch with a commit (using message `"release: prepare for $MAJOR.$MINOR.$PATCH"`, for example) that - - finalizes the release notes in [CHANGELOG.md](../CHANGELOG.md) by - - adding a section for the release (make sure that the version number is a link to a diff between the previous and new version), - - removing the `[Unreleased]` section header, - - ensuring that the release notes are not missing entries (check the `needs-changelog` label on github), and - - including an entry for `### ABI Compatibility` if it doesn't exist, - - sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and, - - if this is not a patch release, - - updates `_PKG_VERSION_*` and `_LIB_VERSION_*` in `configure.ac`, and - - updates `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_*` in `CMakeLists.txt`. + * finalizes the release notes in [CHANGELOG.md](../CHANGELOG.md) by + * adding a section for the release (make sure that the version number is a link to a diff between the previous and new version), + * removing the `[Unreleased]` section header, + * ensuring that the release notes are not missing entries (check the `needs-changelog` label on github), and + * including an entry for `### ABI Compatibility` if it doesn't exist, + * sets `_PKG_VERSION_IS_RELEASE` to `true` in `configure.ac`, and, + * if this is not a patch release, + * updates `_PKG_VERSION_*` and `_LIB_VERSION_*` in `configure.ac`, and + * updates `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_*` in `CMakeLists.txt`. 2. Perform the [sanity checks](#sanity-checks) on the PR branch. 3. After the PR is merged, tag the commit, and push the tag: ``` @@ -60,12 +59,11 @@ Perform these checks when reviewing the release PR (see below): git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH ``` 4. Open a PR to the master branch with a commit (using message `"release cleanup: bump version after $MAJOR.$MINOR.$PATCH"`, for example) that - - sets `_PKG_VERSION_IS_RELEASE` to `false` and increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`, - - increments the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`, and - - adds an `[Unreleased]` section header to the [CHANGELOG.md](../CHANGELOG.md). + * sets `_PKG_VERSION_IS_RELEASE` to `false` and increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac`, + * increments the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt`, and + * adds an `[Unreleased]` section header to the [CHANGELOG.md](../CHANGELOG.md). If other maintainers are not present to approve the PR, it can be merged without ACKs. - 5. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). 6. Send an announcement email to the bitcoin-dev mailing list. @@ -79,9 +77,9 @@ Note that bug fixes need to be backported only to releases for which no compatib git push git@github.com:bitcoin-core/secp256k1.git $MAJOR.$MINOR ``` 2. Open a pull request to the `$MAJOR.$MINOR` branch that - - includes the bug fixes, - - finalizes the release notes similar to a regular release, - - increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac` + * includes the bug fixes, + * finalizes the release notes similar to a regular release, + * increments `_PKG_VERSION_PATCH` and `_LIB_VERSION_REVISION` in `configure.ac` and the `$PATCH` component of `project(libsecp256k1 VERSION ...)` and `${PROJECT_NAME}_LIB_VERSION_REVISION` in `CMakeLists.txt` (with commit message `"release: bump versions for $MAJOR.$MINOR.$PATCH"`, for example). 3. Perform the [sanity checks](#sanity-checks) on the PR branch. @@ -91,6 +89,6 @@ Note that bug fixes need to be backported only to releases for which no compatib git tag -s v$MAJOR.$MINOR.$PATCH -m "libsecp256k1 $MAJOR.$MINOR.$PATCH" git push git@github.com:bitcoin-core/secp256k1.git v$MAJOR.$MINOR.$PATCH ``` -5. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). -6. Send an announcement email to the bitcoin-dev mailing list. -7. Open PR to the master branch that includes a commit (with commit message `"release notes: add $MAJOR.$MINOR.$PATCH"`, for example) that adds release notes to [CHANGELOG.md](../CHANGELOG.md). +6. Create a new GitHub release with a link to the corresponding entry in [CHANGELOG.md](../CHANGELOG.md). +7. Send an announcement email to the bitcoin-dev mailing list. +8. Open PR to the master branch that includes a commit (with commit message `"release notes: add $MAJOR.$MINOR.$PATCH"`, for example) that adds release notes to [CHANGELOG.md](../CHANGELOG.md). diff --git a/external/secp256k1/doc/safegcd_implementation.md b/external/secp256k1/doc/safegcd_implementation.md index 72d99daad3..5dbbb7bbd2 100644 --- a/external/secp256k1/doc/safegcd_implementation.md +++ b/external/secp256k1/doc/safegcd_implementation.md @@ -29,67 +29,65 @@ def gcd(f, g): return abs(f) ``` -It computes the greatest common divisor of an odd integer _f_ and any integer _g_. Its inner loop -keeps rewriting the variables _f_ and _g_ alongside a state variable _δ_ that starts at _1_, until -_g=0_ is reached. At that point, _|f|_ gives the GCD. Each of the transitions in the loop is called a +It computes the greatest common divisor of an odd integer *f* and any integer *g*. Its inner loop +keeps rewriting the variables *f* and *g* alongside a state variable *δ* that starts at *1*, until +*g=0* is reached. At that point, *|f|* gives the GCD. Each of the transitions in the loop is called a "division step" (referred to as divstep in what follows). -For example, _gcd(21, 14)_ would be computed as: - -- Start with _δ=1 f=21 g=14_ -- Take the third branch: _δ=2 f=21 g=7_ -- Take the first branch: _δ=-1 f=7 g=-7_ -- Take the second branch: _δ=0 f=7 g=0_ -- The answer _|f| = 7_. +For example, *gcd(21, 14)* would be computed as: +- Start with *δ=1 f=21 g=14* +- Take the third branch: *δ=2 f=21 g=7* +- Take the first branch: *δ=-1 f=7 g=-7* +- Take the second branch: *δ=0 f=7 g=0* +- The answer *|f| = 7*. Why it works: - - Divsteps can be decomposed into two steps (see paragraph 8.2 in the paper): - - (a) If _g_ is odd, replace _(f,g)_ with _(g,g-f)_ or (f,g+f), resulting in an even _g_. - - (b) Replace _(f,g)_ with _(f,g/2)_ (where _g_ is guaranteed to be even). + - (a) If *g* is odd, replace *(f,g)* with *(g,g-f)* or (f,g+f), resulting in an even *g*. + - (b) Replace *(f,g)* with *(f,g/2)* (where *g* is guaranteed to be even). - Neither of those two operations change the GCD: - - For (a), assume _gcd(f,g)=c_, then it must be the case that _f=a c_ and _g=b c_ for some integers _a_ - and _b_. As _(g,g-f)=(b c,(b-a)c)_ and _(f,f+g)=(a c,(a+b)c)_, the result clearly still has - common factor _c_. Reasoning in the other direction shows that no common factor can be added by + - For (a), assume *gcd(f,g)=c*, then it must be the case that *f=a c* and *g=b c* for some integers *a* + and *b*. As *(g,g-f)=(b c,(b-a)c)* and *(f,f+g)=(a c,(a+b)c)*, the result clearly still has + common factor *c*. Reasoning in the other direction shows that no common factor can be added by doing so either. - - For (b), we know that _f_ is odd, so _gcd(f,g)_ clearly has no factor _2_, and we can remove - it from _g_. -- The algorithm will eventually converge to _g=0_. This is proven in the paper (see theorem G.3). -- It follows that eventually we find a final value _f'_ for which _gcd(f,g) = gcd(f',0)_. As the - gcd of _f'_ and _0_ is _|f'|_ by definition, that is our answer. + - For (b), we know that *f* is odd, so *gcd(f,g)* clearly has no factor *2*, and we can remove + it from *g*. +- The algorithm will eventually converge to *g=0*. This is proven in the paper (see theorem G.3). +- It follows that eventually we find a final value *f'* for which *gcd(f,g) = gcd(f',0)*. As the + gcd of *f'* and *0* is *|f'|* by definition, that is our answer. Compared to more [traditional GCD algorithms](https://en.wikipedia.org/wiki/Euclidean_algorithm), this one has the property of only ever looking at the low-order bits of the variables to decide the next steps, and being easy to make -constant-time (in more low-level languages than Python). The _δ_ parameter is necessary to +constant-time (in more low-level languages than Python). The *δ* parameter is necessary to guide the algorithm towards shrinking the numbers' magnitudes without explicitly needing to look at high order bits. Properties that will become important later: - -- Performing more divsteps than needed is not a problem, as _f_ does not change anymore after _g=0_. -- Only even numbers are divided by _2_. This means that when reasoning about it algebraically we +- Performing more divsteps than needed is not a problem, as *f* does not change anymore after *g=0*. +- Only even numbers are divided by *2*. This means that when reasoning about it algebraically we do not need to worry about rounding. -- At every point during the algorithm's execution the next _N_ steps only depend on the bottom _N_ - bits of _f_ and _g_, and on _δ_. +- At every point during the algorithm's execution the next *N* steps only depend on the bottom *N* + bits of *f* and *g*, and on *δ*. + ## 2. From GCDs to modular inverses -We want an algorithm to compute the inverse _a_ of _x_ modulo _M_, i.e. the number a such that _a x=1 -mod M_. This inverse only exists if the GCD of _x_ and _M_ is _1_, but that is always the case if _M_ is -prime and _0 < x < M_. In what follows, assume that the modular inverse exists. +We want an algorithm to compute the inverse *a* of *x* modulo *M*, i.e. the number a such that *a x=1 +mod M*. This inverse only exists if the GCD of *x* and *M* is *1*, but that is always the case if *M* is +prime and *0 < x < M*. In what follows, assume that the modular inverse exists. It turns out this inverse can be computed as a side effect of computing the GCD by keeping track of how the internal variables can be written as linear combinations of the inputs at every step (see the [extended Euclidean algorithm](https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm)). -Since the GCD is _1_, such an algorithm will compute numbers _a_ and _b_ such that a x + b M = 1*. +Since the GCD is *1*, such an algorithm will compute numbers *a* and *b* such that a x + b M = 1*. Taking that expression *mod M* gives *a x mod M = 1*, and we see that *a* is the modular inverse of *x -mod M\*. +mod M*. A similar approach can be used to calculate modular inverses using the divsteps-based GCD -algorithm shown above, if the modulus _M_ is odd. To do so, compute _gcd(f=M,g=x)_, while keeping -track of extra variables _d_ and _e_, for which at every step _d = f/x (mod M)_ and _e = g/x (mod M)_. -_f/x_ here means the number which multiplied with _x_ gives _f mod M_. As _f_ and _g_ are initialized to _M_ -and _x_ respectively, _d_ and _e_ just start off being _0_ (_M/x mod M = 0/x mod M = 0_) and _1_ (_x/x mod M -= 1_). +algorithm shown above, if the modulus *M* is odd. To do so, compute *gcd(f=M,g=x)*, while keeping +track of extra variables *d* and *e*, for which at every step *d = f/x (mod M)* and *e = g/x (mod M)*. +*f/x* here means the number which multiplied with *x* gives *f mod M*. As *f* and *g* are initialized to *M* +and *x* respectively, *d* and *e* just start off being *0* (*M/x mod M = 0/x mod M = 0*) and *1* (*x/x mod M += 1*). ```python def div2(M, x): @@ -121,16 +119,17 @@ def modinv(M, x): return (d * f) % M ``` -Also note that this approach to track _d_ and _e_ throughout the computation to determine the inverse +Also note that this approach to track *d* and *e* throughout the computation to determine the inverse is different from the paper. There (see paragraph 12.1 in the paper) a transition matrix for the entire computation is determined (see section 3 below) and the inverse is computed from that. The approach here avoids the need for 2x2 matrix multiplications of various sizes, and appears to be faster at the level of optimization we're able to do in C. + ## 3. Batching multiple divsteps -Every divstep can be expressed as a matrix multiplication, applying a transition matrix _(1/2 t)_ -to both vectors _[f, g]_ and _[d, e]_ (see paragraph 8.1 in the paper): +Every divstep can be expressed as a matrix multiplication, applying a transition matrix *(1/2 t)* +to both vectors *[f, g]* and *[d, e]* (see paragraph 8.1 in the paper): ``` t = [ u, v ] @@ -143,15 +142,15 @@ to both vectors _[f, g]_ and _[d, e]_ (see paragraph 8.1 in the paper): [ out_e ] [ in_e ] ``` -where _(u, v, q, r)_ is _(0, 2, -1, 1)_, _(2, 0, 1, 1)_, or _(2, 0, 0, 1)_, depending on which branch is -taken. As above, the resulting _f_ and _g_ are always integers. +where *(u, v, q, r)* is *(0, 2, -1, 1)*, *(2, 0, 1, 1)*, or *(2, 0, 0, 1)*, depending on which branch is +taken. As above, the resulting *f* and *g* are always integers. Performing multiple divsteps corresponds to a multiplication with the product of all the individual divsteps' transition matrices. As each transition matrix consists of integers -divided by _2_, the product of these matrices will consist of integers divided by _2N_ (see also -theorem 9.2 in the paper). These divisions are expensive when updating _d_ and _e_, so we delay -them: we compute the integer coefficients of the combined transition matrix scaled by _2N_, and -do one division by _2N_ as a final step: +divided by *2*, the product of these matrices will consist of integers divided by *2N* (see also +theorem 9.2 in the paper). These divisions are expensive when updating *d* and *e*, so we delay +them: we compute the integer coefficients of the combined transition matrix scaled by *2N*, and +do one division by *2N* as a final step: ```python def divsteps_n_matrix(delta, f, g): @@ -167,13 +166,13 @@ def divsteps_n_matrix(delta, f, g): return delta, (u, v, q, r) ``` -As the branches in the divsteps are completely determined by the bottom _N_ bits of _f_ and _g_, this +As the branches in the divsteps are completely determined by the bottom *N* bits of *f* and *g*, this function to compute the transition matrix only needs to see those bottom bits. Furthermore all -intermediate results and outputs fit in _(N+1)_-bit numbers (unsigned for _f_ and _g_; signed for _u_, _v_, -_q_, and _r_) (see also paragraph 8.3 in the paper). This means that an implementation using 64-bit -integers could set _N=62_ and compute the full transition matrix for 62 steps at once without any +intermediate results and outputs fit in *(N+1)*-bit numbers (unsigned for *f* and *g*; signed for *u*, *v*, +*q*, and *r*) (see also paragraph 8.3 in the paper). This means that an implementation using 64-bit +integers could set *N=62* and compute the full transition matrix for 62 steps at once without any big integer arithmetic at all. This is the reason why this algorithm is efficient: it only needs -to update the full-size _f_, _g_, _d_, and _e_ numbers once every _N_ steps. +to update the full-size *f*, *g*, *d*, and *e* numbers once every *N* steps. We still need functions to compute: @@ -185,8 +184,8 @@ We still need functions to compute: [ out_e ] ( [ q, r ]) [ in_e ] ``` -Because the divsteps transformation only ever divides even numbers by two, the result of _t [f,g]_ is always even. When _t_ is a composition of _N_ divsteps, it follows that the resulting _f_ -and _g_ will be multiple of _2N_, and division by _2N_ is simply shifting them down: +Because the divsteps transformation only ever divides even numbers by two, the result of *t [f,g]* is always even. When *t* is a composition of *N* divsteps, it follows that the resulting *f* +and *g* will be multiple of *2N*, and division by *2N* is simply shifting them down: ```python def update_fg(f, g, t): @@ -200,8 +199,8 @@ def update_fg(f, g, t): return cf >> N, cg >> N ``` -The same is not true for _d_ and _e_, and we need an equivalent of the `div2` function for division by _2N mod M_. -This is easy if we have precomputed _1/M mod 2N_ (which always exists for odd _M_): +The same is not true for *d* and *e*, and we need an equivalent of the `div2` function for division by *2N mod M*. +This is easy if we have precomputed *1/M mod 2N* (which always exists for odd *M*): ```python def div2n(M, Mi, x): @@ -225,7 +224,7 @@ def update_de(d, e, t, M, Mi): return div2n(M, Mi, cd), div2n(M, Mi, ce) ``` -With all of those, we can write a version of `modinv` that performs _N_ divsteps at once: +With all of those, we can write a version of `modinv` that performs *N* divsteps at once: ```python3 def modinv(M, Mi, x): @@ -243,19 +242,20 @@ def modinv(M, Mi, x): return (d * f) % M ``` -This means that in practice we'll always perform a multiple of _N_ divsteps. This is not a problem -because once _g=0_, further divsteps do not affect _f_, _g_, _d_, or _e_ anymore (only _δ_ keeps +This means that in practice we'll always perform a multiple of *N* divsteps. This is not a problem +because once *g=0*, further divsteps do not affect *f*, *g*, *d*, or *e* anymore (only *δ* keeps increasing). For variable time code such excess iterations will be mostly optimized away in later sections. + ## 4. Avoiding modulus operations -So far, there are two places where we compute a remainder of big numbers modulo _M_: at the end of -`div2n` in every `update_de`, and at the very end of `modinv` after potentially negating _d_ due to the -sign of _f_. These are relatively expensive operations when done generically. +So far, there are two places where we compute a remainder of big numbers modulo *M*: at the end of +`div2n` in every `update_de`, and at the very end of `modinv` after potentially negating *d* due to the +sign of *f*. These are relatively expensive operations when done generically. -To deal with the modulus operation in `div2n`, we simply stop requiring _d_ and _e_ to be in range -_[0,M)_ all the time. Let's start by inlining `div2n` into `update_de`, and dropping the modulus +To deal with the modulus operation in `div2n`, we simply stop requiring *d* and *e* to be in range +*[0,M)* all the time. Let's start by inlining `div2n` into `update_de`, and dropping the modulus operation at the end: ```python @@ -272,15 +272,15 @@ def update_de(d, e, t, M, Mi): return cd >> N, ce >> N ``` -Let's look at bounds on the ranges of these numbers. It can be shown that _|u|+|v|_ and _|q|+|r|_ -never exceed _2N_ (see paragraph 8.3 in the paper), and thus a multiplication with _t_ will have -outputs whose absolute values are at most _2N_ times the maximum absolute input value. In case the -inputs _d_ and _e_ are in _(-M,M)_, which is certainly true for the initial values _d=0_ and _e=1_ assuming -_M > 1_, the multiplication results in numbers in range _(-2NM,2NM)_. Subtracting less than _2N_ -times _M_ to cancel out _N_ bits brings that up to _(-2N+1M,2NM)_, and -dividing by _2N_ at the end takes it to _(-2M,M)_. Another application of `update_de` would take that -to _(-3M,2M)_, and so forth. This progressive expansion of the variables' ranges can be -counteracted by incrementing _d_ and _e_ by _M_ whenever they're negative: +Let's look at bounds on the ranges of these numbers. It can be shown that *|u|+|v|* and *|q|+|r|* +never exceed *2N* (see paragraph 8.3 in the paper), and thus a multiplication with *t* will have +outputs whose absolute values are at most *2N* times the maximum absolute input value. In case the +inputs *d* and *e* are in *(-M,M)*, which is certainly true for the initial values *d=0* and *e=1* assuming +*M > 1*, the multiplication results in numbers in range *(-2NM,2NM)*. Subtracting less than *2N* +times *M* to cancel out *N* bits brings that up to *(-2N+1M,2NM)*, and +dividing by *2N* at the end takes it to *(-2M,M)*. Another application of `update_de` would take that +to *(-3M,2M)*, and so forth. This progressive expansion of the variables' ranges can be +counteracted by incrementing *d* and *e* by *M* whenever they're negative: ```python ... @@ -293,12 +293,12 @@ counteracted by incrementing _d_ and _e_ by _M_ whenever they're negative: ... ``` -With inputs in _(-2M,M)_, they will first be shifted into range _(-M,M)_, which means that the -output will again be in _(-2M,M)_, and this remains the case regardless of how many `update_de` +With inputs in *(-2M,M)*, they will first be shifted into range *(-M,M)*, which means that the +output will again be in *(-2M,M)*, and this remains the case regardless of how many `update_de` invocations there are. In what follows, we will try to make this more efficient. -Note that increasing _d_ by _M_ is equal to incrementing _cd_ by _u M_ and _ce_ by _q M_. Similarly, -increasing _e_ by _M_ is equal to incrementing _cd_ by _v M_ and _ce_ by _r M_. So we could instead write: +Note that increasing *d* by *M* is equal to incrementing *cd* by *u M* and *ce* by *q M*. Similarly, +increasing *e* by *M* is equal to incrementing *cd* by *v M* and *ce* by *r M*. So we could instead write: ```python ... @@ -318,10 +318,10 @@ increasing _e_ by _M_ is equal to incrementing _cd_ by _v M_ and _ce_ by ... ``` -Now note that we have two steps of corrections to _cd_ and _ce_ that add multiples of _M_: this +Now note that we have two steps of corrections to *cd* and *ce* that add multiples of *M*: this increment, and the decrement that cancels out bottom bits. The second one depends on the first -one, but they can still be efficiently combined by only computing the bottom bits of _cd_ and _ce_ -at first, and using that to compute the final _md_, _me_ values: +one, but they can still be efficiently combined by only computing the bottom bits of *cd* and *ce* +at first, and using that to compute the final *md*, *me* values: ```python def update_de(d, e, t, M, Mi): @@ -346,8 +346,8 @@ def update_de(d, e, t, M, Mi): return cd >> N, ce >> N ``` -One last optimization: we can avoid the _md M_ and _me M_ multiplications in the bottom bits of _cd_ -and _ce_ by moving them to the _md_ and _me_ correction: +One last optimization: we can avoid the *md M* and *me M* multiplications in the bottom bits of *cd* +and *ce* by moving them to the *md* and *me* correction: ```python ... @@ -362,10 +362,10 @@ and _ce_ by moving them to the _md_ and _me_ correction: ... ``` -The resulting function takes _d_ and _e_ in range _(-2M,M)_ as inputs, and outputs values in the same -range. That also means that the _d_ value at the end of `modinv` will be in that range, while we want -a result in _[0,M)_. To do that, we need a normalization function. It's easy to integrate the -conditional negation of _d_ (based on the sign of _f_) into it as well: +The resulting function takes *d* and *e* in range *(-2M,M)* as inputs, and outputs values in the same +range. That also means that the *d* value at the end of `modinv` will be in that range, while we want +a result in *[0,M)*. To do that, we need a normalization function. It's easy to integrate the +conditional negation of *d* (based on the sign of *f*) into it as well: ```python def normalize(sign, v, M): @@ -391,21 +391,22 @@ And calling it in `modinv` is simply: return normalize(f, d, M) ``` + ## 5. Constant-time operation The primary selling point of the algorithm is fast constant-time operation. What code flow still depends on the input data so far? -- the number of iterations of the while _g ≠ 0_ loop in `modinv` +- the number of iterations of the while *g ≠ 0* loop in `modinv` - the branches inside `divsteps_n_matrix` - the sign checks in `update_de` - the sign checks in `normalize` To make the while loop in `modinv` constant time it can be replaced with a constant number of -iterations. The paper proves (Theorem 11.2) that _741_ divsteps are sufficient for any _256_-bit -inputs, and [safegcd-bounds](https://github.com/sipa/safegcd-bounds) shows that the slightly better bound _724_ is -sufficient even. Given that every loop iteration performs _N_ divsteps, it will run a total of -_⌈724/N⌉_ times. +iterations. The paper proves (Theorem 11.2) that *741* divsteps are sufficient for any *256*-bit +inputs, and [safegcd-bounds](https://github.com/sipa/safegcd-bounds) shows that the slightly better bound *724* is +sufficient even. Given that every loop iteration performs *N* divsteps, it will run a total of +*⌈724/N⌉* times. To deal with the branches in `divsteps_n_matrix` we will replace them with constant-time bitwise operations (and hope the C compiler isn't smart enough to turn them back into branches; see @@ -424,10 +425,10 @@ divstep can be written instead as (compare to the inner loop of `gcd` in section ``` To convert the above to bitwise operations, we rely on a trick to negate conditionally: per the -definition of negative numbers in two's complement, (_-v == ~v + 1_) holds for every number _v_. As -_-1_ in two's complement is all _1_ bits, bitflipping can be expressed as xor with _-1_. It follows -that _-v == (v ^ -1) - (-1)_. Thus, if we have a variable _c_ that takes on values _0_ or _-1_, then -_(v ^ c) - c_ is _v_ if _c=0_ and _-v_ if _c=-1_. +definition of negative numbers in two's complement, (*-v == ~v + 1*) holds for every number *v*. As +*-1* in two's complement is all *1* bits, bitflipping can be expressed as xor with *-1*. It follows +that *-v == (v ^ -1) - (-1)*. Thus, if we have a variable *c* that takes on values *0* or *-1*, then +*(v ^ c) - c* is *v* if *c=0* and *-v* if *c=-1*. Using this we can write: @@ -443,13 +444,13 @@ in constant-time form as: x = (f ^ c1) - c1 ``` -To use that trick, we need a helper mask variable _c1_ that resolves the condition _δ>0_ to _-1_ -(if true) or _0_ (if false). We compute _c1_ using right shifting, which is equivalent to dividing by -the specified power of _2_ and rounding down (in Python, and also in C under the assumption of a typical two's complement system; see -`assumptions.h` for tests that this is the case). Right shifting by _63_ thus maps all -numbers in range _[-263,0)_ to _-1_, and numbers in range _[0,263)_ to _0_. +To use that trick, we need a helper mask variable *c1* that resolves the condition *δ>0* to *-1* +(if true) or *0* (if false). We compute *c1* using right shifting, which is equivalent to dividing by +the specified power of *2* and rounding down (in Python, and also in C under the assumption of a typical two's complement system; see +`assumptions.h` for tests that this is the case). Right shifting by *63* thus maps all +numbers in range *[-263,0)* to *-1*, and numbers in range *[0,263)* to *0*. -Using the facts that _x&0=0_ and _x&(-1)=x_ (on two's complement systems again), we can write: +Using the facts that *x&0=0* and *x&(-1)=x* (on two's complement systems again), we can write: ```python if g & 1: @@ -497,8 +498,8 @@ becomes: ``` It turns out that this can be implemented more efficiently by applying the substitution -_η=-δ_. In this representation, negating _δ_ corresponds to negating _η_, and incrementing -_δ_ corresponds to decrementing _η_. This allows us to remove the negation in the _c1_ +*η=-δ*. In this representation, negating *δ* corresponds to negating *η*, and incrementing +*δ* corresponds to decrementing *η*. This allows us to remove the negation in the *c1* computation: ```python @@ -518,12 +519,12 @@ computation: g >>= 1 ``` -A variant of divsteps with better worst-case performance can be used instead: starting _δ_ at -_1/2_ instead of _1_. This reduces the worst case number of iterations to _590_ for _256_-bit inputs -(which can be shown using convex hull analysis). In this case, the substitution _ζ=-(δ+1/2)_ -is used instead to keep the variable integral. Incrementing _δ_ by _1_ still translates to -decrementing _ζ_ by _1_, but negating _δ_ now corresponds to going from _ζ_ to _-(ζ+1)_, or -_~ζ_. Doing that conditionally based on _c3_ is simply: +A variant of divsteps with better worst-case performance can be used instead: starting *δ* at +*1/2* instead of *1*. This reduces the worst case number of iterations to *590* for *256*-bit inputs +(which can be shown using convex hull analysis). In this case, the substitution *ζ=-(δ+1/2)* +is used instead to keep the variable integral. Incrementing *δ* by *1* still translates to +decrementing *ζ* by *1*, but negating *δ* now corresponds to going from *ζ* to *-(ζ+1)*, or +*~ζ*. Doing that conditionally based on *c3* is simply: ```python ... @@ -533,12 +534,13 @@ _~ζ_. Doing that conditionally based on _c3_ is simply: ``` By replacing the loop in `divsteps_n_matrix` with a variant of the divstep code above (extended to -also apply all _f_ operations to _u_, _v_ and all _g_ operations to _q_, _r_), a constant-time version of +also apply all *f* operations to *u*, *v* and all *g* operations to *q*, *r*), a constant-time version of `divsteps_n_matrix` is obtained. The full code will be in section 7. These bit fiddling tricks can also be used to make the conditional negations and additions in `update_de` and `normalize` constant-time. + ## 6. Variable-time optimizations In section 5, we modified the `divsteps_n_matrix` function (and a few others) to be constant time. @@ -548,7 +550,7 @@ faster non-constant time `divsteps_n_matrix` function. To do so, first consider yet another way of writing the inner loop of divstep operations in `gcd` from section 1. This decomposition is also explained in the paper in section 8.2. We use -the original version with initial _δ=1_ and _η=-δ_ here. +the original version with initial *δ=1* and *η=-δ* here. ```python for _ in range(N): @@ -560,7 +562,7 @@ for _ in range(N): g >>= 1 ``` -Whenever _g_ is even, the loop only shifts _g_ down and decreases _η_. When _g_ ends in multiple zero +Whenever *g* is even, the loop only shifts *g* down and decreases *η*. When *g* ends in multiple zero bits, these iterations can be consolidated into one step. This requires counting the bottom zero bits efficiently, which is possible on most platforms; it is abstracted here as the function `count_trailing_zeros`. @@ -593,20 +595,20 @@ while True: # g is even now, and the eta decrement and g shift will happen in the next loop. ``` -We can now remove multiple bottom _0_ bits from _g_ at once, but still need a full iteration whenever -there is a bottom _1_ bit. In what follows, we will get rid of multiple _1_ bits simultaneously as +We can now remove multiple bottom *0* bits from *g* at once, but still need a full iteration whenever +there is a bottom *1* bit. In what follows, we will get rid of multiple *1* bits simultaneously as well. -Observe that as long as _η ≥ 0_, the loop does not modify _f_. Instead, it cancels out bottom -bits of _g_ and shifts them out, and decreases _η_ and _i_ accordingly - interrupting only when _η_ -becomes negative, or when _i_ reaches _0_. Combined, this is equivalent to adding a multiple of _f_ to -_g_ to cancel out multiple bottom bits, and then shifting them out. +Observe that as long as *η ≥ 0*, the loop does not modify *f*. Instead, it cancels out bottom +bits of *g* and shifts them out, and decreases *η* and *i* accordingly - interrupting only when *η* +becomes negative, or when *i* reaches *0*. Combined, this is equivalent to adding a multiple of *f* to +*g* to cancel out multiple bottom bits, and then shifting them out. -It is easy to find what that multiple is: we want a number _w_ such that _g+w f_ has a few bottom -zero bits. If that number of bits is _L_, we want _g+w f mod 2L = 0_, or _w = -g/f mod 2L_. Since _f_ -is odd, such a _w_ exists for any _L_. _L_ cannot be more than _i_ steps (as we'd finish the loop before -doing more) or more than _η+1_ steps (as we'd run `eta, f, g = -eta, g, -f` at that point), but -apart from that, we're only limited by the complexity of computing _w_. +It is easy to find what that multiple is: we want a number *w* such that *g+w f* has a few bottom +zero bits. If that number of bits is *L*, we want *g+w f mod 2L = 0*, or *w = -g/f mod 2L*. Since *f* +is odd, such a *w* exists for any *L*. *L* cannot be more than *i* steps (as we'd finish the loop before +doing more) or more than *η+1* steps (as we'd run `eta, f, g = -eta, g, -f` at that point), but +apart from that, we're only limited by the complexity of computing *w*. This code demonstrates how to cancel up to 4 bits per step: @@ -640,25 +642,26 @@ some can be found in Hacker's Delight second edition by Henry S. Warren, Jr. pag Here we need the negated modular inverse, which is a simple transformation of those: - Instead of a 3-bit table: - - _-f_ or _f ^ 6_ + - *-f* or *f ^ 6* - Instead of a 4-bit table: - - _1 - f(f + 1)_ - - _-(f + (((f + 1) & 4) << 1))_ -- For larger tables the following technique can be used: if _w=-1/f mod 2L_, then _w(w f+2)_ is - _-1/f mod 22L_. This allows extending the previous formulas (or tables). In particular we + - *1 - f(f + 1)* + - *-(f + (((f + 1) & 4) << 1))* +- For larger tables the following technique can be used: if *w=-1/f mod 2L*, then *w(w f+2)* is + *-1/f mod 22L*. This allows extending the previous formulas (or tables). In particular we have this 6-bit function (based on the 3-bit function above): - - _f(f2 - 2)_ + - *f(f2 - 2)* -This loop, again extended to also handle _u_, _v_, _q_, and _r_ alongside _f_ and _g_, placed in +This loop, again extended to also handle *u*, *v*, *q*, and *r* alongside *f* and *g*, placed in `divsteps_n_matrix`, gives a significantly faster, but non-constant time version. + ## 7. Final Python version All together we need the following functions: - A way to compute the transition matrix in constant time, using the `divsteps_n_matrix` function from section 2, but with its loop replaced by a variant of the constant-time divstep from - section 5, extended to handle _u_, _v_, _q_, _r_: + section 5, extended to handle *u*, *v*, *q*, *r*: ```python def divsteps_n_matrix(zeta, f, g): @@ -681,7 +684,7 @@ def divsteps_n_matrix(zeta, f, g): return zeta, (u, v, q, r) ``` -- The functions to update _f_ and _g_, and _d_ and _e_, from section 2 and section 4, with the constant-time +- The functions to update *f* and *g*, and *d* and *e*, from section 2 and section 4, with the constant-time changes to `update_de` from section 5: ```python @@ -720,7 +723,7 @@ def normalize(sign, v, M): return v ``` -- And finally the `modinv` function too, adapted to use _ζ_ instead of _δ_, and using the fixed +- And finally the `modinv` function too, adapted to use *ζ* instead of *δ*, and using the fixed iteration count from section 5: ```python @@ -769,21 +772,20 @@ def modinv_var(M, Mi, x): ## 8. From GCDs to Jacobi symbol -We can also use a similar approach to calculate Jacobi symbol _(x | M)_ by keeping track of an -extra variable _j_, for which at every step _(x | M) = j (g | f)_. As we update _f_ and _g_, we -make corresponding updates to _j_ using +We can also use a similar approach to calculate Jacobi symbol *(x | M)* by keeping track of an +extra variable *j*, for which at every step *(x | M) = j (g | f)*. As we update *f* and *g*, we +make corresponding updates to *j* using [properties of the Jacobi symbol](https://en.wikipedia.org/wiki/Jacobi_symbol#Properties): +* *((g/2) | f)* is either *(g | f)* or *-(g | f)*, depending on the value of *f mod 8* (negating if it's *3* or *5*). +* *(f | g)* is either *(g | f)* or *-(g | f)*, depending on *f mod 4* and *g mod 4* (negating if both are *3*). -- _((g/2) | f)_ is either _(g | f)_ or _-(g | f)_, depending on the value of _f mod 8_ (negating if it's _3_ or _5_). -- _(f | g)_ is either _(g | f)_ or _-(g | f)_, depending on _f mod 4_ and _g mod 4_ (negating if both are _3_). - -These updates depend only on the values of _f_ and _g_ modulo _4_ or _8_, and can thus be applied -very quickly, as long as we keep track of a few additional bits of _f_ and _g_. Overall, this +These updates depend only on the values of *f* and *g* modulo *4* or *8*, and can thus be applied +very quickly, as long as we keep track of a few additional bits of *f* and *g*. Overall, this calculation is slightly simpler than the one for the modular inverse because we no longer need to -keep track of _d_ and _e_. +keep track of *d* and *e*. -However, one difficulty of this approach is that the Jacobi symbol _(a | n)_ is only defined for -positive odd integers _n_, whereas in the original safegcd algorithm, _f, g_ can take negative +However, one difficulty of this approach is that the Jacobi symbol *(a | n)* is only defined for +positive odd integers *n*, whereas in the original safegcd algorithm, *f, g* can take negative values. We resolve this by using the following modified steps: ```python @@ -797,16 +799,15 @@ values. We resolve this by using the following modified steps: ``` The algorithm is still correct, since the changed divstep, called a "posdivstep" (see section 8.4 -and E.5 in the paper) preserves _gcd(f, g)_. However, there's no proof that the modified algorithm +and E.5 in the paper) preserves *gcd(f, g)*. However, there's no proof that the modified algorithm will converge. The justification for posdivsteps is completely empirical: in practice, it appears -that the vast majority of nonzero inputs converge to _f=g=gcd(f0, g0)_ in a +that the vast majority of nonzero inputs converge to *f=g=gcd(f0, g0)* in a number of steps proportional to their logarithm. Note that: - -- We require inputs to satisfy _gcd(x, M) = 1_, as otherwise _f=1_ is not reached. -- We require inputs _x &neq; 0_, because applying posdivstep with _g=0_ has no effect. -- We need to update the termination condition from _g=0_ to _f=1_. +- We require inputs to satisfy *gcd(x, M) = 1*, as otherwise *f=1* is not reached. +- We require inputs *x &neq; 0*, because applying posdivstep with *g=0* has no effect. +- We need to update the termination condition from *g=0* to *f=1*. We account for the possibility of nonconvergence by only performing a bounded number of posdivsteps, and then falling back to square-root based Jacobi calculation if a solution has not @@ -814,5 +815,5 @@ yet been found. The optimizations in sections 3-7 above are described in the context of the original divsteps, but in the C implementation we also adapt most of them (not including "avoiding modulus operations", -since it's not necessary to track _d, e_, and "constant-time operation", since we never calculate +since it's not necessary to track *d, e*, and "constant-time operation", since we never calculate Jacobi symbols for secret data) to the posdivsteps version. diff --git a/external/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json b/external/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json index 04e34f5a17..9c90747993 100644 --- a/external/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json +++ b/external/secp256k1/src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.json @@ -1,6358 +1,6358 @@ { - "algorithm": "ECDSA", - "schema": "ecdsa_bitcoin_verify_schema.json", - "generatorVersion": "0.9rc5", - "numberOfTests": 463, - "header": [ + "algorithm" : "ECDSA", + "schema" : "ecdsa_bitcoin_verify_schema.json", + "generatorVersion" : "0.9rc5", + "numberOfTests" : 463, + "header" : [ "Test vectors of type EcdsaBitcoinVerify are meant for the verification", "of a ECDSA variant used for bitcoin, that add signature non-malleability." ], - "notes": { - "ArithmeticError": { - "bugType": "EDGE_CASE", - "description": "Some implementations of ECDSA have arithmetic errors that occur when intermediate results have extreme values. This test vector has been constructed to test such occurences.", - "cves": [ + "notes" : { + "ArithmeticError" : { + "bugType" : "EDGE_CASE", + "description" : "Some implementations of ECDSA have arithmetic errors that occur when intermediate results have extreme values. This test vector has been constructed to test such occurences.", + "cves" : [ "CVE-2017-18146" ] }, - "BerEncodedSignature": { - "bugType": "BER_ENCODING", - "description": "ECDSA signatures are usually DER encoded. This signature contains valid values for r and s, but it uses alternative BER encoding.", - "effect": "Accepting alternative BER encodings may be benign in some cases, or be an issue if protocol requires signature malleability.", - "cves": [ + "BerEncodedSignature" : { + "bugType" : "BER_ENCODING", + "description" : "ECDSA signatures are usually DER encoded. This signature contains valid values for r and s, but it uses alternative BER encoding.", + "effect" : "Accepting alternative BER encodings may be benign in some cases, or be an issue if protocol requires signature malleability.", + "cves" : [ "CVE-2020-14966", "CVE-2020-13822", "CVE-2019-14859", "CVE-2016-1000342" ] }, - "EdgeCasePublicKey": { - "bugType": "EDGE_CASE", - "description": "The test vector uses a special case public key. " + "EdgeCasePublicKey" : { + "bugType" : "EDGE_CASE", + "description" : "The test vector uses a special case public key. " }, - "EdgeCaseShamirMultiplication": { - "bugType": "EDGE_CASE", - "description": "Shamir proposed a fast method for computing the sum of two scalar multiplications efficiently. This test vector has been constructed so that an intermediate result is the point at infinity if Shamir's method is used." + "EdgeCaseShamirMultiplication" : { + "bugType" : "EDGE_CASE", + "description" : "Shamir proposed a fast method for computing the sum of two scalar multiplications efficiently. This test vector has been constructed so that an intermediate result is the point at infinity if Shamir's method is used." }, - "IntegerOverflow": { - "bugType": "CAN_OF_WORMS", - "description": "The test vector contains an r and s that has been modified, so that the original value is restored if the implementation ignores the most significant bits.", - "effect": "Without further analysis it is unclear if the modification can be used to forge signatures." + "IntegerOverflow" : { + "bugType" : "CAN_OF_WORMS", + "description" : "The test vector contains an r and s that has been modified, so that the original value is restored if the implementation ignores the most significant bits.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." }, - "InvalidEncoding": { - "bugType": "CAN_OF_WORMS", - "description": "ECDSA signatures are encoded using ASN.1. This test vector contains an incorrectly encoded signature. The test vector itself was generated from a valid signature by modifying its encoding.", - "effect": "Without further analysis it is unclear if the modification can be used to forge signatures." + "InvalidEncoding" : { + "bugType" : "CAN_OF_WORMS", + "description" : "ECDSA signatures are encoded using ASN.1. This test vector contains an incorrectly encoded signature. The test vector itself was generated from a valid signature by modifying its encoding.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." }, - "InvalidSignature": { - "bugType": "AUTH_BYPASS", - "description": "The signature contains special case values such as r=0 and s=0. Buggy implementations may accept such values, if the implementation does not check boundaries and computes s^(-1) == 0.", - "effect": "Accepting such signatures can have the effect that an adversary can forge signatures without even knowning the message to sign.", - "cves": [ + "InvalidSignature" : { + "bugType" : "AUTH_BYPASS", + "description" : "The signature contains special case values such as r=0 and s=0. Buggy implementations may accept such values, if the implementation does not check boundaries and computes s^(-1) == 0.", + "effect" : "Accepting such signatures can have the effect that an adversary can forge signatures without even knowning the message to sign.", + "cves" : [ "CVE-2022-21449", "CVE-2021-43572", "CVE-2022-24884" ] }, - "InvalidTypesInSignature": { - "bugType": "AUTH_BYPASS", - "description": "The signature contains invalid types. Dynamic typed languages sometime coerce such values of different types into integers. If an implementation is careless and has additional bugs, such as not checking integer boundaries then it may be possible that such signatures are accepted.", - "effect": "Accepting such signatures can have the effect that an adversary can forge signatures without even knowning the message to sign.", - "cves": [ + "InvalidTypesInSignature" : { + "bugType" : "AUTH_BYPASS", + "description" : "The signature contains invalid types. Dynamic typed languages sometime coerce such values of different types into integers. If an implementation is careless and has additional bugs, such as not checking integer boundaries then it may be possible that such signatures are accepted.", + "effect" : "Accepting such signatures can have the effect that an adversary can forge signatures without even knowning the message to sign.", + "cves" : [ "CVE-2022-21449" ] }, - "ModifiedInteger": { - "bugType": "CAN_OF_WORMS", - "description": "The test vector contains an r and s that has been modified. The goal is to check for arithmetic errors.", - "effect": "Without further analysis it is unclear if the modification can be used to forge signatures." + "ModifiedInteger" : { + "bugType" : "CAN_OF_WORMS", + "description" : "The test vector contains an r and s that has been modified. The goal is to check for arithmetic errors.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." }, - "ModifiedSignature": { - "bugType": "CAN_OF_WORMS", - "description": "The test vector contains an invalid signature that was generated from a valid signature by modifying it.", - "effect": "Without further analysis it is unclear if the modification can be used to forge signatures." + "ModifiedSignature" : { + "bugType" : "CAN_OF_WORMS", + "description" : "The test vector contains an invalid signature that was generated from a valid signature by modifying it.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." }, - "ModularInverse": { - "bugType": "EDGE_CASE", - "description": "The test vectors contains a signature where computing the modular inverse of s hits an edge case.", - "effect": "While the signature in this test vector is constructed and similar cases are unlikely to occur, it is important to determine if the underlying arithmetic error can be used to forge signatures.", - "cves": [ + "ModularInverse" : { + "bugType" : "EDGE_CASE", + "description" : "The test vectors contains a signature where computing the modular inverse of s hits an edge case.", + "effect" : "While the signature in this test vector is constructed and similar cases are unlikely to occur, it is important to determine if the underlying arithmetic error can be used to forge signatures.", + "cves" : [ "CVE-2019-0865" ] }, - "PointDuplication": { - "bugType": "EDGE_CASE", - "description": "Some implementations of ECDSA do not handle duplication and points at infinity correctly. This is a test vector that has been specially crafted to check for such an omission.", - "cves": [ + "PointDuplication" : { + "bugType" : "EDGE_CASE", + "description" : "Some implementations of ECDSA do not handle duplication and points at infinity correctly. This is a test vector that has been specially crafted to check for such an omission.", + "cves" : [ "2020-12607", "CVE-2015-2730" ] }, - "RangeCheck": { - "bugType": "CAN_OF_WORMS", - "description": "The test vector contains an r and s that has been modified. By adding or subtracting the order of the group (or other values) the test vector checks whether signature verification verifies the range of r and s.", - "effect": "Without further analysis it is unclear if the modification can be used to forge signatures." + "RangeCheck" : { + "bugType" : "CAN_OF_WORMS", + "description" : "The test vector contains an r and s that has been modified. By adding or subtracting the order of the group (or other values) the test vector checks whether signature verification verifies the range of r and s.", + "effect" : "Without further analysis it is unclear if the modification can be used to forge signatures." }, - "SignatureMalleabilityBitcoin": { - "bugType": "SIGNATURE_MALLEABILITY", - "description": "\"BitCoins\"-curves are curves where signature malleability can be a serious issue. An implementation should only accept a signature s where s < n/2. If an implementation is not meant for uses cases that require signature malleability then this implemenation should be tested with another set of test vectors.", - "effect": "In bitcoin exchanges, it may be used to make a double deposits or double withdrawals", - "links": [ + "SignatureMalleabilityBitcoin" : { + "bugType" : "SIGNATURE_MALLEABILITY", + "description" : "\"BitCoins\"-curves are curves where signature malleability can be a serious issue. An implementation should only accept a signature s where s < n/2. If an implementation is not meant for uses cases that require signature malleability then this implemenation should be tested with another set of test vectors.", + "effect" : "In bitcoin exchanges, it may be used to make a double deposits or double withdrawals", + "links" : [ "https://en.bitcoin.it/wiki/Transaction_malleability", "https://en.bitcoinwiki.org/wiki/Transaction_Malleability" ] }, - "SmallRandS": { - "bugType": "EDGE_CASE", - "description": "The test vectors contains a signature where both r and s are small integers. Some libraries cannot verify such signatures.", - "effect": "While the signature in this test vector is constructed and similar cases are unlikely to occur, it is important to determine if the underlying arithmetic error can be used to forge signatures.", - "cves": [ + "SmallRandS" : { + "bugType" : "EDGE_CASE", + "description" : "The test vectors contains a signature where both r and s are small integers. Some libraries cannot verify such signatures.", + "effect" : "While the signature in this test vector is constructed and similar cases are unlikely to occur, it is important to determine if the underlying arithmetic error can be used to forge signatures.", + "cves" : [ "2020-13895" ] }, - "SpecialCaseHash": { - "bugType": "EDGE_CASE", - "description": "The test vector contains a signature where the hash of the message is a special case, e.g., contains a long run of 0 or 1 bits." + "SpecialCaseHash" : { + "bugType" : "EDGE_CASE", + "description" : "The test vector contains a signature where the hash of the message is a special case, e.g., contains a long run of 0 or 1 bits." }, - "ValidSignature": { - "bugType": "BASIC", - "description": "The test vector contains a valid signature that was generated pseudorandomly. Such signatures should not fail to verify unless some of the parameters (e.g. curve or hash function) are not supported." + "ValidSignature" : { + "bugType" : "BASIC", + "description" : "The test vector contains a valid signature that was generated pseudorandomly. Such signatures should not fail to verify unless some of the parameters (e.g. curve or hash function) are not supported." } }, - "testGroups": [ + "testGroups" : [ { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9", - "wx": "00b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6f", - "wy": "00f0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9", + "wx" : "00b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6f", + "wy" : "00f0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEuDj/ROW8F3vyEYnQdmCC/J2EMiaIf8l2\nA3EQC37iCm/wyddb+6ezGmvKGXRJbutW3jVwcZVdg8Sxutqgshgy6Q==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004b838ff44e5bc177bf21189d0766082fc9d843226887fc9760371100b7ee20a6ff0c9d75bfba7b31a6bca1974496eeb56de357071955d83c4b1badaa0b21832e9", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEuDj/ROW8F3vyEYnQdmCC/J2EMiaIf8l2\nA3EQC37iCm/wyddb+6ezGmvKGXRJbutW3jVwcZVdg8Sxutqgshgy6Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 1, - "comment": "Signature malleability", - "flags": [ + "tcId" : 1, + "comment" : "Signature malleability", + "flags" : [ "SignatureMalleabilityBitcoin" ], - "msg": "313233343030", - "sig": "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365022100900e75ad233fcc908509dbff5922647db37c21f4afd3203ae8dc4ae7794b0f87", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365022100900e75ad233fcc908509dbff5922647db37c21f4afd3203ae8dc4ae7794b0f87", + "result" : "invalid" }, { - "tcId": 2, - "comment": "valid", - "flags": [ + "tcId" : 2, + "comment" : "valid", + "flags" : [ "ValidSignature" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "valid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "valid" }, { - "tcId": 3, - "comment": "length of sequence [r, s] uses long form encoding", - "flags": [ + "tcId" : 3, + "comment" : "length of sequence [r, s] uses long form encoding", + "flags" : [ "BerEncodedSignature" ], - "msg": "313233343030", - "sig": "308145022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "308145022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 4, - "comment": "length of sequence [r, s] contains a leading 0", - "flags": [ + "tcId" : 4, + "comment" : "length of sequence [r, s] contains a leading 0", + "flags" : [ "BerEncodedSignature" ], - "msg": "313233343030", - "sig": "30820045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30820045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 5, - "comment": "length of sequence [r, s] uses 70 instead of 69", - "flags": [ + "tcId" : 5, + "comment" : "length of sequence [r, s] uses 70 instead of 69", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 6, - "comment": "length of sequence [r, s] uses 68 instead of 69", - "flags": [ + "tcId" : 6, + "comment" : "length of sequence [r, s] uses 68 instead of 69", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 7, - "comment": "uint32 overflow in length of sequence [r, s]", - "flags": [ + "tcId" : 7, + "comment" : "uint32 overflow in length of sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30850100000045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30850100000045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 8, - "comment": "uint64 overflow in length of sequence [r, s]", - "flags": [ + "tcId" : 8, + "comment" : "uint64 overflow in length of sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3089010000000000000045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3089010000000000000045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 9, - "comment": "length of sequence [r, s] = 2**31 - 1", - "flags": [ + "tcId" : 9, + "comment" : "length of sequence [r, s] = 2**31 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30847fffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30847fffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 10, - "comment": "length of sequence [r, s] = 2**31", - "flags": [ + "tcId" : 10, + "comment" : "length of sequence [r, s] = 2**31", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "308480000000022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "308480000000022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 11, - "comment": "length of sequence [r, s] = 2**32 - 1", - "flags": [ + "tcId" : 11, + "comment" : "length of sequence [r, s] = 2**32 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3084ffffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3084ffffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 12, - "comment": "length of sequence [r, s] = 2**40 - 1", - "flags": [ + "tcId" : 12, + "comment" : "length of sequence [r, s] = 2**40 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3085ffffffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3085ffffffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 13, - "comment": "length of sequence [r, s] = 2**64 - 1", - "flags": [ + "tcId" : 13, + "comment" : "length of sequence [r, s] = 2**64 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3088ffffffffffffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3088ffffffffffffffff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 14, - "comment": "incorrect length of sequence [r, s]", - "flags": [ + "tcId" : 14, + "comment" : "incorrect length of sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30ff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30ff022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 15, - "comment": "replaced sequence [r, s] by an indefinite length tag without termination", - "flags": [ + "tcId" : 15, + "comment" : "replaced sequence [r, s] by an indefinite length tag without termination", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 16, - "comment": "removing sequence [r, s]", - "flags": [ + "tcId" : 16, + "comment" : "removing sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "", - "result": "invalid" + "msg" : "313233343030", + "sig" : "", + "result" : "invalid" }, { - "tcId": 17, - "comment": "lonely sequence tag", - "flags": [ + "tcId" : 17, + "comment" : "lonely sequence tag", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30", + "result" : "invalid" }, { - "tcId": 18, - "comment": "appending 0's to sequence [r, s]", - "flags": [ + "tcId" : 18, + "comment" : "appending 0's to sequence [r, s]", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" }, { - "tcId": 19, - "comment": "prepending 0's to sequence [r, s]", - "flags": [ + "tcId" : 19, + "comment" : "prepending 0's to sequence [r, s]", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "30470000022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30470000022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 20, - "comment": "appending unused 0's to sequence [r, s]", - "flags": [ + "tcId" : 20, + "comment" : "appending unused 0's to sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" }, { - "tcId": 21, - "comment": "appending null value to sequence [r, s]", - "flags": [ + "tcId" : 21, + "comment" : "appending null value to sequence [r, s]", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0500", + "result" : "invalid" }, { - "tcId": 22, - "comment": "prepending garbage to sequence [r, s]", - "flags": [ + "tcId" : 22, + "comment" : "prepending garbage to sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304a4981773045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304a4981773045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 23, - "comment": "prepending garbage to sequence [r, s]", - "flags": [ + "tcId" : 23, + "comment" : "prepending garbage to sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304925003045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304925003045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 24, - "comment": "appending garbage to sequence [r, s]", - "flags": [ + "tcId" : 24, + "comment" : "appending garbage to sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30473045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0004deadbeef", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30473045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0004deadbeef", + "result" : "invalid" }, { - "tcId": 25, - "comment": "including undefined tags", - "flags": [ + "tcId" : 25, + "comment" : "including undefined tags", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "304daa00bb00cd003045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304daa00bb00cd003045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 26, - "comment": "including undefined tags", - "flags": [ + "tcId" : 26, + "comment" : "including undefined tags", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304d2229aa00bb00cd00022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304d2229aa00bb00cd00022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 27, - "comment": "including undefined tags", - "flags": [ + "tcId" : 27, + "comment" : "including undefined tags", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304d022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323652228aa00bb00cd0002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304d022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323652228aa00bb00cd0002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 28, - "comment": "truncated length of sequence [r, s]", - "flags": [ + "tcId" : 28, + "comment" : "truncated length of sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3081", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3081", + "result" : "invalid" }, { - "tcId": 29, - "comment": "including undefined tags to sequence [r, s]", - "flags": [ + "tcId" : 29, + "comment" : "including undefined tags to sequence [r, s]", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "304baa02aabb3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304baa02aabb3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 30, - "comment": "using composition with indefinite length for sequence [r, s]", - "flags": [ + "tcId" : 30, + "comment" : "using composition with indefinite length for sequence [r, s]", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "30803045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30803045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" }, { - "tcId": 31, - "comment": "using composition with wrong tag for sequence [r, s]", - "flags": [ + "tcId" : 31, + "comment" : "using composition with wrong tag for sequence [r, s]", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "30803145022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30803145022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" }, { - "tcId": 32, - "comment": "Replacing sequence [r, s] with NULL", - "flags": [ + "tcId" : 32, + "comment" : "Replacing sequence [r, s] with NULL", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "0500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "0500", + "result" : "invalid" }, { - "tcId": 33, - "comment": "changing tag value of sequence [r, s]", - "flags": [ + "tcId" : 33, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "2e45022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "2e45022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 34, - "comment": "changing tag value of sequence [r, s]", - "flags": [ + "tcId" : 34, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "2f45022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "2f45022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 35, - "comment": "changing tag value of sequence [r, s]", - "flags": [ + "tcId" : 35, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3145022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3145022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 36, - "comment": "changing tag value of sequence [r, s]", - "flags": [ + "tcId" : 36, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3245022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3245022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 37, - "comment": "changing tag value of sequence [r, s]", - "flags": [ + "tcId" : 37, + "comment" : "changing tag value of sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "ff45022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "ff45022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 38, - "comment": "dropping value of sequence [r, s]", - "flags": [ + "tcId" : 38, + "comment" : "dropping value of sequence [r, s]", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3000", + "result" : "invalid" }, { - "tcId": 39, - "comment": "using composition for sequence [r, s]", - "flags": [ + "tcId" : 39, + "comment" : "using composition for sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304930010230442100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304930010230442100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 40, - "comment": "truncated sequence [r, s]", - "flags": [ + "tcId" : 40, + "comment" : "truncated sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31", + "result" : "invalid" }, { - "tcId": 41, - "comment": "truncated sequence [r, s]", - "flags": [ + "tcId" : 41, + "comment" : "truncated sequence [r, s]", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30442100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30442100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 42, - "comment": "sequence [r, s] of size 4166 to check for overflows", - "flags": [ + "tcId" : 42, + "comment" : "sequence [r, s] of size 4166 to check for overflows", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30821046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30821046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid" }, { - "tcId": 43, - "comment": "indefinite length", - "flags": [ + "tcId" : 43, + "comment" : "indefinite length", + "flags" : [ "BerEncodedSignature" ], - "msg": "313233343030", - "sig": "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" }, { - "tcId": 44, - "comment": "indefinite length with truncated delimiter", - "flags": [ + "tcId" : 44, + "comment" : "indefinite length with truncated delimiter", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba00", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba00", + "result" : "invalid" }, { - "tcId": 45, - "comment": "indefinite length with additional element", - "flags": [ + "tcId" : 45, + "comment" : "indefinite length with additional element", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba05000000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba05000000", + "result" : "invalid" }, { - "tcId": 46, - "comment": "indefinite length with truncated element", - "flags": [ + "tcId" : 46, + "comment" : "indefinite length with truncated element", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba060811220000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba060811220000", + "result" : "invalid" }, { - "tcId": 47, - "comment": "indefinite length with garbage", - "flags": [ + "tcId" : 47, + "comment" : "indefinite length with garbage", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000fe02beef", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000fe02beef", + "result" : "invalid" }, { - "tcId": 48, - "comment": "indefinite length with nonempty EOC", - "flags": [ + "tcId" : 48, + "comment" : "indefinite length with nonempty EOC", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0002beef", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3080022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0002beef", + "result" : "invalid" }, { - "tcId": 49, - "comment": "prepend empty sequence", - "flags": [ + "tcId" : 49, + "comment" : "prepend empty sequence", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "30473000022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30473000022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 50, - "comment": "append empty sequence", - "flags": [ + "tcId" : 50, + "comment" : "append empty sequence", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba3000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba3000", + "result" : "invalid" }, { - "tcId": 51, - "comment": "append zero", - "flags": [ + "tcId" : 51, + "comment" : "append zero", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3048022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3048022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba020100", + "result" : "invalid" }, { - "tcId": 52, - "comment": "append garbage with high tag number", - "flags": [ + "tcId" : 52, + "comment" : "append garbage with high tag number", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3048022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31babf7f00", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3048022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31babf7f00", + "result" : "invalid" }, { - "tcId": 53, - "comment": "append null with explicit tag", - "flags": [ + "tcId" : 53, + "comment" : "append null with explicit tag", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31baa0020500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31baa0020500", + "result" : "invalid" }, { - "tcId": 54, - "comment": "append null with implicit tag", - "flags": [ + "tcId" : 54, + "comment" : "append null with implicit tag", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31baa000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31baa000", + "result" : "invalid" }, { - "tcId": 55, - "comment": "sequence of sequence", - "flags": [ + "tcId" : 55, + "comment" : "sequence of sequence", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "30473045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30473045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 56, - "comment": "truncated sequence: removed last 1 elements", - "flags": [ + "tcId" : 56, + "comment" : "truncated sequence: removed last 1 elements", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3023022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3023022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365", + "result" : "invalid" }, { - "tcId": 57, - "comment": "repeating element in sequence", - "flags": [ + "tcId" : 57, + "comment" : "repeating element in sequence", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3067022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3067022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 58, - "comment": "flipped bit 0 in r", - "flags": [ + "tcId" : 58, + "comment" : "flipped bit 0 in r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236402206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236402206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 59, - "comment": "flipped bit 32 in r", - "flags": [ + "tcId" : 59, + "comment" : "flipped bit 32 in r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccac983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccac983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 60, - "comment": "flipped bit 48 in r", - "flags": [ + "tcId" : 60, + "comment" : "flipped bit 48 in r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5133ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5133ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 61, - "comment": "flipped bit 64 in r", - "flags": [ + "tcId" : 61, + "comment" : "flipped bit 64 in r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc08b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc08b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 62, - "comment": "length of r uses long form encoding", - "flags": [ + "tcId" : 62, + "comment" : "length of r uses long form encoding", + "flags" : [ "BerEncodedSignature" ], - "msg": "313233343030", - "sig": "304602812100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304602812100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 63, - "comment": "length of r contains a leading 0", - "flags": [ + "tcId" : 63, + "comment" : "length of r contains a leading 0", + "flags" : [ "BerEncodedSignature" ], - "msg": "313233343030", - "sig": "30470282002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30470282002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 64, - "comment": "length of r uses 34 instead of 33", - "flags": [ + "tcId" : 64, + "comment" : "length of r uses 34 instead of 33", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022200813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022200813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 65, - "comment": "length of r uses 32 instead of 33", - "flags": [ + "tcId" : 65, + "comment" : "length of r uses 32 instead of 33", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 66, - "comment": "uint32 overflow in length of r", - "flags": [ + "tcId" : 66, + "comment" : "uint32 overflow in length of r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304a0285010000002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304a0285010000002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 67, - "comment": "uint64 overflow in length of r", - "flags": [ + "tcId" : 67, + "comment" : "uint64 overflow in length of r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304e028901000000000000002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304e028901000000000000002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 68, - "comment": "length of r = 2**31 - 1", - "flags": [ + "tcId" : 68, + "comment" : "length of r = 2**31 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304902847fffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304902847fffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 69, - "comment": "length of r = 2**31", - "flags": [ + "tcId" : 69, + "comment" : "length of r = 2**31", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304902848000000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304902848000000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 70, - "comment": "length of r = 2**32 - 1", - "flags": [ + "tcId" : 70, + "comment" : "length of r = 2**32 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30490284ffffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30490284ffffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 71, - "comment": "length of r = 2**40 - 1", - "flags": [ + "tcId" : 71, + "comment" : "length of r = 2**40 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304a0285ffffffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304a0285ffffffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 72, - "comment": "length of r = 2**64 - 1", - "flags": [ + "tcId" : 72, + "comment" : "length of r = 2**64 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304d0288ffffffffffffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304d0288ffffffffffffffff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 73, - "comment": "incorrect length of r", - "flags": [ + "tcId" : 73, + "comment" : "incorrect length of r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304502ff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304502ff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 74, - "comment": "replaced r by an indefinite length tag without termination", - "flags": [ + "tcId" : 74, + "comment" : "replaced r by an indefinite length tag without termination", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045028000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045028000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 75, - "comment": "removing r", - "flags": [ + "tcId" : 75, + "comment" : "removing r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "302202206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "302202206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 76, - "comment": "lonely integer tag", - "flags": [ + "tcId" : 76, + "comment" : "lonely integer tag", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30230202206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30230202206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 77, - "comment": "lonely integer tag", - "flags": [ + "tcId" : 77, + "comment" : "lonely integer tag", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3024022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3024022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502", + "result" : "invalid" }, { - "tcId": 78, - "comment": "appending 0's to r", - "flags": [ + "tcId" : 78, + "comment" : "appending 0's to r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3047022300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 79, - "comment": "prepending 0's to r", - "flags": [ + "tcId" : 79, + "comment" : "prepending 0's to r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30470223000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30470223000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 80, - "comment": "appending unused 0's to r", - "flags": [ + "tcId" : 80, + "comment" : "appending unused 0's to r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 81, - "comment": "appending null value to r", - "flags": [ + "tcId" : 81, + "comment" : "appending null value to r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3047022300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365050002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022300813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365050002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 82, - "comment": "prepending garbage to r", - "flags": [ + "tcId" : 82, + "comment" : "prepending garbage to r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304a2226498177022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304a2226498177022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 83, - "comment": "prepending garbage to r", - "flags": [ + "tcId" : 83, + "comment" : "prepending garbage to r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304922252500022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304922252500022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 84, - "comment": "appending garbage to r", - "flags": [ + "tcId" : 84, + "comment" : "appending garbage to r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304d2223022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650004deadbeef02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304d2223022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650004deadbeef02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 85, - "comment": "truncated length of r", - "flags": [ + "tcId" : 85, + "comment" : "truncated length of r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3024028102206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3024028102206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 86, - "comment": "including undefined tags to r", - "flags": [ + "tcId" : 86, + "comment" : "including undefined tags to r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304b2227aa02aabb022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304b2227aa02aabb022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 87, - "comment": "using composition with indefinite length for r", - "flags": [ + "tcId" : 87, + "comment" : "using composition with indefinite length for r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30492280022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30492280022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 88, - "comment": "using composition with wrong tag for r", - "flags": [ + "tcId" : 88, + "comment" : "using composition with wrong tag for r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "30492280032100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30492280032100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 89, - "comment": "Replacing r with NULL", - "flags": [ + "tcId" : 89, + "comment" : "Replacing r with NULL", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3024050002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3024050002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 90, - "comment": "changing tag value of r", - "flags": [ + "tcId" : 90, + "comment" : "changing tag value of r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3045002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045002100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 91, - "comment": "changing tag value of r", - "flags": [ + "tcId" : 91, + "comment" : "changing tag value of r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045012100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045012100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 92, - "comment": "changing tag value of r", - "flags": [ + "tcId" : 92, + "comment" : "changing tag value of r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3045032100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045032100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 93, - "comment": "changing tag value of r", - "flags": [ + "tcId" : 93, + "comment" : "changing tag value of r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3045042100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045042100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 94, - "comment": "changing tag value of r", - "flags": [ + "tcId" : 94, + "comment" : "changing tag value of r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045ff2100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045ff2100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 95, - "comment": "dropping value of r", - "flags": [ + "tcId" : 95, + "comment" : "dropping value of r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3024020002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3024020002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 96, - "comment": "using composition for r", - "flags": [ + "tcId" : 96, + "comment" : "using composition for r", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304922250201000220813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304922250201000220813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 97, - "comment": "modifying first byte of r", - "flags": [ + "tcId" : 97, + "comment" : "modifying first byte of r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3045022102813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022102813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 98, - "comment": "modifying last byte of r", - "flags": [ + "tcId" : 98, + "comment" : "modifying last byte of r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323e502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323e502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 99, - "comment": "truncated r", - "flags": [ + "tcId" : 99, + "comment" : "truncated r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3044022000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832302206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3044022000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832302206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 100, - "comment": "truncated r", - "flags": [ + "tcId" : 100, + "comment" : "truncated r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "30440220813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30440220813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 101, - "comment": "r of size 4130 to check for overflows", - "flags": [ + "tcId" : 101, + "comment" : "r of size 4130 to check for overflows", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "308210480282102200813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "308210480282102200813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 102, - "comment": "leading ff in r", - "flags": [ + "tcId" : 102, + "comment" : "leading ff in r", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "30460222ff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30460222ff00813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 103, - "comment": "replaced r by infinity", - "flags": [ + "tcId" : 103, + "comment" : "replaced r by infinity", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "302509018002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "302509018002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 104, - "comment": "replacing r with zero", - "flags": [ + "tcId" : 104, + "comment" : "replacing r with zero", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "302502010002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "302502010002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 105, - "comment": "flipped bit 0 in s", - "flags": [ + "tcId" : 105, + "comment" : "flipped bit 0 in s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31bb", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31bb", + "result" : "invalid" }, { - "tcId": 106, - "comment": "flipped bit 32 in s", - "flags": [ + "tcId" : 106, + "comment" : "flipped bit 32 in s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a456eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a456eb31ba", + "result" : "invalid" }, { - "tcId": 107, - "comment": "flipped bit 48 in s", - "flags": [ + "tcId" : 107, + "comment" : "flipped bit 48 in s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f713a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f713a556eb31ba", + "result" : "invalid" }, { - "tcId": 108, - "comment": "flipped bit 64 in s", - "flags": [ + "tcId" : 108, + "comment" : "flipped bit 64 in s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758001d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3043022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323656ff18a52dcc0336f7af62400a6dd9b810732baf1ff758001d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 109, - "comment": "length of s uses long form encoding", - "flags": [ + "tcId" : 109, + "comment" : "length of s uses long form encoding", + "flags" : [ "BerEncodedSignature" ], - "msg": "313233343030", - "sig": "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650281206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650281206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 110, - "comment": "length of s contains a leading 0", - "flags": [ + "tcId" : 110, + "comment" : "length of s contains a leading 0", + "flags" : [ "BerEncodedSignature" ], - "msg": "313233343030", - "sig": "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365028200206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365028200206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 111, - "comment": "length of s uses 33 instead of 32", - "flags": [ + "tcId" : 111, + "comment" : "length of s uses 33 instead of 32", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502216ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502216ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 112, - "comment": "length of s uses 31 instead of 32", - "flags": [ + "tcId" : 112, + "comment" : "length of s uses 31 instead of 32", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365021f6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365021f6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 113, - "comment": "uint32 overflow in length of s", - "flags": [ + "tcId" : 113, + "comment" : "uint32 overflow in length of s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304a022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365028501000000206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304a022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365028501000000206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 114, - "comment": "uint64 overflow in length of s", - "flags": [ + "tcId" : 114, + "comment" : "uint64 overflow in length of s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304e022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502890100000000000000206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304e022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502890100000000000000206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 115, - "comment": "length of s = 2**31 - 1", - "flags": [ + "tcId" : 115, + "comment" : "length of s = 2**31 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502847fffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502847fffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 116, - "comment": "length of s = 2**31", - "flags": [ + "tcId" : 116, + "comment" : "length of s = 2**31", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650284800000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650284800000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 117, - "comment": "length of s = 2**32 - 1", - "flags": [ + "tcId" : 117, + "comment" : "length of s = 2**32 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650284ffffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650284ffffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 118, - "comment": "length of s = 2**40 - 1", - "flags": [ + "tcId" : 118, + "comment" : "length of s = 2**40 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304a022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650285ffffffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304a022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650285ffffffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 119, - "comment": "length of s = 2**64 - 1", - "flags": [ + "tcId" : 119, + "comment" : "length of s = 2**64 - 1", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304d022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650288ffffffffffffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304d022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650288ffffffffffffffff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 120, - "comment": "incorrect length of s", - "flags": [ + "tcId" : 120, + "comment" : "incorrect length of s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502ff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502ff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 121, - "comment": "replaced s by an indefinite length tag without termination", - "flags": [ + "tcId" : 121, + "comment" : "replaced s by an indefinite length tag without termination", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502806ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502806ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 122, - "comment": "appending 0's to s", - "flags": [ + "tcId" : 122, + "comment" : "appending 0's to s", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502226ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502226ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" }, { - "tcId": 123, - "comment": "prepending 0's to s", - "flags": [ + "tcId" : 123, + "comment" : "prepending 0's to s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365022200006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365022200006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 124, - "comment": "appending null value to s", - "flags": [ + "tcId" : 124, + "comment" : "appending null value to s", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502226ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3047022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502226ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0500", + "result" : "invalid" }, { - "tcId": 125, - "comment": "prepending garbage to s", - "flags": [ + "tcId" : 125, + "comment" : "prepending garbage to s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304a022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365222549817702206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304a022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365222549817702206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 126, - "comment": "prepending garbage to s", - "flags": [ + "tcId" : 126, + "comment" : "prepending garbage to s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323652224250002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323652224250002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 127, - "comment": "appending garbage to s", - "flags": [ + "tcId" : 127, + "comment" : "appending garbage to s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304d022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365222202206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0004deadbeef", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304d022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365222202206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0004deadbeef", + "result" : "invalid" }, { - "tcId": 128, - "comment": "truncated length of s", - "flags": [ + "tcId" : 128, + "comment" : "truncated length of s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3025022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650281", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650281", + "result" : "invalid" }, { - "tcId": 129, - "comment": "including undefined tags to s", - "flags": [ + "tcId" : 129, + "comment" : "including undefined tags to s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "304b022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323652226aa02aabb02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304b022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323652226aa02aabb02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 130, - "comment": "using composition with indefinite length for s", - "flags": [ + "tcId" : 130, + "comment" : "using composition with indefinite length for s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365228002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365228002206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" }, { - "tcId": 131, - "comment": "using composition with wrong tag for s", - "flags": [ + "tcId" : 131, + "comment" : "using composition with wrong tag for s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365228003206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365228003206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000", + "result" : "invalid" }, { - "tcId": 132, - "comment": "Replacing s with NULL", - "flags": [ + "tcId" : 132, + "comment" : "Replacing s with NULL", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3025022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650500", + "result" : "invalid" }, { - "tcId": 133, - "comment": "changing tag value of s", - "flags": [ + "tcId" : 133, + "comment" : "changing tag value of s", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236500206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236500206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 134, - "comment": "changing tag value of s", - "flags": [ + "tcId" : 134, + "comment" : "changing tag value of s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236501206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236501206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 135, - "comment": "changing tag value of s", - "flags": [ + "tcId" : 135, + "comment" : "changing tag value of s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236503206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236503206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 136, - "comment": "changing tag value of s", - "flags": [ + "tcId" : 136, + "comment" : "changing tag value of s", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236504206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236504206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 137, - "comment": "changing tag value of s", - "flags": [ + "tcId" : 137, + "comment" : "changing tag value of s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365ff206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365ff206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 138, - "comment": "dropping value of s", - "flags": [ + "tcId" : 138, + "comment" : "dropping value of s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3025022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650200", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650200", + "result" : "invalid" }, { - "tcId": 139, - "comment": "using composition for s", - "flags": [ + "tcId" : 139, + "comment" : "using composition for s", + "flags" : [ "InvalidEncoding" ], - "msg": "313233343030", - "sig": "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365222402016f021ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3049022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365222402016f021ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 140, - "comment": "modifying first byte of s", - "flags": [ + "tcId" : 140, + "comment" : "modifying first byte of s", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206df18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206df18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 141, - "comment": "modifying last byte of s", - "flags": [ + "tcId" : 141, + "comment" : "modifying last byte of s", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb313a", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb313a", + "result" : "invalid" }, { - "tcId": 142, - "comment": "truncated s", - "flags": [ + "tcId" : 142, + "comment" : "truncated s", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365021f6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365021f6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31", + "result" : "invalid" }, { - "tcId": 143, - "comment": "truncated s", - "flags": [ + "tcId" : 143, + "comment" : "truncated s", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365021ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3044022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365021ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 144, - "comment": "s of size 4129 to check for overflows", - "flags": [ + "tcId" : 144, + "comment" : "s of size 4129 to check for overflows", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "30821048022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365028210216ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30821048022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365028210216ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid" }, { - "tcId": 145, - "comment": "leading ff in s", - "flags": [ + "tcId" : 145, + "comment" : "leading ff in s", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650221ff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc98323650221ff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 146, - "comment": "replaced s by infinity", - "flags": [ + "tcId" : 146, + "comment" : "replaced s by infinity", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3026022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365090180", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365090180", + "result" : "invalid" }, { - "tcId": 147, - "comment": "replacing s with zero", - "flags": [ + "tcId" : 147, + "comment" : "replacing s with zero", + "flags" : [ "ModifiedSignature" ], - "msg": "313233343030", - "sig": "3026022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc9832365020100", + "result" : "invalid" }, { - "tcId": 148, - "comment": "replaced r by r + n", - "flags": [ + "tcId" : 148, + "comment" : "replaced r by r + n", + "flags" : [ "RangeCheck" ], - "msg": "313233343030", - "sig": "3045022101813ef79ccefa9a56f7ba805f0e478583b90deabca4b05c4574e49b5899b964a602206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022101813ef79ccefa9a56f7ba805f0e478583b90deabca4b05c4574e49b5899b964a602206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 149, - "comment": "replaced r by r - n", - "flags": [ + "tcId" : 149, + "comment" : "replaced r by r - n", + "flags" : [ "RangeCheck" ], - "msg": "313233343030", - "sig": "30440220813ef79ccefa9a56f7ba805f0e47858643b030ef461f1bcdf53fde3ef94ce22402206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30440220813ef79ccefa9a56f7ba805f0e47858643b030ef461f1bcdf53fde3ef94ce22402206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 150, - "comment": "replaced r by r + 256 * n", - "flags": [ + "tcId" : 150, + "comment" : "replaced r by r + 256 * n", + "flags" : [ "RangeCheck" ], - "msg": "313233343030", - "sig": "304602220100813ef79ccefa9a56f7ba805f0e47843fad3bf4853e07f7c98770c99bffc4646502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304602220100813ef79ccefa9a56f7ba805f0e47843fad3bf4853e07f7c98770c99bffc4646502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 151, - "comment": "replaced r by -r", - "flags": [ + "tcId" : 151, + "comment" : "replaced r by -r", + "flags" : [ "ModifiedInteger" ], - "msg": "313233343030", - "sig": "30450221ff7ec10863310565a908457fa0f1b87a7b01a0f22a0a9843f64aedc334367cdc9b02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30450221ff7ec10863310565a908457fa0f1b87a7b01a0f22a0a9843f64aedc334367cdc9b02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 152, - "comment": "replaced r by n - r", - "flags": [ + "tcId" : 152, + "comment" : "replaced r by n - r", + "flags" : [ "ModifiedInteger" ], - "msg": "313233343030", - "sig": "304402207ec10863310565a908457fa0f1b87a79bc4fcf10b9e0e4320ac021c106b31ddc02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304402207ec10863310565a908457fa0f1b87a79bc4fcf10b9e0e4320ac021c106b31ddc02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 153, - "comment": "replaced r by -n - r", - "flags": [ + "tcId" : 153, + "comment" : "replaced r by -n - r", + "flags" : [ "ModifiedInteger" ], - "msg": "313233343030", - "sig": "30450221fe7ec10863310565a908457fa0f1b87a7c46f215435b4fa3ba8b1b64a766469b5a02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30450221fe7ec10863310565a908457fa0f1b87a7c46f215435b4fa3ba8b1b64a766469b5a02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 154, - "comment": "replaced r by r + 2**256", - "flags": [ + "tcId" : 154, + "comment" : "replaced r by r + 2**256", + "flags" : [ "IntegerOverflow" ], - "msg": "313233343030", - "sig": "3045022101813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022101813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 155, - "comment": "replaced r by r + 2**320", - "flags": [ + "tcId" : 155, + "comment" : "replaced r by r + 2**320", + "flags" : [ "IntegerOverflow" ], - "msg": "313233343030", - "sig": "304d0229010000000000000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304d0229010000000000000000813ef79ccefa9a56f7ba805f0e478584fe5f0dd5f567bc09b5123ccbc983236502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 156, - "comment": "replaced s by s + n", - "flags": [ + "tcId" : 156, + "comment" : "replaced s by s + n", + "flags" : [ "RangeCheck" ], - "msg": "313233343030", - "sig": "30450221016ff18a52dcc0336f7af62400a6dd9b7fc1e197d8aebe203c96c87232272172fb02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30450221016ff18a52dcc0336f7af62400a6dd9b7fc1e197d8aebe203c96c87232272172fb02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 157, - "comment": "replaced s by s - n", - "flags": [ + "tcId" : 157, + "comment" : "replaced s by s - n", + "flags" : [ "RangeCheck" ], - "msg": "313233343030", - "sig": "30450221ff6ff18a52dcc0336f7af62400a6dd9b824c83de0b502cdfc51723b51886b4f07902206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30450221ff6ff18a52dcc0336f7af62400a6dd9b824c83de0b502cdfc51723b51886b4f07902206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 158, - "comment": "replaced s by s + 256 * n", - "flags": [ + "tcId" : 158, + "comment" : "replaced s by s + 256 * n", + "flags" : [ "RangeCheck" ], - "msg": "313233343030", - "sig": "3046022201006ff18a52dcc0336f7af62400a6dd9a3bb60fa1a14815bbc0a954a0758d2c72ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022201006ff18a52dcc0336f7af62400a6dd9a3bb60fa1a14815bbc0a954a0758d2c72ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 159, - "comment": "replaced s by -s", - "flags": [ + "tcId" : 159, + "comment" : "replaced s by -s", + "flags" : [ "ModifiedInteger" ], - "msg": "313233343030", - "sig": "30440220900e75ad233fcc908509dbff5922647ef8cd450e008a7fff2909ec5aa914ce4602206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30440220900e75ad233fcc908509dbff5922647ef8cd450e008a7fff2909ec5aa914ce4602206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 160, - "comment": "replaced s by -n - s", - "flags": [ + "tcId" : 160, + "comment" : "replaced s by -n - s", + "flags" : [ "ModifiedInteger" ], - "msg": "313233343030", - "sig": "30450221fe900e75ad233fcc908509dbff592264803e1e68275141dfc369378dcdd8de8d0502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30450221fe900e75ad233fcc908509dbff592264803e1e68275141dfc369378dcdd8de8d0502206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 161, - "comment": "replaced s by s + 2**256", - "flags": [ + "tcId" : 161, + "comment" : "replaced s by s + 2**256", + "flags" : [ "IntegerOverflow" ], - "msg": "313233343030", - "sig": "30450221016ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30450221016ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 162, - "comment": "replaced s by s - 2**256", - "flags": [ + "tcId" : 162, + "comment" : "replaced s by s - 2**256", + "flags" : [ "IntegerOverflow" ], - "msg": "313233343030", - "sig": "30450221ff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30450221ff6ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 163, - "comment": "replaced s by s + 2**320", - "flags": [ + "tcId" : 163, + "comment" : "replaced s by s + 2**320", + "flags" : [ "IntegerOverflow" ], - "msg": "313233343030", - "sig": "304d02290100000000000000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304d02290100000000000000006ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba02206ff18a52dcc0336f7af62400a6dd9b810732baf1ff758000d6f613a556eb31ba", + "result" : "invalid" }, { - "tcId": 164, - "comment": "Signature with special case values r=0 and s=0", - "flags": [ + "tcId" : 164, + "comment" : "Signature with special case values r=0 and s=0", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3006020100020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020100020100", + "result" : "invalid" }, { - "tcId": 165, - "comment": "Signature with special case values r=0 and s=1", - "flags": [ + "tcId" : 165, + "comment" : "Signature with special case values r=0 and s=1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3006020100020101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020100020101", + "result" : "invalid" }, { - "tcId": 166, - "comment": "Signature with special case values r=0 and s=-1", - "flags": [ + "tcId" : 166, + "comment" : "Signature with special case values r=0 and s=-1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30060201000201ff", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201000201ff", + "result" : "invalid" }, { - "tcId": 167, - "comment": "Signature with special case values r=0 and s=n", - "flags": [ + "tcId" : 167, + "comment" : "Signature with special case values r=0 and s=n", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020100022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" }, { - "tcId": 168, - "comment": "Signature with special case values r=0 and s=n - 1", - "flags": [ + "tcId" : 168, + "comment" : "Signature with special case values r=0 and s=n - 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020100022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" }, { - "tcId": 169, - "comment": "Signature with special case values r=0 and s=n + 1", - "flags": [ + "tcId" : 169, + "comment" : "Signature with special case values r=0 and s=n + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020100022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" }, { - "tcId": 170, - "comment": "Signature with special case values r=0 and s=p", - "flags": [ + "tcId" : 170, + "comment" : "Signature with special case values r=0 and s=p", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020100022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" }, { - "tcId": 171, - "comment": "Signature with special case values r=0 and s=p + 1", - "flags": [ + "tcId" : 171, + "comment" : "Signature with special case values r=0 and s=p + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020100022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020100022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" }, { - "tcId": 172, - "comment": "Signature with special case values r=1 and s=0", - "flags": [ + "tcId" : 172, + "comment" : "Signature with special case values r=1 and s=0", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3006020101020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020101020100", + "result" : "invalid" }, { - "tcId": 173, - "comment": "Signature with special case values r=1 and s=1", - "flags": [ + "tcId" : 173, + "comment" : "Signature with special case values r=1 and s=1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3006020101020101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020101020101", + "result" : "invalid" }, { - "tcId": 174, - "comment": "Signature with special case values r=1 and s=-1", - "flags": [ + "tcId" : 174, + "comment" : "Signature with special case values r=1 and s=-1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30060201010201ff", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201010201ff", + "result" : "invalid" }, { - "tcId": 175, - "comment": "Signature with special case values r=1 and s=n", - "flags": [ + "tcId" : 175, + "comment" : "Signature with special case values r=1 and s=n", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" }, { - "tcId": 176, - "comment": "Signature with special case values r=1 and s=n - 1", - "flags": [ + "tcId" : 176, + "comment" : "Signature with special case values r=1 and s=n - 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" }, { - "tcId": 177, - "comment": "Signature with special case values r=1 and s=n + 1", - "flags": [ + "tcId" : 177, + "comment" : "Signature with special case values r=1 and s=n + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" }, { - "tcId": 178, - "comment": "Signature with special case values r=1 and s=p", - "flags": [ + "tcId" : 178, + "comment" : "Signature with special case values r=1 and s=p", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" }, { - "tcId": 179, - "comment": "Signature with special case values r=1 and s=p + 1", - "flags": [ + "tcId" : 179, + "comment" : "Signature with special case values r=1 and s=p + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026020101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026020101022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" }, { - "tcId": 180, - "comment": "Signature with special case values r=-1 and s=0", - "flags": [ + "tcId" : 180, + "comment" : "Signature with special case values r=-1 and s=0", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30060201ff020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201ff020100", + "result" : "invalid" }, { - "tcId": 181, - "comment": "Signature with special case values r=-1 and s=1", - "flags": [ + "tcId" : 181, + "comment" : "Signature with special case values r=-1 and s=1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30060201ff020101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201ff020101", + "result" : "invalid" }, { - "tcId": 182, - "comment": "Signature with special case values r=-1 and s=-1", - "flags": [ + "tcId" : 182, + "comment" : "Signature with special case values r=-1 and s=-1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30060201ff0201ff", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201ff0201ff", + "result" : "invalid" }, { - "tcId": 183, - "comment": "Signature with special case values r=-1 and s=n", - "flags": [ + "tcId" : 183, + "comment" : "Signature with special case values r=-1 and s=n", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30260201ff022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" }, { - "tcId": 184, - "comment": "Signature with special case values r=-1 and s=n - 1", - "flags": [ + "tcId" : 184, + "comment" : "Signature with special case values r=-1 and s=n - 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30260201ff022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" }, { - "tcId": 185, - "comment": "Signature with special case values r=-1 and s=n + 1", - "flags": [ + "tcId" : 185, + "comment" : "Signature with special case values r=-1 and s=n + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30260201ff022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" }, { - "tcId": 186, - "comment": "Signature with special case values r=-1 and s=p", - "flags": [ + "tcId" : 186, + "comment" : "Signature with special case values r=-1 and s=p", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30260201ff022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" }, { - "tcId": 187, - "comment": "Signature with special case values r=-1 and s=p + 1", - "flags": [ + "tcId" : 187, + "comment" : "Signature with special case values r=-1 and s=p + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "30260201ff022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30260201ff022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" }, { - "tcId": 188, - "comment": "Signature with special case values r=n and s=0", - "flags": [ + "tcId" : 188, + "comment" : "Signature with special case values r=n and s=0", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020100", + "result" : "invalid" }, { - "tcId": 189, - "comment": "Signature with special case values r=n and s=1", - "flags": [ + "tcId" : 189, + "comment" : "Signature with special case values r=n and s=1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141020101", + "result" : "invalid" }, { - "tcId": 190, - "comment": "Signature with special case values r=n and s=-1", - "flags": [ + "tcId" : 190, + "comment" : "Signature with special case values r=n and s=-1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410201ff", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410201ff", + "result" : "invalid" }, { - "tcId": 191, - "comment": "Signature with special case values r=n and s=n", - "flags": [ + "tcId" : 191, + "comment" : "Signature with special case values r=n and s=n", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" }, { - "tcId": 192, - "comment": "Signature with special case values r=n and s=n - 1", - "flags": [ + "tcId" : 192, + "comment" : "Signature with special case values r=n and s=n - 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" }, { - "tcId": 193, - "comment": "Signature with special case values r=n and s=n + 1", - "flags": [ + "tcId" : 193, + "comment" : "Signature with special case values r=n and s=n + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" }, { - "tcId": 194, - "comment": "Signature with special case values r=n and s=p", - "flags": [ + "tcId" : 194, + "comment" : "Signature with special case values r=n and s=p", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" }, { - "tcId": 195, - "comment": "Signature with special case values r=n and s=p + 1", - "flags": [ + "tcId" : 195, + "comment" : "Signature with special case values r=n and s=p + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" }, { - "tcId": 196, - "comment": "Signature with special case values r=n - 1 and s=0", - "flags": [ + "tcId" : 196, + "comment" : "Signature with special case values r=n - 1 and s=0", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140020100", + "result" : "invalid" }, { - "tcId": 197, - "comment": "Signature with special case values r=n - 1 and s=1", - "flags": [ + "tcId" : 197, + "comment" : "Signature with special case values r=n - 1 and s=1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140020101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140020101", + "result" : "invalid" }, { - "tcId": 198, - "comment": "Signature with special case values r=n - 1 and s=-1", - "flags": [ + "tcId" : 198, + "comment" : "Signature with special case values r=n - 1 and s=-1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641400201ff", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641400201ff", + "result" : "invalid" }, { - "tcId": 199, - "comment": "Signature with special case values r=n - 1 and s=n", - "flags": [ + "tcId" : 199, + "comment" : "Signature with special case values r=n - 1 and s=n", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" }, { - "tcId": 200, - "comment": "Signature with special case values r=n - 1 and s=n - 1", - "flags": [ + "tcId" : 200, + "comment" : "Signature with special case values r=n - 1 and s=n - 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" }, { - "tcId": 201, - "comment": "Signature with special case values r=n - 1 and s=n + 1", - "flags": [ + "tcId" : 201, + "comment" : "Signature with special case values r=n - 1 and s=n + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" }, { - "tcId": 202, - "comment": "Signature with special case values r=n - 1 and s=p", - "flags": [ + "tcId" : 202, + "comment" : "Signature with special case values r=n - 1 and s=p", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" }, { - "tcId": 203, - "comment": "Signature with special case values r=n - 1 and s=p + 1", - "flags": [ + "tcId" : 203, + "comment" : "Signature with special case values r=n - 1 and s=p + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" }, { - "tcId": 204, - "comment": "Signature with special case values r=n + 1 and s=0", - "flags": [ + "tcId" : 204, + "comment" : "Signature with special case values r=n + 1 and s=0", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142020100", + "result" : "invalid" }, { - "tcId": 205, - "comment": "Signature with special case values r=n + 1 and s=1", - "flags": [ + "tcId" : 205, + "comment" : "Signature with special case values r=n + 1 and s=1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142020101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142020101", + "result" : "invalid" }, { - "tcId": 206, - "comment": "Signature with special case values r=n + 1 and s=-1", - "flags": [ + "tcId" : 206, + "comment" : "Signature with special case values r=n + 1 and s=-1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641420201ff", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641420201ff", + "result" : "invalid" }, { - "tcId": 207, - "comment": "Signature with special case values r=n + 1 and s=n", - "flags": [ + "tcId" : 207, + "comment" : "Signature with special case values r=n + 1 and s=n", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" }, { - "tcId": 208, - "comment": "Signature with special case values r=n + 1 and s=n - 1", - "flags": [ + "tcId" : 208, + "comment" : "Signature with special case values r=n + 1 and s=n - 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" }, { - "tcId": 209, - "comment": "Signature with special case values r=n + 1 and s=n + 1", - "flags": [ + "tcId" : 209, + "comment" : "Signature with special case values r=n + 1 and s=n + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" }, { - "tcId": 210, - "comment": "Signature with special case values r=n + 1 and s=p", - "flags": [ + "tcId" : 210, + "comment" : "Signature with special case values r=n + 1 and s=p", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" }, { - "tcId": 211, - "comment": "Signature with special case values r=n + 1 and s=p + 1", - "flags": [ + "tcId" : 211, + "comment" : "Signature with special case values r=n + 1 and s=p + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" }, { - "tcId": 212, - "comment": "Signature with special case values r=p and s=0", - "flags": [ + "tcId" : 212, + "comment" : "Signature with special case values r=p and s=0", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f020100", + "result" : "invalid" }, { - "tcId": 213, - "comment": "Signature with special case values r=p and s=1", - "flags": [ + "tcId" : 213, + "comment" : "Signature with special case values r=p and s=1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f020101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f020101", + "result" : "invalid" }, { - "tcId": 214, - "comment": "Signature with special case values r=p and s=-1", - "flags": [ + "tcId" : 214, + "comment" : "Signature with special case values r=p and s=-1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0201ff", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0201ff", + "result" : "invalid" }, { - "tcId": 215, - "comment": "Signature with special case values r=p and s=n", - "flags": [ + "tcId" : 215, + "comment" : "Signature with special case values r=p and s=n", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" }, { - "tcId": 216, - "comment": "Signature with special case values r=p and s=n - 1", - "flags": [ + "tcId" : 216, + "comment" : "Signature with special case values r=p and s=n - 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" }, { - "tcId": 217, - "comment": "Signature with special case values r=p and s=n + 1", - "flags": [ + "tcId" : 217, + "comment" : "Signature with special case values r=p and s=n + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" }, { - "tcId": 218, - "comment": "Signature with special case values r=p and s=p", - "flags": [ + "tcId" : 218, + "comment" : "Signature with special case values r=p and s=p", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" }, { - "tcId": 219, - "comment": "Signature with special case values r=p and s=p + 1", - "flags": [ + "tcId" : 219, + "comment" : "Signature with special case values r=p and s=p + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" }, { - "tcId": 220, - "comment": "Signature with special case values r=p + 1 and s=0", - "flags": [ + "tcId" : 220, + "comment" : "Signature with special case values r=p + 1 and s=0", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30020100", + "result" : "invalid" }, { - "tcId": 221, - "comment": "Signature with special case values r=p + 1 and s=1", - "flags": [ + "tcId" : 221, + "comment" : "Signature with special case values r=p + 1 and s=1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30020101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30020101", + "result" : "invalid" }, { - "tcId": 222, - "comment": "Signature with special case values r=p + 1 and s=-1", - "flags": [ + "tcId" : 222, + "comment" : "Signature with special case values r=p + 1 and s=-1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc300201ff", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc300201ff", + "result" : "invalid" }, { - "tcId": 223, - "comment": "Signature with special case values r=p + 1 and s=n", - "flags": [ + "tcId" : 223, + "comment" : "Signature with special case values r=p + 1 and s=n", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", + "result" : "invalid" }, { - "tcId": 224, - "comment": "Signature with special case values r=p + 1 and s=n - 1", - "flags": [ + "tcId" : 224, + "comment" : "Signature with special case values r=p + 1 and s=n - 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140", + "result" : "invalid" }, { - "tcId": 225, - "comment": "Signature with special case values r=p + 1 and s=n + 1", - "flags": [ + "tcId" : 225, + "comment" : "Signature with special case values r=p + 1 and s=n + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364142", + "result" : "invalid" }, { - "tcId": 226, - "comment": "Signature with special case values r=p + 1 and s=p", - "flags": [ + "tcId" : 226, + "comment" : "Signature with special case values r=p + 1 and s=p", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", + "result" : "invalid" }, { - "tcId": 227, - "comment": "Signature with special case values r=p + 1 and s=p + 1", - "flags": [ + "tcId" : 227, + "comment" : "Signature with special case values r=p + 1 and s=p + 1", + "flags" : [ "InvalidSignature" ], - "msg": "313233343030", - "sig": "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3046022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30", + "result" : "invalid" }, { - "tcId": 228, - "comment": "Signature encoding contains incorrect types: r=0, s=0.25", - "flags": [ + "tcId" : 228, + "comment" : "Signature encoding contains incorrect types: r=0, s=0.25", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3008020100090380fe01", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3008020100090380fe01", + "result" : "invalid" }, { - "tcId": 229, - "comment": "Signature encoding contains incorrect types: r=0, s=nan", - "flags": [ + "tcId" : 229, + "comment" : "Signature encoding contains incorrect types: r=0, s=nan", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006020100090142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020100090142", + "result" : "invalid" }, { - "tcId": 230, - "comment": "Signature encoding contains incorrect types: r=0, s=True", - "flags": [ + "tcId" : 230, + "comment" : "Signature encoding contains incorrect types: r=0, s=True", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006020100010101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020100010101", + "result" : "invalid" }, { - "tcId": 231, - "comment": "Signature encoding contains incorrect types: r=0, s=False", - "flags": [ + "tcId" : 231, + "comment" : "Signature encoding contains incorrect types: r=0, s=False", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006020100010100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020100010100", + "result" : "invalid" }, { - "tcId": 232, - "comment": "Signature encoding contains incorrect types: r=0, s=Null", - "flags": [ + "tcId" : 232, + "comment" : "Signature encoding contains incorrect types: r=0, s=Null", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050201000500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050201000500", + "result" : "invalid" }, { - "tcId": 233, - "comment": "Signature encoding contains incorrect types: r=0, s=empyt UTF-8 string", - "flags": [ + "tcId" : 233, + "comment" : "Signature encoding contains incorrect types: r=0, s=empyt UTF-8 string", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050201000c00", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050201000c00", + "result" : "invalid" }, { - "tcId": 234, - "comment": "Signature encoding contains incorrect types: r=0, s=\"0\"", - "flags": [ + "tcId" : 234, + "comment" : "Signature encoding contains incorrect types: r=0, s=\"0\"", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30060201000c0130", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201000c0130", + "result" : "invalid" }, { - "tcId": 235, - "comment": "Signature encoding contains incorrect types: r=0, s=empty list", - "flags": [ + "tcId" : 235, + "comment" : "Signature encoding contains incorrect types: r=0, s=empty list", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050201003000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050201003000", + "result" : "invalid" }, { - "tcId": 236, - "comment": "Signature encoding contains incorrect types: r=0, s=list containing 0", - "flags": [ + "tcId" : 236, + "comment" : "Signature encoding contains incorrect types: r=0, s=list containing 0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30080201003003020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30080201003003020100", + "result" : "invalid" }, { - "tcId": 237, - "comment": "Signature encoding contains incorrect types: r=1, s=0.25", - "flags": [ + "tcId" : 237, + "comment" : "Signature encoding contains incorrect types: r=1, s=0.25", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3008020101090380fe01", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3008020101090380fe01", + "result" : "invalid" }, { - "tcId": 238, - "comment": "Signature encoding contains incorrect types: r=1, s=nan", - "flags": [ + "tcId" : 238, + "comment" : "Signature encoding contains incorrect types: r=1, s=nan", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006020101090142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020101090142", + "result" : "invalid" }, { - "tcId": 239, - "comment": "Signature encoding contains incorrect types: r=1, s=True", - "flags": [ + "tcId" : 239, + "comment" : "Signature encoding contains incorrect types: r=1, s=True", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006020101010101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020101010101", + "result" : "invalid" }, { - "tcId": 240, - "comment": "Signature encoding contains incorrect types: r=1, s=False", - "flags": [ + "tcId" : 240, + "comment" : "Signature encoding contains incorrect types: r=1, s=False", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006020101010100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006020101010100", + "result" : "invalid" }, { - "tcId": 241, - "comment": "Signature encoding contains incorrect types: r=1, s=Null", - "flags": [ + "tcId" : 241, + "comment" : "Signature encoding contains incorrect types: r=1, s=Null", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050201010500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050201010500", + "result" : "invalid" }, { - "tcId": 242, - "comment": "Signature encoding contains incorrect types: r=1, s=empyt UTF-8 string", - "flags": [ + "tcId" : 242, + "comment" : "Signature encoding contains incorrect types: r=1, s=empyt UTF-8 string", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050201010c00", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050201010c00", + "result" : "invalid" }, { - "tcId": 243, - "comment": "Signature encoding contains incorrect types: r=1, s=\"0\"", - "flags": [ + "tcId" : 243, + "comment" : "Signature encoding contains incorrect types: r=1, s=\"0\"", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30060201010c0130", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201010c0130", + "result" : "invalid" }, { - "tcId": 244, - "comment": "Signature encoding contains incorrect types: r=1, s=empty list", - "flags": [ + "tcId" : 244, + "comment" : "Signature encoding contains incorrect types: r=1, s=empty list", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050201013000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050201013000", + "result" : "invalid" }, { - "tcId": 245, - "comment": "Signature encoding contains incorrect types: r=1, s=list containing 0", - "flags": [ + "tcId" : 245, + "comment" : "Signature encoding contains incorrect types: r=1, s=list containing 0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30080201013003020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30080201013003020100", + "result" : "invalid" }, { - "tcId": 246, - "comment": "Signature encoding contains incorrect types: r=-1, s=0.25", - "flags": [ + "tcId" : 246, + "comment" : "Signature encoding contains incorrect types: r=-1, s=0.25", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30080201ff090380fe01", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30080201ff090380fe01", + "result" : "invalid" }, { - "tcId": 247, - "comment": "Signature encoding contains incorrect types: r=-1, s=nan", - "flags": [ + "tcId" : 247, + "comment" : "Signature encoding contains incorrect types: r=-1, s=nan", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30060201ff090142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201ff090142", + "result" : "invalid" }, { - "tcId": 248, - "comment": "Signature encoding contains incorrect types: r=-1, s=True", - "flags": [ + "tcId" : 248, + "comment" : "Signature encoding contains incorrect types: r=-1, s=True", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30060201ff010101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201ff010101", + "result" : "invalid" }, { - "tcId": 249, - "comment": "Signature encoding contains incorrect types: r=-1, s=False", - "flags": [ + "tcId" : 249, + "comment" : "Signature encoding contains incorrect types: r=-1, s=False", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30060201ff010100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201ff010100", + "result" : "invalid" }, { - "tcId": 250, - "comment": "Signature encoding contains incorrect types: r=-1, s=Null", - "flags": [ + "tcId" : 250, + "comment" : "Signature encoding contains incorrect types: r=-1, s=Null", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050201ff0500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050201ff0500", + "result" : "invalid" }, { - "tcId": 251, - "comment": "Signature encoding contains incorrect types: r=-1, s=empyt UTF-8 string", - "flags": [ + "tcId" : 251, + "comment" : "Signature encoding contains incorrect types: r=-1, s=empyt UTF-8 string", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050201ff0c00", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050201ff0c00", + "result" : "invalid" }, { - "tcId": 252, - "comment": "Signature encoding contains incorrect types: r=-1, s=\"0\"", - "flags": [ + "tcId" : 252, + "comment" : "Signature encoding contains incorrect types: r=-1, s=\"0\"", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30060201ff0c0130", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060201ff0c0130", + "result" : "invalid" }, { - "tcId": 253, - "comment": "Signature encoding contains incorrect types: r=-1, s=empty list", - "flags": [ + "tcId" : 253, + "comment" : "Signature encoding contains incorrect types: r=-1, s=empty list", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050201ff3000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050201ff3000", + "result" : "invalid" }, { - "tcId": 254, - "comment": "Signature encoding contains incorrect types: r=-1, s=list containing 0", - "flags": [ + "tcId" : 254, + "comment" : "Signature encoding contains incorrect types: r=-1, s=list containing 0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30080201ff3003020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30080201ff3003020100", + "result" : "invalid" }, { - "tcId": 255, - "comment": "Signature encoding contains incorrect types: r=n, s=0.25", - "flags": [ + "tcId" : 255, + "comment" : "Signature encoding contains incorrect types: r=n, s=0.25", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3028022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141090380fe01", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3028022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141090380fe01", + "result" : "invalid" }, { - "tcId": 256, - "comment": "Signature encoding contains incorrect types: r=n, s=nan", - "flags": [ + "tcId" : 256, + "comment" : "Signature encoding contains incorrect types: r=n, s=nan", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141090142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141090142", + "result" : "invalid" }, { - "tcId": 257, - "comment": "Signature encoding contains incorrect types: r=n, s=True", - "flags": [ + "tcId" : 257, + "comment" : "Signature encoding contains incorrect types: r=n, s=True", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141010101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141010101", + "result" : "invalid" }, { - "tcId": 258, - "comment": "Signature encoding contains incorrect types: r=n, s=False", - "flags": [ + "tcId" : 258, + "comment" : "Signature encoding contains incorrect types: r=n, s=False", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141010100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141010100", + "result" : "invalid" }, { - "tcId": 259, - "comment": "Signature encoding contains incorrect types: r=n, s=Null", - "flags": [ + "tcId" : 259, + "comment" : "Signature encoding contains incorrect types: r=n, s=Null", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3025022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410500", + "result" : "invalid" }, { - "tcId": 260, - "comment": "Signature encoding contains incorrect types: r=n, s=empyt UTF-8 string", - "flags": [ + "tcId" : 260, + "comment" : "Signature encoding contains incorrect types: r=n, s=empyt UTF-8 string", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3025022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410c00", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410c00", + "result" : "invalid" }, { - "tcId": 261, - "comment": "Signature encoding contains incorrect types: r=n, s=\"0\"", - "flags": [ + "tcId" : 261, + "comment" : "Signature encoding contains incorrect types: r=n, s=\"0\"", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410c0130", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641410c0130", + "result" : "invalid" }, { - "tcId": 262, - "comment": "Signature encoding contains incorrect types: r=n, s=empty list", - "flags": [ + "tcId" : 262, + "comment" : "Signature encoding contains incorrect types: r=n, s=empty list", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3025022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641413000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641413000", + "result" : "invalid" }, { - "tcId": 263, - "comment": "Signature encoding contains incorrect types: r=n, s=list containing 0", - "flags": [ + "tcId" : 263, + "comment" : "Signature encoding contains incorrect types: r=n, s=list containing 0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3028022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641413003020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3028022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641413003020100", + "result" : "invalid" }, { - "tcId": 264, - "comment": "Signature encoding contains incorrect types: r=p, s=0.25", - "flags": [ + "tcId" : 264, + "comment" : "Signature encoding contains incorrect types: r=p, s=0.25", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3028022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f090380fe01", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3028022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f090380fe01", + "result" : "invalid" }, { - "tcId": 265, - "comment": "Signature encoding contains incorrect types: r=p, s=nan", - "flags": [ + "tcId" : 265, + "comment" : "Signature encoding contains incorrect types: r=p, s=nan", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f090142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f090142", + "result" : "invalid" }, { - "tcId": 266, - "comment": "Signature encoding contains incorrect types: r=p, s=True", - "flags": [ + "tcId" : 266, + "comment" : "Signature encoding contains incorrect types: r=p, s=True", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f010101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f010101", + "result" : "invalid" }, { - "tcId": 267, - "comment": "Signature encoding contains incorrect types: r=p, s=False", - "flags": [ + "tcId" : 267, + "comment" : "Signature encoding contains incorrect types: r=p, s=False", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f010100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f010100", + "result" : "invalid" }, { - "tcId": 268, - "comment": "Signature encoding contains incorrect types: r=p, s=Null", - "flags": [ + "tcId" : 268, + "comment" : "Signature encoding contains incorrect types: r=p, s=Null", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3025022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0500", + "result" : "invalid" }, { - "tcId": 269, - "comment": "Signature encoding contains incorrect types: r=p, s=empyt UTF-8 string", - "flags": [ + "tcId" : 269, + "comment" : "Signature encoding contains incorrect types: r=p, s=empyt UTF-8 string", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3025022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0c00", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0c00", + "result" : "invalid" }, { - "tcId": 270, - "comment": "Signature encoding contains incorrect types: r=p, s=\"0\"", - "flags": [ + "tcId" : 270, + "comment" : "Signature encoding contains incorrect types: r=p, s=\"0\"", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0c0130", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f0c0130", + "result" : "invalid" }, { - "tcId": 271, - "comment": "Signature encoding contains incorrect types: r=p, s=empty list", - "flags": [ + "tcId" : 271, + "comment" : "Signature encoding contains incorrect types: r=p, s=empty list", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3025022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f3000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f3000", + "result" : "invalid" }, { - "tcId": 272, - "comment": "Signature encoding contains incorrect types: r=p, s=list containing 0", - "flags": [ + "tcId" : 272, + "comment" : "Signature encoding contains incorrect types: r=p, s=list containing 0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3028022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f3003020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3028022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f3003020100", + "result" : "invalid" }, { - "tcId": 273, - "comment": "Signature encoding contains incorrect types: r=0.25, s=0.25", - "flags": [ + "tcId" : 273, + "comment" : "Signature encoding contains incorrect types: r=0.25, s=0.25", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "300a090380fe01090380fe01", - "result": "invalid" + "msg" : "313233343030", + "sig" : "300a090380fe01090380fe01", + "result" : "invalid" }, { - "tcId": 274, - "comment": "Signature encoding contains incorrect types: r=nan, s=nan", - "flags": [ + "tcId" : 274, + "comment" : "Signature encoding contains incorrect types: r=nan, s=nan", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006090142090142", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006090142090142", + "result" : "invalid" }, { - "tcId": 275, - "comment": "Signature encoding contains incorrect types: r=True, s=True", - "flags": [ + "tcId" : 275, + "comment" : "Signature encoding contains incorrect types: r=True, s=True", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006010101010101", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006010101010101", + "result" : "invalid" }, { - "tcId": 276, - "comment": "Signature encoding contains incorrect types: r=False, s=False", - "flags": [ + "tcId" : 276, + "comment" : "Signature encoding contains incorrect types: r=False, s=False", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006010100010100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006010100010100", + "result" : "invalid" }, { - "tcId": 277, - "comment": "Signature encoding contains incorrect types: r=Null, s=Null", - "flags": [ + "tcId" : 277, + "comment" : "Signature encoding contains incorrect types: r=Null, s=Null", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "300405000500", - "result": "invalid" + "msg" : "313233343030", + "sig" : "300405000500", + "result" : "invalid" }, { - "tcId": 278, - "comment": "Signature encoding contains incorrect types: r=empyt UTF-8 string, s=empyt UTF-8 string", - "flags": [ + "tcId" : 278, + "comment" : "Signature encoding contains incorrect types: r=empyt UTF-8 string, s=empyt UTF-8 string", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30040c000c00", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30040c000c00", + "result" : "invalid" }, { - "tcId": 279, - "comment": "Signature encoding contains incorrect types: r=\"0\", s=\"0\"", - "flags": [ + "tcId" : 279, + "comment" : "Signature encoding contains incorrect types: r=\"0\", s=\"0\"", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30060c01300c0130", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060c01300c0130", + "result" : "invalid" }, { - "tcId": 280, - "comment": "Signature encoding contains incorrect types: r=empty list, s=empty list", - "flags": [ + "tcId" : 280, + "comment" : "Signature encoding contains incorrect types: r=empty list, s=empty list", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "300430003000", - "result": "invalid" + "msg" : "313233343030", + "sig" : "300430003000", + "result" : "invalid" }, { - "tcId": 281, - "comment": "Signature encoding contains incorrect types: r=list containing 0, s=list containing 0", - "flags": [ + "tcId" : 281, + "comment" : "Signature encoding contains incorrect types: r=list containing 0, s=list containing 0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "300a30030201003003020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "300a30030201003003020100", + "result" : "invalid" }, { - "tcId": 282, - "comment": "Signature encoding contains incorrect types: r=0.25, s=0", - "flags": [ + "tcId" : 282, + "comment" : "Signature encoding contains incorrect types: r=0.25, s=0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3008090380fe01020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3008090380fe01020100", + "result" : "invalid" }, { - "tcId": 283, - "comment": "Signature encoding contains incorrect types: r=nan, s=0", - "flags": [ + "tcId" : 283, + "comment" : "Signature encoding contains incorrect types: r=nan, s=0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006090142020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006090142020100", + "result" : "invalid" }, { - "tcId": 284, - "comment": "Signature encoding contains incorrect types: r=True, s=0", - "flags": [ + "tcId" : 284, + "comment" : "Signature encoding contains incorrect types: r=True, s=0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006010101020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006010101020100", + "result" : "invalid" }, { - "tcId": 285, - "comment": "Signature encoding contains incorrect types: r=False, s=0", - "flags": [ + "tcId" : 285, + "comment" : "Signature encoding contains incorrect types: r=False, s=0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "3006010100020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3006010100020100", + "result" : "invalid" }, { - "tcId": 286, - "comment": "Signature encoding contains incorrect types: r=Null, s=0", - "flags": [ + "tcId" : 286, + "comment" : "Signature encoding contains incorrect types: r=Null, s=0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050500020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050500020100", + "result" : "invalid" }, { - "tcId": 287, - "comment": "Signature encoding contains incorrect types: r=empyt UTF-8 string, s=0", - "flags": [ + "tcId" : 287, + "comment" : "Signature encoding contains incorrect types: r=empyt UTF-8 string, s=0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30050c00020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30050c00020100", + "result" : "invalid" }, { - "tcId": 288, - "comment": "Signature encoding contains incorrect types: r=\"0\", s=0", - "flags": [ + "tcId" : 288, + "comment" : "Signature encoding contains incorrect types: r=\"0\", s=0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30060c0130020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30060c0130020100", + "result" : "invalid" }, { - "tcId": 289, - "comment": "Signature encoding contains incorrect types: r=empty list, s=0", - "flags": [ + "tcId" : 289, + "comment" : "Signature encoding contains incorrect types: r=empty list, s=0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30053000020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30053000020100", + "result" : "invalid" }, { - "tcId": 290, - "comment": "Signature encoding contains incorrect types: r=list containing 0, s=0", - "flags": [ + "tcId" : 290, + "comment" : "Signature encoding contains incorrect types: r=list containing 0, s=0", + "flags" : [ "InvalidTypesInSignature" ], - "msg": "313233343030", - "sig": "30083003020100020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30083003020100020100", + "result" : "invalid" }, { - "tcId": 291, - "comment": "Edge case for Shamir multiplication", - "flags": [ + "tcId" : 291, + "comment" : "Edge case for Shamir multiplication", + "flags" : [ "EdgeCaseShamirMultiplication" ], - "msg": "3235353835", - "sig": "3045022100dd1b7d09a7bd8218961034a39a87fecf5314f00c4d25eb58a07ac85e85eab516022035138c401ef8d3493d65c9002fe62b43aee568731b744548358996d9cc427e06", - "result": "valid" + "msg" : "3235353835", + "sig" : "3045022100dd1b7d09a7bd8218961034a39a87fecf5314f00c4d25eb58a07ac85e85eab516022035138c401ef8d3493d65c9002fe62b43aee568731b744548358996d9cc427e06", + "result" : "valid" }, { - "tcId": 292, - "comment": "special case hash", - "flags": [ + "tcId" : 292, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "343236343739373234", - "sig": "304502210095c29267d972a043d955224546222bba343fc1d4db0fec262a33ac61305696ae02206edfe96713aed56f8a28a6653f57e0b829712e5eddc67f34682b24f0676b2640", - "result": "valid" + "msg" : "343236343739373234", + "sig" : "304502210095c29267d972a043d955224546222bba343fc1d4db0fec262a33ac61305696ae02206edfe96713aed56f8a28a6653f57e0b829712e5eddc67f34682b24f0676b2640", + "result" : "valid" }, { - "tcId": 293, - "comment": "special case hash", - "flags": [ + "tcId" : 293, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "37313338363834383931", - "sig": "3044022028f94a894e92024699e345fe66971e3edcd050023386135ab3939d550898fb25022032963e5bd41fa5911ed8f37deb86dae0a762bb6121c894615083c5d95ea01db3", - "result": "valid" + "msg" : "37313338363834383931", + "sig" : "3044022028f94a894e92024699e345fe66971e3edcd050023386135ab3939d550898fb25022032963e5bd41fa5911ed8f37deb86dae0a762bb6121c894615083c5d95ea01db3", + "result" : "valid" }, { - "tcId": 294, - "comment": "special case hash", - "flags": [ + "tcId" : 294, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "3130333539333331363638", - "sig": "3045022100be26b18f9549f89f411a9b52536b15aa270b84548d0e859a1952a27af1a77ac6022070c1d4fa9cd03cc8eaa8d506edb97eed7b8358b453c88aefbb880a3f0e8d472f", - "result": "valid" + "msg" : "3130333539333331363638", + "sig" : "3045022100be26b18f9549f89f411a9b52536b15aa270b84548d0e859a1952a27af1a77ac6022070c1d4fa9cd03cc8eaa8d506edb97eed7b8358b453c88aefbb880a3f0e8d472f", + "result" : "valid" }, { - "tcId": 295, - "comment": "special case hash", - "flags": [ + "tcId" : 295, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "33393439343031323135", - "sig": "3045022100b1a4b1478e65cc3eafdf225d1298b43f2da19e4bcff7eacc0a2e98cd4b74b1140220179aa31e304cc142cf5073171751b28f3f5e0fa88c994e7c55f1bc07b8d56c16", - "result": "valid" + "msg" : "33393439343031323135", + "sig" : "3045022100b1a4b1478e65cc3eafdf225d1298b43f2da19e4bcff7eacc0a2e98cd4b74b1140220179aa31e304cc142cf5073171751b28f3f5e0fa88c994e7c55f1bc07b8d56c16", + "result" : "valid" }, { - "tcId": 296, - "comment": "special case hash", - "flags": [ + "tcId" : 296, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31333434323933303739", - "sig": "30440220325332021261f1bd18f2712aa1e2252da23796da8a4b1ff6ea18cafec7e171f2022040b4f5e287ee61fc3c804186982360891eaa35c75f05a43ecd48b35d984a6648", - "result": "valid" + "msg" : "31333434323933303739", + "sig" : "30440220325332021261f1bd18f2712aa1e2252da23796da8a4b1ff6ea18cafec7e171f2022040b4f5e287ee61fc3c804186982360891eaa35c75f05a43ecd48b35d984a6648", + "result" : "valid" }, { - "tcId": 297, - "comment": "special case hash", - "flags": [ + "tcId" : 297, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "33373036323131373132", - "sig": "3045022100a23ad18d8fc66d81af0903890cbd453a554cb04cdc1a8ca7f7f78e5367ed88a0022023e3eb2ce1c04ea748c389bd97374aa9413b9268851c04dcd9f88e78813fee56", - "result": "valid" + "msg" : "33373036323131373132", + "sig" : "3045022100a23ad18d8fc66d81af0903890cbd453a554cb04cdc1a8ca7f7f78e5367ed88a0022023e3eb2ce1c04ea748c389bd97374aa9413b9268851c04dcd9f88e78813fee56", + "result" : "valid" }, { - "tcId": 298, - "comment": "special case hash", - "flags": [ + "tcId" : 298, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "333433363838373132", - "sig": "304402202bdea41cda63a2d14bf47353bd20880a690901de7cd6e3cc6d8ed5ba0cdb109102203cea66bccfc9f9bf8c7ca4e1c1457cc9145e13e936d90b3d9c7786b8b26cf4c7", - "result": "valid" + "msg" : "333433363838373132", + "sig" : "304402202bdea41cda63a2d14bf47353bd20880a690901de7cd6e3cc6d8ed5ba0cdb109102203cea66bccfc9f9bf8c7ca4e1c1457cc9145e13e936d90b3d9c7786b8b26cf4c7", + "result" : "valid" }, { - "tcId": 299, - "comment": "special case hash", - "flags": [ + "tcId" : 299, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31333531353330333730", - "sig": "3045022100d7cd76ec01c1b1079eba9e2aa2a397243c4758c98a1ba0b7404a340b9b00ced602203575001e19d922e6de8b3d6c84ea43b5c3338106cf29990134e7669a826f78e6", - "result": "valid" + "msg" : "31333531353330333730", + "sig" : "3045022100d7cd76ec01c1b1079eba9e2aa2a397243c4758c98a1ba0b7404a340b9b00ced602203575001e19d922e6de8b3d6c84ea43b5c3338106cf29990134e7669a826f78e6", + "result" : "valid" }, { - "tcId": 300, - "comment": "special case hash", - "flags": [ + "tcId" : 300, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "36353533323033313236", - "sig": "3045022100a872c744d936db21a10c361dd5c9063355f84902219652f6fc56dc95a7139d960220400df7575d9756210e9ccc77162c6b593c7746cfb48ac263c42750b421ef4bb9", - "result": "valid" + "msg" : "36353533323033313236", + "sig" : "3045022100a872c744d936db21a10c361dd5c9063355f84902219652f6fc56dc95a7139d960220400df7575d9756210e9ccc77162c6b593c7746cfb48ac263c42750b421ef4bb9", + "result" : "valid" }, { - "tcId": 301, - "comment": "special case hash", - "flags": [ + "tcId" : 301, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31353634333436363033", - "sig": "30450221009fa9afe07752da10b36d3afcd0fe44bfc40244d75203599cf8f5047fa3453854022050e0a7c013bfbf51819736972d44b4b56bc2a2b2c180df6ec672df171410d77a", - "result": "valid" + "msg" : "31353634333436363033", + "sig" : "30450221009fa9afe07752da10b36d3afcd0fe44bfc40244d75203599cf8f5047fa3453854022050e0a7c013bfbf51819736972d44b4b56bc2a2b2c180df6ec672df171410d77a", + "result" : "valid" }, { - "tcId": 302, - "comment": "special case hash", - "flags": [ + "tcId" : 302, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "34343239353339313137", - "sig": "3045022100885640384d0d910efb177b46be6c3dc5cac81f0b88c3190bb6b5f99c2641f2050220738ed9bff116306d9caa0f8fc608be243e0b567779d8dab03e8e19d553f1dc8e", - "result": "valid" + "msg" : "34343239353339313137", + "sig" : "3045022100885640384d0d910efb177b46be6c3dc5cac81f0b88c3190bb6b5f99c2641f2050220738ed9bff116306d9caa0f8fc608be243e0b567779d8dab03e8e19d553f1dc8e", + "result" : "valid" }, { - "tcId": 303, - "comment": "special case hash", - "flags": [ + "tcId" : 303, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "3130393533323631333531", - "sig": "304402202d051f91c5a9d440c5676985710483bc4f1a6c611b10c95a2ff0363d90c2a45802206ddf94e6fba5be586833d0c53cf216ad3948f37953c26c1cf4968e9a9e8243dc", - "result": "valid" + "msg" : "3130393533323631333531", + "sig" : "304402202d051f91c5a9d440c5676985710483bc4f1a6c611b10c95a2ff0363d90c2a45802206ddf94e6fba5be586833d0c53cf216ad3948f37953c26c1cf4968e9a9e8243dc", + "result" : "valid" }, { - "tcId": 304, - "comment": "special case hash", - "flags": [ + "tcId" : 304, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "35393837333530303431", - "sig": "3045022100f3ac2523967482f53d508522712d583f4379cd824101ff635ea0935117baa54f022027f10812227397e02cea96fb0e680761636dab2b080d1fc5d11685cbe8500cfe", - "result": "valid" + "msg" : "35393837333530303431", + "sig" : "3045022100f3ac2523967482f53d508522712d583f4379cd824101ff635ea0935117baa54f022027f10812227397e02cea96fb0e680761636dab2b080d1fc5d11685cbe8500cfe", + "result" : "valid" }, { - "tcId": 305, - "comment": "special case hash", - "flags": [ + "tcId" : 305, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "33343633303036383738", - "sig": "304502210096447cf68c3ab7266ed7447de3ac52fed7cc08cbdfea391c18a9b8ab370bc91302200f5e7874d3ac0e918f01c885a1639177c923f8660d1ceba1ca1f301bc675cdbc", - "result": "valid" + "msg" : "33343633303036383738", + "sig" : "304502210096447cf68c3ab7266ed7447de3ac52fed7cc08cbdfea391c18a9b8ab370bc91302200f5e7874d3ac0e918f01c885a1639177c923f8660d1ceba1ca1f301bc675cdbc", + "result" : "valid" }, { - "tcId": 306, - "comment": "special case hash", - "flags": [ + "tcId" : 306, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "39383137333230323837", - "sig": "30440220530a0832b691da0b5619a0b11de6877f3c0971baaa68ed122758c29caaf46b7202206c89e44f5eb33060ea4b46318c39138eaedec72de42ba576579a6a4690e339f3", - "result": "valid" + "msg" : "39383137333230323837", + "sig" : "30440220530a0832b691da0b5619a0b11de6877f3c0971baaa68ed122758c29caaf46b7202206c89e44f5eb33060ea4b46318c39138eaedec72de42ba576579a6a4690e339f3", + "result" : "valid" }, { - "tcId": 307, - "comment": "special case hash", - "flags": [ + "tcId" : 307, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "33323232303431303436", - "sig": "30450221009c54c25500bde0b92d72d6ec483dc2482f3654294ca74de796b681255ed58a770220677453c6b56f527631c9f67b3f3eb621fd88582b4aff156d2f1567d6211a2a33", - "result": "valid" + "msg" : "33323232303431303436", + "sig" : "30450221009c54c25500bde0b92d72d6ec483dc2482f3654294ca74de796b681255ed58a770220677453c6b56f527631c9f67b3f3eb621fd88582b4aff156d2f1567d6211a2a33", + "result" : "valid" }, { - "tcId": 308, - "comment": "special case hash", - "flags": [ + "tcId" : 308, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "36363636333037313034", - "sig": "3045022100e7909d41439e2f6af29136c7348ca2641a2b070d5b64f91ea9da7070c7a2618b022042d782f132fa1d36c2c88ba27c3d678d80184a5d1eccac7501f0b47e3d205008", - "result": "valid" + "msg" : "36363636333037313034", + "sig" : "3045022100e7909d41439e2f6af29136c7348ca2641a2b070d5b64f91ea9da7070c7a2618b022042d782f132fa1d36c2c88ba27c3d678d80184a5d1eccac7501f0b47e3d205008", + "result" : "valid" }, { - "tcId": 309, - "comment": "special case hash", - "flags": [ + "tcId" : 309, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31303335393531383938", - "sig": "304402205924873209593135a4c3da7bb381227f8a4b6aa9f34fe5bb7f8fbc131a039ffe02201f1bb11b441c8feaa40f44213d9a405ed792d59fb49d5bcdd9a4285ae5693022", - "result": "valid" + "msg" : "31303335393531383938", + "sig" : "304402205924873209593135a4c3da7bb381227f8a4b6aa9f34fe5bb7f8fbc131a039ffe02201f1bb11b441c8feaa40f44213d9a405ed792d59fb49d5bcdd9a4285ae5693022", + "result" : "valid" }, { - "tcId": 310, - "comment": "special case hash", - "flags": [ + "tcId" : 310, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31383436353937313935", - "sig": "3045022100eeb692c9b262969b231c38b5a7f60649e0c875cd64df88f33aa571fa3d29ab0e0220218b3a1eb06379c2c18cf51b06430786d1c64cd2d24c9b232b23e5bac7989acd", - "result": "valid" + "msg" : "31383436353937313935", + "sig" : "3045022100eeb692c9b262969b231c38b5a7f60649e0c875cd64df88f33aa571fa3d29ab0e0220218b3a1eb06379c2c18cf51b06430786d1c64cd2d24c9b232b23e5bac7989acd", + "result" : "valid" }, { - "tcId": 311, - "comment": "special case hash", - "flags": [ + "tcId" : 311, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "33313336303436313839", - "sig": "3045022100a40034177f36091c2b653684a0e3eb5d4bff18e4d09f664c2800e7cafda1daf802203a3ec29853704e52031c58927a800a968353adc3d973beba9172cbbeab4dd149", - "result": "valid" + "msg" : "33313336303436313839", + "sig" : "3045022100a40034177f36091c2b653684a0e3eb5d4bff18e4d09f664c2800e7cafda1daf802203a3ec29853704e52031c58927a800a968353adc3d973beba9172cbbeab4dd149", + "result" : "valid" }, { - "tcId": 312, - "comment": "special case hash", - "flags": [ + "tcId" : 312, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "32363633373834323534", - "sig": "3045022100b5d795cc75cea5c434fa4185180cd6bd21223f3d5a86da6670d71d95680dadbf022054e4d8810a001ecbb9f7ca1c2ebfdb9d009e9031a431aca3c20ab4e0d1374ec1", - "result": "valid" + "msg" : "32363633373834323534", + "sig" : "3045022100b5d795cc75cea5c434fa4185180cd6bd21223f3d5a86da6670d71d95680dadbf022054e4d8810a001ecbb9f7ca1c2ebfdb9d009e9031a431aca3c20ab4e0d1374ec1", + "result" : "valid" }, { - "tcId": 313, - "comment": "special case hash", - "flags": [ + "tcId" : 313, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31363532313030353234", - "sig": "3044022007dc2478d43c1232a4595608c64426c35510051a631ae6a5a6eb1161e57e42e102204a59ea0fdb72d12165cea3bf1ca86ba97517bd188db3dbd21a5a157850021984", - "result": "valid" + "msg" : "31363532313030353234", + "sig" : "3044022007dc2478d43c1232a4595608c64426c35510051a631ae6a5a6eb1161e57e42e102204a59ea0fdb72d12165cea3bf1ca86ba97517bd188db3dbd21a5a157850021984", + "result" : "valid" }, { - "tcId": 314, - "comment": "special case hash", - "flags": [ + "tcId" : 314, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "35373438303831363936", - "sig": "3045022100ddd20c4a05596ca868b558839fce9f6511ddd83d1ccb53f82e5269d559a0155202205b91734729d93093ff22123c4a25819d7feb66a250663fc780cb66fc7b6e6d17", - "result": "valid" + "msg" : "35373438303831363936", + "sig" : "3045022100ddd20c4a05596ca868b558839fce9f6511ddd83d1ccb53f82e5269d559a0155202205b91734729d93093ff22123c4a25819d7feb66a250663fc780cb66fc7b6e6d17", + "result" : "valid" }, { - "tcId": 315, - "comment": "special case hash", - "flags": [ + "tcId" : 315, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "36333433393133343638", - "sig": "30450221009cde6e0ede0a003f02fda0a01b59facfe5dec063318f279ce2de7a9b1062f7b702202886a5b8c679bdf8224c66f908fd6205492cb70b0068d46ae4f33a4149b12a52", - "result": "valid" + "msg" : "36333433393133343638", + "sig" : "30450221009cde6e0ede0a003f02fda0a01b59facfe5dec063318f279ce2de7a9b1062f7b702202886a5b8c679bdf8224c66f908fd6205492cb70b0068d46ae4f33a4149b12a52", + "result" : "valid" }, { - "tcId": 316, - "comment": "special case hash", - "flags": [ + "tcId" : 316, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31353431313033353938", - "sig": "3045022100c5771016d0dd6357143c89f684cd740423502554c0c59aa8c99584f1ff38f609022054b405f4477546686e464c5463b4fd4190572e58d0f7e7357f6e61947d20715c", - "result": "valid" + "msg" : "31353431313033353938", + "sig" : "3045022100c5771016d0dd6357143c89f684cd740423502554c0c59aa8c99584f1ff38f609022054b405f4477546686e464c5463b4fd4190572e58d0f7e7357f6e61947d20715c", + "result" : "valid" }, { - "tcId": 317, - "comment": "special case hash", - "flags": [ + "tcId" : 317, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "3130343738353830313238", - "sig": "3045022100a24ebc0ec224bd67ae397cbe6fa37b3125adbd34891abe2d7c7356921916dfe6022034f6eb6374731bbbafc4924fb8b0bdcdda49456d724cdae6178d87014cb53d8c", - "result": "valid" + "msg" : "3130343738353830313238", + "sig" : "3045022100a24ebc0ec224bd67ae397cbe6fa37b3125adbd34891abe2d7c7356921916dfe6022034f6eb6374731bbbafc4924fb8b0bdcdda49456d724cdae6178d87014cb53d8c", + "result" : "valid" }, { - "tcId": 318, - "comment": "special case hash", - "flags": [ + "tcId" : 318, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "3130353336323835353638", - "sig": "304402202557d64a7aee2e0931c012e4fea1cd3a2c334edae68cdeb7158caf21b68e5a2402207f06cdbb6a90023a973882ed97b080fe6b05af3ec93db6f1a4399a69edf7670d", - "result": "valid" + "msg" : "3130353336323835353638", + "sig" : "304402202557d64a7aee2e0931c012e4fea1cd3a2c334edae68cdeb7158caf21b68e5a2402207f06cdbb6a90023a973882ed97b080fe6b05af3ec93db6f1a4399a69edf7670d", + "result" : "valid" }, { - "tcId": 319, - "comment": "special case hash", - "flags": [ + "tcId" : 319, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "393533393034313035", - "sig": "3045022100c4f2eccbb6a24350c8466450b9d61b207ee359e037b3dcedb42a3f2e6dd6aeb502203263c6b59a2f55cdd1c6e14894d5e5963b28bc3e2469ac9ba1197991ca7ff9c7", - "result": "valid" + "msg" : "393533393034313035", + "sig" : "3045022100c4f2eccbb6a24350c8466450b9d61b207ee359e037b3dcedb42a3f2e6dd6aeb502203263c6b59a2f55cdd1c6e14894d5e5963b28bc3e2469ac9ba1197991ca7ff9c7", + "result" : "valid" }, { - "tcId": 320, - "comment": "special case hash", - "flags": [ + "tcId" : 320, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "393738383438303339", - "sig": "3045022100eff04781c9cbcd162d0a25a6e2ebcca43506c523385cb515d49ea38a1b12fcad022015acd73194c91a95478534f23015b672ebed213e45424dd2c8e26ac8b3eb34a5", - "result": "valid" + "msg" : "393738383438303339", + "sig" : "3045022100eff04781c9cbcd162d0a25a6e2ebcca43506c523385cb515d49ea38a1b12fcad022015acd73194c91a95478534f23015b672ebed213e45424dd2c8e26ac8b3eb34a5", + "result" : "valid" }, { - "tcId": 321, - "comment": "special case hash", - "flags": [ + "tcId" : 321, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "33363130363732343432", - "sig": "3045022100f58b4e3110a64bf1b5db97639ee0e5a9c8dfa49dc59b679891f520fdf0584c8702202cd8fe51888aee9db3e075440fd4db73b5c732fb87b510e97093d66415f62af7", - "result": "valid" + "msg" : "33363130363732343432", + "sig" : "3045022100f58b4e3110a64bf1b5db97639ee0e5a9c8dfa49dc59b679891f520fdf0584c8702202cd8fe51888aee9db3e075440fd4db73b5c732fb87b510e97093d66415f62af7", + "result" : "valid" }, { - "tcId": 322, - "comment": "special case hash", - "flags": [ + "tcId" : 322, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31303534323430373035", - "sig": "3045022100f8abecaa4f0c502de4bf5903d48417f786bf92e8ad72fec0bd7fcb7800c0bbe302204c7f9e231076a30b7ae36b0cebe69ccef1cd194f7cce93a5588fd6814f437c0e", - "result": "valid" + "msg" : "31303534323430373035", + "sig" : "3045022100f8abecaa4f0c502de4bf5903d48417f786bf92e8ad72fec0bd7fcb7800c0bbe302204c7f9e231076a30b7ae36b0cebe69ccef1cd194f7cce93a5588fd6814f437c0e", + "result" : "valid" }, { - "tcId": 323, - "comment": "special case hash", - "flags": [ + "tcId" : 323, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "35313734343438313937", - "sig": "304402205d5b38bd37ad498b2227a633268a8cca879a5c7c94a4e416bd0a614d09e606d2022012b8d664ea9991062ecbb834e58400e25c46007af84f6007d7f1685443269afe", - "result": "valid" + "msg" : "35313734343438313937", + "sig" : "304402205d5b38bd37ad498b2227a633268a8cca879a5c7c94a4e416bd0a614d09e606d2022012b8d664ea9991062ecbb834e58400e25c46007af84f6007d7f1685443269afe", + "result" : "valid" }, { - "tcId": 324, - "comment": "special case hash", - "flags": [ + "tcId" : 324, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31393637353631323531", - "sig": "304402200c1cd9fe4034f086a2b52d65b9d3834d72aebe7f33dfe8f976da82648177d8e3022013105782e3d0cfe85c2778dec1a848b27ac0ae071aa6da341a9553a946b41e59", - "result": "valid" + "msg" : "31393637353631323531", + "sig" : "304402200c1cd9fe4034f086a2b52d65b9d3834d72aebe7f33dfe8f976da82648177d8e3022013105782e3d0cfe85c2778dec1a848b27ac0ae071aa6da341a9553a946b41e59", + "result" : "valid" }, { - "tcId": 325, - "comment": "special case hash", - "flags": [ + "tcId" : 325, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "33343437323533333433", - "sig": "3045022100ae7935fb96ff246b7b5d5662870d1ba587b03d6e1360baf47988b5c02ccc1a5b02205f00c323272083782d4a59f2dfd65e49de0693627016900ef7e61428056664b3", - "result": "valid" + "msg" : "33343437323533333433", + "sig" : "3045022100ae7935fb96ff246b7b5d5662870d1ba587b03d6e1360baf47988b5c02ccc1a5b02205f00c323272083782d4a59f2dfd65e49de0693627016900ef7e61428056664b3", + "result" : "valid" }, { - "tcId": 326, - "comment": "special case hash", - "flags": [ + "tcId" : 326, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "333638323634333138", - "sig": "3044022000a134b5c6ccbcefd4c882b945baeb4933444172795fa6796aae1490675470980220566e46105d24d890151e3eea3ebf88f5b92b3f5ec93a217765a6dcbd94f2c55b", - "result": "valid" + "msg" : "333638323634333138", + "sig" : "3044022000a134b5c6ccbcefd4c882b945baeb4933444172795fa6796aae1490675470980220566e46105d24d890151e3eea3ebf88f5b92b3f5ec93a217765a6dcbd94f2c55b", + "result" : "valid" }, { - "tcId": 327, - "comment": "special case hash", - "flags": [ + "tcId" : 327, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "33323631313938363038", - "sig": "304402202e4721363ad3992c139e5a1c26395d2c2d777824aa24fde075e0d7381171309d0220740f7c494418e1300dd4512f782a58800bff6a7abdfdd20fbbd4f05515ca1a4f", - "result": "valid" + "msg" : "33323631313938363038", + "sig" : "304402202e4721363ad3992c139e5a1c26395d2c2d777824aa24fde075e0d7381171309d0220740f7c494418e1300dd4512f782a58800bff6a7abdfdd20fbbd4f05515ca1a4f", + "result" : "valid" }, { - "tcId": 328, - "comment": "special case hash", - "flags": [ + "tcId" : 328, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "39363738373831303934", - "sig": "304402206852e9d3cd9fe373c2d504877967d365ab1456707b6817a042864694e1960ccf0220064b27ea142b30887b84c86adccb2fa39a6911ad21fc7e819f593be52bc4f3bd", - "result": "valid" + "msg" : "39363738373831303934", + "sig" : "304402206852e9d3cd9fe373c2d504877967d365ab1456707b6817a042864694e1960ccf0220064b27ea142b30887b84c86adccb2fa39a6911ad21fc7e819f593be52bc4f3bd", + "result" : "valid" }, { - "tcId": 329, - "comment": "special case hash", - "flags": [ + "tcId" : 329, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "34393538383233383233", - "sig": "30440220188a8c5648dc79eace158cf886c62b5468f05fd95f03a7635c5b4c31f09af4c5022036361a0b571a00c6cd5e686ccbfcfa703c4f97e48938346d0c103fdc76dc5867", - "result": "valid" + "msg" : "34393538383233383233", + "sig" : "30440220188a8c5648dc79eace158cf886c62b5468f05fd95f03a7635c5b4c31f09af4c5022036361a0b571a00c6cd5e686ccbfcfa703c4f97e48938346d0c103fdc76dc5867", + "result" : "valid" }, { - "tcId": 330, - "comment": "special case hash", - "flags": [ + "tcId" : 330, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "383234363337383337", - "sig": "3045022100a74f1fb9a8263f62fc4416a5b7d584f4206f3996bb91f6fc8e73b9e92bad0e1302206815032e8c7d76c3ab06a86f33249ce9940148cb36d1f417c2e992e801afa3fa", - "result": "valid" + "msg" : "383234363337383337", + "sig" : "3045022100a74f1fb9a8263f62fc4416a5b7d584f4206f3996bb91f6fc8e73b9e92bad0e1302206815032e8c7d76c3ab06a86f33249ce9940148cb36d1f417c2e992e801afa3fa", + "result" : "valid" }, { - "tcId": 331, - "comment": "special case hash", - "flags": [ + "tcId" : 331, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "3131303230383333373736", - "sig": "3044022007244865b72ff37e62e3146f0dc14682badd7197799135f0b00ade7671742bfe02200d80c2238edb4e4a7a86a8c57ca9af1711f406f7f5da0299aa04e2932d960754", - "result": "valid" + "msg" : "3131303230383333373736", + "sig" : "3044022007244865b72ff37e62e3146f0dc14682badd7197799135f0b00ade7671742bfe02200d80c2238edb4e4a7a86a8c57ca9af1711f406f7f5da0299aa04e2932d960754", + "result" : "valid" }, { - "tcId": 332, - "comment": "special case hash", - "flags": [ + "tcId" : 332, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "313333383731363438", - "sig": "3045022100da7fdd05b5badabd619d805c4ee7d9a84f84ddd5cf9c5bf4d4338140d689ef08022028f1cf4fa1c3c5862cfa149c0013cf5fe6cf5076cae000511063e7de25bb38e5", - "result": "valid" + "msg" : "313333383731363438", + "sig" : "3045022100da7fdd05b5badabd619d805c4ee7d9a84f84ddd5cf9c5bf4d4338140d689ef08022028f1cf4fa1c3c5862cfa149c0013cf5fe6cf5076cae000511063e7de25bb38e5", + "result" : "valid" }, { - "tcId": 333, - "comment": "special case hash", - "flags": [ + "tcId" : 333, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "333232313434313632", - "sig": "3045022100d3027c656f6d4fdfd8ede22093e3c303b0133c340d615e7756f6253aea927238022009aef060c8e4cef972974011558df144fed25ca69ae8d0b2eaf1a8feefbec417", - "result": "valid" + "msg" : "333232313434313632", + "sig" : "3045022100d3027c656f6d4fdfd8ede22093e3c303b0133c340d615e7756f6253aea927238022009aef060c8e4cef972974011558df144fed25ca69ae8d0b2eaf1a8feefbec417", + "result" : "valid" }, { - "tcId": 334, - "comment": "special case hash", - "flags": [ + "tcId" : 334, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "3130363836363535353436", - "sig": "304402200bf6c0188dc9571cd0e21eecac5fbb19d2434988e9cc10244593ef3a98099f6902204864a562661f9221ec88e3dd0bc2f6e27ac128c30cc1a80f79ec670a22b042ee", - "result": "valid" + "msg" : "3130363836363535353436", + "sig" : "304402200bf6c0188dc9571cd0e21eecac5fbb19d2434988e9cc10244593ef3a98099f6902204864a562661f9221ec88e3dd0bc2f6e27ac128c30cc1a80f79ec670a22b042ee", + "result" : "valid" }, { - "tcId": 335, - "comment": "special case hash", - "flags": [ + "tcId" : 335, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "3632313535323436", - "sig": "3045022100ae459640d5d1179be47a47fa538e16d94ddea5585e7a244804a51742c686443a02206c8e30e530a634fae80b3ceb062978b39edbe19777e0a24553b68886181fd897", - "result": "valid" + "msg" : "3632313535323436", + "sig" : "3045022100ae459640d5d1179be47a47fa538e16d94ddea5585e7a244804a51742c686443a02206c8e30e530a634fae80b3ceb062978b39edbe19777e0a24553b68886181fd897", + "result" : "valid" }, { - "tcId": 336, - "comment": "special case hash", - "flags": [ + "tcId" : 336, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "37303330383138373734", - "sig": "304402201cf3517ba3bf2ab8b9ead4ebb6e866cb88a1deacb6a785d3b63b483ca02ac4950220249a798b73606f55f5f1c70de67cb1a0cff95d7dc50b3a617df861bad3c6b1c9", - "result": "valid" + "msg" : "37303330383138373734", + "sig" : "304402201cf3517ba3bf2ab8b9ead4ebb6e866cb88a1deacb6a785d3b63b483ca02ac4950220249a798b73606f55f5f1c70de67cb1a0cff95d7dc50b3a617df861bad3c6b1c9", + "result" : "valid" }, { - "tcId": 337, - "comment": "special case hash", - "flags": [ + "tcId" : 337, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "35393234353233373434", - "sig": "3045022100e69b5238265ea35d77e4dd172288d8cea19810a10292617d5976519dc5757cb802204b03c5bc47e826bdb27328abd38d3056d77476b2130f3df6ec4891af08ba1e29", - "result": "valid" + "msg" : "35393234353233373434", + "sig" : "3045022100e69b5238265ea35d77e4dd172288d8cea19810a10292617d5976519dc5757cb802204b03c5bc47e826bdb27328abd38d3056d77476b2130f3df6ec4891af08ba1e29", + "result" : "valid" }, { - "tcId": 338, - "comment": "special case hash", - "flags": [ + "tcId" : 338, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31343935353836363231", - "sig": "304402205f9d7d7c870d085fc1d49fff69e4a275812800d2cf8973e7325866cb40fa2b6f02206d1f5491d9f717a597a15fd540406486d76a44697b3f0d9d6dcef6669f8a0a56", - "result": "valid" + "msg" : "31343935353836363231", + "sig" : "304402205f9d7d7c870d085fc1d49fff69e4a275812800d2cf8973e7325866cb40fa2b6f02206d1f5491d9f717a597a15fd540406486d76a44697b3f0d9d6dcef6669f8a0a56", + "result" : "valid" }, { - "tcId": 339, - "comment": "special case hash", - "flags": [ + "tcId" : 339, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "34303035333134343036", - "sig": "304402200a7d5b1959f71df9f817146ee49bd5c89b431e7993e2fdecab6858957da685ae02200f8aad2d254690bdc13f34a4fec44a02fd745a422df05ccbb54635a8b86b9609", - "result": "valid" + "msg" : "34303035333134343036", + "sig" : "304402200a7d5b1959f71df9f817146ee49bd5c89b431e7993e2fdecab6858957da685ae02200f8aad2d254690bdc13f34a4fec44a02fd745a422df05ccbb54635a8b86b9609", + "result" : "valid" }, { - "tcId": 340, - "comment": "special case hash", - "flags": [ + "tcId" : 340, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "33303936343537353132", - "sig": "3044022079e88bf576b74bc07ca142395fda28f03d3d5e640b0b4ff0752c6d94cd553408022032cea05bd2d706c8f6036a507e2ab7766004f0904e2e5c5862749c0073245d6a", - "result": "valid" + "msg" : "33303936343537353132", + "sig" : "3044022079e88bf576b74bc07ca142395fda28f03d3d5e640b0b4ff0752c6d94cd553408022032cea05bd2d706c8f6036a507e2ab7766004f0904e2e5c5862749c0073245d6a", + "result" : "valid" }, { - "tcId": 341, - "comment": "special case hash", - "flags": [ + "tcId" : 341, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "32373834303235363230", - "sig": "30450221009d54e037a00212b377bc8874798b8da080564bbdf7e07591b861285809d01488022018b4e557667a82bd95965f0706f81a29243fbdd86968a7ebeb43069db3b18c7f", - "result": "valid" + "msg" : "32373834303235363230", + "sig" : "30450221009d54e037a00212b377bc8874798b8da080564bbdf7e07591b861285809d01488022018b4e557667a82bd95965f0706f81a29243fbdd86968a7ebeb43069db3b18c7f", + "result" : "valid" }, { - "tcId": 342, - "comment": "special case hash", - "flags": [ + "tcId" : 342, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "32363138373837343138", - "sig": "304402202664f1ffa982fedbcc7cab1b8bc6e2cb420218d2a6077ad08e591ba9feab33bd022049f5c7cb515e83872a3d41b4cdb85f242ad9d61a5bfc01debfbb52c6c84ba728", - "result": "valid" + "msg" : "32363138373837343138", + "sig" : "304402202664f1ffa982fedbcc7cab1b8bc6e2cb420218d2a6077ad08e591ba9feab33bd022049f5c7cb515e83872a3d41b4cdb85f242ad9d61a5bfc01debfbb52c6c84ba728", + "result" : "valid" }, { - "tcId": 343, - "comment": "special case hash", - "flags": [ + "tcId" : 343, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "31363432363235323632", - "sig": "304402205827518344844fd6a7de73cbb0a6befdea7b13d2dee4475317f0f18ffc81524b02204f5ccb4e0b488b5a5d760aacddb2d791970fe43da61eb30e2e90208a817e46db", - "result": "valid" + "msg" : "31363432363235323632", + "sig" : "304402205827518344844fd6a7de73cbb0a6befdea7b13d2dee4475317f0f18ffc81524b02204f5ccb4e0b488b5a5d760aacddb2d791970fe43da61eb30e2e90208a817e46db", + "result" : "valid" }, { - "tcId": 344, - "comment": "special case hash", - "flags": [ + "tcId" : 344, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "36383234313839343336", - "sig": "304502210097ab19bd139cac319325869218b1bce111875d63fb12098a04b0cd59b6fdd3a30220431d9cea3a243847303cebda56476431d034339f31d785ee8852db4f040d4921", - "result": "valid" + "msg" : "36383234313839343336", + "sig" : "304502210097ab19bd139cac319325869218b1bce111875d63fb12098a04b0cd59b6fdd3a30220431d9cea3a243847303cebda56476431d034339f31d785ee8852db4f040d4921", + "result" : "valid" }, { - "tcId": 345, - "comment": "special case hash", - "flags": [ + "tcId" : 345, + "comment" : "special case hash", + "flags" : [ "SpecialCaseHash" ], - "msg": "343834323435343235", - "sig": "3044022052c683144e44119ae2013749d4964ef67509278f6d38ba869adcfa69970e123d02203479910167408f45bda420a626ec9c4ec711c1274be092198b4187c018b562ca", - "result": "valid" + "msg" : "343834323435343235", + "sig" : "3044022052c683144e44119ae2013749d4964ef67509278f6d38ba869adcfa69970e123d02203479910167408f45bda420a626ec9c4ec711c1274be092198b4187c018b562ca", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0", - "wx": "07310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc362", - "wy": "26a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0", + "wx" : "07310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc362", + "wy" : "26a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEBzEPkKnq4UmghAL1QZSg97SsQnv42b1s\ndoEHHcR9w2ImptN6xG1h/WAMC/G/+HaJ7RF92msOWTGK4BChl6JsoA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000407310f90a9eae149a08402f54194a0f7b4ac427bf8d9bd6c7681071dc47dc36226a6d37ac46d61fd600c0bf1bff87689ed117dda6b0e59318ae010a197a26ca0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEBzEPkKnq4UmghAL1QZSg97SsQnv42b1s\ndoEHHcR9w2ImptN6xG1h/WAMC/G/+HaJ7RF92msOWTGK4BChl6JsoA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 346, - "comment": "k*G has a large x-coordinate", - "flags": [ + "tcId" : 346, + "comment" : "k*G has a large x-coordinate", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "30160211014551231950b75fc4402da1722fc9baeb020103", - "result": "valid" + "msg" : "313233343030", + "sig" : "30160211014551231950b75fc4402da1722fc9baeb020103", + "result" : "valid" }, { - "tcId": 347, - "comment": "r too large", - "flags": [ + "tcId" : 347, + "comment" : "r too large", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2c020103", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2c020103", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5", - "wx": "00bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22", - "wy": "705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5", + "wx" : "00bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22", + "wy" : "705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEvJfnWF7srUjhZoO8QJFwjhqTDGg/xHAB\n1LODWU8sTiJwWYnPadrq3U5OS4FR7YiN/sIPsBco2J1Ws/OPKunIxQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004bc97e7585eecad48e16683bc4091708e1a930c683fc47001d4b383594f2c4e22705989cf69daeadd4e4e4b8151ed888dfec20fb01728d89d56b3f38f2ae9c8c5", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEvJfnWF7srUjhZoO8QJFwjhqTDGg/xHAB\n1LODWU8sTiJwWYnPadrq3U5OS4FR7YiN/sIPsBco2J1Ws/OPKunIxQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 348, - "comment": "r,s are large", - "flags": [ + "tcId" : 348, + "comment" : "r,s are large", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f020103", - "result": "valid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd036413f020103", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0444ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463", - "wx": "44ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252", - "wy": "00b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0444ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463", + "wx" : "44ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252", + "wy" : "00b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000444ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERK0zmvvCHpq/e2AqXKU16jeBNbbRDYEx\nC92Ck9HfMlK2P/fQd0dw+P4dFyL6g6zQL0NOT8EQoMyPbd3TfVbEYw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000444ad339afbc21e9abf7b602a5ca535ea378135b6d10d81310bdd8293d1df3252b63ff7d0774770f8fe1d1722fa83acd02f434e4fc110a0cc8f6dddd37d56c463", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERK0zmvvCHpq/e2AqXKU16jeBNbbRDYEx\nC92Ck9HfMlK2P/fQd0dw+P4dFyL6g6zQL0NOT8EQoMyPbd3TfVbEYw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 349, - "comment": "r and s^-1 have a large Hamming weight", - "flags": [ + "tcId" : 349, + "comment" : "r and s^-1 have a large Hamming weight", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203e9a7582886089c62fb840cf3b83061cd1cff3ae4341808bb5bdee6191174177", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203e9a7582886089c62fb840cf3b83061cd1cff3ae4341808bb5bdee6191174177", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "041260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0", - "wx": "1260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c", - "wy": "5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0", + "wx" : "1260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c", + "wy" : "5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEEmDCEiyeJE4a9RUb7eDDriO1TXxZaIHT\n7rrSHzfdh4xcmgwamt52c3qIEb1qf5KHyXjuOWqonBHkcinSzLVS8A==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041260c2122c9e244e1af5151bede0c3ae23b54d7c596881d3eebad21f37dd878c5c9a0c1a9ade76737a8811bd6a7f9287c978ee396aa89c11e47229d2ccb552f0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEEmDCEiyeJE4a9RUb7eDDriO1TXxZaIHT\n7rrSHzfdh4xcmgwamt52c3qIEb1qf5KHyXjuOWqonBHkcinSzLVS8A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 350, - "comment": "r and s^-1 have a large Hamming weight", - "flags": [ + "tcId" : 350, + "comment" : "r and s^-1 have a large Hamming weight", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022024238e70b431b1a64efdf9032669939d4b77f249503fc6905feb7540dea3e6d2", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022024238e70b431b1a64efdf9032669939d4b77f249503fc6905feb7540dea3e6d2", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "041877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159", - "wx": "1877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce", - "wy": "00821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159", + "wx" : "1877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce", + "wy" : "00821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGHcEW+JdNKHQYA+dXADQZFoqVDebbO76\n0ua/XCozUs6CGlMswXUe4dNtQcPWq06bFD5E7EbXNHjqanmlwOVBWQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041877045be25d34a1d0600f9d5c00d0645a2a54379b6ceefad2e6bf5c2a3352ce821a532cc1751ee1d36d41c3d6ab4e9b143e44ec46d73478ea6a79a5c0e54159", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGHcEW+JdNKHQYA+dXADQZFoqVDebbO76\n0ua/XCozUs6CGlMswXUe4dNtQcPWq06bFD5E7EbXNHjqanmlwOVBWQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 351, - "comment": "small r and s", - "flags": [ + "tcId" : 351, + "comment" : "small r and s", + "flags" : [ "SmallRandS", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3006020101020101", - "result": "valid" + "msg" : "313233343030", + "sig" : "3006020101020101", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77", - "wx": "455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50", - "wy": "00aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77", + "wx" : "455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50", + "wy" : "00aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERVQ5/MPS3uzt3q7OYOe9FzBPNuu2Aq31\noi4Ljx20alCuw4+yuvIh6ajRiHx79iIt0YNGNOdyYzFa9tI2CdBPdw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004455439fcc3d2deeceddeaece60e7bd17304f36ebb602adf5a22e0b8f1db46a50aec38fb2baf221e9a8d1887c7bf6222dd1834634e77263315af6d23609d04f77", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERVQ5/MPS3uzt3q7OYOe9FzBPNuu2Aq31\noi4Ljx20alCuw4+yuvIh6ajRiHx79iIt0YNGNOdyYzFa9tI2CdBPdw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 352, - "comment": "small r and s", - "flags": [ + "tcId" : 352, + "comment" : "small r and s", + "flags" : [ "SmallRandS", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3006020101020102", - "result": "valid" + "msg" : "313233343030", + "sig" : "3006020101020102", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "042e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece7180449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d", - "wx": "2e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece718", - "wy": "0449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece7180449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d", + "wx" : "2e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece718", + "wy" : "0449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece7180449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELh9GawJMDDrOJDfeCRJ/7QS3BvlLGaIb\nscKs81zs5xgESa41I9clNOlklyz9OzivC93ZYZ5a8iPk0aQPNM+fHQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042e1f466b024c0c3ace2437de09127fed04b706f94b19a21bb1c2acf35cece7180449ae3523d72534e964972cfd3b38af0bddd9619e5af223e4d1a40f34cf9f1d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELh9GawJMDDrOJDfeCRJ/7QS3BvlLGaIb\nscKs81zs5xgESa41I9clNOlklyz9OzivC93ZYZ5a8iPk0aQPNM+fHQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 353, - "comment": "small r and s", - "flags": [ + "tcId" : 353, + "comment" : "small r and s", + "flags" : [ "SmallRandS", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3006020101020103", - "result": "valid" + "msg" : "313233343030", + "sig" : "3006020101020103", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "048e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a2337326ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3", - "wx": "008e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a23373", - "wy": "26ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a2337326ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3", + "wx" : "008e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a23373", + "wy" : "26ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a2337326ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjnq9u9GN50UjdMGHmhw7AdEyYefUVxw7\nR6HHbFWiM3Mm7Yl81Rek9TSduAl4D20vK59imdi1qJB38RGacY/Xsw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048e7abdbbd18de7452374c1879a1c3b01d13261e7d4571c3b47a1c76c55a2337326ed897cd517a4f5349db809780f6d2f2b9f6299d8b5a89077f1119a718fd7b3", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjnq9u9GN50UjdMGHmhw7AdEyYefUVxw7\nR6HHbFWiM3Mm7Yl81Rek9TSduAl4D20vK59imdi1qJB38RGacY/Xsw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 354, - "comment": "small r and s", - "flags": [ + "tcId" : 354, + "comment" : "small r and s", + "flags" : [ "SmallRandS", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3006020102020101", - "result": "valid" + "msg" : "313233343030", + "sig" : "3006020102020101", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "047b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af1942117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b", - "wx": "7b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af19", - "wy": "42117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "047b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af1942117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b", + "wx" : "7b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af19", + "wy" : "42117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200047b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af1942117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEezM9Q0DT1xjdPmr/fee7+Lcr/WFshCAF\nYFKEI3a5rxlCEXxa/qx1XW83b8Yymn12BRuHEjpKXQvEpTk4DwPeew==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200047b333d4340d3d718dd3e6aff7de7bbf8b72bfd616c8420056052842376b9af1942117c5afeac755d6f376fc6329a7d76051b87123a4a5d0bc4a539380f03de7b", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEezM9Q0DT1xjdPmr/fee7+Lcr/WFshCAF\nYFKEI3a5rxlCEXxa/qx1XW83b8Yymn12BRuHEjpKXQvEpTk4DwPeew==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 355, - "comment": "small r and s", - "flags": [ + "tcId" : 355, + "comment" : "small r and s", + "flags" : [ "SmallRandS", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3006020102020102", - "result": "valid" + "msg" : "313233343030", + "sig" : "3006020102020102", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e503a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff", - "wx": "00d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e5", - "wy": "03a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e503a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff", + "wx" : "00d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e5", + "wy" : "03a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e503a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE0wykoN22YWyFHTDO1oLED4PGJ1ih8nWZ\niNZ2OojxwOUDqA1UFWUNQSOXhOji+xI16f6ZHREuu4EYbL8Not46/w==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d30ca4a0ddb6616c851d30ced682c40f83c62758a1f2759988d6763a88f1c0e503a80d5415650d41239784e8e2fb1235e9fe991d112ebb81186cbf0da2de3aff", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE0wykoN22YWyFHTDO1oLED4PGJ1ih8nWZ\niNZ2OojxwOUDqA1UFWUNQSOXhOji+xI16f6ZHREuu4EYbL8Not46/w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 356, - "comment": "small r and s", - "flags": [ + "tcId" : 356, + "comment" : "small r and s", + "flags" : [ "SmallRandS", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3006020102020103", - "result": "valid" + "msg" : "313233343030", + "sig" : "3006020102020103", + "result" : "valid" }, { - "tcId": 357, - "comment": "r is larger than n", - "flags": [ + "tcId" : 357, + "comment" : "r is larger than n", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364143020103", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3026022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364143020103", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0448969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec", - "wx": "48969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24", - "wy": "00b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0448969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec", + "wx" : "48969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24", + "wy" : "00b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000448969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAESJabOZkSl7MyplLT7m4B6QmzmQTnH6I1\nSngwx3ULryS0AS0bgw0ZnMsfyXKzK/3tVfCc1i0lfl6ETiflehWU7A==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000448969b39991297b332a652d3ee6e01e909b39904e71fa2354a7830c7750baf24b4012d1b830d199ccb1fc972b32bfded55f09cd62d257e5e844e27e57a1594ec", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAESJabOZkSl7MyplLT7m4B6QmzmQTnH6I1\nSngwx3ULryS0AS0bgw0ZnMsfyXKzK/3tVfCc1i0lfl6ETiflehWU7A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 358, - "comment": "s is larger than n", - "flags": [ + "tcId" : 358, + "comment" : "s is larger than n", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "30080201020203ed2979", - "result": "invalid" + "msg" : "313233343030", + "sig" : "30080201020203ed2979", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0402ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee777eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866", - "wx": "02ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee77", - "wy": "7eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0402ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee777eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866", + "wx" : "02ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee77", + "wy" : "7eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000402ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee777eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAu9NbWz9WpTx13hCJuPipsCkNsVYOWGf\nOPtEcrX57nd+tKzU7r2lzXKHX/0qLyYinC3GtGUAkZpDLIZznzroZg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000402ef4d6d6cfd5a94f1d7784226e3e2a6c0a436c55839619f38fb4472b5f9ee777eb4acd4eebda5cd72875ffd2a2f26229c2dc6b46500919a432c86739f3ae866", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAu9NbWz9WpTx13hCJuPipsCkNsVYOWGf\nOPtEcrX57nd+tKzU7r2lzXKHX/0qLyYinC3GtGUAkZpDLIZznzroZg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 359, - "comment": "small r and s^-1", - "flags": [ + "tcId" : 359, + "comment" : "small r and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "30260202010102203a74e9d3a74e9d3a74e9d3a74e9d3a749f8ab3732a0a89604a09bce5b2916da4", - "result": "valid" + "msg" : "313233343030", + "sig" : "30260202010102203a74e9d3a74e9d3a74e9d3a74e9d3a749f8ab3732a0a89604a09bce5b2916da4", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08", - "wx": "464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584", - "wy": "00b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08", + "wx" : "464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584", + "wy" : "00b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERk9P9xVynK5Qcso72AHTGVtnrsZemwGq\n0gopQ9y8tYSxr9KdMaOaEdVwqhWXQ5s7LRlxvy8avxVDLQIHsQ0dCA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004464f4ff715729cae5072ca3bd801d3195b67aec65e9b01aad20a2943dcbcb584b1afd29d31a39a11d570aa1597439b3b2d1971bf2f1abf15432d0207b10d1d08", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERk9P9xVynK5Qcso72AHTGVtnrsZemwGq\n0gopQ9y8tYSxr9KdMaOaEdVwqhWXQ5s7LRlxvy8avxVDLQIHsQ0dCA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 360, - "comment": "smallish r and s^-1", - "flags": [ + "tcId" : 360, + "comment" : "smallish r and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "302b02072d9b4d347952cc02200343aefc2f25d98b882e86eb9e30d55a6eb508b516510b34024ae4b6362330b3", - "result": "valid" + "msg" : "313233343030", + "sig" : "302b02072d9b4d347952cc02200343aefc2f25d98b882e86eb9e30d55a6eb508b516510b34024ae4b6362330b3", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4cdeadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f", - "wx": "157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4c", - "wy": "00deadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4cdeadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f", + "wx" : "157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4c", + "wy" : "00deadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4cdeadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFX+P3fNz619Jz88Q2LhTz5HLzX1mXDUi\nun3XON23mkzerfGlxEjqPJ9BkaiZmr/MdXrG1kVn7wcsR/7GE0Q7jw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004157f8fddf373eb5f49cfcf10d8b853cf91cbcd7d665c3522ba7dd738ddb79a4cdeadf1a5c448ea3c9f4191a8999abfcc757ac6d64567ef072c47fec613443b8f", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFX+P3fNz619Jz88Q2LhTz5HLzX1mXDUi\nun3XON23mkzerfGlxEjqPJ9BkaiZmr/MdXrG1kVn7wcsR/7GE0Q7jw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 361, - "comment": "100-bit r and small s^-1", - "flags": [ + "tcId" : 361, + "comment" : "100-bit r and small s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3031020d1033e67e37b32b445580bf4efc02206f906f906f906f906f906f906f906f8fe1cab5eefdb214061dce3b22789f1d6f", - "result": "valid" + "msg" : "313233343030", + "sig" : "3031020d1033e67e37b32b445580bf4efc02206f906f906f906f906f906f906f906f8fe1cab5eefdb214061dce3b22789f1d6f", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "040934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa", - "wx": "0934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0", - "wy": "00d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "040934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa", + "wx" : "0934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0", + "wy" : "00d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200040934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECTSlN0ZsB0MOLEj+uZC7Gft4zsyc7kJO\npNEwKRqiN/DU+S0jtGKAS1toxSVYwByZltv3J/zKu+7bliGkAFNa+g==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200040934a537466c07430e2c48feb990bb19fb78cecc9cee424ea4d130291aa237f0d4f92d23b462804b5b68c52558c01c9996dbf727fccabbeedb9621a400535afa", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAECTSlN0ZsB0MOLEj+uZC7Gft4zsyc7kJO\npNEwKRqiN/DU+S0jtGKAS1toxSVYwByZltv3J/zKu+7bliGkAFNa+g==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 362, - "comment": "small r and 100 bit s^-1", - "flags": [ + "tcId" : 362, + "comment" : "small r and 100 bit s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3026020201010220783266e90f43dafe5cd9b3b0be86de22f9de83677d0f50713a468ec72fcf5d57", - "result": "valid" + "msg" : "313233343030", + "sig" : "3026020201010220783266e90f43dafe5cd9b3b0be86de22f9de83677d0f50713a468ec72fcf5d57", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c654a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265", - "wx": "00d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c65", - "wy": "4a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c654a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265", + "wx" : "00d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c65", + "wy" : "4a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c654a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1u8gvmbIk/dBqb+Q2bdGddHCoxKWOXrL\nPvF0/QswDGVKDJVHjKADmRYtfw8tyJ79wrKKMPur4oWFcpWksMTiZQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d6ef20be66c893f741a9bf90d9b74675d1c2a31296397acb3ef174fd0b300c654a0c95478ca00399162d7f0f2dc89efdc2b28a30fbabe285857295a4b0c4e265", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1u8gvmbIk/dBqb+Q2bdGddHCoxKWOXrL\nPvF0/QswDGVKDJVHjKADmRYtfw8tyJ79wrKKMPur4oWFcpWksMTiZQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 363, - "comment": "100-bit r and s^-1", - "flags": [ + "tcId" : 363, + "comment" : "100-bit r and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3031020d062522bbd3ecbe7c39e93e7c260220783266e90f43dafe5cd9b3b0be86de22f9de83677d0f50713a468ec72fcf5d57", - "result": "valid" + "msg" : "313233343030", + "sig" : "3031020d062522bbd3ecbe7c39e93e7c260220783266e90f43dafe5cd9b3b0be86de22f9de83677d0f50713a468ec72fcf5d57", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee0629c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829", - "wx": "00b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee06", - "wy": "29c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee0629c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829", + "wx" : "00b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee06", + "wy" : "29c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee0629c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtykdFATgwMB9q5NyGJ9L1Y0s6qjRXt5U\nTZUUVFup7gYpyaY9XjCHacww7CdqQQ5kZKJ+6v2eWZ2xDwU6T+SoKQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004b7291d1404e0c0c07dab9372189f4bd58d2ceaa8d15ede544d9514545ba9ee0629c9a63d5e308769cc30ec276a410e6464a27eeafd9e599db10f053a4fe4a829", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEtykdFATgwMB9q5NyGJ9L1Y0s6qjRXt5U\nTZUUVFup7gYpyaY9XjCHacww7CdqQQ5kZKJ+6v2eWZ2xDwU6T+SoKQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 364, - "comment": "r and s^-1 are close to n", - "flags": [ + "tcId" : 364, + "comment" : "r and s^-1 are close to n", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3045022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03640c1022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", - "result": "valid" + "msg" : "313233343030", + "sig" : "3045022100fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03640c1022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "046e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6", - "wx": "6e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8", - "wy": "186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6", + "wx" : "6e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8", + "wy" : "186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbigwMwXWQsy5I7ci6oayoLyONzXssm6E\nmxnJ92sv27gYboDWTYyrFk9SOPUxhGG/idTZbuZUTIFsdWaUd3Tg9g==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046e28303305d642ccb923b722ea86b2a0bc8e3735ecb26e849b19c9f76b2fdbb8186e80d64d8cab164f5238f5318461bf89d4d96ee6544c816c7566947774e0f6", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbigwMwXWQsy5I7ci6oayoLyONzXssm6E\nmxnJ92sv27gYboDWTYyrFk9SOPUxhGG/idTZbuZUTIFsdWaUd3Tg9g==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 365, - "comment": "r and s are 64-bit integer", - "flags": [ + "tcId" : 365, + "comment" : "r and s are 64-bit integer", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "30160209009c44febf31c3594d020900839ed28247c2b06b", - "result": "valid" + "msg" : "313233343030", + "sig" : "30160209009c44febf31c3594d020900839ed28247c2b06b", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd", - "wx": "375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9", - "wy": "00a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd", + "wx" : "375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9", + "wy" : "00a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEN1vak/avkvtfj0sbXwU047r6s0y3rZ+5\n0Lci5KXDAqmgC584elo5YJeqIWL8W7z0pSYzcvaByU2lHpeZEgmQ/Q==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004375bda93f6af92fb5f8f4b1b5f0534e3bafab34cb7ad9fb9d0b722e4a5c302a9a00b9f387a5a396097aa2162fc5bbcf4a5263372f681c94da51e9799120990fd", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEN1vak/avkvtfj0sbXwU047r6s0y3rZ+5\n0Lci5KXDAqmgC584elo5YJeqIWL8W7z0pSYzcvaByU2lHpeZEgmQ/Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 366, - "comment": "r and s are 100-bit integer", - "flags": [ + "tcId" : 366, + "comment" : "r and s are 100-bit integer", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "301e020d09df8b682430beef6f5fd7c7cf020d0fd0a62e13778f4222a0d61c8a", - "result": "valid" + "msg" : "313233343030", + "sig" : "301e020d09df8b682430beef6f5fd7c7cf020d0fd0a62e13778f4222a0d61c8a", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44", - "wx": "00d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197", - "wy": "00da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44", + "wx" : "00d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197", + "wy" : "00da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE11toIWur4DriV+lLTjvxxS9E498mbRUk\n/4xepp2nMZfaS/+e0cU/RJF6Z9e5eFmOid81nj1ZE+rqJPOuJZq8RA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d75b68216babe03ae257e94b4e3bf1c52f44e3df266d1524ff8c5ea69da73197da4bff9ed1c53f44917a67d7b978598e89df359e3d5913eaea24f3ae259abc44", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE11toIWur4DriV+lLTjvxxS9E498mbRUk\n/4xepp2nMZfaS/+e0cU/RJF6Z9e5eFmOid81nj1ZE+rqJPOuJZq8RA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 367, - "comment": "r and s are 128-bit integer", - "flags": [ + "tcId" : 367, + "comment" : "r and s are 128-bit integer", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "30260211008a598e563a89f526c32ebec8de26367a02110084f633e2042630e99dd0f1e16f7a04bf", - "result": "valid" + "msg" : "313233343030", + "sig" : "30260211008a598e563a89f526c32ebec8de26367a02110084f633e2042630e99dd0f1e16f7a04bf", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0478bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e", - "wx": "78bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653", - "wy": "118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0478bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e", + "wx" : "78bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653", + "wy" : "118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000478bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeLzaFArtI9QwyyPD3A0B9CPbE07pSjqM\ntIPy3qwqxlMRgRT28zBF1OntkQcIUAe/vd+PWP56GiRF1mqZAEVHbg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000478bcda140aed23d430cb23c3dc0d01f423db134ee94a3a8cb483f2deac2ac653118114f6f33045d4e9ed9107085007bfbddf8f58fe7a1a2445d66a990045476e", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeLzaFArtI9QwyyPD3A0B9CPbE07pSjqM\ntIPy3qwqxlMRgRT28zBF1OntkQcIUAe/vd+PWP56GiRF1mqZAEVHbg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 368, - "comment": "r and s are 160-bit integer", - "flags": [ + "tcId" : 368, + "comment" : "r and s are 160-bit integer", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "302e021500aa6eeb5823f7fa31b466bb473797f0d0314c0bdf021500e2977c479e6d25703cebbc6bd561938cc9d1bfb9", - "result": "valid" + "msg" : "313233343030", + "sig" : "302e021500aa6eeb5823f7fa31b466bb473797f0d0314c0bdf021500e2977c479e6d25703cebbc6bd561938cc9d1bfb9", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677", - "wx": "00bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c", - "wy": "1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677", + "wx" : "00bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c", + "wy" : "1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEu3n2GFf3Q7+htucRHOQJQ3claWnk4VFZ\nEj2VSKzDvmwfnZ+IYNz/0+s23Wwx/y5yJsIAnEyU2NfStWhr96vWdw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004bb79f61857f743bfa1b6e7111ce4094377256969e4e15159123d9548acc3be6c1f9d9f8860dcffd3eb36dd6c31ff2e7226c2009c4c94d8d7d2b5686bf7abd677", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEu3n2GFf3Q7+htucRHOQJQ3claWnk4VFZ\nEj2VSKzDvmwfnZ+IYNz/0+s23Wwx/y5yJsIAnEyU2NfStWhr96vWdw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 369, - "comment": "s == 1", - "flags": [ + "tcId" : 369, + "comment" : "s == 1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3025022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1020101", - "result": "valid" + "msg" : "313233343030", + "sig" : "3025022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1020101", + "result" : "valid" }, { - "tcId": 370, - "comment": "s == 0", - "flags": [ + "tcId" : 370, + "comment" : "s == 0", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3025022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1020100", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3025022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1020100", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0493591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518", - "wx": "0093591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36", - "wy": "073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0493591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518", + "wx" : "0093591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36", + "wy" : "073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000493591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEk1kYJ9nmcTtOn66mLHKyjf76aODAUWC1\n1qroj9LjbDYHP1VFrVr0EK8mr/9oZUz3LUXkk0iTESAyRzR6iQ9FGA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000493591827d9e6713b4e9faea62c72b28dfefa68e0c05160b5d6aae88fd2e36c36073f5545ad5af410af26afff68654cf72d45e493489311203247347a890f4518", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEk1kYJ9nmcTtOn66mLHKyjf76aODAUWC1\n1qroj9LjbDYHP1VFrVr0EK8mr/9oZUz3LUXkk0iTESAyRzR6iQ9FGA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 371, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 371, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10220419d981c515af8cc82545aac0c85e9e308fbb2eab6acd7ed497e0b4145a18fd9", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10220419d981c515af8cc82545aac0c85e9e308fbb2eab6acd7ed497e0b4145a18fd9", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0431ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0dada01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f", - "wx": "31ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0da", - "wy": "00da01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0431ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0dada01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f", + "wx" : "31ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0da", + "wy" : "00da01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000431ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0dada01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMe0wga7+AB62QCBp7izMGGKTe4WZUUTb\nqVA5Q1h78NraAbjMTfNPWrOxo1lhUgiUbl7jX5jud1uMzs2GzMFlDw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000431ed3081aefe001eb6402069ee2ccc1862937b85995144dba9503943587bf0dada01b8cc4df34f5ab3b1a359615208946e5ee35f98ee775b8ccecd86ccc1650f", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMe0wga7+AB62QCBp7izMGGKTe4WZUUTb\nqVA5Q1h78NraAbjMTfNPWrOxo1lhUgiUbl7jX5jud1uMzs2GzMFlDw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 372, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 372, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102201b21717ad71d23bbac60a9ad0baf75b063c9fdf52a00ebf99d022172910993c9", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102201b21717ad71d23bbac60a9ad0baf75b063c9fdf52a00ebf99d022172910993c9", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "047dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea854c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4", - "wx": "7dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea8", - "wy": "54c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "047dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea854c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4", + "wx" : "7dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea8", + "wy" : "54c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200047dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea854c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEff9m+phQn/Pi5RBF9DkFI9zNpDo7wohe\nWMJICQmQ7qhUx2wrmt62u1cYI+B/18ZchjnPnZBSYAZMjnZ1zm2YtA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200047dff66fa98509ff3e2e51045f4390523dccda43a3bc2885e58c248090990eea854c76c2b9adeb6bb571823e07fd7c65c8639cf9d905260064c8e7675ce6d98b4", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEff9m+phQn/Pi5RBF9DkFI9zNpDo7wohe\nWMJICQmQ7qhUx2wrmt62u1cYI+B/18ZchjnPnZBSYAZMjnZ1zm2YtA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 373, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 373, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102202f588f66018f3dd14db3e28e77996487e32486b521ed8e5a20f06591951777e9", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102202f588f66018f3dd14db3e28e77996487e32486b521ed8e5a20f06591951777e9", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "044280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1", - "wx": "4280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a", - "wy": "2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "044280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1", + "wx" : "4280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a", + "wy" : "2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200044280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEQoBQmqtk7fwLSiln5MvOhJy1ROSncxPI\n5uzlefvXQgouif5cwZJ9VU5qO7FAM+p8kizXXLosdBX9q1LyCxhg8Q==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200044280509aab64edfc0b4a2967e4cbce849cb544e4a77313c8e6ece579fbd7420a2e89fe5cc1927d554e6a3bb14033ea7c922cd75cba2c7415fdab52f20b1860f1", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEQoBQmqtk7fwLSiln5MvOhJy1ROSncxPI\n5uzlefvXQgouif5cwZJ9VU5qO7FAM+p8kizXXLosdBX9q1LyCxhg8Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 374, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 374, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10220091a08870ff4daf9123b30c20e8c4fc8505758dcf4074fcaff2170c9bfcf74f4", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10220091a08870ff4daf9123b30c20e8c4fc8505758dcf4074fcaff2170c9bfcf74f4", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "044f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db", - "wx": "4f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb", - "wy": "2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "044f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db", + "wx" : "4f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb", + "wy" : "2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200044f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAET43xRRlOPE/D7qJtQ851tALWsXRy3cuy\nVLinmwvz2csqog2ChEyyZjROccp48q0np1oJ5bwPpX5O/Z1GWgiI2w==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200044f8df145194e3c4fc3eea26d43ce75b402d6b17472ddcbb254b8a79b0bf3d9cb2aa20d82844cb266344e71ca78f2ad27a75a09e5bc0fa57e4efd9d465a0888db", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAET43xRRlOPE/D7qJtQ851tALWsXRy3cuy\nVLinmwvz2csqog2ChEyyZjROccp48q0np1oJ5bwPpX5O/Z1GWgiI2w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 375, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 375, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102207c370dc0ce8c59a8b273cba44a7c1191fc3186dc03cab96b0567312df0d0b250", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102207c370dc0ce8c59a8b273cba44a7c1191fc3186dc03cab96b0567312df0d0b250", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "049598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207", - "wx": "009598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14", - "wy": "122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "049598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207", + "wx" : "009598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14", + "wy" : "122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200049598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElZilfdZ+w+FrWHoziqOhCjo5E7QaOvMu\nPtP/ATWMaxQSKBnt+AdLvFIffUzc6C/velFnBq/7odk9neqcyuGiBw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200049598a57dd67ec3e16b587a338aa3a10a3a3913b41a3af32e3ed3ff01358c6b14122819edf8074bbc521f7d4cdce82fef7a516706affba1d93d9dea9ccae1a207", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElZilfdZ+w+FrWHoziqOhCjo5E7QaOvMu\nPtP/ATWMaxQSKBnt+AdLvFIffUzc6C/velFnBq/7odk9neqcyuGiBw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 376, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 376, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1022070b59a7d1ee77a2f9e0491c2a7cfcd0ed04df4a35192f6132dcc668c79a6160e", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1022070b59a7d1ee77a2f9e0491c2a7cfcd0ed04df4a35192f6132dcc668c79a6160e", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "049171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330", - "wx": "009171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e", - "wy": "634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "049171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330", + "wx" : "009171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e", + "wy" : "634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200049171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEkXH+w8oggGvAhPEvB2CRG2CZC9gOWypx\nygOgSLIPg35jT9F4Y3YbKVjSvk4Un409ervcGL4D9FGrbBf6Ch+DMA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200049171fec3ca20806bc084f12f0760911b60990bd80e5b2a71ca03a048b20f837e634fd17863761b2958d2be4e149f8d3d7abbdc18be03f451ab6c17fa0a1f8330", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEkXH+w8oggGvAhPEvB2CRG2CZC9gOWypx\nygOgSLIPg35jT9F4Y3YbKVjSvk4Un409ervcGL4D9FGrbBf6Ch+DMA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 377, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 377, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102202736d76e412246e097148e2bf62915614eb7c428913a58eb5e9cd4674a9423de", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102202736d76e412246e097148e2bf62915614eb7c428913a58eb5e9cd4674a9423de", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d", - "wx": "777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9", - "wy": "00ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d", + "wx" : "777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9", + "wy" : "00ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEd3yJMLbh0nEQD+aM6T8WP6N2EsX/9n9K\nYvw7r689F6ntc9hvYKUbXtkTU6OwVO3AqpLJ68vQt10Yj9yIJ5HWjQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004777c8930b6e1d271100fe68ce93f163fa37612c5fff67f4a62fc3bafaf3d17a9ed73d86f60a51b5ed91353a3b054edc0aa92c9ebcbd0b75d188fdc882791d68d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEd3yJMLbh0nEQD+aM6T8WP6N2EsX/9n9K\nYvw7r689F6ntc9hvYKUbXtkTU6OwVO3AqpLJ68vQt10Yj9yIJ5HWjQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 378, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 378, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102204a1e12831fbe93627b02d6e7f24bccdd6ef4b2d0f46739eaf3b1eaf0ca117770", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102204a1e12831fbe93627b02d6e7f24bccdd6ef4b2d0f46739eaf3b1eaf0ca117770", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf4700603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000", - "wx": "00eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf470", - "wy": "0603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf4700603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000", + "wx" : "00eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf470", + "wy" : "0603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf4700603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE6rwkj2JuCmPh64HEPUYaOaHbqIHrbuIV\nKwfDLXG89HAGA8qoudM9sTr0TG777IoZjtYSSsnrF+qv0oJKVF7AAA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004eabc248f626e0a63e1eb81c43d461a39a1dba881eb6ee2152b07c32d71bcf4700603caa8b9d33db13af44c6efbec8a198ed6124ac9eb17eaafd2824a545ec000", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE6rwkj2JuCmPh64HEPUYaOaHbqIHrbuIV\nKwfDLXG89HAGA8qoudM9sTr0TG777IoZjtYSSsnrF+qv0oJKVF7AAA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 379, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 379, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1022006c778d4dfff7dee06ed88bc4e0ed34fc553aad67caf796f2a1c6487c1b2e877", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1022006c778d4dfff7dee06ed88bc4e0ed34fc553aad67caf796f2a1c6487c1b2e877", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "049f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73", - "wx": "009f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001", - "wy": "00f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "049f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73", + "wx" : "009f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001", + "wy" : "00f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200049f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEn3oTraFYpV+d3xpF8ETwc9m4ADDv3Pyf\nn1hBj7zq8AH4raAXUJD4DUcifWcTtnQPmgCR2IqDfQoc13tYqPKNcw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200049f7a13ada158a55f9ddf1a45f044f073d9b80030efdcfc9f9f58418fbceaf001f8ada0175090f80d47227d6713b6740f9a0091d88a837d0a1cd77b58a8f28d73", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEn3oTraFYpV+d3xpF8ETwc9m4ADDv3Pyf\nn1hBj7zq8AH4raAXUJD4DUcifWcTtnQPmgCR2IqDfQoc13tYqPKNcw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 380, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 380, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102204de459ef9159afa057feb3ec40fef01c45b809f4ab296ea48c206d4249a2b451", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102204de459ef9159afa057feb3ec40fef01c45b809f4ab296ea48c206d4249a2b451", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0411c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4dbbbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb", - "wx": "11c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4db", - "wy": "00bbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0411c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4dbbbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb", + "wx" : "11c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4db", + "wy" : "00bbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000411c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4dbbbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEEcTz5GHNAZtcBuoM6kxAkMPMPjxdnzxt\nZbQ2gm2ptNu763p35Mv9ogcJfENCNwX3LIBHbaPaxApIOwqw8urRyw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000411c4f3e461cd019b5c06ea0cea4c4090c3cc3e3c5d9f3c6d65b436826da9b4dbbbeb7a77e4cbfda207097c43423705f72c80476da3dac40a483b0ab0f2ead1cb", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEEcTz5GHNAZtcBuoM6kxAkMPMPjxdnzxt\nZbQ2gm2ptNu763p35Mv9ogcJfENCNwX3LIBHbaPaxApIOwqw8urRyw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 381, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 381, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10220745d294978007302033502e1acc48b63ae6500be43adbea1b258d6b423dbb416", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c10220745d294978007302033502e1acc48b63ae6500be43adbea1b258d6b423dbb416", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb", - "wx": "00e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4", - "wy": "161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb", + "wx" : "00e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4", + "wy" : "161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE4uGGgtUxI6oBpsXQCwxiPWcbRi6oC93W\nUif9UQWYiqQWGQez/SUESpSepByOLqhFncbxZUhWuLYbMVQ7sbRb2w==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004e2e18682d53123aa01a6c5d00b0c623d671b462ea80bddd65227fd5105988aa4161907b3fd25044a949ea41c8e2ea8459dc6f1654856b8b61b31543bb1b45bdb", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE4uGGgtUxI6oBpsXQCwxiPWcbRi6oC93W\nUif9UQWYiqQWGQez/SUESpSepByOLqhFncbxZUhWuLYbMVQ7sbRb2w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 382, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 382, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102207b2a785e3896f59b2d69da57648e80ad3c133a750a2847fd2098ccd902042b6c", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102207b2a785e3896f59b2d69da57648e80ad3c133a750a2847fd2098ccd902042b6c", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0490f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197dafadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d", - "wx": "0090f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197da", - "wy": "00fadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0490f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197dafadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d", + "wx" : "0090f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197da", + "wy" : "00fadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000490f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197dafadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEkPjUynPeCKZWSq8AUke28P/peFBNzlJg\nX0a3w+Vhl9r62+Uo63DZ7n6g5wcC21T3IVFMe4YErCyyFPHey344PQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000490f8d4ca73de08a6564aaf005247b6f0ffe978504dce52605f46b7c3e56197dafadbe528eb70d9ee7ea0e70702db54f721514c7b8604ac2cb214f1decb7e383d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEkPjUynPeCKZWSq8AUke28P/peFBNzlJg\nX0a3w+Vhl9r62+Uo63DZ7n6g5wcC21T3IVFMe4YErCyyFPHey344PQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 383, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 383, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1022071ae94a72ca896875e7aa4a4c3d29afdb4b35b6996273e63c47ac519256c5eb1", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c1022071ae94a72ca896875e7aa4a4c3d29afdb4b35b6996273e63c47ac519256c5eb1", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc", - "wx": "00824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e", - "wy": "3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc", + "wx" : "00824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e", + "wy" : "3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEgkwZXHPP/fA40QG84Wh7XDthRvOVyIWX\nb3dTsjdrlI483vpvw0fRPk3LxjoLA6FlGAzSvhQxoM90zh6iUILSvA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004824c195c73cffdf038d101bce1687b5c3b6146f395c885976f7753b2376b948e3cdefa6fc347d13e4dcbc63a0b03a165180cd2be1431a0cf74ce1ea25082d2bc", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEgkwZXHPP/fA40QG84Wh7XDthRvOVyIWX\nb3dTsjdrlI483vpvw0fRPk3LxjoLA6FlGAzSvhQxoM90zh6iUILSvA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 384, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 384, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102200fa527fa7343c0bc9ec35a6278bfbff4d83301b154fc4bd14aee7eb93445b5f9", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102200fa527fa7343c0bc9ec35a6278bfbff4d83301b154fc4bd14aee7eb93445b5f9", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "042788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f", - "wx": "2788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f", - "wy": "30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f", + "wx" : "2788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f", + "wy" : "30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEJ4ilLweOs/ICxPpz4NM4b6899r6FYANj\nb1mZItT1Jo8wtPIHyRm7315nqL5CZagXR1Szq6jxbldbd/9NWn62Tw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042788a52f078eb3f202c4fa73e0d3386faf3df6be856003636f599922d4f5268f30b4f207c919bbdf5e67a8be4265a8174754b3aba8f16e575b77ff4d5a7eb64f", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEJ4ilLweOs/ICxPpz4NM4b6899r6FYANj\nb1mZItT1Jo8wtPIHyRm7315nqL5CZagXR1Szq6jxbldbd/9NWn62Tw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 385, - "comment": "edge case modular inverse", - "flags": [ + "tcId" : 385, + "comment" : "edge case modular inverse", + "flags" : [ "ModularInverse", "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102206539c0adadd0525ff42622164ce9314348bd0863b4c80e936b23ca0414264671", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c102206539c0adadd0525ff42622164ce9314348bd0863b4c80e936b23ca0414264671", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b415087401b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4", - "wx": "00d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b4150874", - "wy": "01b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b415087401b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4", + "wx" : "00d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b4150874", + "wy" : "01b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b415087401b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1TO3iaSviQ+nqCofrljEBPmmKlC0mtr6\ns0nFE7QVCHQBtBcbgD52s0qYYeEPe8KJoGb9Ab0p+EyYehCl+xjC1A==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d533b789a4af890fa7a82a1fae58c404f9a62a50b49adafab349c513b415087401b4171b803e76b34a9861e10f7bc289a066fd01bd29f84c987a10a5fb18c2d4", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE1TO3iaSviQ+nqCofrljEBPmmKlC0mtr6\ns0nFE7QVCHQBtBcbgD52s0qYYeEPe8KJoGb9Ab0p+EyYehCl+xjC1A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 386, - "comment": "point at infinity during verify", - "flags": [ + "tcId" : 386, + "comment" : "point at infinity during verify", + "flags" : [ "PointDuplication", "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a", - "wx": "3a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4", - "wy": "221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a", + "wx" : "3a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4", + "wy" : "221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOjFQeYyK9p0ebpgfOkVAK6HXMvS+gzDF\nFk9J4Q7FVbQiG9hCvF5Nl+/zcWX2DjmYpCTXKkUM+V6kd8eCh9A0Og==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200043a3150798c8af69d1e6e981f3a45402ba1d732f4be8330c5164f49e10ec555b4221bd842bc5e4d97eff37165f60e3998a424d72a450cf95ea477c78287d0343a", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOjFQeYyK9p0ebpgfOkVAK6HXMvS+gzDF\nFk9J4Q7FVbQiG9hCvF5Nl+/zcWX2DjmYpCTXKkUM+V6kd8eCh9A0Og==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 387, - "comment": "edge case for signature malleability", - "flags": [ + "tcId" : 387, + "comment" : "edge case for signature malleability", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "043b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e800de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026", - "wx": "3b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e80", - "wy": "0de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "043b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e800de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026", + "wx" : "3b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e80", + "wy" : "0de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200043b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e800de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOzffX7NHxpoPF9hcDHyoNzaIOoJeExQ9\nD8/IEB6FHoAN48CQtsohulQ1FzMMBLEvlIxrrfFKY6v/3074x1NwJg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200043b37df5fb347c69a0f17d85c0c7ca83736883a825e13143d0fcfc8101e851e800de3c090b6ca21ba543517330c04b12f948c6badf14a63abffdf4ef8c7537026", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOzffX7NHxpoPF9hcDHyoNzaIOoJeExQ9\nD8/IEB6FHoAN48CQtsohulQ1FzMMBLEvlIxrrfFKY6v/3074x1NwJg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 388, - "comment": "edge case for signature malleability", - "flags": [ + "tcId" : 388, + "comment" : "edge case for signature malleability", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", - "result": "invalid" + "msg" : "313233343030", + "sig" : "304402207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a002207fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82ce87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e", - "wx": "00feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82c", - "wy": "00e87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82ce87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e", + "wx" : "00feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82c", + "wy" : "00e87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82ce87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/rUWOw7OMP8+A8fVXEOA+i+oHuLANUlC\n/28IyZ0M2CzofeBe4b2gidPk4kj6D3IRAqz//fUOZUvigUM5md+Jfg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004feb5163b0ece30ff3e03c7d55c4380fa2fa81ee2c0354942ff6f08c99d0cd82ce87de05ee1bda089d3e4e248fa0f721102acfffdf50e654be281433999df897e", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/rUWOw7OMP8+A8fVXEOA+i+oHuLANUlC\n/28IyZ0M2CzofeBe4b2gidPk4kj6D3IRAqz//fUOZUvigUM5md+Jfg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 389, - "comment": "u1 == 1", - "flags": [ + "tcId" : 389, + "comment" : "u1 == 1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd414922897640683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2", - "wx": "238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd4149228976", - "wy": "40683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd414922897640683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2", + "wx" : "238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd4149228976", + "wy" : "40683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd414922897640683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEI4ztABzyK4hT4C7cicvspQULp+BCp6d/\nk4LNQUkiiXZAaD0wlGQ4QPKViQqkwYqjm0HXfdD7O7JwDk+ewoT/wg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004238ced001cf22b8853e02edc89cbeca5050ba7e042a7a77f9382cd414922897640683d3094643840f295890aa4c18aa39b41d77dd0fb3bb2700e4f9ec284ffc2", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEI4ztABzyK4hT4C7cicvspQULp+BCp6d/\nk4LNQUkiiXZAaD0wlGQ4QPKViQqkwYqjm0HXfdD7O7JwDk+ewoT/wg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 390, - "comment": "u1 == n - 1", - "flags": [ + "tcId" : 390, + "comment" : "u1 == n - 1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35ed2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf", - "wx": "00961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35e", - "wy": "00d2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35ed2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf", + "wx" : "00961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35e", + "wy" : "00d2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35ed2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElhz2SBfAbA5Rs8JzbJIv3hi9jEkG/Nf1\n72bEZ4UI817SxdGBaM++cPLxI710GSMruS3WkRPilBBhiJSBxaAnvw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004961cf64817c06c0e51b3c2736c922fde18bd8c4906fcd7f5ef66c4678508f35ed2c5d18168cfbe70f2f123bd7419232bb92dd69113e2941061889481c5a027bf", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElhz2SBfAbA5Rs8JzbJIv3hi9jEkG/Nf1\n72bEZ4UI817SxdGBaM++cPLxI710GSMruS3WkRPilBBhiJSBxaAnvw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 391, - "comment": "u2 == 1", - "flags": [ + "tcId" : 391, + "comment" : "u2 == 1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0413681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b1028816528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384", - "wx": "13681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b10288", - "wy": "16528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0413681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b1028816528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384", + "wx" : "13681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b10288", + "wy" : "16528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000413681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b1028816528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEE2gerhaM1Op88uKkXQUnQtEKn2TnloZ9\nvcuCn+CxAogWUodg0Xc3bAnfed45VXwynMF1NRes/+j6LsKYAmuDhA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000413681eae168cd4ea7cf2e2a45d052742d10a9f64e796867dbdcb829fe0b1028816528760d177376c09df79de39557c329cc1753517acffe8fa2ec298026b8384", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEE2gerhaM1Op88uKkXQUnQtEKn2TnloZ9\nvcuCn+CxAogWUodg0Xc3bAnfed45VXwynMF1NRes/+j6LsKYAmuDhA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 392, - "comment": "u2 == n - 1", - "flags": [ + "tcId" : 392, + "comment" : "u2 == n - 1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215b8", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "045aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c291c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b", - "wx": "5aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c2", - "wy": "0091c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "045aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c291c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b", + "wx" : "5aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c2", + "wy" : "0091c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200045aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c291c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEWqer/ba0CG1UMyXl15xulc5C+GbSu4SQ\nljOgS7GqMcKRyACIeUkF4dozM22HTi+RzPRcxZGFvt5d1vP3rKrhiw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200045aa7abfdb6b4086d543325e5d79c6e95ce42f866d2bb84909633a04bb1aa31c291c80088794905e1da33336d874e2f91ccf45cc59185bede5dd6f3f7acaae18b", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEWqer/ba0CG1UMyXl15xulc5C+GbSu4SQ\nljOgS7GqMcKRyACIeUkF4dozM22HTi+RzPRcxZGFvt5d1vP3rKrhiw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 393, - "comment": "edge case for u1", - "flags": [ + "tcId" : 393, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022016e1e459457679df5b9434ae23f474b3e8d2a70bd6b5dbe692ba16da01f1fb0a", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022016e1e459457679df5b9434ae23f474b3e8d2a70bd6b5dbe692ba16da01f1fb0a", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0400277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e464108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41", - "wx": "277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e4", - "wy": "64108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0400277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e464108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41", + "wx" : "277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e4", + "wy" : "64108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000400277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e464108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEACd3kbMFpFsrOVkLLwXTOSpsgYLO9OtU\nASDg9cIGw+RkEIIz+wuMOsiS15744Pv5LtEzrdtFVCcBMlhNxS7vQQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000400277791b305a45b2b39590b2f05d3392a6c8182cef4eb540120e0f5c206c3e464108233fb0b8c3ac892d79ef8e0fbf92ed133addb4554270132584dc52eef41", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEACd3kbMFpFsrOVkLLwXTOSpsgYLO9OtU\nASDg9cIGw+RkEIIz+wuMOsiS15744Pv5LtEzrdtFVCcBMlhNxS7vQQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 394, - "comment": "edge case for u1", - "flags": [ + "tcId" : 394, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02201c940f313f92647be257eccd7ed08b0baef3f0478f25871b53635302c5f6314a", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02201c940f313f92647be257eccd7ed08b0baef3f0478f25871b53635302c5f6314a", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "046efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1ac75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49", - "wx": "6efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1a", - "wy": "00c75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1ac75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49", + "wx" : "6efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1a", + "wy" : "00c75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1ac75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbvoJK2jelGDwvMkZAFpfboDhnemJaL48\n0sdwqZSb+xrHXm5Qh9ZVDV+b6x555QKTB7wlUjXi1dyZJBrDq4hsSQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046efa092b68de9460f0bcc919005a5f6e80e19de98968be3cd2c770a9949bfb1ac75e6e5087d6550d5f9beb1e79e5029307bc255235e2d5dc99241ac3ab886c49", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbvoJK2jelGDwvMkZAFpfboDhnemJaL48\n0sdwqZSb+xrHXm5Qh9ZVDV+b6x555QKTB7wlUjXi1dyZJBrDq4hsSQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 395, - "comment": "edge case for u1", - "flags": [ + "tcId" : 395, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022015d94a85077b493f91cb7101ec63e1b01be58b594e855f45050a8c14062d689b", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022015d94a85077b493f91cb7101ec63e1b01be58b594e855f45050a8c14062d689b", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0472d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942", - "wx": "72d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058", - "wy": "00e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0472d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942", + "wx" : "72d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058", + "wy" : "00e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000472d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEctShnE+dLPWEjqQERbcNRpa18C1jLAxl\nTMfX7rDG0FjoxM2ZQ+RZF0x6wB+nQhmOR+bBmmvbDE9sI3gxwbP5Qg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000472d4a19c4f9d2cf5848ea40445b70d4696b5f02d632c0c654cc7d7eeb0c6d058e8c4cd9943e459174c7ac01fa742198e47e6c19a6bdb0c4f6c237831c1b3f942", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEctShnE+dLPWEjqQERbcNRpa18C1jLAxl\nTMfX7rDG0FjoxM2ZQ+RZF0x6wB+nQhmOR+bBmmvbDE9sI3gxwbP5Qg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 396, - "comment": "edge case for u1", - "flags": [ + "tcId" : 396, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02205b1d27a7694c146244a5ad0bd0636d9d9ef3b9fb58385418d9c982105077d1b7", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02205b1d27a7694c146244a5ad0bd0636d9d9ef3b9fb58385418d9c982105077d1b7", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "042a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e740258f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec", - "wx": "2a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e7402", - "wy": "58f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e740258f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec", + "wx" : "2a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e7402", + "wy" : "58f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e740258f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEKo6i9Q3M7QwhdXW9+nzUfRxvEABB7A41\nUSeUwb5+dAJY+MFxIu0wP9pxQ+tYvt5wKVtlMmYBOwsOvT8FMTf27A==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042a8ea2f50dcced0c217575bdfa7cd47d1c6f100041ec0e35512794c1be7e740258f8c17122ed303fda7143eb58bede70295b653266013b0b0ebd3f053137f6ec", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEKo6i9Q3M7QwhdXW9+nzUfRxvEABB7A41\nUSeUwb5+dAJY+MFxIu0wP9pxQ+tYvt5wKVtlMmYBOwsOvT8FMTf27A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 397, - "comment": "edge case for u1", - "flags": [ + "tcId" : 397, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202d85896b3eb9dbb5a52f42f9c9261ed3fc46644ec65f06ade3fd78f257e43432", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202d85896b3eb9dbb5a52f42f9c9261ed3fc46644ec65f06ade3fd78f257e43432", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0488de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b80c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946", - "wx": "0088de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b8", - "wy": "0c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0488de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b80c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946", + "wx" : "0088de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b8", + "wy" : "0c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000488de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b80c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEiN5onOmvHpS+aiCJyKixJT/9u2yOnIYk\nm6IgABpK07gMSZjlSEL0E7ntsYJay7YzXoHk0YSysByL69yF0fKJRg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000488de689ce9af1e94be6a2089c8a8b1253ffdbb6c8e9c86249ba220001a4ad3b80c4998e54842f413b9edb1825acbb6335e81e4d184b2b01c8bebdc85d1f28946", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEiN5onOmvHpS+aiCJyKixJT/9u2yOnIYk\nm6IgABpK07gMSZjlSEL0E7ntsYJay7YzXoHk0YSysByL69yF0fKJRg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 398, - "comment": "edge case for u1", - "flags": [ + "tcId" : 398, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02205b0b12d67d73b76b4a5e85f3924c3da7f88cc89d8cbe0d5bc7faf1e4afc86864", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02205b0b12d67d73b76b4a5e85f3924c3da7f88cc89d8cbe0d5bc7faf1e4afc86864", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9", - "wx": "00fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7", - "wy": "00b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9", + "wx" : "00fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7", + "wy" : "00b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/qLTH3D5DV+z4A4YasQqs8FhXO5xTgtO\nETGz1NgiW/ewN6GN8qwVND8w90Bn3fKegX1fd/jc4FcU2lnAlPDNqQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004fea2d31f70f90d5fb3e00e186ac42ab3c1615cee714e0b4e1131b3d4d8225bf7b037a18df2ac15343f30f74067ddf29e817d5f77f8dce05714da59c094f0cda9", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE/qLTH3D5DV+z4A4YasQqs8FhXO5xTgtO\nETGz1NgiW/ewN6GN8qwVND8w90Bn3fKegX1fd/jc4FcU2lnAlPDNqQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 399, - "comment": "edge case for u1", - "flags": [ + "tcId" : 399, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0220694c146244a5ad0bd0636d9e12bc9e09e60e68b90d0b5e6c5dddd0cb694d8799", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0220694c146244a5ad0bd0636d9e12bc9e09e60e68b90d0b5e6c5dddd0cb694d8799", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "047258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0", - "wx": "7258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db", - "wy": "17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "047258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0", + "wx" : "7258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db", + "wy" : "17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200047258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEcliRHj1CM0kWZHnb4Lg0Gvf70D0KfhDt\nzLNrbO6lo9sXrCuJknkRKPo7ltwvvUyjv6eC7ygy/GZWlD2xjnNGsA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200047258911e3d423349166479dbe0b8341af7fbd03d0a7e10edccb36b6ceea5a3db17ac2b8992791128fa3b96dc2fbd4ca3bfa782ef2832fc6656943db18e7346b0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEcliRHj1CM0kWZHnb4Lg0Gvf70D0KfhDt\nzLNrbO6lo9sXrCuJknkRKPo7ltwvvUyjv6eC7ygy/GZWlD2xjnNGsA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 400, - "comment": "edge case for u1", - "flags": [ + "tcId" : 400, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203d7f487c07bfc5f30846938a3dcef696444707cf9677254a92b06c63ab867d22", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203d7f487c07bfc5f30846938a3dcef696444707cf9677254a92b06c63ab867d22", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "044f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470", - "wx": "4f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914", - "wy": "00c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "044f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470", + "wx" : "4f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914", + "wy" : "00c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200044f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAETyhGHepkR01rs00Umcl9N7npVjPfHO7q\nrNRQFsmLORTIgYgQuMwG3bQOihJhxSj6pYlFXVpt+Tt3vF4OSTx0cA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200044f28461dea64474d6bb34d1499c97d37b9e95633df1ceeeaacd45016c98b3914c8818810b8cc06ddb40e8a1261c528faa589455d5a6df93b77bc5e0e493c7470", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAETyhGHepkR01rs00Umcl9N7npVjPfHO7q\nrNRQFsmLORTIgYgQuMwG3bQOihJhxSj6pYlFXVpt+Tt3vF4OSTx0cA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 401, - "comment": "edge case for u1", - "flags": [ + "tcId" : 401, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02206c7648fc0fbf8a06adb8b839f97b4ff7a800f11b1e37c593b261394599792ba4", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02206c7648fc0fbf8a06adb8b839f97b4ff7a800f11b1e37c593b261394599792ba4", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0474f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b", - "wx": "74f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66", - "wy": "00eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0474f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b", + "wx" : "74f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66", + "wy" : "00eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000474f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEdPKoFPtdjsqRppteYHEnMrOTfeMoKb6X\nTte2jFwvXWbv8PB8VvmHplf0IZYgX1iMDx2W/YpjpfI4tI9Hh4j+Ow==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000474f2a814fb5d8eca91a69b5e60712732b3937de32829be974ed7b68c5c2f5d66eff0f07c56f987a657f42196205f588c0f1d96fd8a63a5f238b48f478788fe3b", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEdPKoFPtdjsqRppteYHEnMrOTfeMoKb6X\nTte2jFwvXWbv8PB8VvmHplf0IZYgX1iMDx2W/YpjpfI4tI9Hh4j+Ow==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 402, - "comment": "edge case for u1", - "flags": [ + "tcId" : 402, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0220641c9c5d790dc09cdd3dfabb62cdf453e69747a7e3d7aa1a714189ef53171a99", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0220641c9c5d790dc09cdd3dfabb62cdf453e69747a7e3d7aa1a714189ef53171a99", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6ab2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad", - "wx": "195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6a", - "wy": "00b2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6ab2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad", + "wx" : "195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6a", + "wy" : "00b2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6ab2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGVtRp8xKIbgnSnCpDed5gUw8jKNYMoII\nwJop8za4LWqyQWt8kv/9wpw7EoLdKnek0E3390UgRzk9hJmJxc7prQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004195b51a7cc4a21b8274a70a90de779814c3c8ca358328208c09a29f336b82d6ab2416b7c92fffdc29c3b1282dd2a77a4d04df7f7452047393d849989c5cee9ad", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGVtRp8xKIbgnSnCpDed5gUw8jKNYMoII\nwJop8za4LWqyQWt8kv/9wpw7EoLdKnek0E3390UgRzk9hJmJxc7prQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 403, - "comment": "edge case for u1", - "flags": [ + "tcId" : 403, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022029798c5c45bdf58b4a7b2fdc2c46ab4af1218c7eeb9f0f27a88f1267674de3b0", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022029798c5c45bdf58b4a7b2fdc2c46ab4af1218c7eeb9f0f27a88f1267674de3b0", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0", - "wx": "622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa", - "wy": "736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0", + "wx" : "622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa", + "wy" : "736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYi/HRzIDS+wt3zvBbTSz0fejJ90qjBm6\ntLtP46JLWKpzay8vrnb0367MkJYzOwEyjVHrP9qckifpDQtEmYPE8A==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004622fc74732034bec2ddf3bc16d34b3d1f7a327dd2a8c19bab4bb4fe3a24b58aa736b2f2fae76f4dfaecc9096333b01328d51eb3fda9c9227e90d0b449983c4f0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYi/HRzIDS+wt3zvBbTSz0fejJ90qjBm6\ntLtP46JLWKpzay8vrnb0367MkJYzOwEyjVHrP9qckifpDQtEmYPE8A==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 404, - "comment": "edge case for u1", - "flags": [ + "tcId" : 404, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02200b70f22ca2bb3cefadca1a5711fa3a59f4695385eb5aedf3495d0b6d00f8fd85", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02200b70f22ca2bb3cefadca1a5711fa3a59f4695385eb5aedf3495d0b6d00f8fd85", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "041f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c70827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93", - "wx": "1f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c7", - "wy": "0827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c70827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93", + "wx" : "1f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c7", + "wy" : "0827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c70827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEH3+FyvLXVQ56+bZQI+u03ONFAxFpIwnb\nJplpuDS2EccIJ/RbeAIOy7r0hP3Vv6rmhw8RhMIVgbr274K9e1MPkw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041f7f85caf2d7550e7af9b65023ebb4dce3450311692309db269969b834b611c70827f45b78020ecbbaf484fdd5bfaae6870f1184c21581baf6ef82bd7b530f93", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEH3+FyvLXVQ56+bZQI+u03ONFAxFpIwnb\nJplpuDS2EccIJ/RbeAIOy7r0hP3Vv6rmhw8RhMIVgbr274K9e1MPkw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 405, - "comment": "edge case for u1", - "flags": [ + "tcId" : 405, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022016e1e459457679df5b9434ae23f474b3e8d2a70bd6b5dbe692ba16da01f1fb0a", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022016e1e459457679df5b9434ae23f474b3e8d2a70bd6b5dbe692ba16da01f1fb0a", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0449c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377aefc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d", - "wx": "49c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377a", - "wy": "00efc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0449c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377aefc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d", + "wx" : "49c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377a", + "wy" : "00efc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000449c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377aefc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEScGX3ICtHaR6Q0K5OJPo4fsLuU/DOoPn\ng8ALJMeBN3rvwg2pK6x2KVH3JHS+zHNNTMIrqBuJXigv2sTfevDzfQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000449c197dc80ad1da47a4342b93893e8e1fb0bb94fc33a83e783c00b24c781377aefc20da92bac762951f72474becc734d4cc22ba81b895e282fdac4df7af0f37d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEScGX3ICtHaR6Q0K5OJPo4fsLuU/DOoPn\ng8ALJMeBN3rvwg2pK6x2KVH3JHS+zHNNTMIrqBuJXigv2sTfevDzfQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 406, - "comment": "edge case for u1", - "flags": [ + "tcId" : 406, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202252d685e831b6cf095e4f0535eeaf0ddd3bfa91c210c9d9dc17224702eaf88f", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202252d685e831b6cf095e4f0535eeaf0ddd3bfa91c210c9d9dc17224702eaf88f", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c", - "wx": "00d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe", - "wy": "7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c", + "wx" : "00d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe", + "wy" : "7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE2MtoUXthalZACqOGhjXlS29plZii9hZ3\nV2VJgLr2rL5+yM9EnISaoDRhow762kFFPFfG5vvJO7xvpJrabcBVXA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d8cb68517b616a56400aa3868635e54b6f699598a2f6167757654980baf6acbe7ec8cf449c849aa03461a30efada41453c57c6e6fbc93bbc6fa49ada6dc0555c", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE2MtoUXthalZACqOGhjXlS29plZii9hZ3\nV2VJgLr2rL5+yM9EnISaoDRhow762kFFPFfG5vvJO7xvpJrabcBVXA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 407, - "comment": "edge case for u1", - "flags": [ + "tcId" : 407, + "comment" : "edge case for u1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022075135abd7c425b60371a477f09ce0f274f64a8c6b061a07b5d63e93c65046c53", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022075135abd7c425b60371a477f09ce0f274f64a8c6b061a07b5d63e93c65046c53", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750", - "wx": "030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3", - "wy": "00b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750", + "wx" : "030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3", + "wy" : "00b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAwcT+2Pyqm/iyt8bIO/CWcd0Rdr6h9rD\nmLhAZco0ffOyJ4GN4aObWJywcdg+UxfMzcIzjlHjEv4x2Nw0pIAXUA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004030713fb63f2aa6fe2cadf1b20efc259c77445dafa87dac398b84065ca347df3b227818de1a39b589cb071d83e5317cccdc2338e51e312fe31d8dc34a4801750", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAwcT+2Pyqm/iyt8bIO/CWcd0Rdr6h9rD\nmLhAZco0ffOyJ4GN4aObWJywcdg+UxfMzcIzjlHjEv4x2Nw0pIAXUA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 408, - "comment": "edge case for u2", - "flags": [ + "tcId" : 408, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b17", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b17", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950", - "wx": "00babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7", - "wy": "252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950", + "wx" : "00babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7", + "wy" : "252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEurs2d7CVWALY6SmkE1VkDq8eoTU/incT\nMcSUbjSAr6clLxlsh+09KlnTsbVZE3/tABP+zvwZ+1qSaCubylG5UA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004babb3677b0955802d8e929a41355640eaf1ea1353f8a771331c4946e3480afa7252f196c87ed3d2a59d3b1b559137fed0013fecefc19fb5a92682b9bca51b950", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEurs2d7CVWALY6SmkE1VkDq8eoTU/incT\nMcSUbjSAr6clLxlsh+09KlnTsbVZE3/tABP+zvwZ+1qSaCubylG5UA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 409, - "comment": "edge case for u2", - "flags": [ + "tcId" : 409, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203e888377ac6c71ac9dec3fdb9b56c9feaf0cfaca9f827fc5eb65fc3eac811210", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203e888377ac6c71ac9dec3fdb9b56c9feaf0cfaca9f827fc5eb65fc3eac811210", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "041aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9", - "wx": "1aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60", - "wy": "00bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9", + "wx" : "1aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60", + "wy" : "00bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGqsgGHk0cREaig6bFD/eAvyVkgeW06Y9\n4ym0JDlvumC75BMHBRdHkkQbMY06ox3+hXeCHptEbsVz0nLgNsTr6Q==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041aab2018793471111a8a0e9b143fde02fc95920796d3a63de329b424396fba60bbe4130705174792441b318d3aa31dfe8577821e9b446ec573d272e036c4ebe9", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEGqsgGHk0cREaig6bFD/eAvyVkgeW06Y9\n4ym0JDlvumC75BMHBRdHkkQbMY06ox3+hXeCHptEbsVz0nLgNsTr6Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 410, - "comment": "edge case for u2", - "flags": [ + "tcId" : 410, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022030bbb794db588363b40679f6c182a50d3ce9679acdd3ffbe36d7813dacbdc818", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022030bbb794db588363b40679f6c182a50d3ce9679acdd3ffbe36d7813dacbdc818", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "048cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75", - "wx": "008cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff", - "wy": "47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75", + "wx" : "008cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff", + "wy" : "47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjLC5CUmcg+qAbNiFsd1GegEZ8GqIoCdu\nsM/aJ0U1qP9HtUKIM7w/LIv52QQRWM8zcYpplhzQFym8ABHR5YardQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048cb0b909499c83ea806cd885b1dd467a0119f06a88a0276eb0cfda274535a8ff47b5428833bc3f2c8bf9d9041158cf33718a69961cd01729bc0011d1e586ab75", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjLC5CUmcg+qAbNiFsd1GegEZ8GqIoCdu\nsM/aJ0U1qP9HtUKIM7w/LIv52QQRWM8zcYpplhzQFym8ABHR5YardQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 411, - "comment": "edge case for u2", - "flags": [ + "tcId" : 411, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202c37fd995622c4fb7fffffffffffffffc7cee745110cb45ab558ed7c90c15a2f", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202c37fd995622c4fb7fffffffffffffffc7cee745110cb45ab558ed7c90c15a2f", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "048f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0", - "wx": "008f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d", - "wy": "3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0", + "wx" : "008f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d", + "wy" : "3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjwPPGkInK7FTJyMJP3Lm/urIXhcA6fvp\npqLdZC10v107iacYna2M91/CL28ViqJ/nCygDaynhb4zWPK9o4YsoA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048f03cf1a42272bb1532723093f72e6feeac85e1700e9fbe9a6a2dd642d74bf5d3b89a7189dad8cf75fc22f6f158aa27f9c2ca00daca785be3358f2bda3862ca0", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEjwPPGkInK7FTJyMJP3Lm/urIXhcA6fvp\npqLdZC10v107iacYna2M91/CL28ViqJ/nCygDaynhb4zWPK9o4YsoA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 412, - "comment": "edge case for u2", - "flags": [ + "tcId" : 412, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02207fd995622c4fb7ffffffffffffffffff5d883ffab5b32652ccdcaa290fccb97d", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02207fd995622c4fb7ffffffffffffffffff5d883ffab5b32652ccdcaa290fccb97d", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0444de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8acea2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12", - "wx": "44de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8ace", - "wy": "00a2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0444de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8acea2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12", + "wx" : "44de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8ace", + "wy" : "00a2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000444de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8acea2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERN47nHpXqMnoIJUnU0IefZh7s9efcfAT\ngFyJfgGPis6iRgdYyPmNP9zhIalDZZ43LDJv/y5fwq5/o/edquE8Eg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000444de3b9c7a57a8c9e820952753421e7d987bb3d79f71f013805c897e018f8acea2460758c8f98d3fdce121a943659e372c326fff2e5fc2ae7fa3f79daae13c12", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAERN47nHpXqMnoIJUnU0IefZh7s9efcfAT\ngFyJfgGPis6iRgdYyPmNP9zhIalDZZ43LDJv/y5fwq5/o/edquE8Eg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 413, - "comment": "edge case for u2", - "flags": [ + "tcId" : 413, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304302207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc021f4cd53ba7608fffffffffffffffffffff9e5cf143e2539626190a3ab09cce47", - "result": "valid" + "msg" : "313233343030", + "sig" : "304302207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc021f4cd53ba7608fffffffffffffffffffff9e5cf143e2539626190a3ab09cce47", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "046fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204", - "wx": "6fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a", - "wy": "0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204", + "wx" : "6fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a", + "wy" : "0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEb7iytI4zAxJorWpRdITciDnqkPZmnqDH\nrDIz4qwxOUoKyLvn9zwv9N+ZeHJ6wd/C/VhkfSDzH5kQUxa2RnHyBA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046fb8b2b48e33031268ad6a517484dc8839ea90f6669ea0c7ac3233e2ac31394a0ac8bbe7f73c2ff4df9978727ac1dfc2fd58647d20f31f99105316b64671f204", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEb7iytI4zAxJorWpRdITciDnqkPZmnqDH\nrDIz4qwxOUoKyLvn9zwv9N+ZeHJ6wd/C/VhkfSDzH5kQUxa2RnHyBA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 414, - "comment": "edge case for u2", - "flags": [ + "tcId" : 414, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02205622c4fb7fffffffffffffffffffffff928a8f1c7ac7bec1808b9f61c01ec327", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02205622c4fb7fffffffffffffffffffffff928a8f1c7ac7bec1808b9f61c01ec327", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c", - "wx": "00bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6", - "wy": "00f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c", + "wx" : "00bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6", + "wy" : "00f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEvqcRIqBIaT6QX/YCs8+d0Yr2m5/J2EMd\nKx3Sa5Qsleb0PHuLletiCCwS2529p/445Fy+SkiGkH+4G9sMXqkkbA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004bea71122a048693e905ff602b3cf9dd18af69b9fc9d8431d2b1dd26b942c95e6f43c7b8b95eb62082c12db9dbda7fe38e45cbe4a4886907fb81bdb0c5ea9246c", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEvqcRIqBIaT6QX/YCs8+d0Yr2m5/J2EMd\nKx3Sa5Qsleb0PHuLletiCCwS2529p/445Fy+SkiGkH+4G9sMXqkkbA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 415, - "comment": "edge case for u2", - "flags": [ + "tcId" : 415, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022044104104104104104104104104104103b87853fd3b7d3f8e175125b4382f25ed", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc022044104104104104104104104104104103b87853fd3b7d3f8e175125b4382f25ed", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391", - "wx": "00da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156", - "wy": "00e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391", + "wx" : "00da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156", + "wy" : "00e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE2pGMcxugaiDLlO8zt3jpgaQEowXxlB/j\nNma0WwM1MVbiuyaU9XW0UYO+eOXJtSEL879Ij9TIKUUW2JVyyk9TkQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004da918c731ba06a20cb94ef33b778e981a404a305f1941fe33666b45b03353156e2bb2694f575b45183be78e5c9b5210bf3bf488fd4c8294516d89572ca4f5391", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE2pGMcxugaiDLlO8zt3jpgaQEowXxlB/j\nNma0WwM1MVbiuyaU9XW0UYO+eOXJtSEL879Ij9TIKUUW2JVyyk9TkQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 416, - "comment": "edge case for u2", - "flags": [ + "tcId" : 416, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202739ce739ce739ce739ce739ce739ce705560298d1f2f08dc419ac273a5b54d9", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202739ce739ce739ce739ce739ce739ce705560298d1f2f08dc419ac273a5b54d9", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "043007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5", - "wx": "3007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d", - "wy": "5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "043007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5", + "wx" : "3007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d", + "wy" : "5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200043007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMAfpLDk32t55ZN+jWw7/Ax9+sCrtCgMU\nQREGzetw/j1adUb8BVKZeyDj1vQT514stm4RYyJpcRS3m6xzS/xNxQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200043007e92c3937dade7964dfa35b0eff031f7eb02aed0a0314411106cdeb70fe3d5a7546fc0552997b20e3d6f413e75e2cb66e116322697114b79bac734bfc4dc5", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMAfpLDk32t55ZN+jWw7/Ax9+sCrtCgMU\nQREGzetw/j1adUb8BVKZeyDj1vQT514stm4RYyJpcRS3m6xzS/xNxQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 417, - "comment": "edge case for u2", - "flags": [ + "tcId" : 417, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02204888888888888888888888888888888831c83ae82ebe0898776b4c69d11f88de", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02204888888888888888888888888888888831c83ae82ebe0898776b4c69d11f88de", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0460e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9bd2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567", - "wx": "60e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9b", - "wy": "00d2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0460e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9bd2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567", + "wx" : "60e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9b", + "wy" : "00d2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000460e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9bd2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYOc071Yk08vw3dN1ARvWY9bWrrxkTrWZ\n/fmNvc0YzpvS2Qs6wx8TmvgyzM9sy7ssbqEfqXNw3JkG2kdNfYp1Zw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000460e734ef5624d3cbf0ddd375011bd663d6d6aebc644eb599fdf98dbdcd18ce9bd2d90b3ac31f139af832cccf6ccbbb2c6ea11fa97370dc9906da474d7d8a7567", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEYOc071Yk08vw3dN1ARvWY9bWrrxkTrWZ\n/fmNvc0YzpvS2Qs6wx8TmvgyzM9sy7ssbqEfqXNw3JkG2kdNfYp1Zw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 418, - "comment": "edge case for u2", - "flags": [ + "tcId" : 418, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02206492492492492492492492492492492406dd3a19b8d5fb875235963c593bd2d3", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02206492492492492492492492492492492406dd3a19b8d5fb875235963c593bd2d3", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0485a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba33769744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c", - "wx": "0085a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba337", - "wy": "69744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0485a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba33769744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c", + "wx" : "0085a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba337", + "wy" : "69744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000485a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba33769744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEhakA6XhY9pPAt9+iYeOA2tbqBG0fZd3u\n7dX32K8LozdpdE0VrdT2wLw7DaKuyTs0y4xl+TQN33TnsACe7szOPA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000485a900e97858f693c0b7dfa261e380dad6ea046d1f65ddeeedd5f7d8af0ba33769744d15add4f6c0bc3b0da2aec93b34cb8c65f9340ddf74e7b0009eeeccce3c", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEhakA6XhY9pPAt9+iYeOA2tbqBG0fZd3u\n7dX32K8LozdpdE0VrdT2wLw7DaKuyTs0y4xl+TQN33TnsACe7szOPA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 419, - "comment": "edge case for u2", - "flags": [ + "tcId" : 419, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02206aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b15", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02206aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b15", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0438066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed", - "wx": "38066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046", - "wy": "00a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0438066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed", + "wx" : "38066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046", + "wy" : "00a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000438066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOAZvddiO/EyT3jb0ngN7I0zBix3lYIdQ\npiyrA0VAEEaj6EvtjPy4Ge9NVQRE8s5LZRdmtp4uKQH4iDb/kANP7Q==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000438066f75d88efc4c93de36f49e037b234cc18b1de5608750a62cab0345401046a3e84bed8cfcb819ef4d550444f2ce4b651766b69e2e2901f88836ff90034fed", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEOAZvddiO/EyT3jb0ngN7I0zBix3lYIdQ\npiyrA0VAEEaj6EvtjPy4Ge9NVQRE8s5LZRdmtp4uKQH4iDb/kANP7Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 420, - "comment": "edge case for u2", - "flags": [ + "tcId" : 420, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b17", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02202aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa3e3a49a23a6d8abe95461f8445676b17", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0498f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabfa33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89", - "wx": "0098f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabf", - "wy": "00a33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0498f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabfa33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89", + "wx" : "0098f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabf", + "wy" : "00a33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000498f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabfa33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEmPaBd9yVwbTL+lJFSIylI6fVYpRw0DXW\nIaRDxy85qr+jPSlUb6HGSPLH1cz3DPHOSrebXbGsBZ2+zQaNvf8biQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000498f68177dc95c1b4cbfa5245488ca523a7d5629470d035d621a443c72f39aabfa33d29546fa1c648f2c7d5ccf70cf1ce4ab79b5db1ac059dbecd068dbdff1b89", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEmPaBd9yVwbTL+lJFSIylI6fVYpRw0DXW\nIaRDxy85qr+jPSlUb6HGSPLH1cz3DPHOSrebXbGsBZ2+zQaNvf8biQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 421, - "comment": "edge case for u2", - "flags": [ + "tcId" : 421, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc02203ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "045c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191", - "wx": "5c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277", - "wy": "00e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "045c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191", + "wx" : "5c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277", + "wy" : "00e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200045c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEXCu/ojybmtB/A4qom0kwvyZ9lAHkJV3p\n6NoKUHjsgnfj6IKjHV5qN54Hk5g8ze05uVxDU6sv8B6lNpukewwxkQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200045c2bbfa23c9b9ad07f038aa89b4930bf267d9401e4255de9e8da0a5078ec8277e3e882a31d5e6a379e0793983ccded39b95c4353ab2ff01ea5369ba47b0c3191", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEXCu/ojybmtB/A4qom0kwvyZ9lAHkJV3p\n6NoKUHjsgnfj6IKjHV5qN54Hk5g8ze05uVxDU6sv8B6lNpukewwxkQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 422, - "comment": "edge case for u2", - "flags": [ + "tcId" : 422, + "comment" : "edge case for u2", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0220185ddbca6dac41b1da033cfb60c152869e74b3cd66e9ffdf1b6bc09ed65ee40c", - "result": "valid" + "msg" : "313233343030", + "sig" : "304402207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0220185ddbca6dac41b1da033cfb60c152869e74b3cd66e9ffdf1b6bc09ed65ee40c", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a3853547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc", - "wx": "2ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385", - "wy": "3547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a3853547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc", + "wx" : "2ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385", + "wy" : "3547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a3853547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELqcTNDIznGnSf5smcoG9Ld1fGdYzjUAK\nBc02R7FXo4U1R4CCmESO215wGt6EzV+xrJVnul6Ptoprkz7EtcyEzA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a3853547808298448edb5e701ade84cd5fb1ac9567ba5e8fb68a6b933ec4b5cc84cc", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELqcTNDIznGnSf5smcoG9Ld1fGdYzjUAK\nBc02R7FXo4U1R4CCmESO215wGt6EzV+xrJVnul6Ptoprkz7EtcyEzA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 423, - "comment": "point duplication during verification", - "flags": [ + "tcId" : 423, + "comment" : "point duplication during verification", + "flags" : [ "PointDuplication" ], - "msg": "313233343030", - "sig": "3044022032b0d10d8d0e04bc8d4d064d270699e87cffc9b49c5c20730e1c26f6105ddcda022029ed3d67b3d505be95580d77d5b792b436881179b2b6b2e04c5fe592d38d82d9", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022032b0d10d8d0e04bc8d4d064d270699e87cffc9b49c5c20730e1c26f6105ddcda022029ed3d67b3d505be95580d77d5b792b436881179b2b6b2e04c5fe592d38d82d9", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763", - "wx": "2ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385", - "wy": "00cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763", + "wx" : "2ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385", + "wy" : "00cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELqcTNDIznGnSf5smcoG9Ld1fGdYzjUAK\nBc02R7FXo4XKuH99Z7txJKGP5SF7MqBOU2qYRaFwSXWUbME6SjN3Yw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200042ea7133432339c69d27f9b267281bd2ddd5f19d6338d400a05cd3647b157a385cab87f7d67bb7124a18fe5217b32a04e536a9845a1704975946cc13a4a337763", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAELqcTNDIznGnSf5smcoG9Ld1fGdYzjUAK\nBc02R7FXo4XKuH99Z7txJKGP5SF7MqBOU2qYRaFwSXWUbME6SjN3Yw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 424, - "comment": "duplication bug", - "flags": [ + "tcId" : 424, + "comment" : "duplication bug", + "flags" : [ "PointDuplication" ], - "msg": "313233343030", - "sig": "3044022032b0d10d8d0e04bc8d4d064d270699e87cffc9b49c5c20730e1c26f6105ddcda022029ed3d67b3d505be95580d77d5b792b436881179b2b6b2e04c5fe592d38d82d9", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3044022032b0d10d8d0e04bc8d4d064d270699e87cffc9b49c5c20730e1c26f6105ddcda022029ed3d67b3d505be95580d77d5b792b436881179b2b6b2e04c5fe592d38d82d9", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "048aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff", - "wx": "008aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e", - "wy": "1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff", + "wx" : "008aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e", + "wy" : "1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEiqLGT6nGQ3Vjq/vL0AsgSNSMGMFSoqb0\nkDbedkfr6C4c5kOHmVxooGD6O8A5mwXMBu7H1Zj3UEGkkX5pK39R/w==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048aa2c64fa9c6437563abfbcbd00b2048d48c18c152a2a6f49036de7647ebe82e1ce64387995c68a060fa3bc0399b05cc06eec7d598f75041a4917e692b7f51ff", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEiqLGT6nGQ3Vjq/vL0AsgSNSMGMFSoqb0\nkDbedkfr6C4c5kOHmVxooGD6O8A5mwXMBu7H1Zj3UEGkkX5pK39R/w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 425, - "comment": "comparison with point at infinity ", - "flags": [ + "tcId" : 425, + "comment" : "comparison with point at infinity ", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0022033333333333333333333333333333332f222f8faefdb533f265d461c29a47373", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3044022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0022033333333333333333333333333333332f222f8faefdb533f265d461c29a47373", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71fdd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd", - "wx": "391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71f", - "wy": "00dd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71fdd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd", + "wx" : "391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71f", + "wy" : "00dd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71fdd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEORQn/37ngBPBSux9lqigYiCSmKeDg16U\n/WVJ1QL/9x/dZiTsNDrZ/PTZhyGB5Z+EL5ukzMrgmmwJcvtqxrTGvQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004391427ff7ee78013c14aec7d96a8a062209298a783835e94fd6549d502fff71fdd6624ec343ad9fcf4d9872181e59f842f9ba4cccae09a6c0972fb6ac6b4c6bd", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEORQn/37ngBPBSux9lqigYiCSmKeDg16U\n/WVJ1QL/9x/dZiTsNDrZ/PTZhyGB5Z+EL5ukzMrgmmwJcvtqxrTGvQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 426, - "comment": "extreme value for k and edgecase s", - "flags": [ + "tcId" : 426, + "comment" : "extreme value for k and edgecase s", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", - "result": "valid" + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138ec1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e", - "wx": "00e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138e", - "wy": "00c1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138ec1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e", + "wx" : "00e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138e", + "wy" : "00c1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138ec1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE52K4ohm08YAhnMepBZJF5JYb0ZHAOJl4\nnHo0uJ6ME47BUz7wQZu3N24L/ekxnRCgaWh5HZ6g7tnBzmNFrtl1ng==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004e762b8a219b4f180219cc7a9059245e4961bd191c03899789c7a34b89e8c138ec1533ef0419bb7376e0bfde9319d10a06968791d9ea0eed9c1ce6345aed9759e", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE52K4ohm08YAhnMepBZJF5JYb0ZHAOJl4\nnHo0uJ6ME47BUz7wQZu3N24L/ekxnRCgaWh5HZ6g7tnBzmNFrtl1ng==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 427, - "comment": "extreme value for k and s^-1", - "flags": [ + "tcId" : 427, + "comment" : "extreme value for k and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", - "result": "valid" + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "049aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175", - "wx": "009aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952", - "wy": "00fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "049aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175", + "wx" : "009aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952", + "wy" : "00fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200049aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEmu2w0oHbFk4TAADFaX+uDzBe+Ei+b/+0\nOsWT+7lQ6VL6b2MzWb3NgrVrC5+WWwN3idRrmoFBt5GyrvpxP5bBdQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200049aedb0d281db164e130000c5697fae0f305ef848be6fffb43ac593fbb950e952fa6f633359bdcd82b56b0b9f965b037789d46b9a8141b791b2aefa713f96c175", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEmu2w0oHbFk4TAADFaX+uDzBe+Ei+b/+0\nOsWT+7lQ6VL6b2MzWb3NgrVrC5+WWwN3idRrmoFBt5GyrvpxP5bBdQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 428, - "comment": "extreme value for k and s^-1", - "flags": [ + "tcId" : 428, + "comment" : "extreme value for k and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", - "result": "valid" + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "048ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd", - "wx": "008ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee", - "wy": "1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd", + "wx" : "008ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee", + "wy" : "1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEitRF22KBYmDk5of9GITki5/AY20DFUfW\nMxXnkuGb+u4d5k+Z1fHNi27Jyw94emVK6GmTuj2xAI70PP8GhMsivQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048ad445db62816260e4e687fd1884e48b9fc0636d031547d63315e792e19bfaee1de64f99d5f1cd8b6ec9cb0f787a654ae86993ba3db1008ef43cff0684cb22bd", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEitRF22KBYmDk5of9GITki5/AY20DFUfW\nMxXnkuGb+u4d5k+Z1fHNi27Jyw94emVK6GmTuj2xAI70PP8GhMsivQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 429, - "comment": "extreme value for k and s^-1", - "flags": [ + "tcId" : 429, + "comment" : "extreme value for k and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", - "result": "valid" + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "041f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566", - "wx": "1f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32", - "wy": "00e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "041f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566", + "wx" : "1f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32", + "wy" : "00e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200041f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEH1eZyVvokGOyTybkDLkowahop2+wCUYH\n6AQ9tAnJHDLnVyToE6QZHjqDkAfwji6Jc4iwbUoA3m3mDlNtkfq1Zg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200041f5799c95be89063b24f26e40cb928c1a868a76fb0094607e8043db409c91c32e75724e813a4191e3a839007f08e2e897388b06d4a00de6de60e536d91fab566", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEH1eZyVvokGOyTybkDLkowahop2+wCUYH\n6AQ9tAnJHDLnVyToE6QZHjqDkAfwji6Jc4iwbUoA3m3mDlNtkfq1Zg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 430, - "comment": "extreme value for k and s^-1", - "flags": [ + "tcId" : 430, + "comment" : "extreme value for k and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", - "result": "valid" + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d", - "wx": "00a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc", - "wy": "28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d", + "wx" : "00a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc", + "wy" : "28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEozMaThtCI+wsAn7dSCySihTtNY2T8dQh\nfTmr9p/LXMwo1oTSqqvNY4N3XKpiOd4m1MaTe7YD7LQZYIL0z/1QnQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004a3331a4e1b4223ec2c027edd482c928a14ed358d93f1d4217d39abf69fcb5ccc28d684d2aaabcd6383775caa6239de26d4c6937bb603ecb4196082f4cffd509d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEozMaThtCI+wsAn7dSCySihTtNY2T8dQh\nfTmr9p/LXMwo1oTSqqvNY4N3XKpiOd4m1MaTe7YD7LQZYIL0z/1QnQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 431, - "comment": "extreme value for k", - "flags": [ + "tcId" : 431, + "comment" : "extreme value for k", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee502200eb10e5ab95f2f275348d82ad2e4d7949c8193800d8c9c75df58e343f0ebba7b", - "result": "valid" + "msg" : "313233343030", + "sig" : "3045022100c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee502200eb10e5ab95f2f275348d82ad2e4d7949c8193800d8c9c75df58e343f0ebba7b", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "043f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb248185ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d", - "wx": "3f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb24818", - "wy": "5ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "043f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb248185ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d", + "wx" : "3f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb24818", + "wy" : "5ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200043f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb248185ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEPzlSGZd0x885s4tmyxBCpiYNhoCAOEXk\n1DOtujuySBhepJW2jLx+1Bc+5jyQQtxQJiXH634h+wLKmpEU4KOhjQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200043f3952199774c7cf39b38b66cb1042a6260d8680803845e4d433adba3bb248185ea495b68cbc7ed4173ee63c9042dc502625c7eb7e21fb02ca9a9114e0a3a18d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEPzlSGZd0x885s4tmyxBCpiYNhoCAOEXk\n1DOtujuySBhepJW2jLx+1Bc+5jyQQtxQJiXH634h+wLKmpEU4KOhjQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 432, - "comment": "extreme value for k and edgecase s", - "flags": [ + "tcId" : 432, + "comment" : "extreme value for k and edgecase s", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022055555555555555555555555555555554e8e4f44ce51835693ff0ca2ef01215c0", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698", - "wx": "00cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e", - "wy": "054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698", + "wx" : "00cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e", + "wy" : "054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEzfuMD0IuFE4TfCQSyGwXH1/j+j9bu1RO\nkHYojzzteG4FT9ByG3fBHHm+rLPJQhGwoZvaCGUu/q+SUTo7ChY2mA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004cdfb8c0f422e144e137c2412c86c171f5fe3fa3f5bbb544e9076288f3ced786e054fd0721b77c11c79beacb3c94211b0a19bda08652efeaf92513a3b0a163698", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEzfuMD0IuFE4TfCQSyGwXH1/j+j9bu1RO\nkHYojzzteG4FT9ByG3fBHHm+rLPJQhGwoZvaCGUu/q+SUTo7ChY2mA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 433, - "comment": "extreme value for k and s^-1", - "flags": [ + "tcId" : 433, + "comment" : "extreme value for k and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0473598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d", - "wx": "73598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3", - "wy": "00cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0473598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d", + "wx" : "73598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3", + "wy" : "00cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000473598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEc1mKahxoJ4+mv9DOQGTmgjW8HA9rIKko\nEIvjNnMPh+PLrmElGbUDLsyFrtgRJxqV/nk51dNGAUC6MY9NFKujHQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000473598a6a1c68278fa6bfd0ce4064e68235bc1c0f6b20a928108be336730f87e3cbae612519b5032ecc85aed811271a95fe7939d5d3460140ba318f4d14aba31d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEc1mKahxoJ4+mv9DOQGTmgjW8HA9rIKko\nEIvjNnMPh+PLrmElGbUDLsyFrtgRJxqV/nk51dNGAUC6MY9NFKujHQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 434, - "comment": "extreme value for k and s^-1", - "flags": [ + "tcId" : 434, + "comment" : "extreme value for k and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0458debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a16773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d", - "wx": "58debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a1", - "wy": "6773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0458debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a16773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d", + "wx" : "58debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a1", + "wy" : "6773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000458debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a16773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEWN69mn7iydWRMkeKVECuTV1+1Dcwg2n5\nLqhsghg/EKFnc+dvXtv02g5PG9/6wPVyV+HfpGWEKTEwmiQkX9pqXQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000458debd9a7ee2c9d59132478a5440ae4d5d7ed437308369f92ea86c82183f10a16773e76f5edbf4da0e4f1bdffac0f57257e1dfa465842931309a24245fda6a5d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEWN69mn7iydWRMkeKVECuTV1+1Dcwg2n5\nLqhsghg/EKFnc+dvXtv02g5PG9/6wPVyV+HfpGWEKTEwmiQkX9pqXQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 435, - "comment": "extreme value for k and s^-1", - "flags": [ + "tcId" : 435, + "comment" : "extreme value for k and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022066666666666666666666666666666665e445f1f5dfb6a67e4cba8c385348e6e7", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "048b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f", - "wx": "008b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b", - "wy": "00950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "048b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f", + "wx" : "008b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b", + "wy" : "00950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200048b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEi5BN5HlnNAxfjDVypyCSTvdXhjf+qxlJ\nrLJBpaasP1uVCQRJb5gksdY/MxO64huJ+uia/fyBG17OA/1aowGGTw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200048b904de47967340c5f8c3572a720924ef7578637feab1949acb241a5a6ac3f5b950904496f9824b1d63f3313bae21b89fae89afdfc811b5ece03fd5aa301864f", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEi5BN5HlnNAxfjDVypyCSTvdXhjf+qxlJ\nrLJBpaasP1uVCQRJb5gksdY/MxO64huJ+uia/fyBG17OA/1aowGGTw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 436, - "comment": "extreme value for k and s^-1", - "flags": [ + "tcId" : 436, + "comment" : "extreme value for k and s^-1", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798022049249249249249249249249249249248c79facd43214c011123c1b03a93412a5", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b", - "wx": "00f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a", - "wy": "346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b", + "wx" : "00f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a", + "wy" : "346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE9IkrbVJcdx4DXyolJwjzeE5II4YEtPlN\nxW6qHlRtlBo0axqgvOaLHFDltS9Qn7VSLlwl4Ci8j4Y0Au23vK2LGw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004f4892b6d525c771e035f2a252708f3784e48238604b4f94dc56eaa1e546d941a346b1aa0bce68b1c50e5b52f509fb5522e5c25e028bc8f863402edb7bcad8b1b", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE9IkrbVJcdx4DXyolJwjzeE5II4YEtPlN\nxW6qHlRtlBo0axqgvOaLHFDltS9Qn7VSLlwl4Ci8j4Y0Au23vK2LGw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 437, - "comment": "extreme value for k", - "flags": [ + "tcId" : 437, + "comment" : "extreme value for k", + "flags" : [ "ArithmeticError" ], - "msg": "313233343030", - "sig": "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802200eb10e5ab95f2f275348d82ad2e4d7949c8193800d8c9c75df58e343f0ebba7b", - "result": "valid" + "msg" : "313233343030", + "sig" : "3044022079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8179802200eb10e5ab95f2f275348d82ad2e4d7949c8193800d8c9c75df58e343f0ebba7b", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - "wx": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - "wy": "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "wx" : "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "wy" : "483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeb5mfvncu6xVoGKVzocLBwKb/NstzijZ\nWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuA==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeb5mfvncu6xVoGKVzocLBwKb/NstzijZ\nWfKBWxb4F5hIOtp3JqPEZV2k+/wOEQio/Re0SKaFVBmcR9CP+xDUuA==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 438, - "comment": "public key shares x-coordinate with generator", - "flags": [ + "tcId" : 438, + "comment" : "public key shares x-coordinate with generator", + "flags" : [ "PointDuplication" ], - "msg": "313233343030", - "sig": "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca60502302202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca60502302202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result" : "invalid" }, { - "tcId": 439, - "comment": "public key shares x-coordinate with generator", - "flags": [ + "tcId" : 439, + "comment" : "public key shares x-coordinate with generator", + "flags" : [ "PointDuplication" ], - "msg": "313233343030", - "sig": "3044022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e02202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3044022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e02202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", - "wx": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", - "wy": "00b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", + "wx" : "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", + "wy" : "00b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeb5mfvncu6xVoGKVzocLBwKb/NstzijZ\nWfKBWxb4F5i3xSWI2Vw7mqJbBAPx7vdXAuhLt1l6q+ZjuC9vBO8ndw==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798b7c52588d95c3b9aa25b0403f1eef75702e84bb7597aabe663b82f6f04ef2777", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeb5mfvncu6xVoGKVzocLBwKb/NstzijZ\nWfKBWxb4F5i3xSWI2Vw7mqJbBAPx7vdXAuhLt1l6q+ZjuC9vBO8ndw==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 440, - "comment": "public key shares x-coordinate with generator", - "flags": [ + "tcId" : 440, + "comment" : "public key shares x-coordinate with generator", + "flags" : [ "PointDuplication" ], - "msg": "313233343030", - "sig": "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca60502302202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3045022100bb5a52f42f9c9261ed4361f59422a1e30036e7c32b270c8807a419feca60502302202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result" : "invalid" }, { - "tcId": 441, - "comment": "public key shares x-coordinate with generator", - "flags": [ + "tcId" : 441, + "comment" : "public key shares x-coordinate with generator", + "flags" : [ "PointDuplication" ], - "msg": "313233343030", - "sig": "3044022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e02202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", - "result": "invalid" + "msg" : "313233343030", + "sig" : "3044022044a5ad0bd0636d9e12bc9e0a6bdd5e1bba77f523842193b3b82e448e05d5f11e02202492492492492492492492492492492463cfd66a190a6008891e0d81d49a0952", + "result" : "invalid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152", - "wx": "782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963", - "wy": "00af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152", + "wx" : "782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963", + "wy" : "00af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeCyO0X47Kng7VGTzOwllKnHGeOBexR6E\n4rz8Zjo96WOvmstCgLjH98QvTvmrpiRewewXEv04oPqWQY2M1qphUg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004782c8ed17e3b2a783b5464f33b09652a71c678e05ec51e84e2bcfc663a3de963af9acb4280b8c7f7c42f4ef9aba6245ec1ec1712fd38a0fa96418d8cd6aa6152", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEeCyO0X47Kng7VGTzOwllKnHGeOBexR6E\n4rz8Zjo96WOvmstCgLjH98QvTvmrpiRewewXEv04oPqWQY2M1qphUg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 442, - "comment": "pseudorandom signature", - "flags": [ + "tcId" : 442, + "comment" : "pseudorandom signature", + "flags" : [ "ValidSignature" ], - "msg": "", - "sig": "3045022100f80ae4f96cdbc9d853f83d47aae225bf407d51c56b7776cd67d0dc195d99a9dc02204cfc1d941e08cb9aceadde0f4ccead76b30d332fc442115d50e673e28686b70b", - "result": "valid" + "msg" : "", + "sig" : "3045022100f80ae4f96cdbc9d853f83d47aae225bf407d51c56b7776cd67d0dc195d99a9dc02204cfc1d941e08cb9aceadde0f4ccead76b30d332fc442115d50e673e28686b70b", + "result" : "valid" }, { - "tcId": 443, - "comment": "pseudorandom signature", - "flags": [ + "tcId" : 443, + "comment" : "pseudorandom signature", + "flags" : [ "ValidSignature" ], - "msg": "4d7367", - "sig": "30440220109cd8ae0374358984a8249c0a843628f2835ffad1df1a9a69aa2fe72355545c02205390ff250ac4274e1cb25cd6ca6491f6b91281e32f5b264d87977aed4a94e77b", - "result": "valid" + "msg" : "4d7367", + "sig" : "30440220109cd8ae0374358984a8249c0a843628f2835ffad1df1a9a69aa2fe72355545c02205390ff250ac4274e1cb25cd6ca6491f6b91281e32f5b264d87977aed4a94e77b", + "result" : "valid" }, { - "tcId": 444, - "comment": "pseudorandom signature", - "flags": [ + "tcId" : 444, + "comment" : "pseudorandom signature", + "flags" : [ "ValidSignature" ], - "msg": "313233343030", - "sig": "3045022100d035ee1f17fdb0b2681b163e33c359932659990af77dca632012b30b27a057b302201939d9f3b2858bc13e3474cb50e6a82be44faa71940f876c1cba4c3e989202b6", - "result": "valid" + "msg" : "313233343030", + "sig" : "3045022100d035ee1f17fdb0b2681b163e33c359932659990af77dca632012b30b27a057b302201939d9f3b2858bc13e3474cb50e6a82be44faa71940f876c1cba4c3e989202b6", + "result" : "valid" }, { - "tcId": 445, - "comment": "pseudorandom signature", - "flags": [ + "tcId" : 445, + "comment" : "pseudorandom signature", + "flags" : [ "ValidSignature" ], - "msg": "0000000000000000000000000000000000000000", - "sig": "304402204f053f563ad34b74fd8c9934ce59e79c2eb8e6eca0fef5b323ca67d5ac7ed23802204d4b05daa0719e773d8617dce5631c5fd6f59c9bdc748e4b55c970040af01be5", - "result": "valid" + "msg" : "0000000000000000000000000000000000000000", + "sig" : "304402204f053f563ad34b74fd8c9934ce59e79c2eb8e6eca0fef5b323ca67d5ac7ed23802204d4b05daa0719e773d8617dce5631c5fd6f59c9bdc748e4b55c970040af01be5", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff00000001060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1", - "wx": "6e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff", - "wy": "01060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff00000001060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1", + "wx" : "6e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff", + "wy" : "01060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff00000001060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEboI1VUUpFAmRgsaywdbwtdKNUMzQBa8s\n4bulQapAyv8AAAABBgSS1aVnPg8l2NUPt+WMSdhtRtQhaVXgqj1A4Q==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff00000001060492d5a5673e0f25d8d50fb7e58c49d86d46d4216955e0aa3d40e1", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEboI1VUUpFAmRgsaywdbwtdKNUMzQBa8s\n4bulQapAyv8AAAABBgSS1aVnPg8l2NUPt+WMSdhtRtQhaVXgqj1A4Q==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 446, - "comment": "y-coordinate of the public key is small", - "flags": [ + "tcId" : 446, + "comment" : "y-coordinate of the public key is small", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "304402206d6a4f556ccce154e7fb9f19e76c3deca13d59cc2aeb4ecad968aab2ded45965022053b9fa74803ede0fc4441bf683d56c564d3e274e09ccf47390badd1471c05fb7", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "304402206d6a4f556ccce154e7fb9f19e76c3deca13d59cc2aeb4ecad968aab2ded45965022053b9fa74803ede0fc4441bf683d56c564d3e274e09ccf47390badd1471c05fb7", + "result" : "valid" }, { - "tcId": 447, - "comment": "y-coordinate of the public key is small", - "flags": [ + "tcId" : 447, + "comment" : "y-coordinate of the public key is small", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3044022100aad503de9b9fd66b948e9acf596f0a0e65e700b28b26ec56e6e45e846489b3c4021f0ddc3a2f89abb817bb85c062ce02f823c63fc26b269e0bc9b84d81a5aa123d", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3044022100aad503de9b9fd66b948e9acf596f0a0e65e700b28b26ec56e6e45e846489b3c4021f0ddc3a2f89abb817bb85c062ce02f823c63fc26b269e0bc9b84d81a5aa123d", + "result" : "valid" }, { - "tcId": 448, - "comment": "y-coordinate of the public key is small", - "flags": [ + "tcId" : 448, + "comment" : "y-coordinate of the public key is small", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "30450221009182cebd3bb8ab572e167174397209ef4b1d439af3b200cdf003620089e43225022054477c982ea019d2e1000497fc25fcee1bccae55f2ac27530ae53b29c4b356a4", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "30450221009182cebd3bb8ab572e167174397209ef4b1d439af3b200cdf003620089e43225022054477c982ea019d2e1000497fc25fcee1bccae55f2ac27530ae53b29c4b356a4", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40cafffffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e", - "wx": "6e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff", - "wy": "00fffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40cafffffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e", + "wx" : "6e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40caff", + "wy" : "00fffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40cafffffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEboI1VUUpFAmRgsaywdbwtdKNUMzQBa8s\n4bulQapAyv/////++fttKlqYwfDaJyrwSBpztieSuSvelqoeVcK7Tg==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046e823555452914099182c6b2c1d6f0b5d28d50ccd005af2ce1bba541aa40cafffffffffef9fb6d2a5a98c1f0da272af0481a73b62792b92bde96aa1e55c2bb4e", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEboI1VUUpFAmRgsaywdbwtdKNUMzQBa8s\n4bulQapAyv/////++fttKlqYwfDaJyrwSBpztieSuSvelqoeVcK7Tg==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 449, - "comment": "y-coordinate of the public key is large", - "flags": [ + "tcId" : 449, + "comment" : "y-coordinate of the public key is large", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "304402203854a3998aebdf2dbc28adac4181462ccac7873907ab7f212c42db0e69b56ed802203ed3f6b8a388d02f3e4df9f2ae9c1bd2c3916a686460dffcd42909cd7f82058e", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "304402203854a3998aebdf2dbc28adac4181462ccac7873907ab7f212c42db0e69b56ed802203ed3f6b8a388d02f3e4df9f2ae9c1bd2c3916a686460dffcd42909cd7f82058e", + "result" : "valid" }, { - "tcId": 450, - "comment": "y-coordinate of the public key is large", - "flags": [ + "tcId" : 450, + "comment" : "y-coordinate of the public key is large", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3045022100e94dbdc38795fe5c904d8f16d969d3b587f0a25d2de90b6d8c5c53ff887e360702207a947369c164972521bb8af406813b2d9f94d2aeaa53d4c215aaa0a2578a2c5d", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3045022100e94dbdc38795fe5c904d8f16d969d3b587f0a25d2de90b6d8c5c53ff887e360702207a947369c164972521bb8af406813b2d9f94d2aeaa53d4c215aaa0a2578a2c5d", + "result" : "valid" }, { - "tcId": 451, - "comment": "y-coordinate of the public key is large", - "flags": [ + "tcId" : 451, + "comment" : "y-coordinate of the public key is large", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3044022049fc102a08ca47b60e0858cd0284d22cddd7233f94aaffbb2db1dd2cf08425e102205b16fca5a12cdb39701697ad8e39ffd6bdec0024298afaa2326aea09200b14d6", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3044022049fc102a08ca47b60e0858cd0284d22cddd7233f94aaffbb2db1dd2cf08425e102205b16fca5a12cdb39701697ad8e39ffd6bdec0024298afaa2326aea09200b14d6", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04000000013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d", - "wx": "013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0", - "wy": "00f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04000000013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d", + "wx" : "013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0", + "wy" : "00f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004000000013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAAAAAT/SIkjWTZX3PCm0irSGMYUL5QP9\nAPhGi18PcOD27nqkO8LG/SWx2CaSQcvdnbsNrJbcliMfQwcF+DhxfQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004000000013fd22248d64d95f73c29b48ab48631850be503fd00f8468b5f0f70e0f6ee7aa43bc2c6fd25b1d8269241cbdd9dbb0dac96dc96231f430705f838717d", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEAAAAAT/SIkjWTZX3PCm0irSGMYUL5QP9\nAPhGi18PcOD27nqkO8LG/SWx2CaSQcvdnbsNrJbcliMfQwcF+DhxfQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 452, - "comment": "x-coordinate of the public key is small", - "flags": [ + "tcId" : 452, + "comment" : "x-coordinate of the public key is small", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3044022041efa7d3f05a0010675fcb918a45c693da4b348df21a59d6f9cd73e0d831d67a02204454ada693e5e26b7bd693236d340f80545c834577b6f73d378c7bcc534244da", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3044022041efa7d3f05a0010675fcb918a45c693da4b348df21a59d6f9cd73e0d831d67a02204454ada693e5e26b7bd693236d340f80545c834577b6f73d378c7bcc534244da", + "result" : "valid" }, { - "tcId": 453, - "comment": "x-coordinate of the public key is small", - "flags": [ + "tcId" : 453, + "comment" : "x-coordinate of the public key is small", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3045022100b615698c358b35920dd883eca625a6c5f7563970cdfc378f8fe0cee17092144c022025f47b326b5be1fb610b885153ea84d41eb4716be66a994e8779989df1c863d4", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3045022100b615698c358b35920dd883eca625a6c5f7563970cdfc378f8fe0cee17092144c022025f47b326b5be1fb610b885153ea84d41eb4716be66a994e8779989df1c863d4", + "result" : "valid" }, { - "tcId": 454, - "comment": "x-coordinate of the public key is small", - "flags": [ + "tcId" : 454, + "comment" : "x-coordinate of the public key is small", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "304502210087cf8c0eb82d44f69c60a2ff5457d3aaa322e7ec61ae5aecfd678ae1c1932b0e02203add3b115815047d6eb340a3e008989eaa0f8708d1794814729094d08d2460d3", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "304502210087cf8c0eb82d44f69c60a2ff5457d3aaa322e7ec61ae5aecfd678ae1c1932b0e02203add3b115815047d6eb340a3e008989eaa0f8708d1794814729094d08d2460d3", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "0425afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dfffffffffa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35", - "wx": "25afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dffffffff", - "wy": "00fa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "0425afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dfffffffffa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35", + "wx" : "25afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dffffffff", + "wy" : "00fa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a0342000425afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dfffffffffa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEJa/WiayrrtZ8Hylt5ZQG+MVQ9XFGoLTs\nLJeHbf/////6RqduUgMi37xJHsTwzBl0IPxOpYg9j23VPDVLxPZ8NQ==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a0342000425afd689acabaed67c1f296de59406f8c550f57146a0b4ec2c97876dfffffffffa46a76e520322dfbc491ec4f0cc197420fc4ea5883d8f6dd53c354bc4f67c35", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEJa/WiayrrtZ8Hylt5ZQG+MVQ9XFGoLTs\nLJeHbf/////6RqduUgMi37xJHsTwzBl0IPxOpYg9j23VPDVLxPZ8NQ==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 455, - "comment": "x-coordinate of the public key has many trailing 1's", - "flags": [ + "tcId" : 455, + "comment" : "x-coordinate of the public key has many trailing 1's", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3044022062f48ef71ace27bf5a01834de1f7e3f948b9dce1ca1e911d5e13d3b104471d8202205ea8f33f0c778972c4582080deda9b341857dd64514f0849a05f6964c2e34022", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3044022062f48ef71ace27bf5a01834de1f7e3f948b9dce1ca1e911d5e13d3b104471d8202205ea8f33f0c778972c4582080deda9b341857dd64514f0849a05f6964c2e34022", + "result" : "valid" }, { - "tcId": 456, - "comment": "x-coordinate of the public key has many trailing 1's", - "flags": [ + "tcId" : 456, + "comment" : "x-coordinate of the public key has many trailing 1's", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3045022100f6b0e2f6fe020cf7c0c20137434344ed7add6c4be51861e2d14cbda472a6ffb402206416c8dd3e5c5282b306e8dc8ff34ab64cc99549232d678d714402eb6ca7aa0f", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3045022100f6b0e2f6fe020cf7c0c20137434344ed7add6c4be51861e2d14cbda472a6ffb402206416c8dd3e5c5282b306e8dc8ff34ab64cc99549232d678d714402eb6ca7aa0f", + "result" : "valid" }, { - "tcId": 457, - "comment": "x-coordinate of the public key has many trailing 1's", - "flags": [ + "tcId" : 457, + "comment" : "x-coordinate of the public key has many trailing 1's", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3045022100db09d8460f05eff23bc7e436b67da563fa4b4edb58ac24ce201fa8a358125057022046da116754602940c8999c8d665f786c50f5772c0a3cdbda075e77eabc64df16", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3045022100db09d8460f05eff23bc7e436b67da563fa4b4edb58ac24ce201fa8a358125057022046da116754602940c8999c8d665f786c50f5772c0a3cdbda075e77eabc64df16", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "04d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb93f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff", - "wx": "00d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb9", - "wy": "3f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "04d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb93f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff", + "wx" : "00d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb9", + "wy" : "3f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a03420004d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb93f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE0S5sZrZ3NMPITSYBz1013Al+J2N/CspK\nT9t0tqrdO7k/W9/4i9VzbfiY5pkAbtdQ8RzwfFhmzXrXDHEh/////w==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a03420004d12e6c66b67734c3c84d2601cf5d35dc097e27637f0aca4a4fdb74b6aadd3bb93f5bdff88bd5736df898e699006ed750f11cf07c5866cd7ad70c7121ffffffff", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE0S5sZrZ3NMPITSYBz1013Al+J2N/CspK\nT9t0tqrdO7k/W9/4i9VzbfiY5pkAbtdQ8RzwfFhmzXrXDHEh/////w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 458, - "comment": "y-coordinate of the public key has many trailing 1's", - "flags": [ + "tcId" : 458, + "comment" : "y-coordinate of the public key has many trailing 1's", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "30440220592c41e16517f12fcabd98267674f974b588e9f35d35406c1a7bb2ed1d19b7b802203e65a06bd9f83caaeb7b00f2368d7e0dece6b12221269a9b5b765198f840a3a1", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "30440220592c41e16517f12fcabd98267674f974b588e9f35d35406c1a7bb2ed1d19b7b802203e65a06bd9f83caaeb7b00f2368d7e0dece6b12221269a9b5b765198f840a3a1", + "result" : "valid" }, { - "tcId": 459, - "comment": "y-coordinate of the public key has many trailing 1's", - "flags": [ + "tcId" : 459, + "comment" : "y-coordinate of the public key has many trailing 1's", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3045022100be0d70887d5e40821a61b68047de4ea03debfdf51cdf4d4b195558b959a032b202207d994b2d8f1dbbeb13534eb3f6e5dccd85f5c4133c27d9e64271b1826ce1f67d", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3045022100be0d70887d5e40821a61b68047de4ea03debfdf51cdf4d4b195558b959a032b202207d994b2d8f1dbbeb13534eb3f6e5dccd85f5c4133c27d9e64271b1826ce1f67d", + "result" : "valid" }, { - "tcId": 460, - "comment": "y-coordinate of the public key has many trailing 1's", - "flags": [ + "tcId" : 460, + "comment" : "y-coordinate of the public key has many trailing 1's", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3045022100fae92dfcb2ee392d270af3a5739faa26d4f97bfd39ed3cbee4d29e26af3b206a02206c9ba37f9faa6a1fd3f65f23b4e853d4692a7274240a12db7ba3884830630d16", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3045022100fae92dfcb2ee392d270af3a5739faa26d4f97bfd39ed3cbee4d29e26af3b206a02206c9ba37f9faa6a1fd3f65f23b4e853d4692a7274240a12db7ba3884830630d16", + "result" : "valid" } ] }, { - "type": "EcdsaBitcoinVerify", - "publicKey": { - "type": "EcPublicKey", - "curve": "secp256k1", - "keySize": 256, - "uncompressed": "046d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb", - "wx": "6d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000", - "wy": "00e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb" + "type" : "EcdsaBitcoinVerify", + "publicKey" : { + "type" : "EcPublicKey", + "curve" : "secp256k1", + "keySize" : 256, + "uncompressed" : "046d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb", + "wx" : "6d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000", + "wy" : "00e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb" }, - "publicKeyDer": "3056301006072a8648ce3d020106052b8104000a034200046d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb", - "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbUp/YNR3Sk8KqLve25U8fup5CUB+MWR1\nVmS8KAAAAADmWdNOTfONnoyeqt+6NmEsdpGVvobHeqw/NueLU4aA+w==\n-----END PUBLIC KEY-----\n", - "sha": "SHA-256", - "tests": [ + "publicKeyDer" : "3056301006072a8648ce3d020106052b8104000a034200046d4a7f60d4774a4f0aa8bbdedb953c7eea7909407e3164755664bc2800000000e659d34e4df38d9e8c9eaadfba36612c769195be86c77aac3f36e78b538680fb", + "publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEbUp/YNR3Sk8KqLve25U8fup5CUB+MWR1\nVmS8KAAAAADmWdNOTfONnoyeqt+6NmEsdpGVvobHeqw/NueLU4aA+w==\n-----END PUBLIC KEY-----\n", + "sha" : "SHA-256", + "tests" : [ { - "tcId": 461, - "comment": "x-coordinate of the public key has many trailing 0's", - "flags": [ + "tcId" : 461, + "comment" : "x-coordinate of the public key has many trailing 0's", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "30440220176a2557566ffa518b11226694eb9802ed2098bfe278e5570fe1d5d7af18a94302201291df6a0ed5fc0d15098e70bcf13a009284dfd0689d3bb4be6ceeb9be1487c4", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "30440220176a2557566ffa518b11226694eb9802ed2098bfe278e5570fe1d5d7af18a94302201291df6a0ed5fc0d15098e70bcf13a009284dfd0689d3bb4be6ceeb9be1487c4", + "result" : "valid" }, { - "tcId": 462, - "comment": "x-coordinate of the public key has many trailing 0's", - "flags": [ + "tcId" : 462, + "comment" : "x-coordinate of the public key has many trailing 0's", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3044022060be20c3dbc162dd34d26780621c104bbe5dace630171b2daef0d826409ee5c20220427f7e4d889d549170bda6a9409fb1cb8b0e763d13eea7bd97f64cf41dc6e497", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3044022060be20c3dbc162dd34d26780621c104bbe5dace630171b2daef0d826409ee5c20220427f7e4d889d549170bda6a9409fb1cb8b0e763d13eea7bd97f64cf41dc6e497", + "result" : "valid" }, { - "tcId": 463, - "comment": "x-coordinate of the public key has many trailing 0's", - "flags": [ + "tcId" : 463, + "comment" : "x-coordinate of the public key has many trailing 0's", + "flags" : [ "EdgeCasePublicKey" ], - "msg": "4d657373616765", - "sig": "3045022100edf03cf63f658883289a1a593d1007895b9f236d27c9c1f1313089aaed6b16ae02201a4dd6fc0814dc523d1fefa81c64fbf5e618e651e7096fccadbb94cd48e5e0cd", - "result": "valid" + "msg" : "4d657373616765", + "sig" : "3045022100edf03cf63f658883289a1a593d1007895b9f236d27c9c1f1313089aaed6b16ae02201a4dd6fc0814dc523d1fefa81c64fbf5e618e651e7096fccadbb94cd48e5e0cd", + "result" : "valid" } ] } diff --git a/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger.proto b/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger.proto index 0df9ca5ceb..59c9f51609 100644 --- a/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger.proto +++ b/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger.proto @@ -6,89 +6,81 @@ option java_multiple_files = true; import "org/xrpl/rpc/v1/ledger.proto"; -message GetLedgerRequest -{ +message GetLedgerRequest { + LedgerSpecifier ledger = 1; - LedgerSpecifier ledger = 1; + // If true, include transactions contained in this ledger + bool transactions = 2; - // If true, include transactions contained in this ledger - bool transactions = 2; + // If true and transactions, include full transactions and metadata + // If false and transactions, include only transaction hashes + bool expand = 3; - // If true and transactions, include full transactions and metadata - // If false and transactions, include only transaction hashes - bool expand = 3; + // If true, include state map difference between this ledger and the + // previous ledger. This includes all added, modified or deleted ledger + // objects + bool get_objects = 4; - // If true, include state map difference between this ledger and the - // previous ledger. This includes all added, modified or deleted ledger - // objects - bool get_objects = 4; - - // If the request needs to be forwarded from a reporting node to a p2p node, - // the reporting node will set this field. Clients should not set this - // field. - string client_ip = 5; + // If the request needs to be forwarded from a reporting node to a p2p node, + // the reporting node will set this field. Clients should not set this + // field. + string client_ip = 5; - // Identifying string. If user is set, client_ip is not set, and request is - // coming from a secure_gateway host, then the client is not subject to - // resource controls - string user = 6; + // Identifying string. If user is set, client_ip is not set, and request is + // coming from a secure_gateway host, then the client is not subject to + // resource controls + string user = 6; - // For every object in the diff, get the object's predecessor and successor - // in the state map. Only used if get_objects is also true. - bool get_object_neighbors = 7; + // For every object in the diff, get the object's predecessor and successor + // in the state map. Only used if get_objects is also true. + bool get_object_neighbors = 7; } -message GetLedgerResponse -{ - bytes ledger_header = 1; +message GetLedgerResponse { + bytes ledger_header = 1; - oneof transactions - { - // Just the hashes - TransactionHashList hashes_list = 2; - - // Full transactions and metadata - TransactionAndMetadataList transactions_list = 3; - } + oneof transactions { + // Just the hashes + TransactionHashList hashes_list = 2; - // True if the ledger has been validated - bool validated = 4; + // Full transactions and metadata + TransactionAndMetadataList transactions_list = 3; + } - // State map difference between this ledger and the previous ledger - RawLedgerObjects ledger_objects = 5; + // True if the ledger has been validated + bool validated = 4; - // True if the skiplist object is included in ledger_objects - bool skiplist_included = 6; + // State map difference between this ledger and the previous ledger + RawLedgerObjects ledger_objects = 5; - // True if request was exempt from resource controls - bool is_unlimited = 7; + // True if the skiplist object is included in ledger_objects + bool skiplist_included = 6; - // True if the response contains the state map diff - bool objects_included = 8; + // True if request was exempt from resource controls + bool is_unlimited = 7; - // True if the response contains key of objects adjacent to objects in state - // map diff - bool object_neighbors_included = 9; + // True if the response contains the state map diff + bool objects_included = 8; + // True if the response contains key of objects adjacent to objects in state + // map diff + bool object_neighbors_included = 9; - // Successor information for book directories modified as part of this - // ledger - repeated BookSuccessor book_successors = 10; + // Successor information for book directories modified as part of this + // ledger + repeated BookSuccessor book_successors = 10; } -message TransactionHashList -{ - repeated bytes hashes = 1; +message TransactionHashList { + repeated bytes hashes = 1; } -message TransactionAndMetadata -{ - bytes transaction_blob = 1; +message TransactionAndMetadata { + bytes transaction_blob = 1; - bytes metadata_blob = 2; + bytes metadata_blob = 2; } -message TransactionAndMetadataList -{ - repeated TransactionAndMetadata transactions = 1; +message TransactionAndMetadataList { + repeated TransactionAndMetadata transactions = 1; } diff --git a/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_data.proto b/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_data.proto index c311994ac2..a9e93c743c 100644 --- a/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_data.proto +++ b/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_data.proto @@ -8,46 +8,43 @@ import "org/xrpl/rpc/v1/ledger.proto"; // Get ledger objects for a specific ledger. You can iterate through several // calls to retrieve the entire contents of a single ledger version. -message GetLedgerDataRequest -{ - // If set, only objects with a key greater than marker are returned. - // This can be used to pick up where a previous call left off. - // Set marker to the value of marker in the previous response. - bytes marker = 1; +message GetLedgerDataRequest { + // If set, only objects with a key greater than marker are returned. + // This can be used to pick up where a previous call left off. + // Set marker to the value of marker in the previous response. + bytes marker = 1; - LedgerSpecifier ledger = 2; + LedgerSpecifier ledger = 2; - // If set, only objects with a key less than end_marker are returned - bytes end_marker = 3; + // If set, only objects with a key less than end_marker are returned + bytes end_marker = 3; - // If the request needs to be forwarded from a reporting node to a p2p node, - // the reporting node will set this field. Clients should not set this - // field. - string client_ip = 4; + // If the request needs to be forwarded from a reporting node to a p2p node, + // the reporting node will set this field. Clients should not set this + // field. + string client_ip = 4; - // Identifying string. If user is set, client_ip is not set, and request is - // coming from a secure_gateway host, then the client is not subject to - // resource controls - string user = 6; + // Identifying string. If user is set, client_ip is not set, and request is + // coming from a secure_gateway host, then the client is not subject to + // resource controls + string user = 6; } -message GetLedgerDataResponse -{ - // Sequence of the ledger containing the returned ledger objects - uint32 ledger_index = 1; +message GetLedgerDataResponse { + // Sequence of the ledger containing the returned ledger objects + uint32 ledger_index = 1; - // Hash of the ledger containing the returned ledger objects - bytes ledger_hash = 2; - - // Ledger objects - RawLedgerObjects ledger_objects = 3; + // Hash of the ledger containing the returned ledger objects + bytes ledger_hash = 2; - // Key to be passed into a subsequent call to continue iteration. If not - // set, there are no more objects left in the ledger, or no more objects - // with key less than end_marker (if end_marker was set in the request) - bytes marker = 4; + // Ledger objects + RawLedgerObjects ledger_objects = 3; - // True if request was exempt from resource controls - bool is_unlimited = 7; + // Key to be passed into a subsequent call to continue iteration. If not + // set, there are no more objects left in the ledger, or no more objects + // with key less than end_marker (if end_marker was set in the request) + bytes marker = 4; + + // True if request was exempt from resource controls + bool is_unlimited = 7; } - diff --git a/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_diff.proto b/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_diff.proto index 218cbeb61f..ab6d5551fa 100644 --- a/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_diff.proto +++ b/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_diff.proto @@ -6,27 +6,23 @@ option java_multiple_files = true; import "org/xrpl/rpc/v1/ledger.proto"; - // Get the state map difference between the two specified ledgers -message GetLedgerDiffRequest -{ - LedgerSpecifier base_ledger = 1; +message GetLedgerDiffRequest { + LedgerSpecifier base_ledger = 1; - LedgerSpecifier desired_ledger = 2; + LedgerSpecifier desired_ledger = 2; - // If true, include the full ledger object. If false, only keys are included. - bool include_blobs = 3; + // If true, include the full ledger object. If false, only keys are included. + bool include_blobs = 3; - // If the request needs to be forwarded from a reporting node to a p2p node, - // the reporting node will set this field. Clients should not set this - // field. - string client_ip = 4; + // If the request needs to be forwarded from a reporting node to a p2p node, + // the reporting node will set this field. Clients should not set this + // field. + string client_ip = 4; } -message GetLedgerDiffResponse -{ - // All ledger objects that were added, modified or deleted between - // base_ledger and desired_ledger - RawLedgerObjects ledger_objects = 1; +message GetLedgerDiffResponse { + // All ledger objects that were added, modified or deleted between + // base_ledger and desired_ledger + RawLedgerObjects ledger_objects = 1; } - diff --git a/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_entry.proto b/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_entry.proto index a894c7729f..4da6420e35 100644 --- a/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_entry.proto +++ b/include/xrpl/proto/org/xrpl/rpc/v1/get_ledger_entry.proto @@ -7,25 +7,23 @@ option java_multiple_files = true; import "org/xrpl/rpc/v1/ledger.proto"; // Get a single ledger object -message GetLedgerEntryRequest -{ - // Key of the desired object - bytes key = 1; +message GetLedgerEntryRequest { + // Key of the desired object + bytes key = 1; - // Ledger containing the object - LedgerSpecifier ledger = 2; - - // If the request needs to be forwarded from a reporting node to a p2p node, - // the reporting node will set this field. Clients should not set this - // field. - string client_ip = 3; + // Ledger containing the object + LedgerSpecifier ledger = 2; + + // If the request needs to be forwarded from a reporting node to a p2p node, + // the reporting node will set this field. Clients should not set this + // field. + string client_ip = 3; } -message GetLedgerEntryResponse -{ - RawLedgerObject ledger_object = 1; +message GetLedgerEntryResponse { + RawLedgerObject ledger_object = 1; - // Ledger containing the object. Will match the value specified in the - // request. - LedgerSpecifier ledger = 2; + // Ledger containing the object. Will match the value specified in the + // request. + LedgerSpecifier ledger = 2; } diff --git a/include/xrpl/proto/org/xrpl/rpc/v1/ledger.proto b/include/xrpl/proto/org/xrpl/rpc/v1/ledger.proto index 3bb199de22..63ce86b51c 100644 --- a/include/xrpl/proto/org/xrpl/rpc/v1/ledger.proto +++ b/include/xrpl/proto/org/xrpl/rpc/v1/ledger.proto @@ -5,71 +5,61 @@ option java_package = "org.xrpl.rpc.v1"; option java_multiple_files = true; // Next field: 4 -message LedgerSpecifier -{ - // Next field: 4 - enum Shortcut - { - SHORTCUT_UNSPECIFIED = 0; - SHORTCUT_VALIDATED = 1; - SHORTCUT_CLOSED = 2; - SHORTCUT_CURRENT = 3; - } +message LedgerSpecifier { + // Next field: 4 + enum Shortcut { + SHORTCUT_UNSPECIFIED = 0; + SHORTCUT_VALIDATED = 1; + SHORTCUT_CLOSED = 2; + SHORTCUT_CURRENT = 3; + } - oneof ledger - { - Shortcut shortcut = 1; - uint32 sequence = 2; - // 32 bytes - bytes hash = 3; - } + oneof ledger { + Shortcut shortcut = 1; + uint32 sequence = 2; + // 32 bytes + bytes hash = 3; + } } - // Next field: 3 -message RawLedgerObject -{ - // Raw data of the ledger object. In GetLedgerResponse and - // GetLedgerDiffResponse, data will be empty if the object was deleted. - bytes data = 1; +message RawLedgerObject { + // Raw data of the ledger object. In GetLedgerResponse and + // GetLedgerDiffResponse, data will be empty if the object was deleted. + bytes data = 1; - // Key of the ledger object - bytes key = 2; + // Key of the ledger object + bytes key = 2; - enum ModificationType - { - UNSPECIFIED = 0; - CREATED = 1; - MODIFIED = 2; - DELETED = 3; - } + enum ModificationType { + UNSPECIFIED = 0; + CREATED = 1; + MODIFIED = 2; + DELETED = 3; + } - // Whether the object was created, modified or deleted - ModificationType mod_type = 3; + // Whether the object was created, modified or deleted + ModificationType mod_type = 3; - // Key of the object preceding this object in the desired ledger - bytes predecessor = 4; + // Key of the object preceding this object in the desired ledger + bytes predecessor = 4; - // Key of the object succeeding this object in the desired ledger - bytes successor = 5; + // Key of the object succeeding this object in the desired ledger + bytes successor = 5; } -message RawLedgerObjects -{ - repeated RawLedgerObject objects = 1; +message RawLedgerObjects { + repeated RawLedgerObject objects = 1; } // Successor information for book directories. The book base is (usually) not // an actual object, yet we need to be able to ask for the successor to the // book base. message BookSuccessor { + // Base of the book in question + bytes book_base = 1; - // Base of the book in question - bytes book_base = 1; - - // First book directory in the book. An empty value here means the entire - // book is deleted - bytes first_book = 2; - + // First book directory in the book. An empty value here means the entire + // book is deleted + bytes first_book = 2; }; - diff --git a/include/xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.proto b/include/xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.proto index 01a23fbe37..2b8dc471ce 100644 --- a/include/xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.proto +++ b/include/xrpl/proto/org/xrpl/rpc/v1/xrp_ledger.proto @@ -9,13 +9,11 @@ import "org/xrpl/rpc/v1/get_ledger_entry.proto"; import "org/xrpl/rpc/v1/get_ledger_data.proto"; import "org/xrpl/rpc/v1/get_ledger_diff.proto"; - // These methods are binary only methods for retrieiving arbitrary ledger state // via gRPC. These methods are used by clio, but can also be // used by any client that wants to extract ledger state in an efficient manner. // They do not directly mimic the JSON equivalent methods. service XRPLedgerAPIService { - // Get a specific ledger, optionally including transactions and any modified, // added or deleted ledger objects rpc GetLedger(GetLedgerRequest) returns (GetLedgerResponse); @@ -29,5 +27,4 @@ service XRPLedgerAPIService { // Get all ledger objects that are different between the two specified // ledgers. Note, this method has no JSON equivalent. rpc GetLedgerDiff(GetLedgerDiffRequest) returns (GetLedgerDiffResponse); - } diff --git a/include/xrpl/proto/ripple.proto b/include/xrpl/proto/ripple.proto index a06bbd9a31..f93ebbc72c 100644 --- a/include/xrpl/proto/ripple.proto +++ b/include/xrpl/proto/ripple.proto @@ -4,29 +4,28 @@ package protocol; // Unused numbers in the list below may have been used previously. Please don't // reassign them for reuse unless you are 100% certain that there won't be a // conflict. Even if you're sure, it's probably best to assign a new type. -enum MessageType -{ - mtMANIFESTS = 2; - mtPING = 3; - mtCLUSTER = 5; - mtENDPOINTS = 15; - mtTRANSACTION = 30; - mtGET_LEDGER = 31; - mtLEDGER_DATA = 32; - mtPROPOSE_LEDGER = 33; - mtSTATUS_CHANGE = 34; - mtHAVE_SET = 35; - mtVALIDATION = 41; - mtGET_OBJECTS = 42; - mtVALIDATORLIST = 54; - mtSQUELCH = 55; - mtVALIDATORLISTCOLLECTION = 56; - mtPROOF_PATH_REQ = 57; - mtPROOF_PATH_RESPONSE = 58; - mtREPLAY_DELTA_REQ = 59; - mtREPLAY_DELTA_RESPONSE = 60; - mtHAVE_TRANSACTIONS = 63; - mtTRANSACTIONS = 64; +enum MessageType { + mtMANIFESTS = 2; + mtPING = 3; + mtCLUSTER = 5; + mtENDPOINTS = 15; + mtTRANSACTION = 30; + mtGET_LEDGER = 31; + mtLEDGER_DATA = 32; + mtPROPOSE_LEDGER = 33; + mtSTATUS_CHANGE = 34; + mtHAVE_SET = 35; + mtVALIDATION = 41; + mtGET_OBJECTS = 42; + mtVALIDATORLIST = 54; + mtSQUELCH = 55; + mtVALIDATORLISTCOLLECTION = 56; + mtPROOF_PATH_REQ = 57; + mtPROOF_PATH_RESPONSE = 58; + mtREPLAY_DELTA_REQ = 59; + mtREPLAY_DELTA_RESPONSE = 60; + mtHAVE_TRANSACTIONS = 63; + mtTRANSACTIONS = 64; } // token, iterations, target, challenge = issue demand for proof of work @@ -36,352 +35,309 @@ enum MessageType //------------------------------------------------------------------------------ /* Provides the current ephemeral key for a validator. */ -message TMManifest -{ - // A Manifest object in the Ripple serialization format. - required bytes stobject = 1; +message TMManifest { + // A Manifest object in the Ripple serialization format. + required bytes stobject = 1; } -message TMManifests -{ - repeated TMManifest list = 1; +message TMManifests { + repeated TMManifest list = 1; - // The manifests sent when a peer first connects to another peer are `history`. - optional bool history = 2 [deprecated=true]; + // The manifests sent when a peer first connects to another peer are `history`. + optional bool history = 2 [deprecated = true]; } //------------------------------------------------------------------------------ // The status of a node in our cluster -message TMClusterNode -{ - required string publicKey = 1; - required uint32 reportTime = 2; - required uint32 nodeLoad = 3; - optional string nodeName = 4; - optional string address = 5; +message TMClusterNode { + required string publicKey = 1; + required uint32 reportTime = 2; + required uint32 nodeLoad = 3; + optional string nodeName = 4; + optional string address = 5; } // Sources that are placing load on the server -message TMLoadSource -{ - required string name = 1; - required uint32 cost = 2; - optional uint32 count = 3; // number of connections +message TMLoadSource { + required string name = 1; + required uint32 cost = 2; + optional uint32 count = 3; // number of connections } // The status of all nodes in the cluster -message TMCluster -{ - repeated TMClusterNode clusterNodes = 1; - repeated TMLoadSource loadSources = 2; +message TMCluster { + repeated TMClusterNode clusterNodes = 1; + repeated TMLoadSource loadSources = 2; } // Node public key -message TMLink -{ - required bytes nodePubKey = 1 [deprecated=true]; // node public key +message TMLink { + required bytes nodePubKey = 1 [deprecated = true]; // node public key } // Peer public key -message TMPublicKey -{ - required bytes publicKey = 1; +message TMPublicKey { + required bytes publicKey = 1; } // A transaction can have only one input and one output. // If you want to send an amount that is greater than any single address of yours // you must first combine coins from one address to another. -enum TransactionStatus -{ - tsNEW = 1; // origin node did/could not validate - tsCURRENT = 2; // scheduled to go in this ledger - tsCOMMITED = 3; // in a closed ledger - tsREJECT_CONFLICT = 4; - tsREJECT_INVALID = 5; - tsREJECT_FUNDS = 6; - tsHELD_SEQ = 7; - tsHELD_LEDGER = 8; // held for future ledger +enum TransactionStatus { + tsNEW = 1; // origin node did/could not validate + tsCURRENT = 2; // scheduled to go in this ledger + tsCOMMITED = 3; // in a closed ledger + tsREJECT_CONFLICT = 4; + tsREJECT_INVALID = 5; + tsREJECT_FUNDS = 6; + tsHELD_SEQ = 7; + tsHELD_LEDGER = 8; // held for future ledger } -message TMTransaction -{ - required bytes rawTransaction = 1; - required TransactionStatus status = 2; - optional uint64 receiveTimestamp = 3; - optional bool deferred = 4; // not applied to open ledger +message TMTransaction { + required bytes rawTransaction = 1; + required TransactionStatus status = 2; + optional uint64 receiveTimestamp = 3; + optional bool deferred = 4; // not applied to open ledger } -message TMTransactions -{ - repeated TMTransaction transactions = 1; +message TMTransactions { + repeated TMTransaction transactions = 1; } - -enum NodeStatus -{ - nsCONNECTING = 1; // acquiring connections - nsCONNECTED = 2; // convinced we are connected to the real network - nsMONITORING = 3; // we know what the previous ledger is - nsVALIDATING = 4; // we have the full ledger contents - nsSHUTTING = 5; // node is shutting down +enum NodeStatus { + nsCONNECTING = 1; // acquiring connections + nsCONNECTED = 2; // convinced we are connected to the real network + nsMONITORING = 3; // we know what the previous ledger is + nsVALIDATING = 4; // we have the full ledger contents + nsSHUTTING = 5; // node is shutting down } -enum NodeEvent -{ - neCLOSING_LEDGER = 1; // closing a ledger because its close time has come - neACCEPTED_LEDGER = 2; // accepting a closed ledger, we have finished computing it - neSWITCHED_LEDGER = 3; // changing due to network consensus - neLOST_SYNC = 4; +enum NodeEvent { + neCLOSING_LEDGER = 1; // closing a ledger because its close time has come + neACCEPTED_LEDGER = 2; // accepting a closed ledger, we have finished computing it + neSWITCHED_LEDGER = 3; // changing due to network consensus + neLOST_SYNC = 4; } -message TMStatusChange -{ - optional NodeStatus newStatus = 1; - optional NodeEvent newEvent = 2; - optional uint32 ledgerSeq = 3; - optional bytes ledgerHash = 4; - optional bytes ledgerHashPrevious = 5; - optional uint64 networkTime = 6; - optional uint32 firstSeq = 7; - optional uint32 lastSeq = 8; +message TMStatusChange { + optional NodeStatus newStatus = 1; + optional NodeEvent newEvent = 2; + optional uint32 ledgerSeq = 3; + optional bytes ledgerHash = 4; + optional bytes ledgerHashPrevious = 5; + optional uint64 networkTime = 6; + optional uint32 firstSeq = 7; + optional uint32 lastSeq = 8; } - // Announce to the network our position on a closing ledger -message TMProposeSet -{ - required uint32 proposeSeq = 1; - required bytes currentTxHash = 2; // the hash of the ledger we are proposing - required bytes nodePubKey = 3; - required uint32 closeTime = 4; - required bytes signature = 5; // signature of above fields - required bytes previousledger = 6; - repeated bytes addedTransactions = 10; // not required if number is large - repeated bytes removedTransactions = 11; // not required if number is large +message TMProposeSet { + required uint32 proposeSeq = 1; + required bytes currentTxHash = 2; // the hash of the ledger we are proposing + required bytes nodePubKey = 3; + required uint32 closeTime = 4; + required bytes signature = 5; // signature of above fields + required bytes previousledger = 6; + repeated bytes addedTransactions = 10; // not required if number is large + repeated bytes removedTransactions = 11; // not required if number is large - // node vouches signature is correct - optional bool checkedSignature = 7 [deprecated=true]; + // node vouches signature is correct + optional bool checkedSignature = 7 [deprecated = true]; - // Number of hops traveled - optional uint32 hops = 12 [deprecated=true]; + // Number of hops traveled + optional uint32 hops = 12 [deprecated = true]; } -enum TxSetStatus -{ - tsHAVE = 1; // We have this set locally - tsCAN_GET = 2; // We have a peer with this set - tsNEED = 3; // We need this set and can't get it +enum TxSetStatus { + tsHAVE = 1; // We have this set locally + tsCAN_GET = 2; // We have a peer with this set + tsNEED = 3; // We need this set and can't get it } -message TMHaveTransactionSet -{ - required TxSetStatus status = 1; - required bytes hash = 2; +message TMHaveTransactionSet { + required TxSetStatus status = 1; + required bytes hash = 2; } // Validator list (UNL) -message TMValidatorList -{ - required bytes manifest = 1; - required bytes blob = 2; - required bytes signature = 3; - required uint32 version = 4; +message TMValidatorList { + required bytes manifest = 1; + required bytes blob = 2; + required bytes signature = 3; + required uint32 version = 4; } // Validator List v2 -message ValidatorBlobInfo -{ - optional bytes manifest = 1; - required bytes blob = 2; - required bytes signature = 3; +message ValidatorBlobInfo { + optional bytes manifest = 1; + required bytes blob = 2; + required bytes signature = 3; } // Collection of Validator List v2 (UNL) -message TMValidatorListCollection -{ - required uint32 version = 1; - required bytes manifest = 2; - repeated ValidatorBlobInfo blobs = 3; +message TMValidatorListCollection { + required uint32 version = 1; + required bytes manifest = 2; + repeated ValidatorBlobInfo blobs = 3; } // Used to sign a final closed ledger after reprocessing -message TMValidation -{ - // The serialized validation - required bytes validation = 1; +message TMValidation { + // The serialized validation + required bytes validation = 1; - // node vouches signature is correct - optional bool checkedSignature = 2 [deprecated = true]; + // node vouches signature is correct + optional bool checkedSignature = 2 [deprecated = true]; - // Number of hops traveled - optional uint32 hops = 3 [deprecated = true]; + // Number of hops traveled + optional uint32 hops = 3 [deprecated = true]; } // An array of Endpoint messages -message TMEndpoints -{ - // Previously used - don't reuse. - reserved 2; +message TMEndpoints { + // Previously used - don't reuse. + reserved 2; - // This field is used to allow the TMEndpoints message format to be - // modified as necessary in the future. - required uint32 version = 1; + // This field is used to allow the TMEndpoints message format to be + // modified as necessary in the future. + required uint32 version = 1; - // An update to the Endpoint type that uses a string - // to represent endpoints, thus allowing ipv6 or ipv4 addresses - message TMEndpointv2 - { - required string endpoint = 1; - required uint32 hops = 2; - } - repeated TMEndpointv2 endpoints_v2 = 3; + // An update to the Endpoint type that uses a string + // to represent endpoints, thus allowing ipv6 or ipv4 addresses + message TMEndpointv2 { + required string endpoint = 1; + required uint32 hops = 2; + } + repeated TMEndpointv2 endpoints_v2 = 3; }; -message TMIndexedObject -{ - optional bytes hash = 1; - optional bytes nodeID = 2; - optional bytes index = 3; - optional bytes data = 4; - optional uint32 ledgerSeq = 5; +message TMIndexedObject { + optional bytes hash = 1; + optional bytes nodeID = 2; + optional bytes index = 3; + optional bytes data = 4; + optional uint32 ledgerSeq = 5; } -message TMGetObjectByHash -{ - enum ObjectType { - otUNKNOWN = 0; - otLEDGER = 1; - otTRANSACTION = 2; - otTRANSACTION_NODE = 3; - otSTATE_NODE = 4; - otCAS_OBJECT = 5; - otFETCH_PACK = 6; - otTRANSACTIONS = 7; - } +message TMGetObjectByHash { + enum ObjectType { + otUNKNOWN = 0; + otLEDGER = 1; + otTRANSACTION = 2; + otTRANSACTION_NODE = 3; + otSTATE_NODE = 4; + otCAS_OBJECT = 5; + otFETCH_PACK = 6; + otTRANSACTIONS = 7; + } - required ObjectType type = 1; - required bool query = 2; // is this a query or a reply? - optional uint32 seq = 3; // used to match replies to queries - optional bytes ledgerHash = 4; // the hash of the ledger these queries are for - optional bool fat = 5; // return related nodes - repeated TMIndexedObject objects = 6; // the specific objects requested + required ObjectType type = 1; + required bool query = 2; // is this a query or a reply? + optional uint32 seq = 3; // used to match replies to queries + optional bytes ledgerHash = 4; // the hash of the ledger these queries are for + optional bool fat = 5; // return related nodes + repeated TMIndexedObject objects = 6; // the specific objects requested } - -message TMLedgerNode -{ - required bytes nodedata = 1; - optional bytes nodeid = 2; // missing for ledger base data +message TMLedgerNode { + required bytes nodedata = 1; + optional bytes nodeid = 2; // missing for ledger base data } -enum TMLedgerInfoType -{ - liBASE = 0; // basic ledger info - liTX_NODE = 1; // transaction node - liAS_NODE = 2; // account state node - liTS_CANDIDATE = 3; // candidate transaction set +enum TMLedgerInfoType { + liBASE = 0; // basic ledger info + liTX_NODE = 1; // transaction node + liAS_NODE = 2; // account state node + liTS_CANDIDATE = 3; // candidate transaction set } -enum TMLedgerType -{ - ltACCEPTED = 0; - ltCURRENT = 1; // no longer supported - ltCLOSED = 2; +enum TMLedgerType { + ltACCEPTED = 0; + ltCURRENT = 1; // no longer supported + ltCLOSED = 2; } -enum TMQueryType -{ - qtINDIRECT = 0; +enum TMQueryType { + qtINDIRECT = 0; } -message TMGetLedger -{ - required TMLedgerInfoType itype = 1; - optional TMLedgerType ltype = 2; - optional bytes ledgerHash = 3; // Can also be the transaction set hash if liTS_CANDIDATE - optional uint32 ledgerSeq = 4; - repeated bytes nodeIDs = 5; - optional uint64 requestCookie = 6; - optional TMQueryType queryType = 7; - optional uint32 queryDepth = 8; // How deep to go, number of extra levels +message TMGetLedger { + required TMLedgerInfoType itype = 1; + optional TMLedgerType ltype = 2; + optional bytes ledgerHash = 3; // Can also be the transaction set hash if liTS_CANDIDATE + optional uint32 ledgerSeq = 4; + repeated bytes nodeIDs = 5; + optional uint64 requestCookie = 6; + optional TMQueryType queryType = 7; + optional uint32 queryDepth = 8; // How deep to go, number of extra levels } -enum TMReplyError -{ - reNO_LEDGER = 1; // We don't have the ledger you are asking about - reNO_NODE = 2; // We don't have any of the nodes you are asking for - reBAD_REQUEST = 3; // The request is wrong, e.g. wrong format +enum TMReplyError { + reNO_LEDGER = 1; // We don't have the ledger you are asking about + reNO_NODE = 2; // We don't have any of the nodes you are asking for + reBAD_REQUEST = 3; // The request is wrong, e.g. wrong format } -message TMLedgerData -{ - required bytes ledgerHash = 1; - required uint32 ledgerSeq = 2; - required TMLedgerInfoType type = 3; - repeated TMLedgerNode nodes = 4; - optional uint32 requestCookie = 5; - optional TMReplyError error = 6; +message TMLedgerData { + required bytes ledgerHash = 1; + required uint32 ledgerSeq = 2; + required TMLedgerInfoType type = 3; + repeated TMLedgerNode nodes = 4; + optional uint32 requestCookie = 5; + optional TMReplyError error = 6; } -message TMPing -{ - enum pingType { - ptPING = 0; // we want a reply - ptPONG = 1; // this is a reply - } - required pingType type = 1; - optional uint32 seq = 2; // detect stale replies, ensure other side is reading - optional uint64 pingTime = 3; // know when we think we sent the ping - optional uint64 netTime = 4; +message TMPing { + enum pingType { + ptPING = 0; // we want a reply + ptPONG = 1; // this is a reply + } + required pingType type = 1; + optional uint32 seq = 2; // detect stale replies, ensure other side is reading + optional uint64 pingTime = 3; // know when we think we sent the ping + optional uint64 netTime = 4; } -message TMSquelch -{ - required bool squelch = 1; // squelch if true, otherwise unsquelch - required bytes validatorPubKey = 2; // validator's public key - optional uint32 squelchDuration = 3; // squelch duration in seconds +message TMSquelch { + required bool squelch = 1; // squelch if true, otherwise unsquelch + required bytes validatorPubKey = 2; // validator's public key + optional uint32 squelchDuration = 3; // squelch duration in seconds } -enum TMLedgerMapType -{ - lmTRANASCTION = 1; // transaction map - lmACCOUNT_STATE = 2; // account state map +enum TMLedgerMapType { + lmTRANASCTION = 1; // transaction map + lmACCOUNT_STATE = 2; // account state map } -message TMProofPathRequest -{ - required bytes key = 1; - required bytes ledgerHash = 2; - required TMLedgerMapType type = 3; +message TMProofPathRequest { + required bytes key = 1; + required bytes ledgerHash = 2; + required TMLedgerMapType type = 3; } -message TMProofPathResponse -{ - required bytes key = 1; - required bytes ledgerHash = 2; - required TMLedgerMapType type = 3; - optional bytes ledgerHeader = 4; - repeated bytes path = 5; - optional TMReplyError error = 6; +message TMProofPathResponse { + required bytes key = 1; + required bytes ledgerHash = 2; + required TMLedgerMapType type = 3; + optional bytes ledgerHeader = 4; + repeated bytes path = 5; + optional TMReplyError error = 6; } -message TMReplayDeltaRequest -{ - required bytes ledgerHash = 1; +message TMReplayDeltaRequest { + required bytes ledgerHash = 1; } -message TMReplayDeltaResponse -{ - required bytes ledgerHash = 1; - optional bytes ledgerHeader = 2; - repeated bytes transaction = 3; - optional TMReplyError error = 4; +message TMReplayDeltaResponse { + required bytes ledgerHash = 1; + optional bytes ledgerHeader = 2; + repeated bytes transaction = 3; + optional TMReplyError error = 4; } -message TMHaveTransactions -{ - repeated bytes hashes = 1; +message TMHaveTransactions { + repeated bytes hashes = 1; } - From 58dd07bbdf04772a9f8cda1a006e941189627452 Mon Sep 17 00:00:00 2001 From: Bart Date: Thu, 21 Aug 2025 16:32:04 -0400 Subject: [PATCH 13/66] fix: Skip notify-clio when running in a fork, reorder config fields (#5712) This change will skip running the notify-clio job when a PR is created from a fork, and reorders the strategy matrix configuration fields so GitHub will more clearly show which configuration is running. --- .github/scripts/strategy-matrix/generate.py | 21 ++++++++++++--------- .github/workflows/build-test.yml | 2 +- .github/workflows/notify-clio.yml | 1 + 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 42927b5ccd..0acdca8d4f 100644 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -48,18 +48,18 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] skip = True if os['distro_version'] == 'bookworm': if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-13' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/arm64': - cmake_args = f'{cmake_args} -DUNIT_TEST_REFERENCE_FEE=500' + cmake_args = f'-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}' skip = False if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64': - cmake_args = f'{cmake_args} -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0' + cmake_args = f'-Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0 {cmake_args}' cmake_target = 'coverage' build_only = True skip = False if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-16' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/arm64': - cmake_args = f'{cmake_args} -Dvoidstar=ON' + cmake_args = f'-Dvoidstar=ON {cmake_args}' skip = False if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-17' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/amd64': - cmake_args = f'{cmake_args} -DUNIT_TEST_REFERENCE_FEE=1000' + cmake_args = f'-DUNIT_TEST_REFERENCE_FEE=1000 {cmake_args}' skip = False if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-20' and build_type == 'Debug' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/amd64': skip = False @@ -137,14 +137,17 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] if '-Dunity=ON' in cmake_args: config_name += '-unity' + # Add the configuration to the list, with the most unique fields first, + # so that they are easier to identify in the GitHub Actions UI, as long + # names get truncated. configurations.append({ - 'architecture': architecture, - 'os': os, - 'build_type': build_type, - 'build_only': 'true' if build_only else 'false', + 'config_name': config_name, 'cmake_args': cmake_args, 'cmake_target': cmake_target, - 'config_name': config_name, + 'build_only': 'true' if build_only else 'false', + 'build_type': build_type, + 'os': os, + 'architecture': architecture, }) return {'include': configurations} diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 2fa557f671..e06ff9abab 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -138,7 +138,7 @@ jobs: if: ${{ inputs.os == 'macos' }} run: | echo 'Installing build tools.' - brew install cmake conan ninja coreutils + brew install --quiet cmake conan ninja coreutils - name: Check configuration (Linux and MacOS) if: ${{ inputs.os == 'linux' || inputs.os == 'macos' }} run: | diff --git a/.github/workflows/notify-clio.yml b/.github/workflows/notify-clio.yml index 6ccf527ea6..b92dea65e2 100644 --- a/.github/workflows/notify-clio.yml +++ b/.github/workflows/notify-clio.yml @@ -36,6 +36,7 @@ defaults: jobs: upload: + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} runs-on: ubuntu-latest container: ghcr.io/xrplf/ci/ubuntu-noble:gcc-13 steps: From 896b8c3b54a22b0497cb0d1ce95e1095f9a227ce Mon Sep 17 00:00:00 2001 From: Bart Date: Fri, 22 Aug 2025 10:02:56 -0400 Subject: [PATCH 14/66] chore: Fix file formatting (#5718) --- .github/actions/build-deps/action.yml | 18 ++--- .github/actions/build-test/action.yml | 14 ++-- .github/scripts/levelization/README.md | 2 +- .github/workflows/build-test.yml | 20 ++--- .github/workflows/check-missing-commits.yml | 78 +++++++++---------- .github/workflows/notify-clio.yml | 10 +-- .github/workflows/on-pr.yml | 40 +++++----- .github/workflows/on-trigger.yml | 44 +++++------ .github/workflows/publish-docs.yml | 16 ++-- bin/git/setup-upstreams.sh | 3 +- bin/git/squash-branches.sh | 3 +- cfg/rippled-example.cfg | 8 +- cmake/RippledCore.cmake | 2 +- .../negativeUNLSqDiagram.puml | 20 ++--- .../ledger_replay_classes.puml | 36 ++++----- .../ledger_replay_sequence.puml | 6 +- include/xrpl/protocol/Batch.h | 2 +- include/xrpl/protocol/TxFlags.h | 4 +- .../xrpl/protocol/detail/ledger_entries.macro | 1 - src/libxrpl/protocol/Permissions.cpp | 2 +- src/test/app/Delegate_test.cpp | 2 +- src/test/app/NFTokenAuth_test.cpp | 2 +- src/test/csf/collectors.h | 2 +- src/test/jtx/delegate.h | 2 +- src/test/jtx/impl/delegate.cpp | 2 +- src/xrpld/app/tx/detail/DelegateSet.cpp | 2 +- src/xrpld/app/tx/detail/DelegateSet.h | 2 +- 27 files changed, 170 insertions(+), 173 deletions(-) diff --git a/.github/actions/build-deps/action.yml b/.github/actions/build-deps/action.yml index 272af0f97d..ba4f4e9e2f 100644 --- a/.github/actions/build-deps/action.yml +++ b/.github/actions/build-deps/action.yml @@ -7,33 +7,33 @@ name: Build Conan dependencies # https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs. inputs: build_dir: - description: 'The directory where to build.' + description: "The directory where to build." required: true build_type: description: 'The build type to use ("Debug", "Release").' required: true conan_remote_name: - description: 'The name of the Conan remote to use.' + description: "The name of the Conan remote to use." required: true conan_remote_url: - description: 'The URL of the Conan endpoint to use.' + description: "The URL of the Conan endpoint to use." required: true conan_remote_username: - description: 'The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' + description: "The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded." required: false - default: '' + default: "" conan_remote_password: - description: 'The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' + description: "The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded." required: false - default: '' + default: "" force_build: description: 'Force building of all dependencies ("true", "false").' required: false - default: 'false' + default: "false" force_upload: description: 'Force uploading of all dependencies ("true", "false").' required: false - default: 'false' + default: "false" runs: using: composite diff --git a/.github/actions/build-test/action.yml b/.github/actions/build-test/action.yml index 44292e7318..d68f302698 100644 --- a/.github/actions/build-test/action.yml +++ b/.github/actions/build-test/action.yml @@ -6,26 +6,26 @@ name: Build and Test # https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs. inputs: build_dir: - description: 'The directory where to build.' + description: "The directory where to build." required: true build_only: description: 'Whether to only build or to build and test the code ("true", "false").' required: false - default: 'false' + default: "false" build_type: description: 'The build type to use ("Debug", "Release").' required: true cmake_args: - description: 'Additional arguments to pass to CMake.' + description: "Additional arguments to pass to CMake." required: false - default: '' + default: "" cmake_target: - description: 'The CMake target to build.' + description: "The CMake target to build." required: true codecov_token: - description: 'The Codecov token to use for uploading coverage reports.' + description: "The Codecov token to use for uploading coverage reports." required: false - default: '' + default: "" os: description: 'The operating system to use for the build ("linux", "macos", "windows").' required: true diff --git a/.github/scripts/levelization/README.md b/.github/scripts/levelization/README.md index ec41a021cc..31c6d34b6b 100644 --- a/.github/scripts/levelization/README.md +++ b/.github/scripts/levelization/README.md @@ -111,4 +111,4 @@ get those details locally. 1. Run `levelization.sh` 2. Grep the modules in `paths.txt`. - For example, if a cycle is found `A ~= B`, simply `grep -w - A .github/scripts/levelization/results/paths.txt | grep -w B` +A .github/scripts/levelization/results/paths.txt | grep -w B` diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index e06ff9abab..36145479e1 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -9,25 +9,25 @@ on: workflow_call: inputs: build_dir: - description: 'The directory where to build.' + description: "The directory where to build." required: false type: string - default: '.build' + default: ".build" conan_remote_name: - description: 'The name of the Conan remote to use.' + description: "The name of the Conan remote to use." required: true type: string conan_remote_url: - description: 'The URL of the Conan endpoint to use.' + description: "The URL of the Conan endpoint to use." required: true type: string dependencies_force_build: - description: 'Force building of all dependencies.' + description: "Force building of all dependencies." required: false type: boolean default: false dependencies_force_upload: - description: 'Force uploading of all dependencies.' + description: "Force uploading of all dependencies." required: false type: boolean default: false @@ -40,16 +40,16 @@ on: description: 'The strategy matrix to use for generating the configurations ("minimal", "all").' required: false type: string - default: 'minimal' + default: "minimal" secrets: codecov_token: - description: 'The Codecov token to use for uploading coverage reports.' + description: "The Codecov token to use for uploading coverage reports." required: false conan_remote_username: - description: 'The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' + description: "The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded." required: false conan_remote_password: - description: 'The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded.' + description: "The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded." required: false concurrency: diff --git a/.github/workflows/check-missing-commits.yml b/.github/workflows/check-missing-commits.yml index da0e296e70..07d29174d8 100644 --- a/.github/workflows/check-missing-commits.yml +++ b/.github/workflows/check-missing-commits.yml @@ -18,45 +18,45 @@ jobs: check: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - with: - fetch-depth: 0 - - name: Check for missing commits - env: - MESSAGE: | + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + with: + fetch-depth: 0 + - name: Check for missing commits + env: + MESSAGE: | - If you are reading this, then the commits indicated above are missing - from the "develop" and/or "release" branch. Do a reverse-merge as soon - as possible. See CONTRIBUTING.md for instructions. - run: | - set -o pipefail - # Branches are ordered by how "canonical" they are. Every commit in one - # branch should be in all the branches behind it. - order=(master release develop) - branches=() - for branch in "${order[@]}"; do - # Check that the branches exist so that this job will work on forked - # repos, which don't necessarily have master and release branches. - echo "Checking if ${branch} exists." - if git ls-remote --exit-code --heads origin \ - refs/heads/${branch} > /dev/null; then - branches+=(origin/${branch}) - fi - done + If you are reading this, then the commits indicated above are missing + from the "develop" and/or "release" branch. Do a reverse-merge as soon + as possible. See CONTRIBUTING.md for instructions. + run: | + set -o pipefail + # Branches are ordered by how "canonical" they are. Every commit in one + # branch should be in all the branches behind it. + order=(master release develop) + branches=() + for branch in "${order[@]}"; do + # Check that the branches exist so that this job will work on forked + # repos, which don't necessarily have master and release branches. + echo "Checking if ${branch} exists." + if git ls-remote --exit-code --heads origin \ + refs/heads/${branch} > /dev/null; then + branches+=(origin/${branch}) + fi + done - prior=() - for branch in "${branches[@]}"; do - if [[ ${#prior[@]} -ne 0 ]]; then - echo "Checking ${prior[@]} for commits missing from ${branch}." - git log --oneline --no-merges "${prior[@]}" \ - ^$branch | tee -a "missing-commits.txt" - echo + prior=() + for branch in "${branches[@]}"; do + if [[ ${#prior[@]} -ne 0 ]]; then + echo "Checking ${prior[@]} for commits missing from ${branch}." + git log --oneline --no-merges "${prior[@]}" \ + ^$branch | tee -a "missing-commits.txt" + echo + fi + prior+=("${branch}") + done + + if [[ $(cat missing-commits.txt | wc -l) -ne 0 ]]; then + echo "${MESSAGE}" + exit 1 fi - prior+=("${branch}") - done - - if [[ $(cat missing-commits.txt | wc -l) -ne 0 ]]; then - echo "${MESSAGE}" - exit 1 - fi diff --git a/.github/workflows/notify-clio.yml b/.github/workflows/notify-clio.yml index b92dea65e2..f7e10de7af 100644 --- a/.github/workflows/notify-clio.yml +++ b/.github/workflows/notify-clio.yml @@ -8,22 +8,22 @@ on: workflow_call: inputs: conan_remote_name: - description: 'The name of the Conan remote to use.' + description: "The name of the Conan remote to use." required: true type: string conan_remote_url: - description: 'The URL of the Conan endpoint to use.' + description: "The URL of the Conan endpoint to use." required: true type: string secrets: clio_notify_token: - description: 'The GitHub token to notify Clio about new versions.' + description: "The GitHub token to notify Clio about new versions." required: true conan_remote_username: - description: 'The username for logging into the Conan remote.' + description: "The username for logging into the Conan remote." required: true conan_remote_password: - description: 'The password for logging into the Conan remote.' + description: "The password for logging into the Conan remote." required: true concurrency: diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 9d7bbbf89c..f3811c4ea4 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -7,28 +7,28 @@ name: PR on: pull_request: paths: - - '.github/actions/build-deps/**' - - '.github/actions/build-test/**' - - '.github/scripts/levelization/**' - - '.github/scripts/strategy-matrix/**' - - '.github/workflows/build-test.yml' - - '.github/workflows/check-format.yml' - - '.github/workflows/check-levelization.yml' - - '.github/workflows/notify-clio.yml' - - '.github/workflows/on-pr.yml' + - ".github/actions/build-deps/**" + - ".github/actions/build-test/**" + - ".github/scripts/levelization/**" + - ".github/scripts/strategy-matrix/**" + - ".github/workflows/build-test.yml" + - ".github/workflows/check-format.yml" + - ".github/workflows/check-levelization.yml" + - ".github/workflows/notify-clio.yml" + - ".github/workflows/on-pr.yml" # Keep the list of paths below in sync with those in the `on-trigger.yml` # file. - - 'cmake/**' - - 'conan/**' - - 'external/**' - - 'include/**' - - 'src/**' - - 'tests/**' - - '.clang-format' - - '.codecov.yml' - - '.pre-commit-config.yaml' - - 'CMakeLists.txt' - - 'conanfile.py' + - "cmake/**" + - "conan/**" + - "external/**" + - "include/**" + - "src/**" + - "tests/**" + - ".clang-format" + - ".codecov.yml" + - ".pre-commit-config.yaml" + - "CMakeLists.txt" + - "conanfile.py" types: - opened - synchronize diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index ed9a794985..55e93b9866 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -13,31 +13,31 @@ on: - release - master paths: - - '.github/actions/build-deps/**' - - '.github/actions/build-test/**' - - '.github/scripts/strategy-matrix/**' - - '.github/workflows/build-test.yml' - - '.github/workflows/check-missing-commits.yml' - - '.github/workflows/on-trigger.yml' - - '.github/workflows/publish-docs.yml' + - ".github/actions/build-deps/**" + - ".github/actions/build-test/**" + - ".github/scripts/strategy-matrix/**" + - ".github/workflows/build-test.yml" + - ".github/workflows/check-missing-commits.yml" + - ".github/workflows/on-trigger.yml" + - ".github/workflows/publish-docs.yml" # Keep the list of paths below in sync with those in `on-pr.yml`. - - 'cmake/**' - - 'conan/**' - - 'external/**' - - 'include/**' - - 'src/**' - - 'tests/**' - - '.clang-format' - - '.codecov.yml' - - '.pre-commit-config.yaml' - - 'CMakeLists.txt' - - 'conanfile.py' + - "cmake/**" + - "conan/**" + - "external/**" + - "include/**" + - "src/**" + - "tests/**" + - ".clang-format" + - ".codecov.yml" + - ".pre-commit-config.yaml" + - "CMakeLists.txt" + - "conanfile.py" # Run at 06:32 UTC on every day of the week from Monday through Friday. This # will force all dependencies to be rebuilt, which is useful to verify that # all dependencies can be built successfully. Only the dependencies that # are actually missing from the remote will be uploaded. schedule: - - cron: '32 6 * * 1-5' + - cron: "32 6 * * 1-5" # Run when manually triggered via the GitHub UI or API. If `force_upload` is # true, then the dependencies that were missing (`force_rebuild` is false) or # rebuilt (`force_rebuild` is true) will be uploaded, overwriting existing @@ -45,12 +45,12 @@ on: workflow_dispatch: inputs: dependencies_force_build: - description: 'Force building of all dependencies.' + description: "Force building of all dependencies." required: false type: boolean default: false dependencies_force_upload: - description: 'Force uploading of all dependencies.' + description: "Force uploading of all dependencies." required: false type: boolean default: false @@ -109,7 +109,7 @@ jobs: dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build == 'true' }} dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload == 'true' }} os: ${{ matrix.os }} - strategy_matrix: 'all' + strategy_matrix: "all" secrets: conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} diff --git a/.github/workflows/publish-docs.yml b/.github/workflows/publish-docs.yml index 509644e6b5..2fcdd581d1 100644 --- a/.github/workflows/publish-docs.yml +++ b/.github/workflows/publish-docs.yml @@ -5,13 +5,13 @@ name: Build and publish documentation on: push: paths: - - '.github/workflows/publish-docs.yml' - - '*.md' - - '**/*.md' - - 'docs/**' - - 'include/**' - - 'src/libxrpl/**' - - 'src/xrpld/**' + - ".github/workflows/publish-docs.yml" + - "*.md" + - "**/*.md" + - "docs/**" + - "include/**" + - "src/libxrpl/**" + - "src/xrpld/**" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -22,7 +22,7 @@ defaults: shell: bash env: - BUILD_DIR: .build + BUILD_DIR: .build jobs: publish: diff --git a/bin/git/setup-upstreams.sh b/bin/git/setup-upstreams.sh index cdf3f37f37..61d8171569 100755 --- a/bin/git/setup-upstreams.sh +++ b/bin/git/setup-upstreams.sh @@ -5,7 +5,7 @@ then name=$( basename $0 ) cat <<- USAGE Usage: $name - + Where is the Github username of the upstream repo. e.g. XRPLF USAGE exit 0 @@ -83,4 +83,3 @@ fi _run git fetch --jobs=$(nproc) upstreams exit 0 - diff --git a/bin/git/squash-branches.sh b/bin/git/squash-branches.sh index 66f1a2d715..4dcbf5aaa1 100755 --- a/bin/git/squash-branches.sh +++ b/bin/git/squash-branches.sh @@ -5,7 +5,7 @@ then name=$( basename $0 ) cat <<- USAGE Usage: $name workbranch base/branch user/branch [user/branch [...]] - + * workbranch will be created locally from base/branch * base/branch and user/branch may be specified as user:branch to allow easy copying from Github PRs @@ -66,4 +66,3 @@ git push $push HEAD:$b git fetch $repo ------------------------------------------------------------------- PUSH - diff --git a/cfg/rippled-example.cfg b/cfg/rippled-example.cfg index 8fb7d00875..8bffc150c1 100644 --- a/cfg/rippled-example.cfg +++ b/cfg/rippled-example.cfg @@ -396,8 +396,8 @@ # true - enables compression # false - disables compression [default]. # -# The rippled server can save bandwidth by compressing its peer-to-peer communications, -# at a cost of greater CPU usage. If you enable link compression, +# The rippled server can save bandwidth by compressing its peer-to-peer communications, +# at a cost of greater CPU usage. If you enable link compression, # the server automatically compresses communications with peer servers # that also have link compression enabled. # https://xrpl.org/enable-link-compression.html @@ -1011,7 +1011,7 @@ # that rippled is still in sync with the network, # and that the validated ledger is less than # 'age_threshold_seconds' old. If not, then continue -# sleeping for this number of seconds and +# sleeping for this number of seconds and # checking until healthy. # Default is 5. # @@ -1113,7 +1113,7 @@ # page_size Valid values: integer (MUST be power of 2 between 512 and 65536) # The default is 4096 bytes. This setting determines # the size of a page in the transaction.db file. -# See https://www.sqlite.org/pragma.html#pragma_page_size +# See https://www.sqlite.org/pragma.html#pragma_page_size # for more details about the available options. # # journal_size_limit Valid values: integer diff --git a/cmake/RippledCore.cmake b/cmake/RippledCore.cmake index 83b27e6c4f..7d3561675a 100644 --- a/cmake/RippledCore.cmake +++ b/cmake/RippledCore.cmake @@ -101,7 +101,7 @@ target_link_libraries(xrpl.libxrpl.resource PUBLIC xrpl.libxrpl.protocol) # Level 06 add_module(xrpl net) -target_link_libraries(xrpl.libxrpl.net PUBLIC +target_link_libraries(xrpl.libxrpl.net PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol diff --git a/docs/0001-negative-unl/negativeUNLSqDiagram.puml b/docs/0001-negative-unl/negativeUNLSqDiagram.puml index 8cb491af6a..9f37d43903 100644 --- a/docs/0001-negative-unl/negativeUNLSqDiagram.puml +++ b/docs/0001-negative-unl/negativeUNLSqDiagram.puml @@ -5,8 +5,8 @@ skinparam roundcorner 20 skinparam maxmessagesize 160 actor "Rippled Start" as RS -participant "Timer" as T -participant "NetworkOPs" as NOP +participant "Timer" as T +participant "NetworkOPs" as NOP participant "ValidatorList" as VL #lightgreen participant "Consensus" as GC participant "ConsensusAdaptor" as CA #lightgreen @@ -20,7 +20,7 @@ VL -> NOP NOP -> VL: update trusted validators activate VL VL -> VL: re-calculate quorum -hnote over VL#lightgreen: ignore negative listed validators\nwhen calculate quorum +hnote over VL#lightgreen: ignore negative listed validators\nwhen calculate quorum VL -> NOP deactivate VL NOP -> GC: start round @@ -36,14 +36,14 @@ activate GC end alt phase == OPEN - alt should close ledger + alt should close ledger GC -> GC: phase = ESTABLISH GC -> CA: onClose activate CA - alt sqn%256==0 + alt sqn%256==0 CA -[#green]> RM: getValidations - CA -[#green]> CA: create UNLModify Tx - hnote over CA#lightgreen: use validatations of the last 256 ledgers\nto figure out UNLModify Tx candidates.\nIf any, create UNLModify Tx, and add to TxSet. + CA -[#green]> CA: create UNLModify Tx + hnote over CA#lightgreen: use validatations of the last 256 ledgers\nto figure out UNLModify Tx candidates.\nIf any, create UNLModify Tx, and add to TxSet. end CA -> GC GC -> CA: propose @@ -61,14 +61,14 @@ else phase == ESTABLISH CA -> CA : build LCL hnote over CA #lightgreen: copy negative UNL from parent ledger alt sqn%256==0 - CA -[#green]> CA: Adjust negative UNL + CA -[#green]> CA: Adjust negative UNL CA -[#green]> CA: apply UNLModify Tx end CA -> CA : validate and send validation message activate NOP CA -> NOP : end consensus and\nbegin next consensus round deactivate NOP - deactivate CA + deactivate CA hnote over RM: receive validations end else phase == ACCEPTED @@ -76,4 +76,4 @@ else phase == ACCEPTED end deactivate GC -@enduml \ No newline at end of file +@enduml diff --git a/docs/0010-ledger-replay/ledger_replay_classes.puml b/docs/0010-ledger-replay/ledger_replay_classes.puml index 4c90ef2511..f98cfbe231 100644 --- a/docs/0010-ledger-replay/ledger_replay_classes.puml +++ b/docs/0010-ledger-replay/ledger_replay_classes.puml @@ -4,7 +4,7 @@ class TimeoutCounter { #app_ : Application& } -TimeoutCounter o-- "1" Application +TimeoutCounter o-- "1" Application ': app_ Stoppable <.. Application @@ -14,13 +14,13 @@ class Application { -m_inboundLedgers : uptr } -Application *-- "1" LedgerReplayer +Application *-- "1" LedgerReplayer ': m_ledgerReplayer -Application *-- "1" InboundLedgers +Application *-- "1" InboundLedgers ': m_inboundLedgers Stoppable <.. InboundLedgers -Application "1" --o InboundLedgers +Application "1" --o InboundLedgers ': app_ class InboundLedgers { @@ -28,9 +28,9 @@ class InboundLedgers { } Stoppable <.. LedgerReplayer -InboundLedgers "1" --o LedgerReplayer +InboundLedgers "1" --o LedgerReplayer ': inboundLedgers_ -Application "1" --o LedgerReplayer +Application "1" --o LedgerReplayer ': app_ class LedgerReplayer { @@ -42,17 +42,17 @@ class LedgerReplayer { -skipLists_ : hash_map> } -LedgerReplayer *-- LedgerReplayTask +LedgerReplayer *-- LedgerReplayTask ': tasks_ -LedgerReplayer o-- LedgerDeltaAcquire +LedgerReplayer o-- LedgerDeltaAcquire ': deltas_ -LedgerReplayer o-- SkipListAcquire +LedgerReplayer o-- SkipListAcquire ': skipLists_ TimeoutCounter <.. LedgerReplayTask -InboundLedgers "1" --o LedgerReplayTask +InboundLedgers "1" --o LedgerReplayTask ': inboundLedgers_ -LedgerReplayer "1" --o LedgerReplayTask +LedgerReplayer "1" --o LedgerReplayTask ': replayer_ class LedgerReplayTask { @@ -63,15 +63,15 @@ class LedgerReplayTask { +addDelta(sptr) } -LedgerReplayTask *-- "1" SkipListAcquire +LedgerReplayTask *-- "1" SkipListAcquire ': skipListAcquirer_ -LedgerReplayTask *-- LedgerDeltaAcquire +LedgerReplayTask *-- LedgerDeltaAcquire ': deltas_ TimeoutCounter <.. SkipListAcquire -InboundLedgers "1" --o SkipListAcquire +InboundLedgers "1" --o SkipListAcquire ': inboundLedgers_ -LedgerReplayer "1" --o SkipListAcquire +LedgerReplayer "1" --o SkipListAcquire ': replayer_ LedgerReplayTask --o SkipListAcquire : implicit via callback @@ -83,9 +83,9 @@ class SkipListAcquire { } TimeoutCounter <.. LedgerDeltaAcquire -InboundLedgers "1" --o LedgerDeltaAcquire +InboundLedgers "1" --o LedgerDeltaAcquire ': inboundLedgers_ -LedgerReplayer "1" --o LedgerDeltaAcquire +LedgerReplayer "1" --o LedgerDeltaAcquire ': replayer_ LedgerReplayTask --o LedgerDeltaAcquire : implicit via callback @@ -95,4 +95,4 @@ class LedgerDeltaAcquire { -replayer_ : LedgerReplayer& -dataReadyCallbacks_ : vector } -@enduml \ No newline at end of file +@enduml diff --git a/docs/0010-ledger-replay/ledger_replay_sequence.puml b/docs/0010-ledger-replay/ledger_replay_sequence.puml index 481819b5e8..603b09157b 100644 --- a/docs/0010-ledger-replay/ledger_replay_sequence.puml +++ b/docs/0010-ledger-replay/ledger_replay_sequence.puml @@ -38,7 +38,7 @@ deactivate lr loop lr -> lda : make_shared(ledgerId, ledgerSeq) return delta - lr -> lrt : addDelta(delta) + lr -> lrt : addDelta(delta) lrt -> lda : addDataCallback(callback) return return @@ -62,7 +62,7 @@ deactivate peer lr -> lda : processData(ledgerHeader, txns) lda -> lda : notify() note over lda: call the callbacks added by\naddDataCallback(callback). - lda -> lrt : callback(ledgerId) + lda -> lrt : callback(ledgerId) lrt -> lrt : deltaReady(ledgerId) lrt -> lrt : tryAdvance() loop as long as child can be built @@ -82,4 +82,4 @@ deactivate peer deactivate peer -@enduml \ No newline at end of file +@enduml diff --git a/include/xrpl/protocol/Batch.h b/include/xrpl/protocol/Batch.h index 1388bbd2f1..1307ea0978 100644 --- a/include/xrpl/protocol/Batch.h +++ b/include/xrpl/protocol/Batch.h @@ -34,4 +34,4 @@ serializeBatch( msg.addBitString(txid); } -} // namespace ripple \ No newline at end of file +} // namespace ripple diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index 2831933afb..a37474b780 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -141,7 +141,7 @@ constexpr std::uint32_t const tfTransferable = 0x00000008; constexpr std::uint32_t const tfMutable = 0x00000010; // MPTokenIssuanceCreate flags: -// NOTE - there is intentionally no flag here for lsfMPTLocked, which this transaction cannot mutate. +// NOTE - there is intentionally no flag here for lsfMPTLocked, which this transaction cannot mutate. constexpr std::uint32_t const tfMPTCanLock = lsfMPTCanLock; constexpr std::uint32_t const tfMPTRequireAuth = lsfMPTRequireAuth; constexpr std::uint32_t const tfMPTCanEscrow = lsfMPTCanEscrow; @@ -243,7 +243,7 @@ constexpr std::uint32_t tfUntilFailure = 0x00040000; constexpr std::uint32_t tfIndependent = 0x00080000; /** * @note If nested Batch transactions are supported in the future, the tfInnerBatchTxn flag - * will need to be removed from this mask to allow Batch transaction to be inside + * will need to be removed from this mask to allow Batch transaction to be inside * the sfRawTransactions array. */ constexpr std::uint32_t const tfBatchMask = diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 11306ee0f5..967fb37b94 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -505,4 +505,3 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({ #undef EXPAND #undef LEDGER_ENTRY_DUPLICATE - diff --git a/src/libxrpl/protocol/Permissions.cpp b/src/libxrpl/protocol/Permissions.cpp index dbe5325a4e..ca8cb26f36 100644 --- a/src/libxrpl/protocol/Permissions.cpp +++ b/src/libxrpl/protocol/Permissions.cpp @@ -145,4 +145,4 @@ Permission::permissionToTxType(uint32_t const& value) const return static_cast(value - 1); } -} // namespace ripple \ No newline at end of file +} // namespace ripple diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 179532140d..44cb6a54b6 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -1499,4 +1499,4 @@ class Delegate_test : public beast::unit_test::suite }; BEAST_DEFINE_TESTSUITE(Delegate, app, ripple); } // namespace test -} // namespace ripple \ No newline at end of file +} // namespace ripple diff --git a/src/test/app/NFTokenAuth_test.cpp b/src/test/app/NFTokenAuth_test.cpp index f5eedfce77..f0d7cc3700 100644 --- a/src/test/app/NFTokenAuth_test.cpp +++ b/src/test/app/NFTokenAuth_test.cpp @@ -621,4 +621,4 @@ public: BEAST_DEFINE_TESTSUITE_PRIO(NFTokenAuth, app, ripple, 2); -} // namespace ripple \ No newline at end of file +} // namespace ripple diff --git a/src/test/csf/collectors.h b/src/test/csf/collectors.h index 7b91863cbd..0494178ae9 100644 --- a/src/test/csf/collectors.h +++ b/src/test/csf/collectors.h @@ -720,4 +720,4 @@ struct JumpCollector } // namespace test } // namespace ripple -#endif \ No newline at end of file +#endif diff --git a/src/test/jtx/delegate.h b/src/test/jtx/delegate.h index 9e8850fbe2..ea368557b8 100644 --- a/src/test/jtx/delegate.h +++ b/src/test/jtx/delegate.h @@ -59,4 +59,4 @@ public: } // namespace delegate } // namespace jtx } // namespace test -} // namespace ripple \ No newline at end of file +} // namespace ripple diff --git a/src/test/jtx/impl/delegate.cpp b/src/test/jtx/impl/delegate.cpp index 3ceedff190..8ef2fac13d 100644 --- a/src/test/jtx/impl/delegate.cpp +++ b/src/test/jtx/impl/delegate.cpp @@ -64,4 +64,4 @@ entry(jtx::Env& env, jtx::Account const& account, jtx::Account const& authorize) } // namespace delegate } // namespace jtx } // namespace test -} // namespace ripple \ No newline at end of file +} // namespace ripple diff --git a/src/xrpld/app/tx/detail/DelegateSet.cpp b/src/xrpld/app/tx/detail/DelegateSet.cpp index 34e1c3afd3..708cdf0dc2 100644 --- a/src/xrpld/app/tx/detail/DelegateSet.cpp +++ b/src/xrpld/app/tx/detail/DelegateSet.cpp @@ -159,4 +159,4 @@ DelegateSet::deleteDelegate( return tesSUCCESS; } -} // namespace ripple \ No newline at end of file +} // namespace ripple diff --git a/src/xrpld/app/tx/detail/DelegateSet.h b/src/xrpld/app/tx/detail/DelegateSet.h index 6b01d63281..c72b1e3c58 100644 --- a/src/xrpld/app/tx/detail/DelegateSet.h +++ b/src/xrpld/app/tx/detail/DelegateSet.h @@ -53,4 +53,4 @@ public: } // namespace ripple -#endif \ No newline at end of file +#endif From 2e255812ae8fcd8b0da6acf0281c5f286c2827ed Mon Sep 17 00:00:00 2001 From: Bronek Kozicki Date: Fri, 22 Aug 2025 15:58:36 +0100 Subject: [PATCH 15/66] chore: Workaround for CI build errors on arm64 (#5717) CI builds with `clang-20` on `linux/arm64` are failing due to boost 1.86. This is hopefully fixed in version 1.88. --- .github/scripts/strategy-matrix/generate.py | 4 ++++ .gitignore | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 0acdca8d4f..652cb8871f 100644 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -123,6 +123,10 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] if os['distro_name'] == 'rhel' and architecture['platform'] == 'linux/arm64': continue + # We skip all clang-20 on arm64 due to boost 1.86 build error + if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-20' and architecture['platform'] == 'linux/arm64': + continue + # Generate a unique name for the configuration, e.g. macos-arm64-debug # or debian-bookworm-gcc-12-amd64-release-unity. config_name = os['distro_name'] diff --git a/.gitignore b/.gitignore index ab54adba74..5476f21a41 100644 --- a/.gitignore +++ b/.gitignore @@ -110,4 +110,4 @@ bld.rippled/ .vscode # Suggested in-tree build directory -/.build/ +/.build*/ From 095dc4d9cc89f96aaf82940e822a753bff4b96af Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 22 Aug 2025 12:15:03 -0400 Subject: [PATCH 16/66] fix(test): handle null metadata for unvalidated tx in Env::meta (#5715) This change handles errors better when calling `env.meta`. It prints some debug help and throws an error if `env.meta` is going to return a `nullptr`. --- src/test/jtx/impl/Env.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/test/jtx/impl/Env.cpp b/src/test/jtx/impl/Env.cpp index 46558a188a..d6956b30c7 100644 --- a/src/test/jtx/impl/Env.cpp +++ b/src/test/jtx/impl/Env.cpp @@ -499,7 +499,16 @@ Env::meta() close(); } auto const item = closed()->txRead(txid_); - return item.second; + auto const result = item.second; + if (result == nullptr) + { + test.log << "Env::meta: no metadata for txid: " << txid_ << std::endl; + test.log << "This is probably because the transaction failed with a " + "non-tec error." + << std::endl; + Throw("Env::meta: no metadata for txid"); + } + return result; } std::shared_ptr From c14ce956adeabe476ad73c18d73103f347c9c613 Mon Sep 17 00:00:00 2001 From: Bart Date: Fri, 22 Aug 2025 13:37:11 -0400 Subject: [PATCH 17/66] chore: Update clang-format and prettier with pre-commit (#5709) The change updates how clang-format is called in CI and locally, and adds prettier to the pre-commit hook. Proto files are now also formatted, while external files are excluded. --- .clang-format | 36 +++++------ .git-blame-ignore-revs | 2 + .github/scripts/strategy-matrix/linux.json | 24 ++------ .github/scripts/strategy-matrix/macos.json | 12 +--- .github/scripts/strategy-matrix/windows.json | 14 +---- .github/workflows/check-format.yml | 63 ++++--------------- .github/workflows/on-pr.yml | 4 +- .pre-commit-config.yaml | 64 +++++++++++++++++++- .prettierignore | 1 - 9 files changed, 103 insertions(+), 117 deletions(-) diff --git a/.clang-format b/.clang-format index 9c3820a6bf..bd446022df 100644 --- a/.clang-format +++ b/.clang-format @@ -1,4 +1,20 @@ --- +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false +KeepEmptyLinesAtTheStartOfBlocks: false +MaxEmptyLinesToKeep: 1 +--- Language: Cpp AccessModifierOffset: -4 AlignAfterOpenBracket: AlwaysBreak @@ -18,20 +34,7 @@ AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true BinPackArguments: false BinPackParameters: false -BraceWrapping: - AfterClass: true - AfterControlStatement: true - AfterEnum: false - AfterFunction: true - AfterNamespace: false - AfterObjCDeclaration: true - AfterStruct: true - AfterUnion: true - BeforeCatch: true - BeforeElse: true - IndentBraces: false BreakBeforeBinaryOperators: false -BreakBeforeBraces: Custom BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: true ColumnLimit: 80 @@ -66,8 +69,6 @@ IndentFunctionDeclarationAfterType: false IndentRequiresClause: true IndentWidth: 4 IndentWrappedFunctionNames: false -KeepEmptyLinesAtTheStartOfBlocks: false -MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: false @@ -96,11 +97,6 @@ TabWidth: 8 UseTab: Never QualifierAlignment: Right --- -Language: JavaScript ---- -Language: Json -IndentWidth: 2 ---- Language: Proto BasedOnStyle: Google ColumnLimit: 0 diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index a9805e705c..cf50d48f95 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -12,3 +12,5 @@ fe9a5365b8a52d4acc42eb27369247e6f238a4f9 9a93577314e6a8d4b4a8368cc9d2b15a5d8303e8 552377c76f55b403a1c876df873a23d780fcc81c 97f0747e103f13e26e45b731731059b32f7679ac +b13370ac0d207217354f1fc1c29aef87769fb8a1 +896b8c3b54a22b0497cb0d1ce95e1095f9a227ce diff --git a/.github/scripts/strategy-matrix/linux.json b/.github/scripts/strategy-matrix/linux.json index d8f176273d..44eaebd074 100644 --- a/.github/scripts/strategy-matrix/linux.json +++ b/.github/scripts/strategy-matrix/linux.json @@ -2,21 +2,11 @@ "architecture": [ { "platform": "linux/amd64", - "runner": [ - "self-hosted", - "Linux", - "X64", - "heavy" - ] + "runner": ["self-hosted", "Linux", "X64", "heavy"] }, { "platform": "linux/arm64", - "runner": [ - "self-hosted", - "Linux", - "ARM64", - "heavy-arm64" - ] + "runner": ["self-hosted", "Linux", "ARM64", "heavy-arm64"] } ], "os": [ @@ -159,12 +149,6 @@ "compiler_version": "19" } ], - "build_type": [ - "Debug", - "Release" - ], - "cmake_args": [ - "-Dunity=OFF", - "-Dunity=ON" - ] + "build_type": ["Debug", "Release"], + "cmake_args": ["-Dunity=OFF", "-Dunity=ON"] } diff --git a/.github/scripts/strategy-matrix/macos.json b/.github/scripts/strategy-matrix/macos.json index a6ffdf14b7..de37639ddd 100644 --- a/.github/scripts/strategy-matrix/macos.json +++ b/.github/scripts/strategy-matrix/macos.json @@ -2,12 +2,7 @@ "architecture": [ { "platform": "macos/arm64", - "runner": [ - "self-hosted", - "macOS", - "ARM64", - "mac-runner-m1" - ] + "runner": ["self-hosted", "macOS", "ARM64", "mac-runner-m1"] } ], "os": [ @@ -18,10 +13,7 @@ "compiler_version": "" } ], - "build_type": [ - "Debug", - "Release" - ], + "build_type": ["Debug", "Release"], "cmake_args": [ "-Dunity=OFF -DCMAKE_POLICY_VERSION_MINIMUM=3.5", "-Dunity=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5" diff --git a/.github/scripts/strategy-matrix/windows.json b/.github/scripts/strategy-matrix/windows.json index aaa8c94411..5e6e536750 100644 --- a/.github/scripts/strategy-matrix/windows.json +++ b/.github/scripts/strategy-matrix/windows.json @@ -2,9 +2,7 @@ "architecture": [ { "platform": "windows/amd64", - "runner": [ - "windows-latest" - ] + "runner": ["windows-latest"] } ], "os": [ @@ -15,12 +13,6 @@ "compiler_version": "" } ], - "build_type": [ - "Debug", - "Release" - ], - "cmake_args": [ - "-Dunity=OFF", - "-Dunity=ON" - ] + "build_type": ["Debug", "Release"], + "cmake_args": ["-Dunity=OFF", "-Dunity=ON"] } diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml index 5e3da10028..359e3e634b 100644 --- a/.github/workflows/check-format.yml +++ b/.github/workflows/check-format.yml @@ -13,9 +13,9 @@ defaults: shell: bash jobs: - clang-format: + pre-commit: runs-on: ubuntu-latest - container: ghcr.io/xrplf/ci/tools-rippled-clang-format + container: ghcr.io/xrplf/ci/tools-rippled-pre-commit steps: # The $GITHUB_WORKSPACE and ${{ github.workspace }} might not point to the # same directory for jobs running in containers. The actions/checkout step @@ -38,48 +38,11 @@ jobs: echo 'Checking environment variables.' env | sort + echo 'Checking pre-commit version.' + pre-commit --version + echo 'Checking clang-format version.' clang-format --version - - name: Format code - run: find include src tests -type f \( -name '*.cpp' -o -name '*.hpp' -o -name '*.h' -o -name '*.ipp' \) -exec clang-format -i {} + - - name: Check for differences - env: - MESSAGE: | - One or more files did not conform to the formatting specified in - .clang-format. Maybe you did not run 'git-clang-format' or - 'clang-format' before committing, or your version of clang-format - has an incompatibility with the one used here (see the "Check - configuration" step above). - - Run 'git-clang-format --extensions cpp,h,hpp,ipp develop' in your - repo, and then commit and push the changes. - run: | - DIFF=$(git status --porcelain) - if [ -n "${DIFF}" ]; then - # Print the files that changed to give the contributor a hint about - # what to expect when running git-clang-format on their own machine. - git status - echo "${MESSAGE}" - exit 1 - fi - - prettier: - runs-on: ubuntu-latest - container: ghcr.io/xrplf/ci/tools-rippled-prettier - steps: - - name: Configure git safe.directory - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git config --global --add safe.directory ${{ github.workspace }} - - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 - - name: Check configuration - run: | - echo 'Checking path.' - echo ${PATH} | tr ':' '\n' - - echo 'Checking environment variables.' - env | sort echo 'Checking NPM version.' npm --version @@ -90,22 +53,22 @@ jobs: echo 'Checking prettier version.' prettier --version - name: Format code - run: prettier --check . + run: pre-commit run --show-diff-on-failure --color=always --all-files - name: Check for differences env: MESSAGE: | - One or more files did not conform to the formatting rules specified - by Prettier. Maybe you did not run 'prettier' before committing, or - your version of prettier has an incompatibility with the one used - here (see the "Check configuration" step above). + One or more files did not conform to the formatting. Maybe you did + not run 'pre-commit' before committing, or your version of + 'clang-format' or 'prettier' has an incompatibility with the ones + used here (see the "Check configuration" step above). - Run 'prettier --check .' in your repo, and then commit and push the - changes. + Run 'pre-commit run --all-files' in your repo, and then commit and + push the changes. run: | DIFF=$(git status --porcelain) if [ -n "${DIFF}" ]; then # Print the files that changed to give the contributor a hint about - # what to expect when running prettier on their own machine. + # what to expect when running pre-commit on their own machine. git status echo "${MESSAGE}" exit 1 diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index f3811c4ea4..d1623874ac 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -69,7 +69,7 @@ jobs: runs-on: ubuntu-latest steps: - name: No-op - run: echo '' + run: true check-format: needs: should-run @@ -86,7 +86,7 @@ jobs: runs-on: ubuntu-latest steps: - name: No-op - run: echo '' + run: true outputs: conan_remote_name: ${{ env.CONAN_REMOTE_NAME }} conan_remote_url: ${{ env.CONAN_REMOTE_URL }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7daecdb5ec..3bd60b76d0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,64 @@ -# .pre-commit-config.yaml +# To run pre-commit hooks, first install pre-commit: +# - `pip install pre-commit==${PRE_COMMIT_VERSION}` +# - `pip install pre-commit-hooks==${PRE_COMMIT_HOOKS_VERSION}` +# +# Depending on your system, you can use `brew install` or `apt install` as well +# for installing the pre-commit package, but `pip` is needed to install the +# hooks; you can also use `pipx` if you prefer. +# Next, install the required formatters: +# - `pip install clang-format==${CLANG_VERSION}` +# - `npm install prettier@${PRETTIER_VERSION}` +# +# See https://github.com/XRPLF/ci/blob/main/.github/workflows/tools-rippled.yml +# for the versions used in the CI pipeline. You will need to have the exact same +# versions of the tools installed on your system to produce the same results as +# the pipeline. +# +# Then, run the following command to install the git hook scripts: +# - `pre-commit install` +# You can run all configured hooks against all files with: +# - `pre-commit run --all-files` +# To manually run a specific hook, use: +# - `pre-commit run --all-files` +# To run the hooks against only the files changed in the current commit, use: +# - `pre-commit run` repos: - - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v18.1.8 + - repo: local hooks: - id: clang-format + name: clang-format + language: system + entry: clang-format -i + files: '\.(cpp|hpp|h|ipp|proto)$' + - id: trailing-whitespace + name: trailing-whitespace + entry: trailing-whitespace-fixer + language: system + types: [text] + - id: end-of-file + name: end-of-file + entry: end-of-file-fixer + language: system + types: [text] + - id: mixed-line-ending + name: mixed-line-ending + entry: mixed-line-ending + language: system + types: [text] + - id: check-merge-conflict + name: check-merge-conflict + entry: check-merge-conflict --assume-in-merge + language: system + types: [text] + - repo: local + hooks: + - id: prettier + name: prettier + language: system + entry: prettier --ignore-unknown --write + +exclude: | + (?x)^( + external/.*| + .github/scripts/levelization/results/.*\.txt + )$ diff --git a/.prettierignore b/.prettierignore index 477120ade1..5446323fad 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1 @@ external -.* From c57cd8b23ead8a092ff28a7be67c23d610e29c46 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Fri, 22 Aug 2025 17:30:08 -0400 Subject: [PATCH 18/66] Revert "perf: Move mutex to the partition level (#5486)" This reverts commit 94decc753b515e7499808ca0d5b9e24d172c691e. --- include/xrpl/basics/SHAMapHash.h | 1 + include/xrpl/basics/TaggedCache.h | 26 ++- include/xrpl/basics/TaggedCache.ipp | 197 +++++++++--------- .../xrpl/basics/partitioned_unordered_map.h | 12 -- include/xrpl/protocol/Protocol.h | 1 + src/test/basics/TaggedCache_test.cpp | 24 +-- src/xrpld/app/ledger/LedgerHistory.cpp | 15 +- src/xrpld/rpc/handlers/GetCounts.cpp | 2 +- 8 files changed, 143 insertions(+), 135 deletions(-) diff --git a/include/xrpl/basics/SHAMapHash.h b/include/xrpl/basics/SHAMapHash.h index 1ec326409c..2d2dcdc3ef 100644 --- a/include/xrpl/basics/SHAMapHash.h +++ b/include/xrpl/basics/SHAMapHash.h @@ -21,6 +21,7 @@ #define RIPPLE_BASICS_SHAMAP_HASH_H_INCLUDED #include +#include #include diff --git a/include/xrpl/basics/TaggedCache.h b/include/xrpl/basics/TaggedCache.h index 7eace6fe72..99c91fe393 100644 --- a/include/xrpl/basics/TaggedCache.h +++ b/include/xrpl/basics/TaggedCache.h @@ -90,6 +90,9 @@ public: int getCacheSize() const; + int + getTrackSize() const; + float getHitRate(); @@ -167,6 +170,9 @@ public: bool retrieve(key_type const& key, T& data); + mutex_type& + peekMutex(); + std::vector getKeys() const; @@ -187,14 +193,11 @@ public: private: SharedPointerType - initialFetch(key_type const& key); + initialFetch(key_type const& key, std::lock_guard const& l); void collect_metrics(); - Mutex& - lockPartition(key_type const& key) const; - private: struct Stats { @@ -297,8 +300,8 @@ private: [[maybe_unused]] clock_type::time_point const& now, typename KeyValueCacheType::map_type& partition, SweptPointersVector& stuffToSweep, - std::atomic& allRemoval, - Mutex& partitionLock); + std::atomic& allRemovals, + std::lock_guard const&); [[nodiscard]] std::thread sweepHelper( @@ -307,12 +310,14 @@ private: typename KeyOnlyCacheType::map_type& partition, SweptPointersVector&, std::atomic& allRemovals, - Mutex& partitionLock); + std::lock_guard const&); beast::Journal m_journal; clock_type& m_clock; Stats m_stats; + mutex_type mutable m_mutex; + // Used for logging std::string m_name; @@ -323,11 +328,10 @@ private: clock_type::duration const m_target_age; // Number of items cached - std::atomic m_cache_count; + int m_cache_count; cache_type m_cache; // Hold strong reference to recent objects - std::atomic m_hits; - std::atomic m_misses; - mutable std::vector partitionLocks_; + std::uint64_t m_hits; + std::uint64_t m_misses; }; } // namespace ripple diff --git a/include/xrpl/basics/TaggedCache.ipp b/include/xrpl/basics/TaggedCache.ipp index c909ec6ad1..16a3f7587a 100644 --- a/include/xrpl/basics/TaggedCache.ipp +++ b/include/xrpl/basics/TaggedCache.ipp @@ -22,7 +22,6 @@ #include #include -#include namespace ripple { @@ -61,7 +60,6 @@ inline TaggedCache< , m_hits(0) , m_misses(0) { - partitionLocks_ = std::vector(m_cache.partitions()); } template < @@ -107,13 +105,8 @@ TaggedCache< KeyEqual, Mutex>::size() const { - std::size_t totalSize = 0; - for (size_t i = 0; i < partitionLocks_.size(); ++i) - { - std::lock_guard lock(partitionLocks_[i]); - totalSize += m_cache.map()[i].size(); - } - return totalSize; + std::lock_guard lock(m_mutex); + return m_cache.size(); } template < @@ -136,7 +129,32 @@ TaggedCache< KeyEqual, Mutex>::getCacheSize() const { - return m_cache_count.load(std::memory_order_relaxed); + std::lock_guard lock(m_mutex); + return m_cache_count; +} + +template < + class Key, + class T, + bool IsKeyCache, + class SharedWeakUnionPointer, + class SharedPointerType, + class Hash, + class KeyEqual, + class Mutex> +inline int +TaggedCache< + Key, + T, + IsKeyCache, + SharedWeakUnionPointer, + SharedPointerType, + Hash, + KeyEqual, + Mutex>::getTrackSize() const +{ + std::lock_guard lock(m_mutex); + return m_cache.size(); } template < @@ -159,10 +177,9 @@ TaggedCache< KeyEqual, Mutex>::getHitRate() { - auto const hits = m_hits.load(std::memory_order_relaxed); - auto const misses = m_misses.load(std::memory_order_relaxed); - float const total = float(hits + misses); - return hits * (100.0f / std::max(1.0f, total)); + std::lock_guard lock(m_mutex); + auto const total = static_cast(m_hits + m_misses); + return m_hits * (100.0f / std::max(1.0f, total)); } template < @@ -185,12 +202,9 @@ TaggedCache< KeyEqual, Mutex>::clear() { - for (auto& mutex : partitionLocks_) - mutex.lock(); + std::lock_guard lock(m_mutex); m_cache.clear(); - for (auto& mutex : partitionLocks_) - mutex.unlock(); - m_cache_count.store(0, std::memory_order_relaxed); + m_cache_count = 0; } template < @@ -213,9 +227,11 @@ TaggedCache< KeyEqual, Mutex>::reset() { - clear(); - m_hits.store(0, std::memory_order_relaxed); - m_misses.store(0, std::memory_order_relaxed); + std::lock_guard lock(m_mutex); + m_cache.clear(); + m_cache_count = 0; + m_hits = 0; + m_misses = 0; } template < @@ -239,7 +255,7 @@ TaggedCache< KeyEqual, Mutex>::touch_if_exists(KeyComparable const& key) { - std::lock_guard lock(lockPartition(key)); + std::lock_guard lock(m_mutex); auto const iter(m_cache.find(key)); if (iter == m_cache.end()) { @@ -281,6 +297,8 @@ TaggedCache< auto const start = std::chrono::steady_clock::now(); { + std::lock_guard lock(m_mutex); + if (m_target_size == 0 || (static_cast(m_cache.size()) <= m_target_size)) { @@ -312,13 +330,12 @@ TaggedCache< m_cache.map()[p], allStuffToSweep[p], allRemovals, - partitionLocks_[p])); + lock)); } for (std::thread& worker : workers) worker.join(); - int removals = allRemovals.load(std::memory_order_relaxed); - m_cache_count.fetch_sub(removals, std::memory_order_relaxed); + m_cache_count -= allRemovals; } // At this point allStuffToSweep will go out of scope outside the lock // and decrement the reference count on each strong pointer. @@ -352,8 +369,7 @@ TaggedCache< { // Remove from cache, if !valid, remove from map too. Returns true if // removed from cache - - std::lock_guard lock(lockPartition(key)); + std::lock_guard lock(m_mutex); auto cit = m_cache.find(key); @@ -366,7 +382,7 @@ TaggedCache< if (entry.isCached()) { - m_cache_count.fetch_sub(1, std::memory_order_relaxed); + --m_cache_count; entry.ptr.convertToWeak(); ret = true; } @@ -404,16 +420,17 @@ TaggedCache< { // Return canonical value, store if needed, refresh in cache // Return values: true=we had the data already + std::lock_guard lock(m_mutex); - std::lock_guard lock(lockPartition(key)); auto cit = m_cache.find(key); + if (cit == m_cache.end()) { m_cache.emplace( std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(m_clock.now(), data)); - m_cache_count.fetch_add(1, std::memory_order_relaxed); + ++m_cache_count; return false; } @@ -462,12 +479,12 @@ TaggedCache< data = cachedData; } - m_cache_count.fetch_add(1, std::memory_order_relaxed); + ++m_cache_count; return true; } entry.ptr = data; - m_cache_count.fetch_add(1, std::memory_order_relaxed); + ++m_cache_count; return false; } @@ -543,11 +560,10 @@ TaggedCache< KeyEqual, Mutex>::fetch(key_type const& key) { - std::lock_guard lock(lockPartition(key)); - - auto ret = initialFetch(key); + std::lock_guard l(m_mutex); + auto ret = initialFetch(key, l); if (!ret) - m_misses.fetch_add(1, std::memory_order_relaxed); + ++m_misses; return ret; } @@ -611,8 +627,8 @@ TaggedCache< Mutex>::insert(key_type const& key) -> std::enable_if_t { + std::lock_guard lock(m_mutex); clock_type::time_point const now(m_clock.now()); - std::lock_guard lock(lockPartition(key)); auto [it, inserted] = m_cache.emplace( std::piecewise_construct, std::forward_as_tuple(key), @@ -652,6 +668,29 @@ TaggedCache< return true; } +template < + class Key, + class T, + bool IsKeyCache, + class SharedWeakUnionPointer, + class SharedPointerType, + class Hash, + class KeyEqual, + class Mutex> +inline auto +TaggedCache< + Key, + T, + IsKeyCache, + SharedWeakUnionPointer, + SharedPointerType, + Hash, + KeyEqual, + Mutex>::peekMutex() -> mutex_type& +{ + return m_mutex; +} + template < class Key, class T, @@ -675,13 +714,10 @@ TaggedCache< std::vector v; { + std::lock_guard lock(m_mutex); v.reserve(m_cache.size()); - for (std::size_t i = 0; i < partitionLocks_.size(); ++i) - { - std::lock_guard lock(partitionLocks_[i]); - for (auto const& entry : m_cache.map()[i]) - v.push_back(entry.first); - } + for (auto const& _ : m_cache) + v.push_back(_.first); } return v; @@ -707,12 +743,11 @@ TaggedCache< KeyEqual, Mutex>::rate() const { - auto const hits = m_hits.load(std::memory_order_relaxed); - auto const misses = m_misses.load(std::memory_order_relaxed); - auto const tot = hits + misses; + std::lock_guard lock(m_mutex); + auto const tot = m_hits + m_misses; if (tot == 0) - return 0.0; - return double(hits) / tot; + return 0; + return double(m_hits) / tot; } template < @@ -736,16 +771,18 @@ TaggedCache< KeyEqual, Mutex>::fetch(key_type const& digest, Handler const& h) { - std::lock_guard lock(lockPartition(digest)); - - if (auto ret = initialFetch(digest)) - return ret; + { + std::lock_guard l(m_mutex); + if (auto ret = initialFetch(digest, l)) + return ret; + } auto sle = h(); if (!sle) return {}; - m_misses.fetch_add(1, std::memory_order_relaxed); + std::lock_guard l(m_mutex); + ++m_misses; auto const [it, inserted] = m_cache.emplace(digest, Entry(m_clock.now(), std::move(sle))); if (!inserted) @@ -772,10 +809,9 @@ TaggedCache< SharedPointerType, Hash, KeyEqual, - Mutex>::initialFetch(key_type const& key) + Mutex>:: + initialFetch(key_type const& key, std::lock_guard const& l) { - std::lock_guard lock(lockPartition(key)); - auto cit = m_cache.find(key); if (cit == m_cache.end()) return {}; @@ -783,7 +819,7 @@ TaggedCache< Entry& entry = cit->second; if (entry.isCached()) { - m_hits.fetch_add(1, std::memory_order_relaxed); + ++m_hits; entry.touch(m_clock.now()); return entry.ptr.getStrong(); } @@ -791,13 +827,12 @@ TaggedCache< if (entry.isCached()) { // independent of cache size, so not counted as a hit - m_cache_count.fetch_add(1, std::memory_order_relaxed); + ++m_cache_count; entry.touch(m_clock.now()); return entry.ptr.getStrong(); } m_cache.erase(cit); - return {}; } @@ -826,11 +861,10 @@ TaggedCache< { beast::insight::Gauge::value_type hit_rate(0); { - auto const hits = m_hits.load(std::memory_order_relaxed); - auto const misses = m_misses.load(std::memory_order_relaxed); - auto const total = hits + misses; + std::lock_guard lock(m_mutex); + auto const total(m_hits + m_misses); if (total != 0) - hit_rate = (hits * 100) / total; + hit_rate = (m_hits * 100) / total; } m_stats.hit_rate.set(hit_rate); } @@ -861,16 +895,12 @@ TaggedCache< typename KeyValueCacheType::map_type& partition, SweptPointersVector& stuffToSweep, std::atomic& allRemovals, - Mutex& partitionLock) + std::lock_guard const&) { return std::thread([&, this]() { - beast::setCurrentThreadName("sweep-KVCache"); - int cacheRemovals = 0; int mapRemovals = 0; - std::lock_guard lock(partitionLock); - // Keep references to all the stuff we sweep // so that we can destroy them outside the lock. stuffToSweep.reserve(partition.size()); @@ -954,16 +984,12 @@ TaggedCache< typename KeyOnlyCacheType::map_type& partition, SweptPointersVector&, std::atomic& allRemovals, - Mutex& partitionLock) + std::lock_guard const&) { return std::thread([&, this]() { - beast::setCurrentThreadName("sweep-KCache"); - int cacheRemovals = 0; int mapRemovals = 0; - std::lock_guard lock(partitionLock); - // Keep references to all the stuff we sweep // so that we can destroy them outside the lock. { @@ -998,29 +1024,6 @@ TaggedCache< }); } -template < - class Key, - class T, - bool IsKeyCache, - class SharedWeakUnionPointer, - class SharedPointerType, - class Hash, - class KeyEqual, - class Mutex> -inline Mutex& -TaggedCache< - Key, - T, - IsKeyCache, - SharedWeakUnionPointer, - SharedPointerType, - Hash, - KeyEqual, - Mutex>::lockPartition(key_type const& key) const -{ - return partitionLocks_[m_cache.partition_index(key)]; -} - } // namespace ripple #endif diff --git a/include/xrpl/basics/partitioned_unordered_map.h b/include/xrpl/basics/partitioned_unordered_map.h index ecaf16a47e..4e503ad0fa 100644 --- a/include/xrpl/basics/partitioned_unordered_map.h +++ b/include/xrpl/basics/partitioned_unordered_map.h @@ -277,12 +277,6 @@ public: return map_; } - partition_map_type const& - map() const - { - return map_; - } - iterator begin() { @@ -327,12 +321,6 @@ public: return cend(); } - std::size_t - partition_index(key_type const& key) const - { - return partitioner(key); - } - private: template void diff --git a/include/xrpl/protocol/Protocol.h b/include/xrpl/protocol/Protocol.h index bd39233cca..898fd06fbd 100644 --- a/include/xrpl/protocol/Protocol.h +++ b/include/xrpl/protocol/Protocol.h @@ -22,6 +22,7 @@ #include #include +#include #include diff --git a/src/test/basics/TaggedCache_test.cpp b/src/test/basics/TaggedCache_test.cpp index ec450e46dd..3d3dba698d 100644 --- a/src/test/basics/TaggedCache_test.cpp +++ b/src/test/basics/TaggedCache_test.cpp @@ -58,10 +58,10 @@ public: // Insert an item, retrieve it, and age it so it gets purged. { BEAST_EXPECT(c.getCacheSize() == 0); - BEAST_EXPECT(c.size() == 0); + BEAST_EXPECT(c.getTrackSize() == 0); BEAST_EXPECT(!c.insert(1, "one")); BEAST_EXPECT(c.getCacheSize() == 1); - BEAST_EXPECT(c.size() == 1); + BEAST_EXPECT(c.getTrackSize() == 1); { std::string s; @@ -72,7 +72,7 @@ public: ++clock; c.sweep(); BEAST_EXPECT(c.getCacheSize() == 0); - BEAST_EXPECT(c.size() == 0); + BEAST_EXPECT(c.getTrackSize() == 0); } // Insert an item, maintain a strong pointer, age it, and @@ -80,7 +80,7 @@ public: { BEAST_EXPECT(!c.insert(2, "two")); BEAST_EXPECT(c.getCacheSize() == 1); - BEAST_EXPECT(c.size() == 1); + BEAST_EXPECT(c.getTrackSize() == 1); { auto p = c.fetch(2); @@ -88,14 +88,14 @@ public: ++clock; c.sweep(); BEAST_EXPECT(c.getCacheSize() == 0); - BEAST_EXPECT(c.size() == 1); + BEAST_EXPECT(c.getTrackSize() == 1); } // Make sure its gone now that our reference is gone ++clock; c.sweep(); BEAST_EXPECT(c.getCacheSize() == 0); - BEAST_EXPECT(c.size() == 0); + BEAST_EXPECT(c.getTrackSize() == 0); } // Insert the same key/value pair and make sure we get the same result @@ -111,7 +111,7 @@ public: ++clock; c.sweep(); BEAST_EXPECT(c.getCacheSize() == 0); - BEAST_EXPECT(c.size() == 0); + BEAST_EXPECT(c.getTrackSize() == 0); } // Put an object in but keep a strong pointer to it, advance the clock a @@ -121,24 +121,24 @@ public: // Put an object in BEAST_EXPECT(!c.insert(4, "four")); BEAST_EXPECT(c.getCacheSize() == 1); - BEAST_EXPECT(c.size() == 1); + BEAST_EXPECT(c.getTrackSize() == 1); { // Keep a strong pointer to it auto const p1 = c.fetch(4); BEAST_EXPECT(p1 != nullptr); BEAST_EXPECT(c.getCacheSize() == 1); - BEAST_EXPECT(c.size() == 1); + BEAST_EXPECT(c.getTrackSize() == 1); // Advance the clock a lot ++clock; c.sweep(); BEAST_EXPECT(c.getCacheSize() == 0); - BEAST_EXPECT(c.size() == 1); + BEAST_EXPECT(c.getTrackSize() == 1); // Canonicalize a new object with the same key auto p2 = std::make_shared("four"); BEAST_EXPECT(c.canonicalize_replace_client(4, p2)); BEAST_EXPECT(c.getCacheSize() == 1); - BEAST_EXPECT(c.size() == 1); + BEAST_EXPECT(c.getTrackSize() == 1); // Make sure we get the original object BEAST_EXPECT(p1.get() == p2.get()); } @@ -146,7 +146,7 @@ public: ++clock; c.sweep(); BEAST_EXPECT(c.getCacheSize() == 0); - BEAST_EXPECT(c.size() == 0); + BEAST_EXPECT(c.getTrackSize() == 0); } } }; diff --git a/src/xrpld/app/ledger/LedgerHistory.cpp b/src/xrpld/app/ledger/LedgerHistory.cpp index dcbd722120..ccec209bd4 100644 --- a/src/xrpld/app/ledger/LedgerHistory.cpp +++ b/src/xrpld/app/ledger/LedgerHistory.cpp @@ -63,6 +63,8 @@ LedgerHistory::insert( ledger->stateMap().getHash().isNonZero(), "ripple::LedgerHistory::insert : nonzero hash"); + std::unique_lock sl(m_ledgers_by_hash.peekMutex()); + bool const alreadyHad = m_ledgers_by_hash.canonicalize_replace_cache( ledger->info().hash, ledger); if (validated) @@ -74,6 +76,7 @@ LedgerHistory::insert( LedgerHash LedgerHistory::getLedgerHash(LedgerIndex index) { + std::unique_lock sl(m_ledgers_by_hash.peekMutex()); if (auto it = mLedgersByIndex.find(index); it != mLedgersByIndex.end()) return it->second; return {}; @@ -83,11 +86,13 @@ std::shared_ptr LedgerHistory::getLedgerBySeq(LedgerIndex index) { { + std::unique_lock sl(m_ledgers_by_hash.peekMutex()); auto it = mLedgersByIndex.find(index); if (it != mLedgersByIndex.end()) { uint256 hash = it->second; + sl.unlock(); return getLedgerByHash(hash); } } @@ -103,6 +108,7 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index) { // Add this ledger to the local tracking by index + std::unique_lock sl(m_ledgers_by_hash.peekMutex()); XRPL_ASSERT( ret->isImmutable(), @@ -452,6 +458,8 @@ LedgerHistory::builtLedger( XRPL_ASSERT( !hash.isZero(), "ripple::LedgerHistory::builtLedger : nonzero hash"); + std::unique_lock sl(m_consensus_validated.peekMutex()); + auto entry = std::make_shared(); m_consensus_validated.canonicalize_replace_client(index, entry); @@ -492,6 +500,8 @@ LedgerHistory::validatedLedger( !hash.isZero(), "ripple::LedgerHistory::validatedLedger : nonzero hash"); + std::unique_lock sl(m_consensus_validated.peekMutex()); + auto entry = std::make_shared(); m_consensus_validated.canonicalize_replace_client(index, entry); @@ -525,9 +535,10 @@ LedgerHistory::validatedLedger( bool LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash) { - auto ledger = m_ledgers_by_hash.fetch(ledgerHash); + std::unique_lock sl(m_ledgers_by_hash.peekMutex()); auto it = mLedgersByIndex.find(ledgerIndex); - if (ledger && (it != mLedgersByIndex.end()) && (it->second != ledgerHash)) + + if ((it != mLedgersByIndex.end()) && (it->second != ledgerHash)) { it->second = ledgerHash; return false; diff --git a/src/xrpld/rpc/handlers/GetCounts.cpp b/src/xrpld/rpc/handlers/GetCounts.cpp index 2987da46d5..3c1d8cccdd 100644 --- a/src/xrpld/rpc/handlers/GetCounts.cpp +++ b/src/xrpld/rpc/handlers/GetCounts.cpp @@ -114,7 +114,7 @@ getCountsJson(Application& app, int minObjectCount) ret[jss::treenode_cache_size] = app.getNodeFamily().getTreeNodeCache()->getCacheSize(); ret[jss::treenode_track_size] = - static_cast(app.getNodeFamily().getTreeNodeCache()->size()); + app.getNodeFamily().getTreeNodeCache()->getTrackSize(); std::string uptime; auto s = UptimeClock::now(); From c5fe97064678ff8cdf2762acc69b814142db0757 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Fri, 22 Aug 2025 17:32:31 -0400 Subject: [PATCH 19/66] Set version to 2.6.0-rc3 --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 0d7ea1a7ca..4b55f82f49 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -36,7 +36,7 @@ namespace BuildInfo { // and follow the format described at http://semver.org/ //------------------------------------------------------------------------------ // clang-format off -char const* const versionString = "2.6.0-rc2" +char const* const versionString = "2.6.0-rc3" // clang-format on #if defined(DEBUG) || defined(SANITIZER) From c61096239c5d672b40e1da08c778cbb9e9d9189c Mon Sep 17 00:00:00 2001 From: Bart Date: Fri, 22 Aug 2025 19:31:01 -0400 Subject: [PATCH 20/66] chore: Remove codecov token check to support tokenless uploads on forks (#5722) --- .github/actions/build-test/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build-test/action.yml b/.github/actions/build-test/action.yml index d68f302698..ee945dcf38 100644 --- a/.github/actions/build-test/action.yml +++ b/.github/actions/build-test/action.yml @@ -83,7 +83,7 @@ runs: ./rippled --unittest --unittest-jobs $(nproc) ctest -j $(nproc) --output-on-failure - name: Upload coverage report - if: ${{ inputs.cmake_target == 'coverage' && inputs.codecov_token }} + if: ${{ inputs.cmake_target == 'coverage' }} uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 with: disable_search: true From 77fef8732bff67229440114cbc10d08bb776a4db Mon Sep 17 00:00:00 2001 From: Bart Date: Mon, 25 Aug 2025 13:32:07 -0400 Subject: [PATCH 21/66] fix: Simplify PR pipeline trigger rules (#5727) This change removes `labeled` and `unlabeled` as pipeline trigger actions, and instead adds `reopened` and `ready_for_review`. The logic whether to run the pipeline jobs is then simplified, although to get a draft PR with the `DraftCIRun` label to run it can be necessary to close and reopen a PR. --- .github/workflows/on-pr.yml | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index d1623874ac..e626222865 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -31,9 +31,9 @@ on: - "conanfile.py" types: - opened + - reopened - synchronize - - labeled - - unlabeled + - ready_for_review concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -48,24 +48,10 @@ env: CONAN_REMOTE_URL: https://conan.ripplex.io jobs: - # This job determines whether the workflow should run. It runs when: - # * Opened as a non-draft PR. - # * A commit is added to a non-draft PR or the PR has the 'DraftRunCI' label. - # * A draft PR has the 'DraftRunCI' label added. - # * A non-draft PR has the 'DraftRunCI' label removed. - # These checks are in part to ensure the workflow won't run needlessly while - # also allowing it to be triggered without having to add a no-op commit. A new - # workflow execution can be triggered by adding and then removing the label on - # a non-draft PR, or conversely by removing it and then adding it back on a - # draft PR; this can be useful in certain cases. + # This job determines whether the workflow should run. It runs when the PR is + # not a draft or has the 'DraftRunCI' label. should-run: - if: >- - ${{ - (github.event.action == 'opened' && !github.event.pull_request.draft) || - (github.event.action == 'synchronize' && (!github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'DraftRunCI'))) || - (github.event.action == 'labeled' && github.event.pull_request.draft && github.event.label.name == 'DraftRunCI') || - (github.event.action == 'unlabeled' && !github.event.pull_request.draft && github.event.label.name == 'DraftRunCI') - }} + if: ${{ !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }} runs-on: ubuntu-latest steps: - name: No-op From 285120684c3bb8fb1f700529b2346926e34d99c6 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 26 Aug 2025 16:00:00 -0400 Subject: [PATCH 22/66] refactor: Replace 'on: pull_request: paths' by 'changed-files' action (#5728) This PR moves the list of files from the `paths:` section in the `on: pull_request` into a separate job. --- .github/scripts/strategy-matrix/generate.py | 12 ++-- .github/workflows/on-pr.yml | 77 ++++++++++++++------- .github/workflows/on-trigger.yml | 16 +++-- 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 652cb8871f..9743d5a4e3 100644 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -38,7 +38,7 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] # - Bookworm using GCC 13: Release and Unity on linux/arm64, set # the reference fee to 500. # - Bookworm using GCC 15: Debug and no Unity on linux/amd64, enable - # code coverage. + # code coverage (which will be done below). # - Bookworm using Clang 16: Debug and no Unity on linux/arm64, # enable voidstar. # - Bookworm using Clang 17: Release and no Unity on linux/amd64, @@ -51,9 +51,6 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] cmake_args = f'-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}' skip = False if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64': - cmake_args = f'-Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0 {cmake_args}' - cmake_target = 'coverage' - build_only = True skip = False if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-16' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/arm64': cmake_args = f'-Dvoidstar=ON {cmake_args}' @@ -127,6 +124,13 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] if f'{os['compiler_name']}-{os['compiler_version']}' == 'clang-20' and architecture['platform'] == 'linux/arm64': continue + # Enable code coverage for Debian Bookworm using GCC 15 in Debug and no + # Unity on linux/amd64 + if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64': + cmake_args = f'-Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0 {cmake_args}' + cmake_target = 'coverage' + build_only = True + # Generate a unique name for the configuration, e.g. macos-arm64-debug # or debian-bookworm-gcc-12-amd64-release-unity. config_name = os['distro_name'] diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index e626222865..02048efa64 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -6,29 +6,6 @@ name: PR on: pull_request: - paths: - - ".github/actions/build-deps/**" - - ".github/actions/build-test/**" - - ".github/scripts/levelization/**" - - ".github/scripts/strategy-matrix/**" - - ".github/workflows/build-test.yml" - - ".github/workflows/check-format.yml" - - ".github/workflows/check-levelization.yml" - - ".github/workflows/notify-clio.yml" - - ".github/workflows/on-pr.yml" - # Keep the list of paths below in sync with those in the `on-trigger.yml` - # file. - - "cmake/**" - - "conan/**" - - "external/**" - - "include/**" - - "src/**" - - "tests/**" - - ".clang-format" - - ".codecov.yml" - - ".pre-commit-config.yaml" - - "CMakeLists.txt" - - "conanfile.py" types: - opened - reopened @@ -57,18 +34,66 @@ jobs: - name: No-op run: true - check-format: + # This job checks whether any files have changed that should cause the next + # jobs to run. We do it this way rather than using `paths` in the `on:` + # section, because all required checks must pass, even for changes that do not + # modify anything that affects those checks. We would therefore like to make + # the checks required only if the job runs, but GitHub does not support that + # directly. By always executing the workflow on new commits and by using the + # changed-files action below, we ensure that Github considers any skipped jobs + # to have passed, and in turn the required checks as well. + any-changed: needs: should-run + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Determine changed files + id: changes + uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5 + with: + files: | + # These paths are unique to `on-pr.yml`. + .github/scripts/levelization/** + .github/workflows/check-format.yml + .github/workflows/check-levelization.yml + .github/workflows/notify-clio.yml + .github/workflows/on-pr.yml + .clang-format + .pre-commit-config.yaml + + # Keep the paths below in sync with those in `on-trigger.yml`. + .github/actions/build-deps/** + .github/actions/build-test/** + .github/scripts/strategy-matrix/** + .github/workflows/build-test.yml + .codecov.yml + cmake/** + conan/** + external/** + include/** + src/** + tests/** + CMakeLists.txt + conanfile.py + outputs: + changed: ${{ steps.changes.outputs.any_changed }} + + check-format: + needs: any-changed + if: needs.any-changed.outputs.changed == 'true' uses: ./.github/workflows/check-format.yml check-levelization: - needs: should-run + needs: any-changed + if: needs.any-changed.outputs.changed == 'true' uses: ./.github/workflows/check-levelization.yml # This job works around the limitation that GitHub Actions does not support # using environment variables as inputs for reusable workflows. generate-outputs: - needs: should-run + needs: any-changed + if: needs.any-changed.outputs.changed == 'true' runs-on: ubuntu-latest steps: - name: No-op diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index 55e93b9866..b4c940ae4e 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -13,31 +13,33 @@ on: - release - master paths: + # These paths are unique to `on-trigger.yml`. + - ".github/workflows/check-missing-commits.yml" + - ".github/workflows/on-trigger.yml" + - ".github/workflows/publish-docs.yml" + + # Keep the paths below in sync with those in `on-pr.yml`. - ".github/actions/build-deps/**" - ".github/actions/build-test/**" - ".github/scripts/strategy-matrix/**" - ".github/workflows/build-test.yml" - - ".github/workflows/check-missing-commits.yml" - - ".github/workflows/on-trigger.yml" - - ".github/workflows/publish-docs.yml" - # Keep the list of paths below in sync with those in `on-pr.yml`. + - ".codecov.yml" - "cmake/**" - "conan/**" - "external/**" - "include/**" - "src/**" - "tests/**" - - ".clang-format" - - ".codecov.yml" - - ".pre-commit-config.yaml" - "CMakeLists.txt" - "conanfile.py" + # Run at 06:32 UTC on every day of the week from Monday through Friday. This # will force all dependencies to be rebuilt, which is useful to verify that # all dependencies can be built successfully. Only the dependencies that # are actually missing from the remote will be uploaded. schedule: - cron: "32 6 * * 1-5" + # Run when manually triggered via the GitHub UI or API. If `force_upload` is # true, then the dependencies that were missing (`force_rebuild` is false) or # rebuilt (`force_rebuild` is true) will be uploaded, overwriting existing From 92431a42387c6e5c9944b3514b0d0c1b30b29ced Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 26 Aug 2025 17:12:37 -0400 Subject: [PATCH 23/66] chore: Add support for merge_group event (#5734) This change adds support for the merge_group CI event, which will allow us to enable merge queues. --- .github/workflows/on-pr.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 02048efa64..a5f1d60c42 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -5,6 +5,9 @@ name: PR on: + merge_group: + types: + - checks_requested pull_request: types: - opened From 808c86663c79159812389b325ac52db3323c5f28 Mon Sep 17 00:00:00 2001 From: Bart Date: Tue, 26 Aug 2025 19:07:23 -0400 Subject: [PATCH 24/66] fix: Add codecov token to trigger workflow (#5736) This change adds the Codecov token to the on-trigger workflow. --- .github/workflows/on-trigger.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index b4c940ae4e..7732b814ad 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -113,5 +113,6 @@ jobs: os: ${{ matrix.os }} strategy_matrix: "all" secrets: + codecov_token: ${{ secrets.CODECOV_TOKEN }} conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} From 1506e65558818ea73a2233d2a55c1d1441d5fb10 Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Wed, 27 Aug 2025 10:34:50 +0100 Subject: [PATCH 25/66] refactor: Update to Boost 1.88 (#5570) This updates Boost to 1.88, which is needed because Clio wants to move to 1.88 as that fixes several ASAN false positives around coroutine usage. In order for Clio to move to newer boost, libXRPL needs to move too. Hence the changes in this PR. A lot has changed between 1.83 and 1.88 so there are lots of changes in the diff, especially in regards to Boost.Asio and coroutines in particular. --- cmake/RippledSettings.cmake | 2 +- cmake/deps/Boost.cmake | 1 + conanfile.py | 5 +- include/xrpl/basics/ResolverAsio.h | 4 +- include/xrpl/beast/asio/io_latency_probe.h | 39 ++++--- include/xrpl/beast/test/yield_to.h | 26 +++-- include/xrpl/json/json_reader.h | 2 +- include/xrpl/net/AutoSocket.h | 4 +- include/xrpl/net/HTTPClient.h | 8 +- include/xrpl/net/HTTPClientSSLContext.h | 8 +- include/xrpl/server/Server.h | 6 +- include/xrpl/server/Session.h | 6 +- include/xrpl/server/detail/BaseHTTPPeer.h | 20 ++-- include/xrpl/server/detail/BasePeer.h | 4 +- include/xrpl/server/detail/BaseWSPeer.h | 27 +++-- include/xrpl/server/detail/Door.h | 17 +-- include/xrpl/server/detail/PlainHTTPPeer.h | 4 +- include/xrpl/server/detail/SSLHTTPPeer.h | 6 +- include/xrpl/server/detail/ServerImpl.h | 28 +++-- include/xrpl/server/detail/Spawn.h | 108 ++++++++++++++++++ include/xrpl/server/detail/io_list.h | 2 +- src/libxrpl/basics/ResolverAsio.cpp | 87 +++++++++----- src/libxrpl/beast/insight/StatsDCollector.cpp | 105 ++++++++++------- src/libxrpl/beast/net/IPAddressV4.cpp | 8 +- src/libxrpl/beast/net/IPAddressV6.cpp | 6 +- src/libxrpl/beast/net/IPEndpoint.cpp | 4 +- src/libxrpl/net/HTTPClient.cpp | 68 ++++++----- src/libxrpl/server/Port.cpp | 3 +- src/test/app/DNS_test.cpp | 9 +- src/test/app/LedgerReplay_test.cpp | 2 +- src/test/app/ValidatorSite_test.cpp | 2 +- src/test/beast/IPEndpoint_test.cpp | 35 +++--- .../beast/beast_io_latency_probe_test.cpp | 23 ++-- src/test/jtx/TrustedPublisherServer.h | 4 +- src/test/jtx/impl/JSONRPCClient.cpp | 2 +- src/test/jtx/impl/WSClient.cpp | 55 ++++++--- src/test/overlay/compression_test.cpp | 2 +- src/test/overlay/reduce_relay_test.cpp | 2 +- src/test/overlay/short_read_test.cpp | 28 +++-- src/test/overlay/tx_reduce_relay_test.cpp | 8 +- src/test/rpc/ValidatorRPC_test.cpp | 4 +- src/test/server/ServerStatus_test.cpp | 23 ++-- src/test/server/Server_test.cpp | 29 ++--- .../app/ledger/detail/TimeoutCounter.cpp | 2 +- src/xrpld/app/ledger/detail/TimeoutCounter.h | 2 +- src/xrpld/app/main/Application.cpp | 60 +++++----- src/xrpld/app/main/Application.h | 4 +- src/xrpld/app/main/BasicApp.cpp | 6 +- src/xrpld/app/main/BasicApp.h | 16 +-- src/xrpld/app/main/Main.cpp | 12 +- src/xrpld/app/misc/NetworkOPs.cpp | 38 +++--- src/xrpld/app/misc/NetworkOPs.h | 2 +- src/xrpld/app/misc/detail/ValidatorSite.cpp | 8 +- src/xrpld/app/misc/detail/WorkBase.h | 69 ++++++----- src/xrpld/app/misc/detail/WorkFile.h | 23 ++-- src/xrpld/app/misc/detail/WorkPlain.h | 4 +- src/xrpld/app/misc/detail/WorkSSL.cpp | 10 +- src/xrpld/app/misc/detail/WorkSSL.h | 2 +- src/xrpld/overlay/detail/ConnectAttempt.cpp | 105 +++++++++++------ src/xrpld/overlay/detail/ConnectAttempt.h | 4 +- src/xrpld/overlay/detail/Handshake.cpp | 4 +- src/xrpld/overlay/detail/OverlayImpl.cpp | 29 ++--- src/xrpld/overlay/detail/OverlayImpl.h | 11 +- src/xrpld/overlay/detail/PeerImp.cpp | 36 ++++-- src/xrpld/overlay/detail/PeerImp.h | 2 +- src/xrpld/overlay/detail/PeerSet.cpp | 2 +- src/xrpld/overlay/detail/ZeroCopyStream.h | 4 +- src/xrpld/overlay/make_Overlay.h | 4 +- src/xrpld/peerfinder/detail/Checker.h | 20 ++-- .../peerfinder/detail/PeerfinderManager.cpp | 21 ++-- src/xrpld/peerfinder/make_Manager.h | 4 +- src/xrpld/rpc/RPCCall.h | 4 +- src/xrpld/rpc/RPCSub.h | 6 +- src/xrpld/rpc/ServerHandler.h | 6 +- src/xrpld/rpc/detail/RPCCall.cpp | 6 +- src/xrpld/rpc/detail/RPCSub.cpp | 12 +- src/xrpld/rpc/detail/ServerHandler.cpp | 11 +- src/xrpld/rpc/handlers/Subscribe.cpp | 2 +- 78 files changed, 871 insertions(+), 516 deletions(-) create mode 100644 include/xrpl/server/detail/Spawn.h diff --git a/cmake/RippledSettings.cmake b/cmake/RippledSettings.cmake index 9dc8609f58..9f59d9e9eb 100644 --- a/cmake/RippledSettings.cmake +++ b/cmake/RippledSettings.cmake @@ -118,7 +118,7 @@ option(beast_no_unit_test_inline "Prevents unit test definitions from being inserted into global table" OFF) option(single_io_service_thread - "Restricts the number of threads calling io_service::run to one. \ + "Restricts the number of threads calling io_context::run to one. \ This can be useful when debugging." OFF) option(boost_show_deprecated diff --git a/cmake/deps/Boost.cmake b/cmake/deps/Boost.cmake index 031202f4d2..bde40c0ce5 100644 --- a/cmake/deps/Boost.cmake +++ b/cmake/deps/Boost.cmake @@ -30,6 +30,7 @@ target_link_libraries(ripple_boost Boost::date_time Boost::filesystem Boost::json + Boost::process Boost::program_options Boost::regex Boost::system diff --git a/conanfile.py b/conanfile.py index da99836157..01f61c5d4e 100644 --- a/conanfile.py +++ b/conanfile.py @@ -100,11 +100,13 @@ class Xrpl(ConanFile): def configure(self): if self.settings.compiler == 'apple-clang': self.options['boost'].visibility = 'global' + if self.settings.compiler in ['clang', 'gcc']: + self.options['boost'].without_cobalt = True def requirements(self): # Conan 2 requires transitive headers to be specified transitive_headers_opt = {'transitive_headers': True} if conan_version.split('.')[0] == '2' else {} - self.requires('boost/1.86.0', force=True, **transitive_headers_opt) + self.requires('boost/1.88.0', force=True, **transitive_headers_opt) self.requires('date/3.0.4', **transitive_headers_opt) self.requires('lz4/1.10.0', force=True) self.requires('protobuf/3.21.12', force=True) @@ -175,6 +177,7 @@ class Xrpl(ConanFile): 'boost::filesystem', 'boost::json', 'boost::program_options', + 'boost::process', 'boost::regex', 'boost::system', 'boost::thread', diff --git a/include/xrpl/basics/ResolverAsio.h b/include/xrpl/basics/ResolverAsio.h index 49700d2b24..94688de650 100644 --- a/include/xrpl/basics/ResolverAsio.h +++ b/include/xrpl/basics/ResolverAsio.h @@ -23,7 +23,7 @@ #include #include -#include +#include namespace ripple { @@ -33,7 +33,7 @@ public: explicit ResolverAsio() = default; static std::unique_ptr - New(boost::asio::io_service&, beast::Journal); + New(boost::asio::io_context&, beast::Journal); }; } // namespace ripple diff --git a/include/xrpl/beast/asio/io_latency_probe.h b/include/xrpl/beast/asio/io_latency_probe.h index 966b4686ae..37f75cf649 100644 --- a/include/xrpl/beast/asio/io_latency_probe.h +++ b/include/xrpl/beast/asio/io_latency_probe.h @@ -23,7 +23,8 @@ #include #include -#include +#include +#include #include #include @@ -32,7 +33,7 @@ namespace beast { -/** Measures handler latency on an io_service queue. */ +/** Measures handler latency on an io_context queue. */ template class io_latency_probe { @@ -44,12 +45,12 @@ private: std::condition_variable_any m_cond; std::size_t m_count; duration const m_period; - boost::asio::io_service& m_ios; + boost::asio::io_context& m_ios; boost::asio::basic_waitable_timer m_timer; bool m_cancel; public: - io_latency_probe(duration const& period, boost::asio::io_service& ios) + io_latency_probe(duration const& period, boost::asio::io_context& ios) : m_count(1) , m_period(period) , m_ios(ios) @@ -64,16 +65,16 @@ public: cancel(lock, true); } - /** Return the io_service associated with the latency probe. */ + /** Return the io_context associated with the latency probe. */ /** @{ */ - boost::asio::io_service& - get_io_service() + boost::asio::io_context& + get_io_context() { return m_ios; } - boost::asio::io_service const& - get_io_service() const + boost::asio::io_context const& + get_io_context() const { return m_ios; } @@ -109,8 +110,10 @@ public: std::lock_guard lock(m_mutex); if (m_cancel) throw std::logic_error("io_latency_probe is canceled"); - m_ios.post(sample_op( - std::forward(handler), Clock::now(), false, this)); + boost::asio::post( + m_ios, + sample_op( + std::forward(handler), Clock::now(), false, this)); } /** Initiate continuous i/o latency sampling. @@ -124,8 +127,10 @@ public: std::lock_guard lock(m_mutex); if (m_cancel) throw std::logic_error("io_latency_probe is canceled"); - m_ios.post(sample_op( - std::forward(handler), Clock::now(), true, this)); + boost::asio::post( + m_ios, + sample_op( + std::forward(handler), Clock::now(), true, this)); } private: @@ -236,12 +241,13 @@ private: // The latency is too high to maintain the desired // period so don't bother with a timer. // - m_probe->m_ios.post( + boost::asio::post( + m_probe->m_ios, sample_op(m_handler, now, m_repeat, m_probe)); } else { - m_probe->m_timer.expires_from_now(when - now); + m_probe->m_timer.expires_after(when - now); m_probe->m_timer.async_wait( sample_op(m_handler, now, m_repeat, m_probe)); } @@ -254,7 +260,8 @@ private: if (!m_probe) return; typename Clock::time_point const now(Clock::now()); - m_probe->m_ios.post( + boost::asio::post( + m_probe->m_ios, sample_op(m_handler, now, m_repeat, m_probe)); } }; diff --git a/include/xrpl/beast/test/yield_to.h b/include/xrpl/beast/test/yield_to.h index 27a3a2db20..a222e8627e 100644 --- a/include/xrpl/beast/test/yield_to.h +++ b/include/xrpl/beast/test/yield_to.h @@ -8,9 +8,11 @@ #ifndef BEAST_TEST_YIELD_TO_HPP #define BEAST_TEST_YIELD_TO_HPP -#include +#include +#include #include #include +#include #include #include @@ -29,10 +31,12 @@ namespace test { class enable_yield_to { protected: - boost::asio::io_service ios_; + boost::asio::io_context ios_; private: - boost::optional work_; + boost::optional> + work_; std::vector threads_; std::mutex m_; std::condition_variable cv_; @@ -42,7 +46,8 @@ public: /// The type of yield context passed to functions. using yield_context = boost::asio::yield_context; - explicit enable_yield_to(std::size_t concurrency = 1) : work_(ios_) + explicit enable_yield_to(std::size_t concurrency = 1) + : work_(boost::asio::make_work_guard(ios_)) { threads_.reserve(concurrency); while (concurrency--) @@ -56,9 +61,9 @@ public: t.join(); } - /// Return the `io_service` associated with the object - boost::asio::io_service& - get_io_service() + /// Return the `io_context` associated with the object + boost::asio::io_context& + get_io_context() { return ios_; } @@ -111,13 +116,18 @@ enable_yield_to::spawn(F0&& f, FN&&... fn) { boost::asio::spawn( ios_, + boost::allocator_arg, + boost::context::fixedsize_stack(2 * 1024 * 1024), [&](yield_context yield) { f(yield); std::lock_guard lock{m_}; if (--running_ == 0) cv_.notify_all(); }, - boost::coroutines::attributes(2 * 1024 * 1024)); + [](std::exception_ptr e) { + if (e) + std::rethrow_exception(e); + }); spawn(fn...); } diff --git a/include/xrpl/json/json_reader.h b/include/xrpl/json/json_reader.h index 81866819a5..8eceee1f1c 100644 --- a/include/xrpl/json/json_reader.h +++ b/include/xrpl/json/json_reader.h @@ -217,7 +217,7 @@ Reader::parse(Value& root, BufferSequence const& bs) std::string s; s.reserve(buffer_size(bs)); for (auto const& b : bs) - s.append(buffer_cast(b), buffer_size(b)); + s.append(static_cast(b.data()), buffer_size(b)); return parse(s, root); } diff --git a/include/xrpl/net/AutoSocket.h b/include/xrpl/net/AutoSocket.h index d06787340b..5f82854039 100644 --- a/include/xrpl/net/AutoSocket.h +++ b/include/xrpl/net/AutoSocket.h @@ -47,7 +47,7 @@ public: public: AutoSocket( - boost::asio::io_service& s, + boost::asio::io_context& s, boost::asio::ssl::context& c, bool secureOnly, bool plainOnly) @@ -58,7 +58,7 @@ public: mSocket = std::make_unique(s, c); } - AutoSocket(boost::asio::io_service& s, boost::asio::ssl::context& c) + AutoSocket(boost::asio::io_context& s, boost::asio::ssl::context& c) : AutoSocket(s, c, false, false) { } diff --git a/include/xrpl/net/HTTPClient.h b/include/xrpl/net/HTTPClient.h index ef295e8e5a..b5043cd024 100644 --- a/include/xrpl/net/HTTPClient.h +++ b/include/xrpl/net/HTTPClient.h @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include @@ -51,7 +51,7 @@ public: static void get(bool bSSL, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, std::deque deqSites, unsigned short const port, std::string const& strPath, @@ -65,7 +65,7 @@ public: static void get(bool bSSL, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, std::string strSite, unsigned short const port, std::string const& strPath, @@ -80,7 +80,7 @@ public: static void request( bool bSSL, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, std::string strSite, unsigned short const port, std::function< diff --git a/include/xrpl/net/HTTPClientSSLContext.h b/include/xrpl/net/HTTPClientSSLContext.h index 2f7d6c005e..f5dd1e54c6 100644 --- a/include/xrpl/net/HTTPClientSSLContext.h +++ b/include/xrpl/net/HTTPClientSSLContext.h @@ -153,7 +153,7 @@ public: { strm.set_verify_callback( std::bind( - &rfc2818_verify, + &rfc6125_verify, host, std::placeholders::_1, std::placeholders::_2, @@ -167,7 +167,7 @@ public: /** * @brief callback invoked for name verification - just passes through - * to the asio rfc2818 implementation. + * to the asio `host_name_verification` (rfc6125) implementation. * * @param domain hostname expected * @param preverified passed by implementation @@ -175,13 +175,13 @@ public: * @param j journal for logging */ static bool - rfc2818_verify( + rfc6125_verify( std::string const& domain, bool preverified, boost::asio::ssl::verify_context& ctx, beast::Journal j) { - if (boost::asio::ssl::rfc2818_verification(domain)(preverified, ctx)) + if (boost::asio::ssl::host_name_verification(domain)(preverified, ctx)) return true; JLOG(j.warn()) << "Outbound SSL connection to " << domain diff --git a/include/xrpl/server/Server.h b/include/xrpl/server/Server.h index 232d1c381b..a8f9c7f8af 100644 --- a/include/xrpl/server/Server.h +++ b/include/xrpl/server/Server.h @@ -25,7 +25,7 @@ #include #include -#include +#include namespace ripple { @@ -34,10 +34,10 @@ template std::unique_ptr make_Server( Handler& handler, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, beast::Journal journal) { - return std::make_unique>(handler, io_service, journal); + return std::make_unique>(handler, io_context, journal); } } // namespace ripple diff --git a/include/xrpl/server/Session.h b/include/xrpl/server/Session.h index 196f8c78c2..586172a5da 100644 --- a/include/xrpl/server/Session.h +++ b/include/xrpl/server/Session.h @@ -88,9 +88,7 @@ public: ++iter) { typename BufferSequence::value_type const& buffer(*iter); - write( - boost::asio::buffer_cast(buffer), - boost::asio::buffer_size(buffer)); + write(buffer.data(), boost::asio::buffer_size(buffer)); } } @@ -104,7 +102,7 @@ public: /** Detach the session. This holds the session open so that the response can be sent - asynchronously. Calls to io_service::run made by the server + asynchronously. Calls to io_context::run made by the server will not return until all detached sessions are closed. */ virtual std::shared_ptr diff --git a/include/xrpl/server/detail/BaseHTTPPeer.h b/include/xrpl/server/detail/BaseHTTPPeer.h index b065a97cf0..b7f471bdee 100644 --- a/include/xrpl/server/detail/BaseHTTPPeer.h +++ b/include/xrpl/server/detail/BaseHTTPPeer.h @@ -24,11 +24,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -215,8 +217,8 @@ BaseHTTPPeer::BaseHTTPPeer( ConstBufferSequence const& buffers) : port_(port) , handler_(handler) - , work_(executor) - , strand_(executor) + , work_(boost::asio::make_work_guard(executor)) + , strand_(boost::asio::make_strand(executor)) , remote_address_(remote_address) , journal_(journal) { @@ -356,7 +358,7 @@ BaseHTTPPeer::on_write( return; if (graceful_) return do_close(); - boost::asio::spawn( + util::spawn( strand_, std::bind( &BaseHTTPPeer::do_read, @@ -375,7 +377,7 @@ BaseHTTPPeer::do_writer( { auto const p = impl().shared_from_this(); resume = std::function([this, p, writer, keep_alive]() { - boost::asio::spawn( + util::spawn( strand_, std::bind( &BaseHTTPPeer::do_writer, @@ -406,7 +408,7 @@ BaseHTTPPeer::do_writer( if (!keep_alive) return do_close(); - boost::asio::spawn( + util::spawn( strand_, std::bind( &BaseHTTPPeer::do_read, @@ -448,14 +450,14 @@ BaseHTTPPeer::write( std::shared_ptr const& writer, bool keep_alive) { - boost::asio::spawn(bind_executor( + util::spawn( strand_, std::bind( &BaseHTTPPeer::do_writer, impl().shared_from_this(), writer, keep_alive, - std::placeholders::_1))); + std::placeholders::_1)); } // DEPRECATED @@ -490,12 +492,12 @@ BaseHTTPPeer::complete() } // keep-alive - boost::asio::spawn(bind_executor( + util::spawn( strand_, std::bind( &BaseHTTPPeer::do_read, impl().shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1)); } // DEPRECATED diff --git a/include/xrpl/server/detail/BasePeer.h b/include/xrpl/server/detail/BasePeer.h index 35975efafb..30de63e6ff 100644 --- a/include/xrpl/server/detail/BasePeer.h +++ b/include/xrpl/server/detail/BasePeer.h @@ -91,8 +91,8 @@ BasePeer::BasePeer( return "##" + std::to_string(++id) + " "; }()) , j_(sink_) - , work_(executor) - , strand_(executor) + , work_(boost::asio::make_work_guard(executor)) + , strand_(boost::asio::make_strand(executor)) { } diff --git a/include/xrpl/server/detail/BaseWSPeer.h b/include/xrpl/server/detail/BaseWSPeer.h index 027b0cbf7c..391c5c337e 100644 --- a/include/xrpl/server/detail/BaseWSPeer.h +++ b/include/xrpl/server/detail/BaseWSPeer.h @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -420,11 +421,17 @@ BaseWSPeer::start_timer() // Max seconds without completing a message static constexpr std::chrono::seconds timeout{30}; static constexpr std::chrono::seconds timeoutLocal{3}; - error_code ec; - timer_.expires_from_now( - remote_endpoint().address().is_loopback() ? timeoutLocal : timeout, ec); - if (ec) - return fail(ec, "start_timer"); + + try + { + timer_.expires_after( + remote_endpoint().address().is_loopback() ? timeoutLocal : timeout); + } + catch (boost::system::system_error const& e) + { + return fail(e.code(), "start_timer"); + } + timer_.async_wait(bind_executor( strand_, std::bind( @@ -438,8 +445,14 @@ template void BaseWSPeer::cancel_timer() { - error_code ec; - timer_.cancel(ec); + try + { + timer_.cancel(); + } + catch (boost::system::system_error const&) + { + // ignored + } } template diff --git a/include/xrpl/server/detail/Door.h b/include/xrpl/server/detail/Door.h index 88e19db8cd..7906af2a52 100644 --- a/include/xrpl/server/detail/Door.h +++ b/include/xrpl/server/detail/Door.h @@ -69,7 +69,7 @@ private: stream_type stream_; socket_type& socket_; endpoint_type remote_address_; - boost::asio::io_context::strand strand_; + boost::asio::strand strand_; beast::Journal const j_; public: @@ -95,7 +95,7 @@ private: Handler& handler_; boost::asio::io_context& ioc_; acceptor_type acceptor_; - boost::asio::io_context::strand strand_; + boost::asio::strand strand_; bool ssl_; bool plain_; @@ -155,7 +155,7 @@ Door::Detector::Detector( , stream_(std::move(stream)) , socket_(stream_.socket()) , remote_address_(remote_address) - , strand_(ioc_) + , strand_(boost::asio::make_strand(ioc_)) , j_(j) { } @@ -164,7 +164,7 @@ template void Door::Detector::run() { - boost::asio::spawn( + util::spawn( strand_, std::bind( &Detector::do_detect, @@ -269,7 +269,7 @@ Door::reOpen() Throw(); } - acceptor_.listen(boost::asio::socket_base::max_connections, ec); + acceptor_.listen(boost::asio::socket_base::max_listen_connections, ec); if (ec) { JLOG(j_.error()) << "Listen on port '" << port_.name @@ -291,7 +291,7 @@ Door::Door( , handler_(handler) , ioc_(io_context) , acceptor_(io_context) - , strand_(io_context) + , strand_(boost::asio::make_strand(io_context)) , ssl_( port_.protocol.count("https") > 0 || port_.protocol.count("wss") > 0 || port_.protocol.count("wss2") > 0 || @@ -307,7 +307,7 @@ template void Door::run() { - boost::asio::spawn( + util::spawn( strand_, std::bind( &Door::do_accept, @@ -320,7 +320,8 @@ void Door::close() { if (!strand_.running_in_this_thread()) - return strand_.post( + return boost::asio::post( + strand_, std::bind(&Door::close, this->shared_from_this())); error_code ec; acceptor_.close(ec); diff --git a/include/xrpl/server/detail/PlainHTTPPeer.h b/include/xrpl/server/detail/PlainHTTPPeer.h index ee31c78cad..f6f8e5b010 100644 --- a/include/xrpl/server/detail/PlainHTTPPeer.h +++ b/include/xrpl/server/detail/PlainHTTPPeer.h @@ -105,7 +105,7 @@ PlainHTTPPeer::run() { if (!this->handler_.onAccept(this->session(), this->remote_address_)) { - boost::asio::spawn( + util::spawn( this->strand_, std::bind(&PlainHTTPPeer::do_close, this->shared_from_this())); return; @@ -114,7 +114,7 @@ PlainHTTPPeer::run() if (!socket_.is_open()) return; - boost::asio::spawn( + util::spawn( this->strand_, std::bind( &PlainHTTPPeer::do_read, diff --git a/include/xrpl/server/detail/SSLHTTPPeer.h b/include/xrpl/server/detail/SSLHTTPPeer.h index fac4b866d3..8564263114 100644 --- a/include/xrpl/server/detail/SSLHTTPPeer.h +++ b/include/xrpl/server/detail/SSLHTTPPeer.h @@ -115,14 +115,14 @@ SSLHTTPPeer::run() { if (!this->handler_.onAccept(this->session(), this->remote_address_)) { - boost::asio::spawn( + util::spawn( this->strand_, std::bind(&SSLHTTPPeer::do_close, this->shared_from_this())); return; } if (!socket_.is_open()) return; - boost::asio::spawn( + util::spawn( this->strand_, std::bind( &SSLHTTPPeer::do_handshake, @@ -164,7 +164,7 @@ SSLHTTPPeer::do_handshake(yield_context do_yield) this->port().protocol.count("https") > 0; if (http) { - boost::asio::spawn( + util::spawn( this->strand_, std::bind( &SSLHTTPPeer::do_read, diff --git a/include/xrpl/server/detail/ServerImpl.h b/include/xrpl/server/detail/ServerImpl.h index fd0b082b46..4090aa0a6b 100644 --- a/include/xrpl/server/detail/ServerImpl.h +++ b/include/xrpl/server/detail/ServerImpl.h @@ -26,6 +26,8 @@ #include #include +#include +#include #include #include @@ -85,9 +87,11 @@ private: Handler& handler_; beast::Journal const j_; - boost::asio::io_service& io_service_; - boost::asio::io_service::strand strand_; - std::optional work_; + boost::asio::io_context& io_context_; + boost::asio::strand strand_; + std::optional> + work_; std::mutex m_; std::vector ports_; @@ -100,7 +104,7 @@ private: public: ServerImpl( Handler& handler, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, beast::Journal journal); ~ServerImpl(); @@ -123,10 +127,10 @@ public: return ios_; } - boost::asio::io_service& - get_io_service() + boost::asio::io_context& + get_io_context() { - return io_service_; + return io_context_; } bool @@ -140,13 +144,13 @@ private: template ServerImpl::ServerImpl( Handler& handler, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, beast::Journal journal) : handler_(handler) , j_(journal) - , io_service_(io_service) - , strand_(io_service_) - , work_(io_service_) + , io_context_(io_context) + , strand_(boost::asio::make_strand(io_context_)) + , work_(std::in_place, boost::asio::make_work_guard(io_context_)) { } @@ -173,7 +177,7 @@ ServerImpl::ports(std::vector const& ports) ports_.push_back(port); auto& internalPort = ports_.back(); if (auto sp = ios_.emplace>( - handler_, io_service_, internalPort, j_)) + handler_, io_context_, internalPort, j_)) { list_.push_back(sp); diff --git a/include/xrpl/server/detail/Spawn.h b/include/xrpl/server/detail/Spawn.h new file mode 100644 index 0000000000..56f173dec3 --- /dev/null +++ b/include/xrpl/server/detail/Spawn.h @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright(c) 2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_SERVER_SPAWN_H_INCLUDED +#define RIPPLE_SERVER_SPAWN_H_INCLUDED + +#include + +#include +#include + +#include +#include + +namespace ripple::util { +namespace impl { + +template +concept IsStrand = std::same_as< + std::decay_t, + boost::asio::strand::inner_executor_type>>; + +/** + * @brief A completion handler that restores `boost::asio::spawn`'s behaviour + * from Boost 1.83 + * + * This is intended to be passed as the third argument to `boost::asio::spawn` + * so that exceptions are not ignored but propagated to `io_context.run()` call + * site. + * + * @param ePtr The exception that was caught on the coroutine + */ +inline constexpr auto kPROPAGATE_EXCEPTIONS = [](std::exception_ptr ePtr) { + if (ePtr) + { + try + { + std::rethrow_exception(ePtr); + } + catch (std::exception const& e) + { + JLOG(debugLog().warn()) << "Spawn exception: " << e.what(); + throw; + } + catch (...) + { + JLOG(debugLog().warn()) << "Spawn exception: Unknown"; + throw; + } + } +}; + +} // namespace impl + +/** + * @brief Spawns a coroutine using `boost::asio::spawn` + * + * @note This uses kPROPAGATE_EXCEPTIONS to force asio to propagate exceptions + * through `io_context` + * @note Since implicit strand was removed from boost::asio::spawn this helper + * function adds the strand back + * + * @tparam Ctx The type of the context/strand + * @tparam F The type of the function to execute + * @param ctx The execution context + * @param func The function to execute. Must return `void` + */ +template + requires std::is_invocable_r_v +void +spawn(Ctx&& ctx, F&& func) +{ + if constexpr (impl::IsStrand) + { + boost::asio::spawn( + std::forward(ctx), + std::forward(func), + impl::kPROPAGATE_EXCEPTIONS); + } + else + { + boost::asio::spawn( + boost::asio::make_strand( + boost::asio::get_associated_executor(std::forward(ctx))), + std::forward(func), + impl::kPROPAGATE_EXCEPTIONS); + } +} + +} // namespace ripple::util + +#endif diff --git a/include/xrpl/server/detail/io_list.h b/include/xrpl/server/detail/io_list.h index fba8b28f87..6292794864 100644 --- a/include/xrpl/server/detail/io_list.h +++ b/include/xrpl/server/detail/io_list.h @@ -166,7 +166,7 @@ public: May be called concurrently. Preconditions: - No call to io_service::run on any io_service + No call to io_context::run on any io_context used by work objects associated with this io_list exists in the caller's call stack. */ diff --git a/src/libxrpl/basics/ResolverAsio.cpp b/src/libxrpl/basics/ResolverAsio.cpp index fde27189e7..1b52465a80 100644 --- a/src/libxrpl/basics/ResolverAsio.cpp +++ b/src/libxrpl/basics/ResolverAsio.cpp @@ -25,8 +25,9 @@ #include #include +#include #include -#include +#include #include #include @@ -124,8 +125,8 @@ public: beast::Journal m_journal; - boost::asio::io_service& m_io_service; - boost::asio::io_service::strand m_strand; + boost::asio::io_context& m_io_context; + boost::asio::strand m_strand; boost::asio::ip::tcp::resolver m_resolver; std::condition_variable m_cv; @@ -155,12 +156,12 @@ public: std::deque m_work; ResolverAsioImpl( - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, beast::Journal journal) : m_journal(journal) - , m_io_service(io_service) - , m_strand(io_service) - , m_resolver(io_service) + , m_io_context(io_context) + , m_strand(boost::asio::make_strand(io_context)) + , m_resolver(io_context) , m_asyncHandlersCompleted(true) , m_stop_called(false) , m_stopped(true) @@ -216,8 +217,14 @@ public: { if (m_stop_called.exchange(true) == false) { - m_io_service.dispatch(m_strand.wrap(std::bind( - &ResolverAsioImpl::do_stop, this, CompletionCounter(this)))); + boost::asio::dispatch( + m_io_context, + boost::asio::bind_executor( + m_strand, + std::bind( + &ResolverAsioImpl::do_stop, + this, + CompletionCounter(this)))); JLOG(m_journal.debug()) << "Queued a stop request"; } @@ -248,12 +255,16 @@ public: // TODO NIKB use rvalue references to construct and move // reducing cost. - m_io_service.dispatch(m_strand.wrap(std::bind( - &ResolverAsioImpl::do_resolve, - this, - names, - handler, - CompletionCounter(this)))); + boost::asio::dispatch( + m_io_context, + boost::asio::bind_executor( + m_strand, + std::bind( + &ResolverAsioImpl::do_resolve, + this, + names, + handler, + CompletionCounter(this)))); } //------------------------------------------------------------------------- @@ -279,19 +290,20 @@ public: std::string name, boost::system::error_code const& ec, HandlerType handler, - boost::asio::ip::tcp::resolver::iterator iter, + boost::asio::ip::tcp::resolver::results_type results, CompletionCounter) { if (ec == boost::asio::error::operation_aborted) return; std::vector addresses; + auto iter = results.begin(); // If we get an error message back, we don't return any // results that we may have gotten. if (!ec) { - while (iter != boost::asio::ip::tcp::resolver::iterator()) + while (iter != results.end()) { addresses.push_back( beast::IPAddressConversion::from_asio(*iter)); @@ -301,8 +313,14 @@ public: handler(name, addresses); - m_io_service.post(m_strand.wrap(std::bind( - &ResolverAsioImpl::do_work, this, CompletionCounter(this)))); + boost::asio::post( + m_io_context, + boost::asio::bind_executor( + m_strand, + std::bind( + &ResolverAsioImpl::do_work, + this, + CompletionCounter(this)))); } HostAndPort @@ -383,16 +401,21 @@ public: { JLOG(m_journal.error()) << "Unable to parse '" << name << "'"; - m_io_service.post(m_strand.wrap(std::bind( - &ResolverAsioImpl::do_work, this, CompletionCounter(this)))); + boost::asio::post( + m_io_context, + boost::asio::bind_executor( + m_strand, + std::bind( + &ResolverAsioImpl::do_work, + this, + CompletionCounter(this)))); return; } - boost::asio::ip::tcp::resolver::query query(host, port); - m_resolver.async_resolve( - query, + host, + port, std::bind( &ResolverAsioImpl::do_finish, this, @@ -423,10 +446,14 @@ public: if (m_work.size() > 0) { - m_io_service.post(m_strand.wrap(std::bind( - &ResolverAsioImpl::do_work, - this, - CompletionCounter(this)))); + boost::asio::post( + m_io_context, + boost::asio::bind_executor( + m_strand, + std::bind( + &ResolverAsioImpl::do_work, + this, + CompletionCounter(this)))); } } } @@ -435,9 +462,9 @@ public: //----------------------------------------------------------------------------- std::unique_ptr -ResolverAsio::New(boost::asio::io_service& io_service, beast::Journal journal) +ResolverAsio::New(boost::asio::io_context& io_context, beast::Journal journal) { - return std::make_unique(io_service, journal); + return std::make_unique(io_context, journal); } //----------------------------------------------------------------------------- diff --git a/src/libxrpl/beast/insight/StatsDCollector.cpp b/src/libxrpl/beast/insight/StatsDCollector.cpp index b0e00c3cfd..7a3929e0d5 100644 --- a/src/libxrpl/beast/insight/StatsDCollector.cpp +++ b/src/libxrpl/beast/insight/StatsDCollector.cpp @@ -30,9 +30,11 @@ #include #include +#include #include #include -#include +#include +#include #include #include #include @@ -238,9 +240,11 @@ private: Journal m_journal; IP::Endpoint m_address; std::string m_prefix; - boost::asio::io_service m_io_service; - std::optional m_work; - boost::asio::io_service::strand m_strand; + boost::asio::io_context m_io_context; + std::optional> + m_work; + boost::asio::strand m_strand; boost::asio::basic_waitable_timer m_timer; boost::asio::ip::udp::socket m_socket; std::deque m_data; @@ -264,18 +268,24 @@ public: : m_journal(journal) , m_address(address) , m_prefix(prefix) - , m_work(std::ref(m_io_service)) - , m_strand(m_io_service) - , m_timer(m_io_service) - , m_socket(m_io_service) + , m_work(boost::asio::make_work_guard(m_io_context)) + , m_strand(boost::asio::make_strand(m_io_context)) + , m_timer(m_io_context) + , m_socket(m_io_context) , m_thread(&StatsDCollectorImp::run, this) { } ~StatsDCollectorImp() override { - boost::system::error_code ec; - m_timer.cancel(ec); + try + { + m_timer.cancel(); + } + catch (boost::system::system_error const&) + { + // ignored + } m_work.reset(); m_thread.join(); @@ -334,10 +344,10 @@ public: //-------------------------------------------------------------------------- - boost::asio::io_service& - get_io_service() + boost::asio::io_context& + get_io_context() { - return m_io_service; + return m_io_context; } std::string const& @@ -355,8 +365,14 @@ public: void post_buffer(std::string&& buffer) { - m_io_service.dispatch(m_strand.wrap(std::bind( - &StatsDCollectorImp::do_post_buffer, this, std::move(buffer)))); + boost::asio::dispatch( + m_io_context, + boost::asio::bind_executor( + m_strand, + std::bind( + &StatsDCollectorImp::do_post_buffer, + this, + std::move(buffer)))); } // The keepAlive parameter makes sure the buffers sent to @@ -386,8 +402,7 @@ public: for (auto const& buffer : buffers) { std::string const s( - boost::asio::buffer_cast(buffer), - boost::asio::buffer_size(buffer)); + buffer.data(), boost::asio::buffer_size(buffer)); std::cerr << s; } std::cerr << '\n'; @@ -456,7 +471,7 @@ public: set_timer() { using namespace std::chrono_literals; - m_timer.expires_from_now(1s); + m_timer.expires_after(1s); m_timer.async_wait(std::bind( &StatsDCollectorImp::on_timer, this, std::placeholders::_1)); } @@ -498,13 +513,13 @@ public: set_timer(); - m_io_service.run(); + m_io_context.run(); m_socket.shutdown(boost::asio::ip::udp::socket::shutdown_send, ec); m_socket.close(); - m_io_service.poll(); + m_io_context.poll(); } }; @@ -547,10 +562,12 @@ StatsDCounterImpl::~StatsDCounterImpl() void StatsDCounterImpl::increment(CounterImpl::value_type amount) { - m_impl->get_io_service().dispatch(std::bind( - &StatsDCounterImpl::do_increment, - std::static_pointer_cast(shared_from_this()), - amount)); + boost::asio::dispatch( + m_impl->get_io_context(), + std::bind( + &StatsDCounterImpl::do_increment, + std::static_pointer_cast(shared_from_this()), + amount)); } void @@ -592,10 +609,12 @@ StatsDEventImpl::StatsDEventImpl( void StatsDEventImpl::notify(EventImpl::value_type const& value) { - m_impl->get_io_service().dispatch(std::bind( - &StatsDEventImpl::do_notify, - std::static_pointer_cast(shared_from_this()), - value)); + boost::asio::dispatch( + m_impl->get_io_context(), + std::bind( + &StatsDEventImpl::do_notify, + std::static_pointer_cast(shared_from_this()), + value)); } void @@ -625,19 +644,23 @@ StatsDGaugeImpl::~StatsDGaugeImpl() void StatsDGaugeImpl::set(GaugeImpl::value_type value) { - m_impl->get_io_service().dispatch(std::bind( - &StatsDGaugeImpl::do_set, - std::static_pointer_cast(shared_from_this()), - value)); + boost::asio::dispatch( + m_impl->get_io_context(), + std::bind( + &StatsDGaugeImpl::do_set, + std::static_pointer_cast(shared_from_this()), + value)); } void StatsDGaugeImpl::increment(GaugeImpl::difference_type amount) { - m_impl->get_io_service().dispatch(std::bind( - &StatsDGaugeImpl::do_increment, - std::static_pointer_cast(shared_from_this()), - amount)); + boost::asio::dispatch( + m_impl->get_io_context(), + std::bind( + &StatsDGaugeImpl::do_increment, + std::static_pointer_cast(shared_from_this()), + amount)); } void @@ -713,10 +736,12 @@ StatsDMeterImpl::~StatsDMeterImpl() void StatsDMeterImpl::increment(MeterImpl::value_type amount) { - m_impl->get_io_service().dispatch(std::bind( - &StatsDMeterImpl::do_increment, - std::static_pointer_cast(shared_from_this()), - amount)); + boost::asio::dispatch( + m_impl->get_io_context(), + std::bind( + &StatsDMeterImpl::do_increment, + std::static_pointer_cast(shared_from_this()), + amount)); } void diff --git a/src/libxrpl/beast/net/IPAddressV4.cpp b/src/libxrpl/beast/net/IPAddressV4.cpp index 29455024f6..22162c2bbe 100644 --- a/src/libxrpl/beast/net/IPAddressV4.cpp +++ b/src/libxrpl/beast/net/IPAddressV4.cpp @@ -25,11 +25,11 @@ namespace IP { bool is_private(AddressV4 const& addr) { - return ((addr.to_ulong() & 0xff000000) == + return ((addr.to_uint() & 0xff000000) == 0x0a000000) || // Prefix /8, 10. #.#.# - ((addr.to_ulong() & 0xfff00000) == + ((addr.to_uint() & 0xfff00000) == 0xac100000) || // Prefix /12 172. 16.#.# - 172.31.#.# - ((addr.to_ulong() & 0xffff0000) == + ((addr.to_uint() & 0xffff0000) == 0xc0a80000) || // Prefix /16 192.168.#.# addr.is_loopback(); } @@ -44,7 +44,7 @@ char get_class(AddressV4 const& addr) { static char const* table = "AAAABBCD"; - return table[(addr.to_ulong() & 0xE0000000) >> 29]; + return table[(addr.to_uint() & 0xE0000000) >> 29]; } } // namespace IP diff --git a/src/libxrpl/beast/net/IPAddressV6.cpp b/src/libxrpl/beast/net/IPAddressV6.cpp index f90a6d066b..d1b86ba9bd 100644 --- a/src/libxrpl/beast/net/IPAddressV6.cpp +++ b/src/libxrpl/beast/net/IPAddressV6.cpp @@ -20,6 +20,8 @@ #include #include +#include + namespace beast { namespace IP { @@ -28,7 +30,9 @@ is_private(AddressV6 const& addr) { return ( (addr.to_bytes()[0] & 0xfd) || // TODO fc00::/8 too ? - (addr.is_v4_mapped() && is_private(addr.to_v4()))); + (addr.is_v4_mapped() && + is_private(boost::asio::ip::make_address_v4( + boost::asio::ip::v4_mapped, addr)))); } bool diff --git a/src/libxrpl/beast/net/IPEndpoint.cpp b/src/libxrpl/beast/net/IPEndpoint.cpp index ffe664498c..f1ffc23e82 100644 --- a/src/libxrpl/beast/net/IPEndpoint.cpp +++ b/src/libxrpl/beast/net/IPEndpoint.cpp @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include @@ -167,7 +169,7 @@ operator>>(std::istream& is, Endpoint& endpoint) } boost::system::error_code ec; - auto addr = Address::from_string(addrStr, ec); + auto addr = boost::asio::ip::make_address(addrStr, ec); if (ec) { is.setstate(std::ios_base::failbit); diff --git a/src/libxrpl/net/HTTPClient.cpp b/src/libxrpl/net/HTTPClient.cpp index f7d540750a..964be32dd8 100644 --- a/src/libxrpl/net/HTTPClient.cpp +++ b/src/libxrpl/net/HTTPClient.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -55,16 +56,16 @@ class HTTPClientImp : public std::enable_shared_from_this, { public: HTTPClientImp( - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, unsigned short const port, std::size_t maxResponseSize, beast::Journal& j) - : mSocket(io_service, httpClientSSLContext->context()) - , mResolver(io_service) + : mSocket(io_context, httpClientSSLContext->context()) + , mResolver(io_context) , mHeader(maxClientHeaderBytes) , mPort(port) , maxResponseSize_(maxResponseSize) - , mDeadline(io_service) + , mDeadline(io_context) , j_(j) { } @@ -146,18 +147,21 @@ public: { JLOG(j_.trace()) << "Fetch: " << mDeqSites[0]; - auto query = std::make_shared( + auto query = std::make_shared( mDeqSites[0], std::to_string(mPort), boost::asio::ip::resolver_query_base::numeric_service); mQuery = query; - mDeadline.expires_from_now(mTimeout, mShutdown); - - JLOG(j_.trace()) << "expires_from_now: " << mShutdown.message(); - - if (!mShutdown) + try { + mDeadline.expires_after(mTimeout); + } + catch (boost::system::system_error const& e) + { + mShutdown = e.code(); + + JLOG(j_.trace()) << "expires_after: " << mShutdown.message(); mDeadline.async_wait(std::bind( &HTTPClientImp::handleDeadline, shared_from_this(), @@ -169,7 +173,9 @@ public: JLOG(j_.trace()) << "Resolving: " << mDeqSites[0]; mResolver.async_resolve( - *mQuery, + mQuery->host, + mQuery->port, + mQuery->flags, std::bind( &HTTPClientImp::handleResolve, shared_from_this(), @@ -233,7 +239,7 @@ public: void handleResolve( boost::system::error_code const& ecResult, - boost::asio::ip::tcp::resolver::iterator itrEndpoint) + boost::asio::ip::tcp::resolver::results_type result) { if (!mShutdown) { @@ -255,7 +261,7 @@ public: boost::asio::async_connect( mSocket.lowest_layer(), - itrEndpoint, + result, std::bind( &HTTPClientImp::handleConnect, shared_from_this(), @@ -475,13 +481,15 @@ public: std::string const& strData = "") { boost::system::error_code ecCancel; - - (void)mDeadline.cancel(ecCancel); - - if (ecCancel) + try { - JLOG(j_.trace()) << "invokeComplete: Deadline cancel error: " - << ecCancel.message(); + mDeadline.cancel(); + } + catch (boost::system::system_error const& e) + { + JLOG(j_.trace()) + << "invokeComplete: Deadline cancel error: " << e.what(); + ecCancel = e.code(); } JLOG(j_.debug()) << "invokeComplete: Deadline popping: " @@ -515,7 +523,15 @@ private: bool mSSL; AutoSocket mSocket; boost::asio::ip::tcp::resolver mResolver; - std::shared_ptr mQuery; + + struct Query + { + std::string host; + std::string port; + boost::asio::ip::resolver_query_base::flags flags; + }; + std::shared_ptr mQuery; + boost::asio::streambuf mRequest; boost::asio::streambuf mHeader; boost::asio::streambuf mResponse; @@ -546,7 +562,7 @@ private: void HTTPClient::get( bool bSSL, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, std::deque deqSites, unsigned short const port, std::string const& strPath, @@ -559,14 +575,14 @@ HTTPClient::get( beast::Journal& j) { auto client = - std::make_shared(io_service, port, responseMax, j); + std::make_shared(io_context, port, responseMax, j); client->get(bSSL, deqSites, strPath, timeout, complete); } void HTTPClient::get( bool bSSL, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, std::string strSite, unsigned short const port, std::string const& strPath, @@ -581,14 +597,14 @@ HTTPClient::get( std::deque deqSites(1, strSite); auto client = - std::make_shared(io_service, port, responseMax, j); + std::make_shared(io_context, port, responseMax, j); client->get(bSSL, deqSites, strPath, timeout, complete); } void HTTPClient::request( bool bSSL, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, std::string strSite, unsigned short const port, std::function @@ -604,7 +620,7 @@ HTTPClient::request( std::deque deqSites(1, strSite); auto client = - std::make_shared(io_service, port, responseMax, j); + std::make_shared(io_context, port, responseMax, j); client->request(bSSL, deqSites, setRequest, timeout, complete); } diff --git a/src/libxrpl/server/Port.cpp b/src/libxrpl/server/Port.cpp index 95709fc298..be86a77a9f 100644 --- a/src/libxrpl/server/Port.cpp +++ b/src/libxrpl/server/Port.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -219,7 +220,7 @@ parse_Port(ParsedPort& port, Section const& section, std::ostream& log) { try { - port.ip = boost::asio::ip::address::from_string(*optResult); + port.ip = boost::asio::ip::make_address(*optResult); } catch (std::exception const&) { diff --git a/src/test/app/DNS_test.cpp b/src/test/app/DNS_test.cpp index 28a143e93d..c4e476de9f 100644 --- a/src/test/app/DNS_test.cpp +++ b/src/test/app/DNS_test.cpp @@ -63,7 +63,7 @@ public: pUrl_.domain, pUrl_.path, port_, - env_.app().getIOService(), + env_.app().getIOContext(), env_.journal, env_.app().config(), lastEndpoint, @@ -80,10 +80,11 @@ public: isMultipleEndpoints() { using boost::asio::ip::tcp; - tcp::resolver resolver(env_.app().getIOService()); + tcp::resolver resolver(env_.app().getIOContext()); std::string port = pUrl_.port ? std::to_string(*pUrl_.port) : "443"; - tcp::resolver::iterator it = resolver.resolve(pUrl_.domain, port); - tcp::resolver::iterator end; + auto results = resolver.resolve(pUrl_.domain, port); + auto it = results.begin(); + auto end = results.end(); int n = 0; for (; it != end; ++it) ++n; diff --git a/src/test/app/LedgerReplay_test.cpp b/src/test/app/LedgerReplay_test.cpp index 76ab5b3218..88d944d789 100644 --- a/src/test/app/LedgerReplay_test.cpp +++ b/src/test/app/LedgerReplay_test.cpp @@ -1107,7 +1107,7 @@ struct LedgerReplayer_test : public beast::unit_test::suite return false; beast::IP::Address addr = - boost::asio::ip::address::from_string("172.1.1.100"); + boost::asio::ip::make_address("172.1.1.100"); jtx::Env serverEnv(*this); serverEnv.app().config().LEDGER_REPLAY = server; auto http_resp = ripple::makeResponse( diff --git a/src/test/app/ValidatorSite_test.cpp b/src/test/app/ValidatorSite_test.cpp index 7a7511e6f0..579cd79a5a 100644 --- a/src/test/app/ValidatorSite_test.cpp +++ b/src/test/app/ValidatorSite_test.cpp @@ -205,7 +205,7 @@ private: NetClock::time_point const expires2 = effective2 + cfg.expiresFromNow; item.server = make_TrustedPublisherServer( - env.app().getIOService(), + env.app().getIOContext(), item.list, expires, {{effective2, expires2}}, diff --git a/src/test/beast/IPEndpoint_test.cpp b/src/test/beast/IPEndpoint_test.cpp index aed6d715d4..a99dccf5a0 100644 --- a/src/test/beast/IPEndpoint_test.cpp +++ b/src/test/beast/IPEndpoint_test.cpp @@ -45,13 +45,13 @@ public: std::string const& normal = "") { boost::system::error_code ec; - Address const result{Address::from_string(s, ec)}; + Address const result{boost::asio::ip::make_address(s, ec)}; if (!BEAST_EXPECTS(!ec, ec.message())) return; if (!BEAST_EXPECTS(result.is_v4(), s + " not v4")) return; if (!BEAST_EXPECTS( - result.to_v4().to_ulong() == value, s + " value mismatch")) + result.to_v4().to_uint() == value, s + " value mismatch")) return; BEAST_EXPECTS( result.to_string() == (normal.empty() ? s : normal), @@ -62,7 +62,7 @@ public: failParseAddr(std::string const& s) { boost::system::error_code ec; - auto a = Address::from_string(s, ec); + auto a = boost::asio::ip::make_address(s, ec); BEAST_EXPECTS(ec, s + " parses as " + a.to_string()); } @@ -71,24 +71,24 @@ public: { testcase("AddressV4"); - BEAST_EXPECT(AddressV4{}.to_ulong() == 0); + BEAST_EXPECT(AddressV4{}.to_uint() == 0); BEAST_EXPECT(is_unspecified(AddressV4{})); - BEAST_EXPECT(AddressV4{0x01020304}.to_ulong() == 0x01020304); + BEAST_EXPECT(AddressV4{0x01020304}.to_uint() == 0x01020304); { AddressV4::bytes_type d = {{1, 2, 3, 4}}; - BEAST_EXPECT(AddressV4{d}.to_ulong() == 0x01020304); + BEAST_EXPECT(AddressV4{d}.to_uint() == 0x01020304); unexpected(is_unspecified(AddressV4{d})); } AddressV4 const v1{1}; - BEAST_EXPECT(AddressV4{v1}.to_ulong() == 1); + BEAST_EXPECT(AddressV4{v1}.to_uint() == 1); { AddressV4 v; v = v1; - BEAST_EXPECT(v.to_ulong() == v1.to_ulong()); + BEAST_EXPECT(v.to_uint() == v1.to_uint()); } { @@ -99,7 +99,7 @@ public: d[2] = 3; d[3] = 4; v = AddressV4{d}; - BEAST_EXPECT(v.to_ulong() == 0x01020304); + BEAST_EXPECT(v.to_uint() == 0x01020304); } BEAST_EXPECT(AddressV4(0x01020304).to_string() == "1.2.3.4"); @@ -161,7 +161,7 @@ public: testcase("Address"); boost::system::error_code ec; - Address result{Address::from_string("1.2.3.4", ec)}; + Address result{boost::asio::ip::make_address("1.2.3.4", ec)}; AddressV4::bytes_type d = {{1, 2, 3, 4}}; BEAST_EXPECT(!ec); BEAST_EXPECT(result.is_v4() && result.to_v4() == AddressV4{d}); @@ -263,7 +263,10 @@ public: BEAST_EXPECT(is_loopback(ep)); BEAST_EXPECT(to_string(ep) == "127.0.0.1:80"); // same address as v4 mapped in ipv6 - ep = Endpoint(AddressV6::v4_mapped(AddressV4{d}), 80); + ep = Endpoint( + boost::asio::ip::make_address_v6( + boost::asio::ip::v4_mapped, AddressV4{d}), + 80); BEAST_EXPECT(!is_unspecified(ep)); BEAST_EXPECT(!is_public(ep)); BEAST_EXPECT(is_private(ep)); @@ -281,8 +284,11 @@ public: BEAST_EXPECT(!is_loopback(ep)); BEAST_EXPECT(to_string(ep) == "10.0.0.1"); // same address as v4 mapped in ipv6 - ep = Endpoint(AddressV6::v4_mapped(AddressV4{d})); - BEAST_EXPECT(get_class(ep.to_v6().to_v4()) == 'A'); + ep = Endpoint(boost::asio::ip::make_address_v6( + boost::asio::ip::v4_mapped, AddressV4{d})); + BEAST_EXPECT( + get_class(boost::asio::ip::make_address_v4( + boost::asio::ip::v4_mapped, ep.to_v6())) == 'A'); BEAST_EXPECT(!is_unspecified(ep)); BEAST_EXPECT(!is_public(ep)); BEAST_EXPECT(is_private(ep)); @@ -299,7 +305,8 @@ public: BEAST_EXPECT(!is_loopback(ep)); BEAST_EXPECT(to_string(ep) == "166.78.151.147"); // same address as v4 mapped in ipv6 - ep = Endpoint(AddressV6::v4_mapped(AddressV4{d})); + ep = Endpoint(boost::asio::ip::make_address_v6( + boost::asio::ip::v4_mapped, AddressV4{d})); BEAST_EXPECT(!is_unspecified(ep)); BEAST_EXPECT(is_public(ep)); BEAST_EXPECT(!is_private(ep)); diff --git a/src/test/beast/beast_io_latency_probe_test.cpp b/src/test/beast/beast_io_latency_probe_test.cpp index c72336bf27..841272d05a 100644 --- a/src/test/beast/beast_io_latency_probe_test.cpp +++ b/src/test/beast/beast_io_latency_probe_test.cpp @@ -23,7 +23,8 @@ #include #include -#include +#include +#include #include #include @@ -60,8 +61,10 @@ class io_latency_probe_test : public beast::unit_test::suite, measure_asio_timers(duration interval = 100ms, size_t num_samples = 50) { using namespace std::chrono; - boost::asio::io_service ios; - std::optional work{ios}; + boost::asio::io_context ios; + std::optional> + work{boost::asio::make_work_guard(ios)}; std::thread worker{[&] { ios.run(); }}; boost::asio::basic_waitable_timer timer{ios}; elapsed_times_.reserve(num_samples); @@ -135,7 +138,7 @@ class io_latency_probe_test : public beast::unit_test::suite, test_sampler( std::chrono::milliseconds interval, - boost::asio::io_service& ios) + boost::asio::io_context& ios) : probe_(interval, ios) { } @@ -164,9 +167,9 @@ class io_latency_probe_test : public beast::unit_test::suite, { testcase << "sample one"; boost::system::error_code ec; - test_sampler io_probe{100ms, get_io_service()}; + test_sampler io_probe{100ms, get_io_context()}; io_probe.start_one(); - MyTimer timer{get_io_service(), 1s}; + MyTimer timer{get_io_context(), 1s}; timer.async_wait(yield[ec]); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -198,9 +201,9 @@ class io_latency_probe_test : public beast::unit_test::suite, duration_cast(probe_duration).count()) / static_cast(tt.getMean()); #endif - test_sampler io_probe{interval, get_io_service()}; + test_sampler io_probe{interval, get_io_context()}; io_probe.start(); - MyTimer timer{get_io_service(), probe_duration}; + MyTimer timer{get_io_context(), probe_duration}; timer.async_wait(yield[ec]); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -212,7 +215,7 @@ class io_latency_probe_test : public beast::unit_test::suite, io_probe.probe_.cancel_async(); // wait again in order to flush the remaining // probes from the work queue - timer.expires_from_now(1s); + timer.expires_after(1s); timer.async_wait(yield[ec]); } @@ -220,7 +223,7 @@ class io_latency_probe_test : public beast::unit_test::suite, testCanceled(boost::asio::yield_context& yield) { testcase << "canceled"; - test_sampler io_probe{100ms, get_io_service()}; + test_sampler io_probe{100ms, get_io_context()}; io_probe.probe_.cancel_async(); except([&io_probe]() { io_probe.start_one(); }); except([&io_probe]() { io_probe.start(); }); diff --git a/src/test/jtx/TrustedPublisherServer.h b/src/test/jtx/TrustedPublisherServer.h index 7bc092cbe3..26e676c024 100644 --- a/src/test/jtx/TrustedPublisherServer.h +++ b/src/test/jtx/TrustedPublisherServer.h @@ -183,7 +183,7 @@ public: bool immediateStart = true, int sequence = 1) : sock_{ioc} - , ep_{beast::IP::Address::from_string( + , ep_{boost::asio::ip::make_address( ripple::test::getEnvLocalhostAddr()), // 0 means let OS pick the port based on what's available 0} @@ -284,7 +284,7 @@ public: acceptor_.set_option( boost::asio::ip::tcp::acceptor::reuse_address(true), ec); acceptor_.bind(ep_); - acceptor_.listen(boost::asio::socket_base::max_connections); + acceptor_.listen(boost::asio::socket_base::max_listen_connections); acceptor_.async_accept( sock_, [wp = std::weak_ptr{shared_from_this()}]( diff --git a/src/test/jtx/impl/JSONRPCClient.cpp b/src/test/jtx/impl/JSONRPCClient.cpp index 4db13c95fd..a4c5817788 100644 --- a/src/test/jtx/impl/JSONRPCClient.cpp +++ b/src/test/jtx/impl/JSONRPCClient.cpp @@ -78,7 +78,7 @@ class JSONRPCClient : public AbstractClient } boost::asio::ip::tcp::endpoint ep_; - boost::asio::io_service ios_; + boost::asio::io_context ios_; boost::asio::ip::tcp::socket stream_; boost::beast::multi_buffer bin_; boost::beast::multi_buffer bout_; diff --git a/src/test/jtx/impl/WSClient.cpp b/src/test/jtx/impl/WSClient.cpp index 20cca3179a..a3dc7d9733 100644 --- a/src/test/jtx/impl/WSClient.cpp +++ b/src/test/jtx/impl/WSClient.cpp @@ -25,6 +25,9 @@ #include #include +#include +#include +#include #include #include @@ -89,9 +92,11 @@ class WSClientImpl : public WSClient return s; } - boost::asio::io_service ios_; - std::optional work_; - boost::asio::io_service::strand strand_; + boost::asio::io_context ios_; + std::optional> + work_; + boost::asio::strand strand_; std::thread thread_; boost::asio::ip::tcp::socket stream_; boost::beast::websocket::stream ws_; @@ -114,14 +119,24 @@ class WSClientImpl : public WSClient void cleanup() { - ios_.post(strand_.wrap([this] { - if (!peerClosed_) - { - ws_.async_close({}, strand_.wrap([&](error_code ec) { - stream_.cancel(ec); - })); - } - })); + boost::asio::post( + ios_, boost::asio::bind_executor(strand_, [this] { + if (!peerClosed_) + { + ws_.async_close( + {}, + boost::asio::bind_executor(strand_, [&](error_code) { + try + { + stream_.cancel(); + } + catch (boost::system::system_error const&) + { + // ignored + } + })); + } + })); work_ = std::nullopt; thread_.join(); } @@ -132,8 +147,8 @@ public: bool v2, unsigned rpc_version, std::unordered_map const& headers = {}) - : work_(ios_) - , strand_(ios_) + : work_(std::in_place, boost::asio::make_work_guard(ios_)) + , strand_(boost::asio::make_strand(ios_)) , thread_([&] { ios_.run(); }) , stream_(ios_) , ws_(stream_) @@ -153,8 +168,12 @@ public: "/"); ws_.async_read( rb_, - strand_.wrap(std::bind( - &WSClientImpl::on_read_msg, this, std::placeholders::_1))); + boost::asio::bind_executor( + strand_, + std::bind( + &WSClientImpl::on_read_msg, + this, + std::placeholders::_1))); } catch (std::exception&) { @@ -284,8 +303,10 @@ private: } ws_.async_read( rb_, - strand_.wrap(std::bind( - &WSClientImpl::on_read_msg, this, std::placeholders::_1))); + boost::asio::bind_executor( + strand_, + std::bind( + &WSClientImpl::on_read_msg, this, std::placeholders::_1))); } // Called when the read op terminates diff --git a/src/test/overlay/compression_test.cpp b/src/test/overlay/compression_test.cpp index 01be43d58b..4bfbcae4f0 100644 --- a/src/test/overlay/compression_test.cpp +++ b/src/test/overlay/compression_test.cpp @@ -485,7 +485,7 @@ public: }; auto handshake = [&](int outboundEnable, int inboundEnable) { beast::IP::Address addr = - boost::asio::ip::address::from_string("172.1.1.100"); + boost::asio::ip::make_address("172.1.1.100"); auto env = getEnv(outboundEnable); auto request = ripple::makeRequest( diff --git a/src/test/overlay/reduce_relay_test.cpp b/src/test/overlay/reduce_relay_test.cpp index 0047454cf9..e53f53f2db 100644 --- a/src/test/overlay/reduce_relay_test.cpp +++ b/src/test/overlay/reduce_relay_test.cpp @@ -1655,7 +1655,7 @@ vp_base_squelch_max_selected_peers=2 }; auto handshake = [&](int outboundEnable, int inboundEnable) { beast::IP::Address addr = - boost::asio::ip::address::from_string("172.1.1.100"); + boost::asio::ip::make_address("172.1.1.100"); setEnv(outboundEnable); auto request = ripple::makeRequest( diff --git a/src/test/overlay/short_read_test.cpp b/src/test/overlay/short_read_test.cpp index 739d7ea954..88c6e7698b 100644 --- a/src/test/overlay/short_read_test.cpp +++ b/src/test/overlay/short_read_test.cpp @@ -23,12 +23,17 @@ #include #include -#include +#include +#include +#include +#include #include +#include +#include #include +#include #include -#include #include #include @@ -49,7 +54,7 @@ class short_read_test : public beast::unit_test::suite { private: using io_context_type = boost::asio::io_context; - using strand_type = boost::asio::io_context::strand; + using strand_type = boost::asio::strand; using timer_type = boost::asio::basic_waitable_timer; using acceptor_type = boost::asio::ip::tcp::acceptor; @@ -60,7 +65,8 @@ private: using address_type = boost::asio::ip::address; io_context_type io_context_; - std::optional> + boost::optional> work_; std::thread thread_; std::shared_ptr context_; @@ -72,7 +78,7 @@ private: using boost::asio::buffer; using boost::asio::buffer_copy; using boost::asio::buffer_size; - boost::asio::const_buffers_1 buf(s.data(), s.size()); + boost::asio::const_buffer buf(s.data(), s.size()); sb.commit(buffer_copy(sb.prepare(buffer_size(buf)), buf)); } @@ -185,11 +191,11 @@ private: , acceptor_( test_.io_context_, endpoint_type( - beast::IP::Address::from_string( + boost::asio::ip::make_address( test::getEnvLocalhostAddr()), 0)) , socket_(test_.io_context_) - , strand_(test_.io_context_) + , strand_(boost::asio::make_strand(test_.io_context_)) { acceptor_.listen(); server_.endpoint_ = acceptor_.local_endpoint(); @@ -265,7 +271,7 @@ private: , test_(server_.test_) , socket_(std::move(socket)) , stream_(socket_, *test_.context_) - , strand_(test_.io_context_) + , strand_(boost::asio::make_strand(test_.io_context_)) , timer_(test_.io_context_) { } @@ -287,7 +293,7 @@ private: void run() { - timer_.expires_from_now(std::chrono::seconds(3)); + timer_.expires_after(std::chrono::seconds(3)); timer_.async_wait(bind_executor( strand_, std::bind( @@ -450,7 +456,7 @@ private: , test_(client_.test_) , socket_(test_.io_context_) , stream_(socket_, *test_.context_) - , strand_(test_.io_context_) + , strand_(boost::asio::make_strand(test_.io_context_)) , timer_(test_.io_context_) , ep_(ep) { @@ -473,7 +479,7 @@ private: void run(endpoint_type const& ep) { - timer_.expires_from_now(std::chrono::seconds(3)); + timer_.expires_after(std::chrono::seconds(3)); timer_.async_wait(bind_executor( strand_, std::bind( diff --git a/src/test/overlay/tx_reduce_relay_test.cpp b/src/test/overlay/tx_reduce_relay_test.cpp index 0024f2b98e..0c67fd581c 100644 --- a/src/test/overlay/tx_reduce_relay_test.cpp +++ b/src/test/overlay/tx_reduce_relay_test.cpp @@ -174,13 +174,13 @@ private: makeFeaturesRequestHeader(false, false, true, false)) : (void)nDisabled--; auto stream_ptr = std::make_unique( - socket_type(std::forward( - env.app().getIOService())), + socket_type(std::forward( + env.app().getIOContext())), *context_); beast::IP::Endpoint local( - beast::IP::Address::from_string("172.1.1." + std::to_string(lid_))); + boost::asio::ip::make_address("172.1.1." + std::to_string(lid_))); beast::IP::Endpoint remote( - beast::IP::Address::from_string("172.1.1." + std::to_string(rid_))); + boost::asio::ip::make_address("172.1.1." + std::to_string(rid_))); PublicKey key(std::get<0>(randomKeyPair(KeyType::ed25519))); auto consumer = overlay.resourceManager().newInboundEndpoint(remote); auto slot = overlay.peerFinder().new_inbound_slot(local, remote); diff --git a/src/test/rpc/ValidatorRPC_test.cpp b/src/test/rpc/ValidatorRPC_test.cpp index d139a662de..bc54c8567c 100644 --- a/src/test/rpc/ValidatorRPC_test.cpp +++ b/src/test/rpc/ValidatorRPC_test.cpp @@ -187,14 +187,14 @@ public: for (auto const& val : validators) expectedKeys.insert(toStr(val.masterPublic)); - // Manage single-thread io_service for server. + // Manage single-thread io_context for server. BasicApp worker{1}; using namespace std::chrono_literals; NetClock::time_point const validUntil{3600s}; NetClock::time_point const validFrom2{validUntil - 60s}; NetClock::time_point const validUntil2{validFrom2 + 3600s}; auto server = make_TrustedPublisherServer( - worker.get_io_service(), + worker.get_io_context(), validators, validUntil, {{validFrom2, validUntil2}}, diff --git a/src/test/server/ServerStatus_test.cpp b/src/test/server/ServerStatus_test.cpp index b27dee6e0a..8bbad2cd99 100644 --- a/src/test/server/ServerStatus_test.cpp +++ b/src/test/server/ServerStatus_test.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -165,12 +166,11 @@ class ServerStatus_test : public beast::unit_test::suite, { using namespace boost::asio; using namespace boost::beast::http; - io_service& ios = get_io_service(); + io_context& ios = get_io_context(); ip::tcp::resolver r{ios}; boost::beast::multi_buffer sb; - auto it = r.async_resolve( - ip::tcp::resolver::query{host, std::to_string(port)}, yield[ec]); + auto it = r.async_resolve(host, std::to_string(port), yield[ec]); if (ec) return; @@ -476,12 +476,11 @@ class ServerStatus_test : public beast::unit_test::suite, auto req_string = boost::lexical_cast(req); req_string.erase(req_string.find_last_of("13"), std::string::npos); - io_service& ios = get_io_service(); + io_context& ios = get_io_context(); ip::tcp::resolver r{ios}; boost::beast::multi_buffer sb; - auto it = r.async_resolve( - ip::tcp::resolver::query{*ip, std::to_string(*port)}, yield[ec]); + auto it = r.async_resolve(*ip, std::to_string(*port), yield[ec]); if (!BEAST_EXPECTS(!ec, ec.message())) return; @@ -610,14 +609,13 @@ class ServerStatus_test : public beast::unit_test::suite, env.app().config()["port_rpc"].get("ip").value(); boost::system::error_code ec; - io_service& ios = get_io_service(); + io_context& ios = get_io_context(); ip::tcp::resolver r{ios}; Json::Value jr; jr[jss::method] = "server_info"; - auto it = r.async_resolve( - ip::tcp::resolver::query{ip, std::to_string(port)}, yield[ec]); + auto it = r.async_resolve(ip, std::to_string(port), yield[ec]); BEAST_EXPECT(!ec); std::vector> @@ -681,7 +679,7 @@ class ServerStatus_test : public beast::unit_test::suite, resp["Upgrade"] == "websocket"); BEAST_EXPECT( resp.find("Connection") != resp.end() && - resp["Connection"] == "Upgrade"); + boost::iequals(resp["Connection"], "upgrade")); } void @@ -728,11 +726,10 @@ class ServerStatus_test : public beast::unit_test::suite, env.app().config()["port_ws"].get("ip").value(); boost::system::error_code ec; - io_service& ios = get_io_service(); + io_context& ios = get_io_context(); ip::tcp::resolver r{ios}; - auto it = r.async_resolve( - ip::tcp::resolver::query{ip, std::to_string(port)}, yield[ec]); + auto it = r.async_resolve(ip, std::to_string(port), yield[ec]); if (!BEAST_EXPECT(!ec)) return; diff --git a/src/test/server/Server_test.cpp b/src/test/server/Server_test.cpp index fab271ff1c..874558f428 100644 --- a/src/test/server/Server_test.cpp +++ b/src/test/server/Server_test.cpp @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -52,14 +53,16 @@ public: class TestThread { private: - boost::asio::io_service io_service_; - std::optional work_; + boost::asio::io_context io_context_; + std::optional> + work_; std::thread thread_; public: TestThread() - : work_(std::in_place, std::ref(io_service_)) - , thread_([&]() { this->io_service_.run(); }) + : work_(std::in_place, boost::asio::make_work_guard(io_context_)) + , thread_([&]() { this->io_context_.run(); }) { } @@ -69,10 +72,10 @@ public: thread_.join(); } - boost::asio::io_service& - get_io_service() + boost::asio::io_context& + get_io_context() { - return io_service_; + return io_context_; } }; @@ -234,7 +237,7 @@ public: void test_request(boost::asio::ip::tcp::endpoint const& ep) { - boost::asio::io_service ios; + boost::asio::io_context ios; using socket = boost::asio::ip::tcp::socket; socket s(ios); @@ -260,7 +263,7 @@ public: void test_keepalive(boost::asio::ip::tcp::endpoint const& ep) { - boost::asio::io_service ios; + boost::asio::io_context ios; using socket = boost::asio::ip::tcp::socket; socket s(ios); @@ -300,10 +303,10 @@ public: sink.threshold(beast::severities::Severity::kAll); beast::Journal journal{sink}; TestHandler handler; - auto s = make_Server(handler, thread.get_io_service(), journal); + auto s = make_Server(handler, thread.get_io_context(), journal); std::vector serverPort(1); serverPort.back().ip = - beast::IP::Address::from_string(getEnvLocalhostAddr()), + boost::asio::ip::make_address(getEnvLocalhostAddr()), serverPort.back().port = 0; serverPort.back().protocol.insert("http"); auto eps = s->ports(serverPort); @@ -375,10 +378,10 @@ public: for (int i = 0; i < 1000; ++i) { TestThread thread; - auto s = make_Server(h, thread.get_io_service(), journal); + auto s = make_Server(h, thread.get_io_context(), journal); std::vector serverPort(1); serverPort.back().ip = - beast::IP::Address::from_string(getEnvLocalhostAddr()), + boost::asio::ip::make_address(getEnvLocalhostAddr()), serverPort.back().port = 0; serverPort.back().protocol.insert("http"); s->ports(serverPort); diff --git a/src/xrpld/app/ledger/detail/TimeoutCounter.cpp b/src/xrpld/app/ledger/detail/TimeoutCounter.cpp index e81ec6574d..774b70e4d1 100644 --- a/src/xrpld/app/ledger/detail/TimeoutCounter.cpp +++ b/src/xrpld/app/ledger/detail/TimeoutCounter.cpp @@ -39,7 +39,7 @@ TimeoutCounter::TimeoutCounter( , progress_(false) , timerInterval_(interval) , queueJobParameter_(std::move(jobParameter)) - , timer_(app_.getIOService()) + , timer_(app_.getIOContext()) { XRPL_ASSERT( (timerInterval_ > 10ms) && (timerInterval_ < 30s), diff --git a/src/xrpld/app/ledger/detail/TimeoutCounter.h b/src/xrpld/app/ledger/detail/TimeoutCounter.h index 85ce6fc3b4..8da290dd36 100644 --- a/src/xrpld/app/ledger/detail/TimeoutCounter.h +++ b/src/xrpld/app/ledger/detail/TimeoutCounter.h @@ -120,7 +120,7 @@ protected: return complete_ || failed_; } - // Used in this class for access to boost::asio::io_service and + // Used in this class for access to boost::asio::io_context and // ripple::Overlay. Used in subtypes for the kitchen sink. Application& app_; beast::Journal journal_; diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index c824eccfba..beaf85ce2e 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -83,7 +83,6 @@ #include #include #include -#include #include namespace ripple { @@ -108,7 +107,7 @@ private: beast::insight::Event ev, beast::Journal journal, std::chrono::milliseconds interval, - boost::asio::io_service& ios) + boost::asio::io_context& ios) : m_event(ev) , m_journal(journal) , m_probe(interval, ios) @@ -136,7 +135,7 @@ private: if (lastSample >= 500ms) { JLOG(m_journal.warn()) - << "io_service latency = " << lastSample.count(); + << "io_context latency = " << lastSample.count(); } } @@ -405,7 +404,7 @@ public: *m_jobQueue, *m_ledgerMaster, validatorKeys_, - get_io_service(), + get_io_context(), logs_->journal("NetworkOPs"), m_collectorManager->collector())) @@ -432,7 +431,7 @@ public: , serverHandler_(make_ServerHandler( *this, - get_io_service(), + get_io_context(), *m_jobQueue, *m_networkOPs, *m_resourceManager, @@ -456,22 +455,22 @@ public: , txQ_( std::make_unique(setup_TxQ(*config_), logs_->journal("TxQ"))) - , sweepTimer_(get_io_service()) + , sweepTimer_(get_io_context()) - , entropyTimer_(get_io_service()) + , entropyTimer_(get_io_context()) - , m_signals(get_io_service()) + , m_signals(get_io_context()) , checkSigs_(true) , m_resolver( - ResolverAsio::New(get_io_service(), logs_->journal("Resolver"))) + ResolverAsio::New(get_io_context(), logs_->journal("Resolver"))) , m_io_latency_sampler( m_collectorManager->collector()->make_event("ios_latency"), logs_->journal("Application"), std::chrono::milliseconds(100), - get_io_service()) + get_io_context()) , grpcServer_(std::make_unique(*this)) { initAccountIdCache(config_->getValueFor(SizedItem::accountIdCacheSize)); @@ -594,10 +593,10 @@ public: return *serverHandler_; } - boost::asio::io_service& - getIOService() override + boost::asio::io_context& + getIOContext() override { - return get_io_service(); + return get_io_context(); } std::chrono::milliseconds @@ -935,9 +934,8 @@ public: })) { using namespace std::chrono; - sweepTimer_.expires_from_now( - seconds{config_->SWEEP_INTERVAL.value_or( - config_->getValueFor(SizedItem::sweepInterval))}); + sweepTimer_.expires_after(seconds{config_->SWEEP_INTERVAL.value_or( + config_->getValueFor(SizedItem::sweepInterval))}); sweepTimer_.async_wait(std::move(*optionalCountedHandler)); } } @@ -966,7 +964,7 @@ public: })) { using namespace std::chrono_literals; - entropyTimer_.expires_from_now(5min); + entropyTimer_.expires_after(5min); entropyTimer_.async_wait(std::move(*optionalCountedHandler)); } } @@ -1398,7 +1396,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) *serverHandler_, *m_resourceManager, *m_resolver, - get_io_service(), + get_io_context(), *config_, m_collectorManager->collector()); add(*overlay_); // add to PropertyStream @@ -1571,11 +1569,11 @@ ApplicationImp::run() m_io_latency_sampler.cancel_async(); // VFALCO Enormous hack, we have to force the probe to cancel - // before we stop the io_service queue or else it never + // before we stop the io_context queue or else it never // unblocks in its destructor. The fix is to make all // io_objects gracefully handle exit so that we can - // naturally return from io_service::run() instead of - // forcing a call to io_service::stop() + // naturally return from io_context::run() instead of + // forcing a call to io_context::stop() m_io_latency_sampler.cancel(); m_resolver->stop_async(); @@ -1586,20 +1584,24 @@ ApplicationImp::run() m_resolver->stop(); { - boost::system::error_code ec; - sweepTimer_.cancel(ec); - if (ec) + try + { + sweepTimer_.cancel(); + } + catch (boost::system::system_error const& e) { JLOG(m_journal.error()) - << "Application: sweepTimer cancel error: " << ec.message(); + << "Application: sweepTimer cancel error: " << e.what(); } - ec.clear(); - entropyTimer_.cancel(ec); - if (ec) + try + { + entropyTimer_.cancel(); + } + catch (boost::system::system_error const& e) { JLOG(m_journal.error()) - << "Application: entropyTimer cancel error: " << ec.message(); + << "Application: entropyTimer cancel error: " << e.what(); } } diff --git a/src/xrpld/app/main/Application.h b/src/xrpld/app/main/Application.h index 36477cb75c..b3a433fee8 100644 --- a/src/xrpld/app/main/Application.h +++ b/src/xrpld/app/main/Application.h @@ -162,8 +162,8 @@ public: virtual Config& config() = 0; - virtual boost::asio::io_service& - getIOService() = 0; + virtual boost::asio::io_context& + getIOContext() = 0; virtual CollectorManager& getCollectorManager() = 0; diff --git a/src/xrpld/app/main/BasicApp.cpp b/src/xrpld/app/main/BasicApp.cpp index a4b1a74685..87f440dfc8 100644 --- a/src/xrpld/app/main/BasicApp.cpp +++ b/src/xrpld/app/main/BasicApp.cpp @@ -21,9 +21,11 @@ #include +#include + BasicApp::BasicApp(std::size_t numberOfThreads) { - work_.emplace(io_service_); + work_.emplace(boost::asio::make_work_guard(io_context_)); threads_.reserve(numberOfThreads); while (numberOfThreads--) @@ -31,7 +33,7 @@ BasicApp::BasicApp(std::size_t numberOfThreads) threads_.emplace_back([this, numberOfThreads]() { beast::setCurrentThreadName( "io svc #" + std::to_string(numberOfThreads)); - this->io_service_.run(); + this->io_context_.run(); }); } } diff --git a/src/xrpld/app/main/BasicApp.h b/src/xrpld/app/main/BasicApp.h index cd1e8c1a71..276676ca18 100644 --- a/src/xrpld/app/main/BasicApp.h +++ b/src/xrpld/app/main/BasicApp.h @@ -20,28 +20,30 @@ #ifndef RIPPLE_APP_BASICAPP_H_INCLUDED #define RIPPLE_APP_BASICAPP_H_INCLUDED -#include +#include #include #include #include -// This is so that the io_service can outlive all the children +// This is so that the io_context can outlive all the children class BasicApp { private: - std::optional work_; + std::optional> + work_; std::vector threads_; - boost::asio::io_service io_service_; + boost::asio::io_context io_context_; public: BasicApp(std::size_t numberOfThreads); ~BasicApp(); - boost::asio::io_service& - get_io_service() + boost::asio::io_context& + get_io_context() { - return io_service_; + return io_context_; } }; diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index 3fdf362dd9..2353d7acd1 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -28,12 +28,17 @@ #include #include +#include +#include +#include +#include + #ifdef ENABLE_TESTS #include #include #endif // ENABLE_TESTS -#include +#include #include #include @@ -283,7 +288,7 @@ runUnitTests( if (!child) { multi_runner_parent parent_runner; - std::vector children; + std::vector children; std::string const exe_name = argv[0]; std::vector args; @@ -296,7 +301,8 @@ runUnitTests( for (std::size_t i = 0; i < num_jobs; ++i) children.emplace_back( - boost::process::exe = exe_name, boost::process::args = args); + boost::process::v1::exe = exe_name, + boost::process::v1::args = args); int bad_child_exits = 0; int terminated_child_exits = 0; diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index 3220ce99fc..403090c390 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -233,7 +233,7 @@ public: JobQueue& job_queue, LedgerMaster& ledgerMaster, ValidatorKeys const& validatorKeys, - boost::asio::io_service& io_svc, + boost::asio::io_context& io_svc, beast::Journal journal, beast::insight::Collector::ptr const& collector) : app_(app) @@ -588,31 +588,35 @@ public: stop() override { { - boost::system::error_code ec; - heartbeatTimer_.cancel(ec); - if (ec) + try + { + heartbeatTimer_.cancel(); + } + catch (boost::system::system_error const& e) { JLOG(m_journal.error()) - << "NetworkOPs: heartbeatTimer cancel error: " - << ec.message(); + << "NetworkOPs: heartbeatTimer cancel error: " << e.what(); } - ec.clear(); - clusterTimer_.cancel(ec); - if (ec) + try + { + clusterTimer_.cancel(); + } + catch (boost::system::system_error const& e) { JLOG(m_journal.error()) - << "NetworkOPs: clusterTimer cancel error: " - << ec.message(); + << "NetworkOPs: clusterTimer cancel error: " << e.what(); } - ec.clear(); - accountHistoryTxTimer_.cancel(ec); - if (ec) + try + { + accountHistoryTxTimer_.cancel(); + } + catch (boost::system::system_error const& e) { JLOG(m_journal.error()) << "NetworkOPs: accountHistoryTxTimer cancel error: " - << ec.message(); + << e.what(); } } // Make sure that any waitHandlers pending in our timers are done. @@ -984,7 +988,7 @@ NetworkOPsImp::setTimer( } })) { - timer.expires_from_now(expiry_time); + timer.expires_after(expiry_time); timer.async_wait(std::move(*optionalCountedHandler)); } } @@ -4855,7 +4859,7 @@ make_NetworkOPs( JobQueue& job_queue, LedgerMaster& ledgerMaster, ValidatorKeys const& validatorKeys, - boost::asio::io_service& io_svc, + boost::asio::io_context& io_svc, beast::Journal journal, beast::insight::Collector::ptr const& collector) { diff --git a/src/xrpld/app/misc/NetworkOPs.h b/src/xrpld/app/misc/NetworkOPs.h index 639cd782b7..9587d63b3a 100644 --- a/src/xrpld/app/misc/NetworkOPs.h +++ b/src/xrpld/app/misc/NetworkOPs.h @@ -290,7 +290,7 @@ make_NetworkOPs( JobQueue& job_queue, LedgerMaster& ledgerMaster, ValidatorKeys const& validatorKeys, - boost::asio::io_service& io_svc, + boost::asio::io_context& io_svc, beast::Journal journal, beast::insight::Collector::ptr const& collector); diff --git a/src/xrpld/app/misc/detail/ValidatorSite.cpp b/src/xrpld/app/misc/detail/ValidatorSite.cpp index 42d4e9e271..e235ff3e66 100644 --- a/src/xrpld/app/misc/detail/ValidatorSite.cpp +++ b/src/xrpld/app/misc/detail/ValidatorSite.cpp @@ -91,7 +91,7 @@ ValidatorSite::ValidatorSite( std::chrono::seconds timeout) : app_{app} , j_{j ? *j : app_.logs().journal("ValidatorSite")} - , timer_{app_.getIOService()} + , timer_{app_.getIOContext()} , fetching_{false} , pending_{false} , stopping_{false} @@ -271,7 +271,7 @@ ValidatorSite::makeRequest( resource->pUrl.domain, resource->pUrl.path, std::to_string(*resource->pUrl.port), - app_.getIOService(), + app_.getIOContext(), j_, app_.config(), sites_[siteIdx].lastRequestEndpoint, @@ -284,7 +284,7 @@ ValidatorSite::makeRequest( resource->pUrl.domain, resource->pUrl.path, std::to_string(*resource->pUrl.port), - app_.getIOService(), + app_.getIOContext(), sites_[siteIdx].lastRequestEndpoint, sites_[siteIdx].lastRequestSuccessful, onFetch); @@ -293,7 +293,7 @@ ValidatorSite::makeRequest( { BOOST_ASSERT(resource->pUrl.scheme == "file"); sp = std::make_shared( - resource->pUrl.path, app_.getIOService(), onFetchFile); + resource->pUrl.path, app_.getIOContext(), onFetchFile); } sites_[siteIdx].lastRequestSuccessful = false; diff --git a/src/xrpld/app/misc/detail/WorkBase.h b/src/xrpld/app/misc/detail/WorkBase.h index 17f935126b..a73cd3d597 100644 --- a/src/xrpld/app/misc/detail/WorkBase.h +++ b/src/xrpld/app/misc/detail/WorkBase.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -57,8 +58,8 @@ protected: std::string path_; std::string port_; callback_type cb_; - boost::asio::io_service& ios_; - boost::asio::io_service::strand strand_; + boost::asio::io_context& ios_; + boost::asio::strand strand_; resolver_type resolver_; socket_type socket_; request_type req_; @@ -72,7 +73,7 @@ public: std::string const& host, std::string const& path, std::string const& port, - boost::asio::io_service& ios, + boost::asio::io_context& ios, endpoint_type const& lastEndpoint, bool lastStatus, callback_type cb); @@ -120,7 +121,7 @@ WorkBase::WorkBase( std::string const& host, std::string const& path, std::string const& port, - boost::asio::io_service& ios, + boost::asio::io_context& ios, endpoint_type const& lastEndpoint, bool lastStatus, callback_type cb) @@ -129,7 +130,7 @@ WorkBase::WorkBase( , port_(port) , cb_(std::move(cb)) , ios_(ios) - , strand_(ios) + , strand_(boost::asio::make_strand(ios)) , resolver_(ios) , socket_(ios) , lastEndpoint_{lastEndpoint} @@ -152,17 +153,21 @@ void WorkBase::run() { if (!strand_.running_in_this_thread()) - return ios_.post( - strand_.wrap(std::bind(&WorkBase::run, impl().shared_from_this()))); + return boost::asio::post( + ios_, + boost::asio::bind_executor( + strand_, std::bind(&WorkBase::run, impl().shared_from_this()))); resolver_.async_resolve( host_, port_, - strand_.wrap(std::bind( - &WorkBase::onResolve, - impl().shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); + boost::asio::bind_executor( + strand_, + std::bind( + &WorkBase::onResolve, + impl().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); } template @@ -171,8 +176,12 @@ WorkBase::cancel() { if (!strand_.running_in_this_thread()) { - return ios_.post(strand_.wrap( - std::bind(&WorkBase::cancel, impl().shared_from_this()))); + return boost::asio::post( + ios_, + + boost::asio::bind_executor( + strand_, + std::bind(&WorkBase::cancel, impl().shared_from_this()))); } error_code ec; @@ -201,11 +210,13 @@ WorkBase::onResolve(error_code const& ec, results_type results) boost::asio::async_connect( socket_, results, - strand_.wrap(std::bind( - &WorkBase::onConnect, - impl().shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); + boost::asio::bind_executor( + strand_, + std::bind( + &WorkBase::onConnect, + impl().shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); } template @@ -233,10 +244,12 @@ WorkBase::onStart() boost::beast::http::async_write( impl().stream(), req_, - strand_.wrap(std::bind( - &WorkBase::onRequest, - impl().shared_from_this(), - std::placeholders::_1))); + boost::asio::bind_executor( + strand_, + std::bind( + &WorkBase::onRequest, + impl().shared_from_this(), + std::placeholders::_1))); } template @@ -250,10 +263,12 @@ WorkBase::onRequest(error_code const& ec) impl().stream(), readBuf_, res_, - strand_.wrap(std::bind( - &WorkBase::onResponse, - impl().shared_from_this(), - std::placeholders::_1))); + boost::asio::bind_executor( + strand_, + std::bind( + &WorkBase::onResponse, + impl().shared_from_this(), + std::placeholders::_1))); } template diff --git a/src/xrpld/app/misc/detail/WorkFile.h b/src/xrpld/app/misc/detail/WorkFile.h index 51fd6db78c..562e1c9ec1 100644 --- a/src/xrpld/app/misc/detail/WorkFile.h +++ b/src/xrpld/app/misc/detail/WorkFile.h @@ -26,6 +26,10 @@ #include #include +#include +#include +#include + namespace ripple { namespace detail { @@ -45,7 +49,7 @@ public: public: WorkFile( std::string const& path, - boost::asio::io_service& ios, + boost::asio::io_context& ios, callback_type cb); ~WorkFile(); @@ -58,17 +62,20 @@ public: private: std::string path_; callback_type cb_; - boost::asio::io_service& ios_; - boost::asio::io_service::strand strand_; + boost::asio::io_context& ios_; + boost::asio::strand strand_; }; //------------------------------------------------------------------------------ WorkFile::WorkFile( std::string const& path, - boost::asio::io_service& ios, + boost::asio::io_context& ios, callback_type cb) - : path_(path), cb_(std::move(cb)), ios_(ios), strand_(ios) + : path_(path) + , cb_(std::move(cb)) + , ios_(ios) + , strand_(boost::asio::make_strand(ios)) { } @@ -82,8 +89,10 @@ void WorkFile::run() { if (!strand_.running_in_this_thread()) - return ios_.post( - strand_.wrap(std::bind(&WorkFile::run, shared_from_this()))); + return boost::asio::post( + ios_, + boost::asio::bind_executor( + strand_, std::bind(&WorkFile::run, shared_from_this()))); error_code ec; auto const fileContents = getFileContents(ec, path_, megabytes(1)); diff --git a/src/xrpld/app/misc/detail/WorkPlain.h b/src/xrpld/app/misc/detail/WorkPlain.h index 16bf424131..38dd0df9fa 100644 --- a/src/xrpld/app/misc/detail/WorkPlain.h +++ b/src/xrpld/app/misc/detail/WorkPlain.h @@ -37,7 +37,7 @@ public: std::string const& host, std::string const& path, std::string const& port, - boost::asio::io_service& ios, + boost::asio::io_context& ios, endpoint_type const& lastEndpoint, bool lastStatus, callback_type cb); @@ -60,7 +60,7 @@ WorkPlain::WorkPlain( std::string const& host, std::string const& path, std::string const& port, - boost::asio::io_service& ios, + boost::asio::io_context& ios, endpoint_type const& lastEndpoint, bool lastStatus, callback_type cb) diff --git a/src/xrpld/app/misc/detail/WorkSSL.cpp b/src/xrpld/app/misc/detail/WorkSSL.cpp index 0d6801ab84..a262a66ca7 100644 --- a/src/xrpld/app/misc/detail/WorkSSL.cpp +++ b/src/xrpld/app/misc/detail/WorkSSL.cpp @@ -26,7 +26,7 @@ WorkSSL::WorkSSL( std::string const& host, std::string const& path, std::string const& port, - boost::asio::io_service& ios, + boost::asio::io_context& ios, beast::Journal j, Config const& config, endpoint_type const& lastEndpoint, @@ -56,8 +56,12 @@ WorkSSL::onConnect(error_code const& ec) stream_.async_handshake( boost::asio::ssl::stream_base::client, - strand_.wrap(std::bind( - &WorkSSL::onHandshake, shared_from_this(), std::placeholders::_1))); + boost::asio::bind_executor( + strand_, + std::bind( + &WorkSSL::onHandshake, + shared_from_this(), + std::placeholders::_1))); } void diff --git a/src/xrpld/app/misc/detail/WorkSSL.h b/src/xrpld/app/misc/detail/WorkSSL.h index 6a310986e7..cadc3fd8fd 100644 --- a/src/xrpld/app/misc/detail/WorkSSL.h +++ b/src/xrpld/app/misc/detail/WorkSSL.h @@ -52,7 +52,7 @@ public: std::string const& host, std::string const& path, std::string const& port, - boost::asio::io_service& ios, + boost::asio::io_context& ios, beast::Journal j, Config const& config, endpoint_type const& lastEndpoint, diff --git a/src/xrpld/overlay/detail/ConnectAttempt.cpp b/src/xrpld/overlay/detail/ConnectAttempt.cpp index 61049579c5..397ac06ba6 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.cpp +++ b/src/xrpld/overlay/detail/ConnectAttempt.cpp @@ -28,7 +28,7 @@ namespace ripple { ConnectAttempt::ConnectAttempt( Application& app, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, endpoint_type const& remote_endpoint, Resource::Consumer usage, shared_context const& context, @@ -43,10 +43,10 @@ ConnectAttempt::ConnectAttempt( , journal_(sink_) , remote_endpoint_(remote_endpoint) , usage_(usage) - , strand_(io_service) - , timer_(io_service) + , strand_(boost::asio::make_strand(io_context)) + , timer_(io_context) , stream_ptr_(std::make_unique( - socket_type(std::forward(io_service)), + socket_type(std::forward(io_context)), *context)) , socket_(stream_ptr_->next_layer().socket()) , stream_(*stream_ptr_) @@ -66,8 +66,8 @@ void ConnectAttempt::stop() { if (!strand_.running_in_this_thread()) - return strand_.post( - std::bind(&ConnectAttempt::stop, shared_from_this())); + return boost::asio::post( + strand_, std::bind(&ConnectAttempt::stop, shared_from_this())); if (socket_.is_open()) { JLOG(journal_.debug()) << "Stop"; @@ -80,10 +80,12 @@ ConnectAttempt::run() { stream_.next_layer().async_connect( remote_endpoint_, - strand_.wrap(std::bind( - &ConnectAttempt::onConnect, - shared_from_this(), - std::placeholders::_1))); + boost::asio::bind_executor( + strand_, + std::bind( + &ConnectAttempt::onConnect, + shared_from_this(), + std::placeholders::_1))); } //------------------------------------------------------------------------------ @@ -96,9 +98,16 @@ ConnectAttempt::close() "ripple::ConnectAttempt::close : strand in this thread"); if (socket_.is_open()) { - error_code ec; - timer_.cancel(ec); - socket_.close(ec); + try + { + timer_.cancel(); + socket_.close(); + } + catch (boost::system::system_error const&) + { + // ignored + } + JLOG(journal_.debug()) << "Closed"; } } @@ -120,23 +129,35 @@ ConnectAttempt::fail(std::string const& name, error_code ec) void ConnectAttempt::setTimer() { - error_code ec; - timer_.expires_from_now(std::chrono::seconds(15), ec); - if (ec) + try { - JLOG(journal_.error()) << "setTimer: " << ec.message(); + timer_.expires_after(std::chrono::seconds(15)); + } + catch (boost::system::system_error const& e) + { + JLOG(journal_.error()) << "setTimer: " << e.code(); return; } - timer_.async_wait(strand_.wrap(std::bind( - &ConnectAttempt::onTimer, shared_from_this(), std::placeholders::_1))); + timer_.async_wait(boost::asio::bind_executor( + strand_, + std::bind( + &ConnectAttempt::onTimer, + shared_from_this(), + std::placeholders::_1))); } void ConnectAttempt::cancelTimer() { - error_code ec; - timer_.cancel(ec); + try + { + timer_.cancel(); + } + catch (boost::system::system_error const&) + { + // ignored + } } void @@ -175,10 +196,12 @@ ConnectAttempt::onConnect(error_code ec) stream_.set_verify_mode(boost::asio::ssl::verify_none); stream_.async_handshake( boost::asio::ssl::stream_base::client, - strand_.wrap(std::bind( - &ConnectAttempt::onHandshake, - shared_from_this(), - std::placeholders::_1))); + boost::asio::bind_executor( + strand_, + std::bind( + &ConnectAttempt::onHandshake, + shared_from_this(), + std::placeholders::_1))); } void @@ -223,10 +246,12 @@ ConnectAttempt::onHandshake(error_code ec) boost::beast::http::async_write( stream_, req_, - strand_.wrap(std::bind( - &ConnectAttempt::onWrite, - shared_from_this(), - std::placeholders::_1))); + boost::asio::bind_executor( + strand_, + std::bind( + &ConnectAttempt::onWrite, + shared_from_this(), + std::placeholders::_1))); } void @@ -243,10 +268,12 @@ ConnectAttempt::onWrite(error_code ec) stream_, read_buf_, response_, - strand_.wrap(std::bind( - &ConnectAttempt::onRead, - shared_from_this(), - std::placeholders::_1))); + boost::asio::bind_executor( + strand_, + std::bind( + &ConnectAttempt::onRead, + shared_from_this(), + std::placeholders::_1))); } void @@ -262,10 +289,12 @@ ConnectAttempt::onRead(error_code ec) { JLOG(journal_.info()) << "EOF"; setTimer(); - return stream_.async_shutdown(strand_.wrap(std::bind( - &ConnectAttempt::onShutdown, - shared_from_this(), - std::placeholders::_1))); + return stream_.async_shutdown(boost::asio::bind_executor( + strand_, + std::bind( + &ConnectAttempt::onShutdown, + shared_from_this(), + std::placeholders::_1))); } if (ec) return fail("onRead", ec); @@ -299,7 +328,7 @@ ConnectAttempt::processResponse() s.reserve(boost::asio::buffer_size(response_.body().data())); for (auto const buffer : response_.body().data()) s.append( - boost::asio::buffer_cast(buffer), + static_cast(buffer.data()), boost::asio::buffer_size(buffer)); auto const success = r.parse(s, json); if (success) diff --git a/src/xrpld/overlay/detail/ConnectAttempt.h b/src/xrpld/overlay/detail/ConnectAttempt.h index c3e07f956a..febbe88f45 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.h +++ b/src/xrpld/overlay/detail/ConnectAttempt.h @@ -50,7 +50,7 @@ private: beast::Journal const journal_; endpoint_type remote_endpoint_; Resource::Consumer usage_; - boost::asio::io_service::strand strand_; + boost::asio::strand strand_; boost::asio::basic_waitable_timer timer_; std::unique_ptr stream_ptr_; socket_type& socket_; @@ -63,7 +63,7 @@ private: public: ConnectAttempt( Application& app, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, endpoint_type const& remote_endpoint, Resource::Consumer usage, shared_context const& context, diff --git a/src/xrpld/overlay/detail/Handshake.cpp b/src/xrpld/overlay/detail/Handshake.cpp index e3617a1d98..5ce4954a5e 100644 --- a/src/xrpld/overlay/detail/Handshake.cpp +++ b/src/xrpld/overlay/detail/Handshake.cpp @@ -326,7 +326,7 @@ verifyHandshake( { boost::system::error_code ec; auto const local_ip = - boost::asio::ip::address::from_string(iter->value(), ec); + boost::asio::ip::make_address(std::string_view(iter->value()), ec); if (ec) throw std::runtime_error("Invalid Local-IP"); @@ -341,7 +341,7 @@ verifyHandshake( { boost::system::error_code ec; auto const remote_ip = - boost::asio::ip::address::from_string(iter->value(), ec); + boost::asio::ip::make_address(std::string_view(iter->value()), ec); if (ec) throw std::runtime_error("Invalid Remote-IP"); diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index 874f951f56..f2c683b69f 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -41,6 +41,7 @@ #include #include +#include namespace ripple { @@ -68,7 +69,7 @@ OverlayImpl::Child::~Child() //------------------------------------------------------------------------------ OverlayImpl::Timer::Timer(OverlayImpl& overlay) - : Child(overlay), timer_(overlay_.io_service_) + : Child(overlay), timer_(overlay_.io_context_) { } @@ -85,8 +86,10 @@ void OverlayImpl::Timer::async_wait() { timer_.expires_after(std::chrono::seconds(1)); - timer_.async_wait(overlay_.strand_.wrap(std::bind( - &Timer::on_timer, shared_from_this(), std::placeholders::_1))); + timer_.async_wait(boost::asio::bind_executor( + overlay_.strand_, + std::bind( + &Timer::on_timer, shared_from_this(), std::placeholders::_1))); } void @@ -121,19 +124,19 @@ OverlayImpl::OverlayImpl( ServerHandler& serverHandler, Resource::Manager& resourceManager, Resolver& resolver, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, BasicConfig const& config, beast::insight::Collector::ptr const& collector) : app_(app) - , io_service_(io_service) - , work_(std::in_place, std::ref(io_service_)) - , strand_(io_service_) + , io_context_(io_context) + , work_(std::in_place, boost::asio::make_work_guard(io_context_)) + , strand_(boost::asio::make_strand(io_context_)) , setup_(setup) , journal_(app_.journal("Overlay")) , serverHandler_(serverHandler) , m_resourceManager(resourceManager) , m_peerFinder(PeerFinder::make_Manager( - io_service, + io_context, stopwatch(), app_.journal("PeerFinder"), config, @@ -408,7 +411,7 @@ OverlayImpl::connect(beast::IP::Endpoint const& remote_endpoint) auto const p = std::make_shared( app_, - io_service_, + io_context_, beast::IPAddressConversion::to_asio_endpoint(remote_endpoint), usage, setup_.context, @@ -560,7 +563,7 @@ OverlayImpl::start() void OverlayImpl::stop() { - strand_.dispatch(std::bind(&OverlayImpl::stopChildren, this)); + boost::asio::dispatch(strand_, std::bind(&OverlayImpl::stopChildren, this)); { std::unique_lock lock(mutex_); cond_.wait(lock, [this] { return list_.empty(); }); @@ -1498,7 +1501,7 @@ setup_Overlay(BasicConfig const& config) if (!ip.empty()) { boost::system::error_code ec; - setup.public_ip = beast::IP::Address::from_string(ip, ec); + setup.public_ip = boost::asio::ip::make_address(ip, ec); if (ec || beast::IP::is_private(setup.public_ip)) Throw("Configured public IP is invalid"); } @@ -1592,7 +1595,7 @@ make_Overlay( ServerHandler& serverHandler, Resource::Manager& resourceManager, Resolver& resolver, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, BasicConfig const& config, beast::insight::Collector::ptr const& collector) { @@ -1602,7 +1605,7 @@ make_Overlay( serverHandler, resourceManager, resolver, - io_service, + io_context, config, collector); } diff --git a/src/xrpld/overlay/detail/OverlayImpl.h b/src/xrpld/overlay/detail/OverlayImpl.h index 86107fc591..b4ea3307ec 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.h +++ b/src/xrpld/overlay/detail/OverlayImpl.h @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -100,9 +101,11 @@ private: }; Application& app_; - boost::asio::io_service& io_service_; - std::optional work_; - boost::asio::io_service::strand strand_; + boost::asio::io_context& io_context_; + std::optional> + work_; + boost::asio::strand strand_; mutable std::recursive_mutex mutex_; // VFALCO use std::mutex std::condition_variable_any cond_; std::weak_ptr timer_; @@ -143,7 +146,7 @@ public: ServerHandler& serverHandler, Resource::Manager& resourceManager, Resolver& resolver, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, BasicConfig const& config, beast::insight::Collector::ptr const& collector); diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 23b4760488..69f25e1eb4 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -84,7 +84,7 @@ PeerImp::PeerImp( , stream_ptr_(std::move(stream_ptr)) , socket_(stream_ptr_->next_layer().socket()) , stream_(*stream_ptr_) - , strand_(socket_.get_executor()) + , strand_(boost::asio::make_strand(socket_.get_executor())) , timer_(waitable_timer{socket_.get_executor()}) , remote_address_(slot->remote_endpoint()) , overlay_(overlay) @@ -581,9 +581,16 @@ PeerImp::close() if (socket_.is_open()) { detaching_ = true; // DEPRECATED - error_code ec; - timer_.cancel(ec); - socket_.close(ec); + try + { + timer_.cancel(); + socket_.close(); + } + catch (boost::system::system_error const&) + { + // ignored + } + overlay_.incPeerDisconnect(); if (inbound_) { @@ -654,12 +661,13 @@ PeerImp::gracefulClose() void PeerImp::setTimer() { - error_code ec; - timer_.expires_from_now(peerTimerInterval, ec); - - if (ec) + try { - JLOG(journal_.error()) << "setTimer: " << ec.message(); + timer_.expires_after(peerTimerInterval); + } + catch (boost::system::system_error const& e) + { + JLOG(journal_.error()) << "setTimer: " << e.code(); return; } timer_.async_wait(bind_executor( @@ -672,8 +680,14 @@ PeerImp::setTimer() void PeerImp::cancelTimer() { - error_code ec; - timer_.cancel(ec); + try + { + timer_.cancel(); + } + catch (boost::system::system_error const&) + { + // ignored + } } //------------------------------------------------------------------------------ diff --git a/src/xrpld/overlay/detail/PeerImp.h b/src/xrpld/overlay/detail/PeerImp.h index 5aa49fd152..3d9a0c0b1e 100644 --- a/src/xrpld/overlay/detail/PeerImp.h +++ b/src/xrpld/overlay/detail/PeerImp.h @@ -669,7 +669,7 @@ PeerImp::PeerImp( , stream_ptr_(std::move(stream_ptr)) , socket_(stream_ptr_->next_layer().socket()) , stream_(*stream_ptr_) - , strand_(socket_.get_executor()) + , strand_(boost::asio::make_strand(socket_.get_executor())) , timer_(waitable_timer{socket_.get_executor()}) , remote_address_(slot->remote_endpoint()) , overlay_(overlay) diff --git a/src/xrpld/overlay/detail/PeerSet.cpp b/src/xrpld/overlay/detail/PeerSet.cpp index 611728839c..74290f50d3 100644 --- a/src/xrpld/overlay/detail/PeerSet.cpp +++ b/src/xrpld/overlay/detail/PeerSet.cpp @@ -46,7 +46,7 @@ public: getPeerIds() const override; private: - // Used in this class for access to boost::asio::io_service and + // Used in this class for access to boost::asio::io_context and // ripple::Overlay. Application& app_; beast::Journal journal_; diff --git a/src/xrpld/overlay/detail/ZeroCopyStream.h b/src/xrpld/overlay/detail/ZeroCopyStream.h index 87a5e10bc2..23e26c5351 100644 --- a/src/xrpld/overlay/detail/ZeroCopyStream.h +++ b/src/xrpld/overlay/detail/ZeroCopyStream.h @@ -78,7 +78,7 @@ template bool ZeroCopyInputStream::Next(void const** data, int* size) { - *data = boost::asio::buffer_cast(pos_); + *data = pos_.data(); *size = boost::asio::buffer_size(pos_); if (first_ == last_) return false; @@ -195,7 +195,7 @@ ZeroCopyOutputStream::Next(void** data, int* size) pos_ = buffers_.begin(); } - *data = boost::asio::buffer_cast(*pos_); + *data = *pos_.data(); *size = boost::asio::buffer_size(*pos_); commit_ = *size; ++pos_; diff --git a/src/xrpld/overlay/make_Overlay.h b/src/xrpld/overlay/make_Overlay.h index 3476026562..142c922551 100644 --- a/src/xrpld/overlay/make_Overlay.h +++ b/src/xrpld/overlay/make_Overlay.h @@ -25,7 +25,7 @@ #include -#include +#include namespace ripple { @@ -40,7 +40,7 @@ make_Overlay( ServerHandler& serverHandler, Resource::Manager& resourceManager, Resolver& resolver, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, BasicConfig const& config, beast::insight::Collector::ptr const& collector); diff --git a/src/xrpld/peerfinder/detail/Checker.h b/src/xrpld/peerfinder/detail/Checker.h index e7983471a5..c5221fcc13 100644 --- a/src/xrpld/peerfinder/detail/Checker.h +++ b/src/xrpld/peerfinder/detail/Checker.h @@ -22,7 +22,7 @@ #include -#include +#include #include #include @@ -65,7 +65,7 @@ private: async_op( Checker& owner, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, Handler&& handler); ~async_op(); @@ -85,17 +85,17 @@ private: std::mutex mutex_; std::condition_variable cond_; - boost::asio::io_service& io_service_; + boost::asio::io_context& io_context_; list_type list_; bool stop_ = false; public: - explicit Checker(boost::asio::io_service& io_service); + explicit Checker(boost::asio::io_context& io_context); /** Destroy the service. Any pending I/O operations will be canceled. This call blocks until all pending operations complete (either with success or with - operation_aborted) and the associated thread and io_service have + operation_aborted) and the associated thread and io_context have no more work remaining. */ ~Checker(); @@ -132,10 +132,10 @@ template template Checker::async_op::async_op( Checker& owner, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, Handler&& handler) : checker_(owner) - , socket_(io_service) + , socket_(io_context) , handler_(std::forward(handler)) { } @@ -167,8 +167,8 @@ Checker::async_op::operator()(error_code const& ec) //------------------------------------------------------------------------------ template -Checker::Checker(boost::asio::io_service& io_service) - : io_service_(io_service) +Checker::Checker(boost::asio::io_context& io_context) + : io_context_(io_context) { } @@ -208,7 +208,7 @@ Checker::async_connect( Handler&& handler) { auto const op = std::make_shared>( - *this, io_service_, std::forward(handler)); + *this, io_context_, std::forward(handler)); { std::lock_guard lock(mutex_); list_.push_back(*op); diff --git a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp index 86093fa166..205df67fa6 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp @@ -23,7 +23,8 @@ #include #include -#include +#include +#include #include #include @@ -34,8 +35,10 @@ namespace PeerFinder { class ManagerImp : public Manager { public: - boost::asio::io_service& io_service_; - std::optional work_; + boost::asio::io_context& io_context_; + std::optional> + work_; clock_type& m_clock; beast::Journal m_journal; StoreSqdb m_store; @@ -46,18 +49,18 @@ public: //-------------------------------------------------------------------------- ManagerImp( - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, clock_type& clock, beast::Journal journal, BasicConfig const& config, beast::insight::Collector::ptr const& collector) : Manager() - , io_service_(io_service) - , work_(std::in_place, std::ref(io_service_)) + , io_context_(io_context) + , work_(std::in_place, boost::asio::make_work_guard(io_context_)) , m_clock(clock) , m_journal(journal) , m_store(journal) - , checker_(io_service_) + , checker_(io_context_) , m_logic(clock, m_store, checker_, journal) , m_config(config) , m_stats(std::bind(&ManagerImp::collect_metrics, this), collector) @@ -271,14 +274,14 @@ Manager::Manager() noexcept : beast::PropertyStream::Source("peerfinder") std::unique_ptr make_Manager( - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, clock_type& clock, beast::Journal journal, BasicConfig const& config, beast::insight::Collector::ptr const& collector) { return std::make_unique( - io_service, clock, journal, config, collector); + io_context, clock, journal, config, collector); } } // namespace PeerFinder diff --git a/src/xrpld/peerfinder/make_Manager.h b/src/xrpld/peerfinder/make_Manager.h index fba95e8f22..e55964f4a7 100644 --- a/src/xrpld/peerfinder/make_Manager.h +++ b/src/xrpld/peerfinder/make_Manager.h @@ -22,7 +22,7 @@ #include -#include +#include #include @@ -32,7 +32,7 @@ namespace PeerFinder { /** Create a new Manager. */ std::unique_ptr make_Manager( - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, clock_type& clock, beast::Journal journal, BasicConfig const& config, diff --git a/src/xrpld/rpc/RPCCall.h b/src/xrpld/rpc/RPCCall.h index 4c6d25ca57..9e160b8fbd 100644 --- a/src/xrpld/rpc/RPCCall.h +++ b/src/xrpld/rpc/RPCCall.h @@ -25,7 +25,7 @@ #include #include -#include +#include #include #include @@ -51,7 +51,7 @@ fromCommandLine( void fromNetwork( - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, std::string const& strIp, std::uint16_t const iPort, std::string const& strUsername, diff --git a/src/xrpld/rpc/RPCSub.h b/src/xrpld/rpc/RPCSub.h index 0f106be018..2fd1be0b20 100644 --- a/src/xrpld/rpc/RPCSub.h +++ b/src/xrpld/rpc/RPCSub.h @@ -23,7 +23,7 @@ #include #include -#include +#include namespace ripple { @@ -40,11 +40,11 @@ protected: explicit RPCSub(InfoSub::Source& source); }; -// VFALCO Why is the io_service needed? +// VFALCO Why is the io_context needed? std::shared_ptr make_RPCSub( InfoSub::Source& source, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, JobQueue& jobQueue, std::string const& strUrl, std::string const& strUsername, diff --git a/src/xrpld/rpc/ServerHandler.h b/src/xrpld/rpc/ServerHandler.h index 5f72673313..d0ebdcd67d 100644 --- a/src/xrpld/rpc/ServerHandler.h +++ b/src/xrpld/rpc/ServerHandler.h @@ -111,7 +111,7 @@ private: friend std::unique_ptr make_ServerHandler( Application& app, - boost::asio::io_service&, + boost::asio::io_context&, JobQueue&, NetworkOPs&, Resource::Manager&, @@ -122,7 +122,7 @@ public: ServerHandler( ServerHandlerCreator const&, Application& app, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, JobQueue& jobQueue, NetworkOPs& networkOPs, Resource::Manager& resourceManager, @@ -223,7 +223,7 @@ setup_ServerHandler(Config const& c, std::ostream&& log); std::unique_ptr make_ServerHandler( Application& app, - boost::asio::io_service&, + boost::asio::io_context&, JobQueue&, NetworkOPs&, Resource::Manager&, diff --git a/src/xrpld/rpc/detail/RPCCall.cpp b/src/xrpld/rpc/detail/RPCCall.cpp index aa8c80fff7..57432d920f 100644 --- a/src/xrpld/rpc/detail/RPCCall.cpp +++ b/src/xrpld/rpc/detail/RPCCall.cpp @@ -1543,7 +1543,7 @@ rpcClient( } { - boost::asio::io_service isService; + boost::asio::io_context isService; RPCCall::fromNetwork( isService, setup.client.ip, @@ -1647,7 +1647,7 @@ fromCommandLine( void fromNetwork( - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, std::string const& strIp, std::uint16_t const iPort, std::string const& strUsername, @@ -1685,7 +1685,7 @@ fromNetwork( HTTPClient::request( bSSL, - io_service, + io_context, strIp, iPort, std::bind( diff --git a/src/xrpld/rpc/detail/RPCSub.cpp b/src/xrpld/rpc/detail/RPCSub.cpp index 966ad6df4b..6619b5ddc5 100644 --- a/src/xrpld/rpc/detail/RPCSub.cpp +++ b/src/xrpld/rpc/detail/RPCSub.cpp @@ -35,14 +35,14 @@ class RPCSubImp : public RPCSub public: RPCSubImp( InfoSub::Source& source, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, JobQueue& jobQueue, std::string const& strUrl, std::string const& strUsername, std::string const& strPassword, Logs& logs) : RPCSub(source) - , m_io_service(io_service) + , m_io_context(io_context) , m_jobQueue(jobQueue) , mUrl(strUrl) , mSSL(false) @@ -155,7 +155,7 @@ private: JLOG(j_.info()) << "RPCCall::fromNetwork: " << mIp; RPCCall::fromNetwork( - m_io_service, + m_io_context, mIp, mPort, mUsername, @@ -177,7 +177,7 @@ private: } private: - boost::asio::io_service& m_io_service; + boost::asio::io_context& m_io_context; JobQueue& m_jobQueue; std::string mUrl; @@ -207,7 +207,7 @@ RPCSub::RPCSub(InfoSub::Source& source) : InfoSub(source, Consumer()) std::shared_ptr make_RPCSub( InfoSub::Source& source, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, JobQueue& jobQueue, std::string const& strUrl, std::string const& strUsername, @@ -216,7 +216,7 @@ make_RPCSub( { return std::make_shared( std::ref(source), - std::ref(io_service), + std::ref(io_context), std::ref(jobQueue), strUrl, strUsername, diff --git a/src/xrpld/rpc/detail/ServerHandler.cpp b/src/xrpld/rpc/detail/ServerHandler.cpp index 0c84e59413..f5f5e53238 100644 --- a/src/xrpld/rpc/detail/ServerHandler.cpp +++ b/src/xrpld/rpc/detail/ServerHandler.cpp @@ -104,7 +104,7 @@ authorized(Port const& port, std::map const& h) ServerHandler::ServerHandler( ServerHandlerCreator const&, Application& app, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, JobQueue& jobQueue, NetworkOPs& networkOPs, Resource::Manager& resourceManager, @@ -113,7 +113,7 @@ ServerHandler::ServerHandler( , m_resourceManager(resourceManager) , m_journal(app_.journal("Server")) , m_networkOPs(networkOPs) - , m_server(make_Server(*this, io_service, app_.journal("Server"))) + , m_server(make_Server(*this, io_context, app_.journal("Server"))) , m_jobQueue(jobQueue) { auto const& group(cm.group("rpc")); @@ -282,14 +282,13 @@ template static std::string buffers_to_string(ConstBufferSequence const& bs) { - using boost::asio::buffer_cast; using boost::asio::buffer_size; std::string s; s.reserve(buffer_size(bs)); // Use auto&& so the right thing happens whether bs returns a copy or // a reference for (auto&& b : bs) - s.append(buffer_cast(b), buffer_size(b)); + s.append(static_cast(b.data()), buffer_size(b)); return s; } @@ -1267,7 +1266,7 @@ setup_ServerHandler(Config const& config, std::ostream&& log) std::unique_ptr make_ServerHandler( Application& app, - boost::asio::io_service& io_service, + boost::asio::io_context& io_context, JobQueue& jobQueue, NetworkOPs& networkOPs, Resource::Manager& resourceManager, @@ -1276,7 +1275,7 @@ make_ServerHandler( return std::make_unique( ServerHandler::ServerHandlerCreator(), app, - io_service, + io_context, jobQueue, networkOPs, resourceManager, diff --git a/src/xrpld/rpc/handlers/Subscribe.cpp b/src/xrpld/rpc/handlers/Subscribe.cpp index c089f0255d..1696754e9c 100644 --- a/src/xrpld/rpc/handlers/Subscribe.cpp +++ b/src/xrpld/rpc/handlers/Subscribe.cpp @@ -76,7 +76,7 @@ doSubscribe(RPC::JsonContext& context) { auto rspSub = make_RPCSub( context.app.getOPs(), - context.app.getIOService(), + context.app.getIOContext(), context.app.getJobQueue(), strUrl, strUsername, From 2df7dcfdebcb0cdbd030c1f4b09ac748af95659c Mon Sep 17 00:00:00 2001 From: Michael Legleux Date: Wed, 27 Aug 2025 10:25:53 -0700 Subject: [PATCH 26/66] Set version to 2.6.0 --- src/libxrpl/protocol/BuildInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libxrpl/protocol/BuildInfo.cpp b/src/libxrpl/protocol/BuildInfo.cpp index 4b55f82f49..d5077aa44d 100644 --- a/src/libxrpl/protocol/BuildInfo.cpp +++ b/src/libxrpl/protocol/BuildInfo.cpp @@ -36,7 +36,7 @@ namespace BuildInfo { // and follow the format described at http://semver.org/ //------------------------------------------------------------------------------ // clang-format off -char const* const versionString = "2.6.0-rc3" +char const* const versionString = "2.6.0" // clang-format on #if defined(DEBUG) || defined(SANITIZER) From dedf3d3983c514930dd2883e83917649b3b6b45a Mon Sep 17 00:00:00 2001 From: Bronek Kozicki Date: Thu, 28 Aug 2025 15:15:17 +0100 Subject: [PATCH 27/66] Remove extraneous // LCOV_EXCL_START, and fix CMake warning (#5744) * Remove extraneous // LCOV_EXCL_START * Fix "At least one COMMAND must be given" CMake warning --- cmake/CodeCoverage.cmake | 5 ++++- src/xrpld/ledger/detail/View.cpp | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index ce1733988b..09ec3b9569 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -101,6 +101,9 @@ # 2025-05-12, Jingchen Wu # - add -fprofile-update=atomic to ensure atomic profile generation # +# 2025-08-28, Bronek Kozicki +# - fix "At least one COMMAND must be given" CMake warning from policy CMP0175 +# # USAGE: # # 1. Copy this file into your cmake modules path. @@ -446,7 +449,7 @@ function(setup_target_for_coverage_gcovr) # Show info where to find the report add_custom_command(TARGET ${Coverage_NAME} POST_BUILD - COMMAND ; + COMMAND echo COMMENT "Code coverage report saved in ${GCOVR_OUTPUT_FILE} formatted as ${Coverage_FORMAT}" ) endfunction() # setup_target_for_coverage_gcovr diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 7c6e1d60f1..4f8a29d15c 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -3074,7 +3074,6 @@ rippleUnlockEscrowMPT( auto const delta = amount.mpt().value(); // Underflow check for subtraction - // LCOV_EXCL_START if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta))) { // LCOV_EXCL_START JLOG(j.error()) From 1e37d00d6c176f8bce562f5ecb19c2cbc0cf098f Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 28 Aug 2025 20:32:49 +0100 Subject: [PATCH 28/66] ci: Use XRPLF/prepare-runner action (#5740) * ci: Use XRPLF/prepare-runner action * Remove some old boost workaround --- .github/workflows/build-test.yml | 34 +++++++------------------------- cmake/deps/Boost.cmake | 6 ------ 2 files changed, 7 insertions(+), 33 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 36145479e1..05b65edbfd 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -101,6 +101,7 @@ jobs: echo 'CMake arguments: ${{ matrix.cmake_args }}' echo 'CMake target: ${{ matrix.cmake_target }}' echo 'Config name: ${{ matrix.config_name }}' + - name: Clean workspace (MacOS) if: ${{ inputs.os == 'macos' }} run: | @@ -111,18 +112,12 @@ jobs: exit 1 fi find "${WORKSPACE}" -depth 1 | xargs rm -rfv + - name: Checkout repository uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - - name: Set up Python (Windows) - if: ${{ inputs.os == 'windows' }} - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: 3.13 - - name: Install build tools (Windows) - if: ${{ inputs.os == 'windows' }} - run: | - echo 'Installing build tools.' - pip install wheel conan + - name: Prepare runner + uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5 + - name: Check configuration (Windows) if: ${{ inputs.os == 'windows' }} run: | @@ -134,11 +129,6 @@ jobs: echo 'Checking Conan version.' conan --version - - name: Install build tools (MacOS) - if: ${{ inputs.os == 'macos' }} - run: | - echo 'Installing build tools.' - brew install --quiet cmake conan ninja coreutils - name: Check configuration (Linux and MacOS) if: ${{ inputs.os == 'linux' || inputs.os == 'macos' }} run: | @@ -162,18 +152,7 @@ jobs: echo 'Checking nproc version.' nproc --version - - name: Set up Conan home directory (MacOS) - if: ${{ inputs.os == 'macos' }} - run: | - echo 'Setting up Conan home directory.' - export CONAN_HOME=${{ github.workspace }}/.conan - mkdir -p ${CONAN_HOME} - - name: Set up Conan home directory (Windows) - if: ${{ inputs.os == 'windows' }} - run: | - echo 'Setting up Conan home directory.' - set CONAN_HOME=${{ github.workspace }}\.conan - mkdir -p %CONAN_HOME% + - name: Set up Conan configuration run: | echo 'Installing configuration.' @@ -196,6 +175,7 @@ jobs: echo 'Listing Conan remotes.' conan remote list + - name: Build dependencies uses: ./.github/actions/build-deps with: diff --git a/cmake/deps/Boost.cmake b/cmake/deps/Boost.cmake index bde40c0ce5..e431e57b0c 100644 --- a/cmake/deps/Boost.cmake +++ b/cmake/deps/Boost.cmake @@ -14,12 +14,6 @@ find_package(Boost 1.82 REQUIRED add_library(ripple_boost INTERFACE) add_library(Ripple::boost ALIAS ripple_boost) -if(XCODE) - target_include_directories(ripple_boost BEFORE INTERFACE ${Boost_INCLUDE_DIRS}) - target_compile_options(ripple_boost INTERFACE --system-header-prefix="boost/") -else() - target_include_directories(ripple_boost SYSTEM BEFORE INTERFACE ${Boost_INCLUDE_DIRS}) -endif() target_link_libraries(ripple_boost INTERFACE From 6e814d7ebdaf245464cece678e098c6fe7da17a2 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Thu, 28 Aug 2025 16:33:11 -0400 Subject: [PATCH 29/66] chore: Run CI jobs in more situations, and add "passed" job (#5739) Test jobs will run if * Either the PR is non-draft or has the "DraftRunCI" label set *AND* * One of the following: * Certain files were changed *OR* * The PR is non-draft and has the "Ready to merge" flag *OR* * The workflow is being run from the merge queue. Additionally, a meta "passed" job was added that is dependent on all the other test jobs, so the required jobs list under branch protection rules only needs to specify "passed" to ensure that *either* all the test jobs pass *or* all the test jobs are skipped because they don't need to be run. This allows PRs that don't affect the build or binary to be merged without overriding. --- .github/workflows/on-pr.yml | 70 ++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index a5f1d60c42..a4bbfd0997 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -28,30 +28,26 @@ env: CONAN_REMOTE_URL: https://conan.ripplex.io jobs: - # This job determines whether the workflow should run. It runs when the PR is - # not a draft or has the 'DraftRunCI' label. + # This job determines whether the rest of the workflow should run. It runs + # when the PR is not a draft (which should also cover merge-group) or + # has the 'DraftRunCI' label. should-run: if: ${{ !github.event.pull_request.draft || contains(github.event.pull_request.labels.*.name, 'DraftRunCI') }} runs-on: ubuntu-latest - steps: - - name: No-op - run: true - - # This job checks whether any files have changed that should cause the next - # jobs to run. We do it this way rather than using `paths` in the `on:` - # section, because all required checks must pass, even for changes that do not - # modify anything that affects those checks. We would therefore like to make - # the checks required only if the job runs, but GitHub does not support that - # directly. By always executing the workflow on new commits and by using the - # changed-files action below, we ensure that Github considers any skipped jobs - # to have passed, and in turn the required checks as well. - any-changed: - needs: should-run - runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Determine changed files + # This step checks whether any files have changed that should + # cause the next jobs to run. We do it this way rather than + # using `paths` in the `on:` section, because all required + # checks must pass, even for changes that do not modify anything + # that affects those checks. We would therefore like to make the + # checks required only if the job runs, but GitHub does not + # support that directly. By always executing the workflow on new + # commits and by using the changed-files action below, we ensure + # that Github considers any skipped jobs to have passed, and in + # turn the required checks as well. id: changes uses: tj-actions/changed-files@ed68ef82c095e0d48ec87eccea555d944a631a4c # v46.0.5 with: @@ -79,24 +75,40 @@ jobs: tests/** CMakeLists.txt conanfile.py + - name: Check whether to run + # This step determines whether the rest of the workflow should + # run. The rest of the workflow will run if this job runs AND at + # least one of: + # * Any of the files checked in the `changes` step were modified + # * The PR is NOT a draft and is labeled "Ready to merge" + # * The workflow is running from the merge queue + id: go + env: + FILES: ${{ steps.changes.outputs.any_changed }} + DRAFT: ${{ github.event.pull_request.draft }} + READY: ${{ contains(github.event.pull_request.labels.*.name, 'Ready to merge') }} + MERGE: ${{ github.event_name == 'merge_group' }} + run: | + echo "go=${{ (env.DRAFT != 'true' && env.READY == 'true') || env.FILES == 'true' || env.MERGE == 'true' }}" >> "${GITHUB_OUTPUT}" + cat "${GITHUB_OUTPUT}" outputs: - changed: ${{ steps.changes.outputs.any_changed }} + go: ${{ steps.go.outputs.go == 'true' }} check-format: - needs: any-changed - if: needs.any-changed.outputs.changed == 'true' + needs: should-run + if: needs.should-run.outputs.go == 'true' uses: ./.github/workflows/check-format.yml check-levelization: - needs: any-changed - if: needs.any-changed.outputs.changed == 'true' + needs: should-run + if: needs.should-run.outputs.go == 'true' uses: ./.github/workflows/check-levelization.yml # This job works around the limitation that GitHub Actions does not support # using environment variables as inputs for reusable workflows. generate-outputs: - needs: any-changed - if: needs.any-changed.outputs.changed == 'true' + needs: should-run + if: needs.should-run.outputs.go == 'true' runs-on: ubuntu-latest steps: - name: No-op @@ -130,3 +142,13 @@ jobs: clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }} conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} + + passed: + needs: + - build-test + - check-format + - check-levelization + runs-on: ubuntu-latest + steps: + - name: No-op + run: true From e4fdf3315805c7678500bd7c2babad77cf66148a Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Fri, 29 Aug 2025 02:00:38 +0200 Subject: [PATCH 30/66] adds additional logging to differentiate why connections were refused (#5690) This is a follow-up to PR #5664 that further improves the specificity of logging for refused peer connections. The previous changes did not account for several key scenarios, leading to potentially misleading log messages. It addresses the following - Inbound Disabled: Connections are now explicitly logged as rejected when the server is not configured to accept inbound peers. Previously, this was logged as the server being "full," which was technically correct but lacked diagnostic clarity. - Duplicate Connections: The logging now distinguishes between two types of duplicate connection refusals: - When a peer with the same node public key is already connected (duplicate connection). - When a connection is rejected because the limit for connections from a single IP address has been reached. These changes provide more accurate and actionable diagnostic information when analyzing peer connection behavior. --- src/test/overlay/tx_reduce_relay_test.cpp | 2 +- src/test/peerfinder/PeerFinder_test.cpp | 274 ++++++++++++++---- src/xrpld/overlay/detail/OverlayImpl.cpp | 11 +- src/xrpld/peerfinder/PeerfinderManager.h | 27 +- src/xrpld/peerfinder/detail/Counts.h | 2 +- src/xrpld/peerfinder/detail/Logic.h | 26 +- .../peerfinder/detail/PeerfinderConfig.cpp | 11 + .../peerfinder/detail/PeerfinderManager.cpp | 4 +- 8 files changed, 280 insertions(+), 77 deletions(-) diff --git a/src/test/overlay/tx_reduce_relay_test.cpp b/src/test/overlay/tx_reduce_relay_test.cpp index 0c67fd581c..83b3013514 100644 --- a/src/test/overlay/tx_reduce_relay_test.cpp +++ b/src/test/overlay/tx_reduce_relay_test.cpp @@ -183,7 +183,7 @@ private: boost::asio::ip::make_address("172.1.1." + std::to_string(rid_))); PublicKey key(std::get<0>(randomKeyPair(KeyType::ed25519))); auto consumer = overlay.resourceManager().newInboundEndpoint(remote); - auto slot = overlay.peerFinder().new_inbound_slot(local, remote); + auto [slot, _] = overlay.peerFinder().new_inbound_slot(local, remote); auto const peer = std::make_shared( env.app(), slot, diff --git a/src/test/peerfinder/PeerFinder_test.cpp b/src/test/peerfinder/PeerFinder_test.cpp index f35cbbdaae..64a1eb5091 100644 --- a/src/test/peerfinder/PeerFinder_test.cpp +++ b/src/test/peerfinder/PeerFinder_test.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -98,7 +99,7 @@ public: if (!list.empty()) { BEAST_EXPECT(list.size() == 1); - auto const slot = logic.new_outbound_slot(list.front()); + auto const [slot, _] = logic.new_outbound_slot(list.front()); BEAST_EXPECT(logic.onConnected( slot, beast::IP::Endpoint::from_string("65.0.0.2:5"))); logic.on_closed(slot); @@ -139,7 +140,7 @@ public: if (!list.empty()) { BEAST_EXPECT(list.size() == 1); - auto const slot = logic.new_outbound_slot(list.front()); + auto const [slot, _] = logic.new_outbound_slot(list.front()); if (!BEAST_EXPECT(logic.onConnected( slot, beast::IP::Endpoint::from_string("65.0.0.2:5")))) return; @@ -158,6 +159,7 @@ public: BEAST_EXPECT(n <= (seconds + 59) / 60); } + // test accepting an incoming slot for an already existing outgoing slot void test_duplicateOutIn() { @@ -166,8 +168,6 @@ public: TestChecker checker; TestStopwatch clock; Logic logic(clock, store, checker, journal_); - logic.addFixedPeer( - "test", beast::IP::Endpoint::from_string("65.0.0.1:5")); { Config c; c.autoConnect = false; @@ -176,28 +176,24 @@ public: logic.config(c); } - auto const list = logic.autoconnect(); - if (BEAST_EXPECT(!list.empty())) - { - BEAST_EXPECT(list.size() == 1); - auto const remote = list.front(); - auto const slot1 = logic.new_outbound_slot(remote); - if (BEAST_EXPECT(slot1 != nullptr)) - { - BEAST_EXPECT( - logic.connectedAddresses_.count(remote.address()) == 1); - auto const local = - beast::IP::Endpoint::from_string("65.0.0.2:1024"); - auto const slot2 = logic.new_inbound_slot(local, remote); - BEAST_EXPECT( - logic.connectedAddresses_.count(remote.address()) == 1); - if (!BEAST_EXPECT(slot2 == nullptr)) - logic.on_closed(slot2); - logic.on_closed(slot1); - } - } + auto const remote = beast::IP::Endpoint::from_string("65.0.0.1:5"); + auto const [slot1, r] = logic.new_outbound_slot(remote); + BEAST_EXPECT(slot1 != nullptr); + BEAST_EXPECT(r == Result::success); + BEAST_EXPECT(logic.connectedAddresses_.count(remote.address()) == 1); + + auto const local = beast::IP::Endpoint::from_string("65.0.0.2:1024"); + auto const [slot2, r2] = logic.new_inbound_slot(local, remote); + BEAST_EXPECT(logic.connectedAddresses_.count(remote.address()) == 1); + BEAST_EXPECT(r2 == Result::duplicatePeer); + + if (!BEAST_EXPECT(slot2 == nullptr)) + logic.on_closed(slot2); + + logic.on_closed(slot1); } + // test establishing outgoing slot for an already existing incoming slot void test_duplicateInOut() { @@ -206,8 +202,6 @@ public: TestChecker checker; TestStopwatch clock; Logic logic(clock, store, checker, journal_); - logic.addFixedPeer( - "test", beast::IP::Endpoint::from_string("65.0.0.1:5")); { Config c; c.autoConnect = false; @@ -216,33 +210,202 @@ public: logic.config(c); } - auto const list = logic.autoconnect(); - if (BEAST_EXPECT(!list.empty())) + auto const remote = beast::IP::Endpoint::from_string("65.0.0.1:5"); + auto const local = beast::IP::Endpoint::from_string("65.0.0.2:1024"); + + auto const [slot1, r] = logic.new_inbound_slot(local, remote); + BEAST_EXPECT(slot1 != nullptr); + BEAST_EXPECT(r == Result::success); + BEAST_EXPECT(logic.connectedAddresses_.count(remote.address()) == 1); + + auto const [slot2, r2] = logic.new_outbound_slot(remote); + BEAST_EXPECT(r2 == Result::duplicatePeer); + BEAST_EXPECT(logic.connectedAddresses_.count(remote.address()) == 1); + if (!BEAST_EXPECT(slot2 == nullptr)) + logic.on_closed(slot2); + logic.on_closed(slot1); + } + + void + test_peerLimitExceeded() + { + testcase("peer limit exceeded"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); { - BEAST_EXPECT(list.size() == 1); - auto const remote = list.front(); - auto const local = - beast::IP::Endpoint::from_string("65.0.0.2:1024"); - auto const slot1 = logic.new_inbound_slot(local, remote); - if (BEAST_EXPECT(slot1 != nullptr)) - { - BEAST_EXPECT( - logic.connectedAddresses_.count(remote.address()) == 1); - auto const slot2 = logic.new_outbound_slot(remote); - BEAST_EXPECT( - logic.connectedAddresses_.count(remote.address()) == 1); - if (!BEAST_EXPECT(slot2 == nullptr)) - logic.on_closed(slot2); - logic.on_closed(slot1); - } + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); } + + auto const local = beast::IP::Endpoint::from_string("65.0.0.2:1024"); + auto const [slot, r] = logic.new_inbound_slot( + local, beast::IP::Endpoint::from_string("55.104.0.2:1025")); + BEAST_EXPECT(slot != nullptr); + BEAST_EXPECT(r == Result::success); + + auto const [slot1, r1] = logic.new_inbound_slot( + local, beast::IP::Endpoint::from_string("55.104.0.2:1026")); + BEAST_EXPECT(slot1 != nullptr); + BEAST_EXPECT(r1 == Result::success); + + auto const [slot2, r2] = logic.new_inbound_slot( + local, beast::IP::Endpoint::from_string("55.104.0.2:1027")); + BEAST_EXPECT(r2 == Result::ipLimitExceeded); + + if (!BEAST_EXPECT(slot2 == nullptr)) + logic.on_closed(slot2); + logic.on_closed(slot1); + logic.on_closed(slot); + } + + void + test_activate_duplicate_peer() + { + testcase("test activate duplicate peer"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); + } + + auto const local = beast::IP::Endpoint::from_string("65.0.0.2:1024"); + + PublicKey const pk1(randomKeyPair(KeyType::secp256k1).first); + + auto const [slot, rSlot] = logic.new_outbound_slot( + beast::IP::Endpoint::from_string("55.104.0.2:1025")); + BEAST_EXPECT(slot != nullptr); + BEAST_EXPECT(rSlot == Result::success); + + auto const [slot2, r2Slot] = logic.new_outbound_slot( + beast::IP::Endpoint::from_string("55.104.0.2:1026")); + BEAST_EXPECT(slot2 != nullptr); + BEAST_EXPECT(r2Slot == Result::success); + + BEAST_EXPECT(logic.onConnected(slot, local)); + BEAST_EXPECT(logic.onConnected(slot2, local)); + + BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::success); + + // activating a different slot with the same node ID (pk) must fail + BEAST_EXPECT( + logic.activate(slot2, pk1, false) == Result::duplicatePeer); + + logic.on_closed(slot); + + // accept the same key for a new slot after removing the old slot + BEAST_EXPECT(logic.activate(slot2, pk1, false) == Result::success); + logic.on_closed(slot2); + } + + void + test_activate_inbound_disabled() + { + testcase("test activate inbound disabled"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); + } + + PublicKey const pk1(randomKeyPair(KeyType::secp256k1).first); + auto const local = beast::IP::Endpoint::from_string("65.0.0.2:1024"); + + auto const [slot, rSlot] = logic.new_inbound_slot( + local, beast::IP::Endpoint::from_string("55.104.0.2:1025")); + BEAST_EXPECT(slot != nullptr); + BEAST_EXPECT(rSlot == Result::success); + + BEAST_EXPECT( + logic.activate(slot, pk1, false) == Result::inboundDisabled); + + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + c.inPeers = 1; + logic.config(c); + } + // new inbound slot must succeed when inbound connections are enabled + BEAST_EXPECT(logic.activate(slot, pk1, false) == Result::success); + + // creating a new inbound slot must succeed as IP Limit is not exceeded + auto const [slot2, r2Slot] = logic.new_inbound_slot( + local, beast::IP::Endpoint::from_string("55.104.0.2:1026")); + BEAST_EXPECT(slot2 != nullptr); + BEAST_EXPECT(r2Slot == Result::success); + + PublicKey const pk2(randomKeyPair(KeyType::secp256k1).first); + + // an inbound slot exceeding inPeers limit must fail + BEAST_EXPECT(logic.activate(slot2, pk2, false) == Result::full); + + logic.on_closed(slot2); + logic.on_closed(slot); + } + + void + test_addFixedPeer_no_port() + { + testcase("test addFixedPeer no port"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + try + { + logic.addFixedPeer( + "test", beast::IP::Endpoint::from_string("65.0.0.2")); + fail("invalid endpoint successfully added"); + } + catch (std::runtime_error const& e) + { + pass(); + } + } + + void + test_onConnected_self_connection() + { + testcase("test onConnected self connection"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + + auto const local = beast::IP::Endpoint::from_string("65.0.0.2:1234"); + auto const [slot, r] = logic.new_outbound_slot(local); + BEAST_EXPECT(slot != nullptr); + BEAST_EXPECT(r == Result::success); + + // Must fail when a slot is to our own IP address + BEAST_EXPECT(!logic.onConnected(slot, local)); + logic.on_closed(slot); } void test_config() { - // if peers_max is configured then peers_in_max and peers_out_max are - // ignored + // if peers_max is configured then peers_in_max and peers_out_max + // are ignored auto run = [&](std::string const& test, std::optional maxPeers, std::optional maxIn, @@ -282,13 +445,21 @@ public: Counts counts; counts.onConfig(config); BEAST_EXPECT( - counts.out_max() == expectOut && - counts.inboundSlots() == expectIn && + counts.out_max() == expectOut && counts.in_max() == expectIn && config.ipLimit == expectIpLimit); + + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + logic.config(config); + + BEAST_EXPECT(logic.config() == config); }; // if max_peers == 0 => maxPeers = 21, - // else if max_peers < 10 => maxPeers = 10 else maxPeers = max_peers + // else if max_peers < 10 => maxPeers = 10 else maxPeers = + // max_peers // expectOut => if legacy => max(0.15 * maxPeers, 10), // if legacy && !wantIncoming => maxPeers else max_out_peers // expectIn => if legacy && wantIncoming => maxPeers - outPeers @@ -364,6 +535,11 @@ public: test_duplicateInOut(); test_config(); test_invalid_config(); + test_peerLimitExceeded(); + test_activate_duplicate_peer(); + test_activate_inbound_disabled(); + test_addFixedPeer_no_port(); + test_onConnected_self_connection(); } }; diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index f2c683b69f..8d295faace 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -195,14 +195,16 @@ OverlayImpl::onHandoff( if (consumer.disconnect(journal)) return handoff; - auto const slot = m_peerFinder->new_inbound_slot( + auto const [slot, result] = m_peerFinder->new_inbound_slot( beast::IPAddressConversion::from_asio(local_endpoint), beast::IPAddressConversion::from_asio(remote_endpoint)); if (slot == nullptr) { - // self-connect, close + // connection refused either IP limit exceeded or self-connect handoff.moved = false; + JLOG(journal.debug()) + << "Peer " << remote_endpoint << " refused, " << to_string(result); return handoff; } @@ -402,10 +404,11 @@ OverlayImpl::connect(beast::IP::Endpoint const& remote_endpoint) return; } - auto const slot = peerFinder().new_outbound_slot(remote_endpoint); + auto const [slot, result] = peerFinder().new_outbound_slot(remote_endpoint); if (slot == nullptr) { - JLOG(journal_.debug()) << "Connect: No slot for " << remote_endpoint; + JLOG(journal_.debug()) << "Connect: No slot for " << remote_endpoint + << ": " << to_string(result); return; } diff --git a/src/xrpld/peerfinder/PeerfinderManager.h b/src/xrpld/peerfinder/PeerfinderManager.h index f399251c38..b7ea738a81 100644 --- a/src/xrpld/peerfinder/PeerfinderManager.h +++ b/src/xrpld/peerfinder/PeerfinderManager.h @@ -109,6 +109,9 @@ struct Config std::uint16_t port, bool validationPublicKey, int ipLimit); + + friend bool + operator==(Config const& lhs, Config const& rhs); }; //------------------------------------------------------------------------------ @@ -136,7 +139,13 @@ using Endpoints = std::vector; //------------------------------------------------------------------------------ /** Possible results from activating a slot. */ -enum class Result { duplicate, full, success }; +enum class Result { + inboundDisabled, + duplicatePeer, + ipLimitExceeded, + full, + success +}; /** * @brief Converts a `Result` enum value to its string representation. @@ -157,12 +166,16 @@ to_string(Result result) noexcept { switch (result) { - case Result::success: - return "success"; - case Result::duplicate: - return "duplicate connection"; + case Result::inboundDisabled: + return "inbound disabled"; + case Result::duplicatePeer: + return "peer already connected"; + case Result::ipLimitExceeded: + return "ip limit exceeded"; case Result::full: return "slots full"; + case Result::success: + return "success"; } return "unknown"; @@ -234,7 +247,7 @@ public: If nullptr is returned, then the slot could not be assigned. Usually this is because of a detected self-connection. */ - virtual std::shared_ptr + virtual std::pair, Result> new_inbound_slot( beast::IP::Endpoint const& local_endpoint, beast::IP::Endpoint const& remote_endpoint) = 0; @@ -243,7 +256,7 @@ public: If nullptr is returned, then the slot could not be assigned. Usually this is because of a duplicate connection. */ - virtual std::shared_ptr + virtual std::pair, Result> new_outbound_slot(beast::IP::Endpoint const& remote_endpoint) = 0; /** Called when mtENDPOINTS is received. */ diff --git a/src/xrpld/peerfinder/detail/Counts.h b/src/xrpld/peerfinder/detail/Counts.h index c91b27b026..821431c5bb 100644 --- a/src/xrpld/peerfinder/detail/Counts.h +++ b/src/xrpld/peerfinder/detail/Counts.h @@ -163,7 +163,7 @@ public: /** Returns the total number of inbound slots. */ int - inboundSlots() const + in_max() const { return m_in_max; } diff --git a/src/xrpld/peerfinder/detail/Logic.h b/src/xrpld/peerfinder/detail/Logic.h index e23bbc29e1..4b92a1d143 100644 --- a/src/xrpld/peerfinder/detail/Logic.h +++ b/src/xrpld/peerfinder/detail/Logic.h @@ -172,9 +172,7 @@ public: void addFixedPeer(std::string const& name, beast::IP::Endpoint const& ep) { - std::vector v; - v.push_back(ep); - addFixedPeer(name, v); + addFixedPeer(name, std::vector{ep}); } void @@ -261,7 +259,7 @@ public: //-------------------------------------------------------------------------- - SlotImp::ptr + std::pair new_inbound_slot( beast::IP::Endpoint const& local_endpoint, beast::IP::Endpoint const& remote_endpoint) @@ -277,12 +275,12 @@ public: { auto const count = connectedAddresses_.count(remote_endpoint.address()); - if (count > config_.ipLimit) + if (count + 1 > config_.ipLimit) { JLOG(m_journal.debug()) << beast::leftw(18) << "Logic dropping inbound " << remote_endpoint << " because of ip limits."; - return SlotImp::ptr(); + return {SlotImp::ptr(), Result::ipLimitExceeded}; } } @@ -292,7 +290,7 @@ public: JLOG(m_journal.debug()) << beast::leftw(18) << "Logic dropping " << remote_endpoint << " as duplicate incoming"; - return SlotImp::ptr(); + return {SlotImp::ptr(), Result::duplicatePeer}; } // Create the slot @@ -314,11 +312,11 @@ public: // Update counts counts_.add(*slot); - return result.first->second; + return {result.first->second, Result::success}; } // Can't check for self-connect because we don't know the local endpoint - SlotImp::ptr + std::pair new_outbound_slot(beast::IP::Endpoint const& remote_endpoint) { JLOG(m_journal.debug()) @@ -332,7 +330,7 @@ public: JLOG(m_journal.debug()) << beast::leftw(18) << "Logic dropping " << remote_endpoint << " as duplicate connect"; - return SlotImp::ptr(); + return {SlotImp::ptr(), Result::duplicatePeer}; } // Create the slot @@ -353,7 +351,7 @@ public: // Update counts counts_.add(*slot); - return result.first->second; + return {result.first->second, Result::success}; } bool @@ -417,7 +415,7 @@ public: // Check for duplicate connection by key if (keys_.find(key) != keys_.end()) - return Result::duplicate; + return Result::duplicatePeer; // If the peer belongs to a cluster or is reserved, // update the slot to reflect that. @@ -430,6 +428,8 @@ public: { if (!slot->inbound()) bootcache_.on_success(slot->remote_endpoint()); + if (slot->inbound() && counts_.in_max() == 0) + return Result::inboundDisabled; return Result::full; } @@ -651,7 +651,7 @@ public: // 2. We have slots // 3. We haven't failed the firewalled test // - if (config_.wantIncoming && counts_.inboundSlots() > 0) + if (config_.wantIncoming && counts_.in_max() > 0) { Endpoint ep; ep.hops = 0; diff --git a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp index 3075224189..30eb778770 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp @@ -34,6 +34,17 @@ Config::Config() { } +bool +operator==(Config const& lhs, Config const& rhs) +{ + return lhs.autoConnect == rhs.autoConnect && + lhs.peerPrivate == rhs.peerPrivate && + lhs.wantIncoming == rhs.wantIncoming && lhs.inPeers == rhs.inPeers && + lhs.maxPeers == rhs.maxPeers && lhs.outPeers == rhs.outPeers && + lhs.features == lhs.features && lhs.ipLimit == rhs.ipLimit && + lhs.listeningPort == rhs.listeningPort; +} + std::size_t Config::calcOutPeers() const { diff --git a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp index 205df67fa6..462820cca2 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderManager.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderManager.cpp @@ -125,7 +125,7 @@ public: //-------------------------------------------------------------------------- - std::shared_ptr + std::pair, Result> new_inbound_slot( beast::IP::Endpoint const& local_endpoint, beast::IP::Endpoint const& remote_endpoint) override @@ -133,7 +133,7 @@ public: return m_logic.new_inbound_slot(local_endpoint, remote_endpoint); } - std::shared_ptr + std::pair, Result> new_outbound_slot(beast::IP::Endpoint const& remote_endpoint) override { return m_logic.new_outbound_slot(remote_endpoint); From e0b9812fc590fb87796b8dae7253aeca6f1737e7 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Fri, 29 Aug 2025 15:52:09 -0400 Subject: [PATCH 31/66] Refactor `ledger_entry` RPC source code and tests (#5237) This is a major refactor of LedgerEntry.cpp. It adds a number of helper functions to make the code easier to maintain. It also splits up the ledger and ledger_entry tests into different files, and cleans up the ledger_entry tests to make them easier to write and maintain. This refactor also caught a few bugs in some of the other RPC processing, so those are fixed along the way. --- include/xrpl/protocol/ErrorCodes.h | 7 +- include/xrpl/protocol/jss.h | 4 + src/libxrpl/json/json_value.cpp | 5 +- src/libxrpl/protocol/ErrorCodes.cpp | 5 +- src/libxrpl/protocol/STXChainBridge.cpp | 19 +- src/test/app/Vault_test.cpp | 12 - src/test/jtx/impl/xchain_bridge.cpp | 16 +- src/test/rpc/LedgerEntry_test.cpp | 1869 ++++++++----------- src/xrpld/rpc/detail/RPCHelpers.cpp | 17 +- src/xrpld/rpc/handlers/LedgerEntry.cpp | 1171 +++++------- src/xrpld/rpc/handlers/LedgerEntryHelpers.h | 299 +++ 11 files changed, 1577 insertions(+), 1847 deletions(-) create mode 100644 src/xrpld/rpc/handlers/LedgerEntryHelpers.h diff --git a/include/xrpl/protocol/ErrorCodes.h b/include/xrpl/protocol/ErrorCodes.h index f06b927566..5da3ad0b33 100644 --- a/include/xrpl/protocol/ErrorCodes.h +++ b/include/xrpl/protocol/ErrorCodes.h @@ -157,7 +157,12 @@ enum error_code_i { // Pathfinding rpcDOMAIN_MALFORMED = 97, - rpcLAST = rpcDOMAIN_MALFORMED // rpcLAST should always equal the last code. + // ledger_entry + rpcENTRY_NOT_FOUND = 98, + rpcUNEXPECTED_LEDGER_TYPE = 99, + + rpcLAST = + rpcUNEXPECTED_LEDGER_TYPE // rpcLAST should always equal the last code. }; /** Codes returned in the `warnings` array of certain RPC commands. diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 67a045fa58..68d2497aca 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -68,9 +68,13 @@ JSS(Flags); // in/out: TransactionSign; field. JSS(Holder); // field. JSS(Invalid); // JSS(Issuer); // in: Credential transactions +JSS(IssuingChainDoor); // field. +JSS(IssuingChainIssue); // field. JSS(LastLedgerSequence); // in: TransactionSign; field JSS(LastUpdateTime); // field. JSS(LimitAmount); // field. +JSS(LockingChainDoor); // field. +JSS(LockingChainIssue); // field. JSS(NetworkID); // field. JSS(LPTokenOut); // in: AMM Liquidity Provider deposit tokens JSS(LPTokenIn); // in: AMM Liquidity Provider withdraw tokens diff --git a/src/libxrpl/json/json_value.cpp b/src/libxrpl/json/json_value.cpp index a1e0a04875..1df8f6cf31 100644 --- a/src/libxrpl/json/json_value.cpp +++ b/src/libxrpl/json/json_value.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -685,7 +686,9 @@ Value::isConvertibleTo(ValueType other) const (other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt) || (other == uintValue && value_.real_ >= 0 && - value_.real_ <= maxUInt) || + value_.real_ <= maxUInt && + std::fabs(round(value_.real_) - value_.real_) < + std::numeric_limits::epsilon()) || other == realValue || other == stringValue || other == booleanValue; diff --git a/src/libxrpl/protocol/ErrorCodes.cpp b/src/libxrpl/protocol/ErrorCodes.cpp index 3109f51d05..ec295343ce 100644 --- a/src/libxrpl/protocol/ErrorCodes.cpp +++ b/src/libxrpl/protocol/ErrorCodes.cpp @@ -117,7 +117,10 @@ constexpr static ErrorInfo unorderedErrorInfos[]{ {rpcORACLE_MALFORMED, "oracleMalformed", "Oracle request is malformed.", 400}, {rpcBAD_CREDENTIALS, "badCredentials", "Credentials do not exist, are not accepted, or have expired.", 400}, {rpcTX_SIGNED, "transactionSigned", "Transaction should not be signed.", 400}, - {rpcDOMAIN_MALFORMED, "domainMalformed", "Domain is malformed.", 400}}; + {rpcDOMAIN_MALFORMED, "domainMalformed", "Domain is malformed.", 400}, + {rpcENTRY_NOT_FOUND, "entryNotFound", "Entry not found.", 400}, + {rpcUNEXPECTED_LEDGER_TYPE, "unexpectedLedgerType", "Unexpected ledger type.", 400}, +}; // clang-format on // Sort and validate unorderedErrorInfos at compile time. Should be diff --git a/src/libxrpl/protocol/STXChainBridge.cpp b/src/libxrpl/protocol/STXChainBridge.cpp index fb192d82d6..e835735f08 100644 --- a/src/libxrpl/protocol/STXChainBridge.cpp +++ b/src/libxrpl/protocol/STXChainBridge.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -98,12 +99,10 @@ STXChainBridge::STXChainBridge(SField const& name, Json::Value const& v) }; checkExtra(v); - Json::Value const& lockingChainDoorStr = - v[sfLockingChainDoor.getJsonName()]; - Json::Value const& lockingChainIssue = v[sfLockingChainIssue.getJsonName()]; - Json::Value const& issuingChainDoorStr = - v[sfIssuingChainDoor.getJsonName()]; - Json::Value const& issuingChainIssue = v[sfIssuingChainIssue.getJsonName()]; + Json::Value const& lockingChainDoorStr = v[jss::LockingChainDoor]; + Json::Value const& lockingChainIssue = v[jss::LockingChainIssue]; + Json::Value const& issuingChainDoorStr = v[jss::IssuingChainDoor]; + Json::Value const& issuingChainIssue = v[jss::IssuingChainIssue]; if (!lockingChainDoorStr.isString()) { @@ -161,10 +160,10 @@ Json::Value STXChainBridge::getJson(JsonOptions jo) const { Json::Value v; - v[sfLockingChainDoor.getJsonName()] = lockingChainDoor_.getJson(jo); - v[sfLockingChainIssue.getJsonName()] = lockingChainIssue_.getJson(jo); - v[sfIssuingChainDoor.getJsonName()] = issuingChainDoor_.getJson(jo); - v[sfIssuingChainIssue.getJsonName()] = issuingChainIssue_.getJson(jo); + v[jss::LockingChainDoor] = lockingChainDoor_.getJson(jo); + v[jss::LockingChainIssue] = lockingChainIssue_.getJson(jo); + v[jss::IssuingChainDoor] = issuingChainDoor_.getJson(jo); + v[jss::IssuingChainIssue] = issuingChainIssue_.getJson(jo); return v; } diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 7ea38db2b1..7add8b3eda 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -3028,18 +3028,6 @@ class Vault_test : public beast::unit_test::suite "malformedRequest"); } - { - testcase("RPC ledger_entry zero seq"); - Json::Value jvParams; - jvParams[jss::ledger_index] = jss::validated; - jvParams[jss::vault][jss::owner] = issuer.human(); - jvParams[jss::vault][jss::seq] = 0; - auto jvVault = env.rpc("json", "ledger_entry", to_string(jvParams)); - BEAST_EXPECT( - jvVault[jss::result][jss::error].asString() == - "malformedRequest"); - } - { testcase("RPC ledger_entry negative seq"); Json::Value jvParams; diff --git a/src/test/jtx/impl/xchain_bridge.cpp b/src/test/jtx/impl/xchain_bridge.cpp index 6f167d7508..9e8fa4795f 100644 --- a/src/test/jtx/impl/xchain_bridge.cpp +++ b/src/test/jtx/impl/xchain_bridge.cpp @@ -44,10 +44,10 @@ bridge( Issue const& issuingChainIssue) { Json::Value jv; - jv[sfLockingChainDoor.getJsonName()] = lockingChainDoor.human(); - jv[sfLockingChainIssue.getJsonName()] = to_json(lockingChainIssue); - jv[sfIssuingChainDoor.getJsonName()] = issuingChainDoor.human(); - jv[sfIssuingChainIssue.getJsonName()] = to_json(issuingChainIssue); + jv[jss::LockingChainDoor] = lockingChainDoor.human(); + jv[jss::LockingChainIssue] = to_json(lockingChainIssue); + jv[jss::IssuingChainDoor] = issuingChainDoor.human(); + jv[jss::IssuingChainIssue] = to_json(issuingChainIssue); return jv; } @@ -60,10 +60,10 @@ bridge_rpc( Issue const& issuingChainIssue) { Json::Value jv; - jv[sfLockingChainDoor.getJsonName()] = lockingChainDoor.human(); - jv[sfLockingChainIssue.getJsonName()] = to_json(lockingChainIssue); - jv[sfIssuingChainDoor.getJsonName()] = issuingChainDoor.human(); - jv[sfIssuingChainIssue.getJsonName()] = to_json(issuingChainIssue); + jv[jss::LockingChainDoor] = lockingChainDoor.human(); + jv[jss::LockingChainIssue] = to_json(lockingChainIssue); + jv[jss::IssuingChainDoor] = issuingChainDoor.human(); + jv[jss::IssuingChainIssue] = to_json(issuingChainIssue); return jv; } diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index 89cb7b72eb..a88f6ab612 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -31,40 +31,435 @@ #include #include +#if (defined(__clang_major__) && __clang_major__ < 15) +#include +using source_location = std::experimental::source_location; +#else +#include +using std::source_location; +#endif namespace ripple { namespace test { +enum class FieldType { + AccountField, + BlobField, + ArrayField, + CurrencyField, + HashField, + HashOrObjectField, + ObjectField, + StringField, + TwoAccountArrayField, + UInt32Field, + UInt64Field, +}; + +std::vector> mappings{ + {jss::account, FieldType::AccountField}, + {jss::accounts, FieldType::TwoAccountArrayField}, + {jss::authorize, FieldType::AccountField}, + {jss::authorized, FieldType::AccountField}, + {jss::credential_type, FieldType::BlobField}, + {jss::currency, FieldType::CurrencyField}, + {jss::issuer, FieldType::AccountField}, + {jss::oracle_document_id, FieldType::UInt32Field}, + {jss::owner, FieldType::AccountField}, + {jss::seq, FieldType::UInt32Field}, + {jss::subject, FieldType::AccountField}, + {jss::ticket_seq, FieldType::UInt32Field}, +}; + +FieldType +getFieldType(Json::StaticString fieldName) +{ + auto it = std::ranges::find_if(mappings, [&fieldName](auto const& pair) { + return pair.first == fieldName; + }); + if (it != mappings.end()) + { + return it->second; + } + else + { + Throw( + "`mappings` is missing field " + std::string(fieldName.c_str())); + } +} + +std::string +getTypeName(FieldType typeID) +{ + switch (typeID) + { + case FieldType::UInt32Field: + return "number"; + case FieldType::UInt64Field: + return "number"; + case FieldType::HashField: + return "hex string"; + case FieldType::AccountField: + return "AccountID"; + case FieldType::BlobField: + return "hex string"; + case FieldType::CurrencyField: + return "Currency"; + case FieldType::ArrayField: + return "array"; + case FieldType::HashOrObjectField: + return "hex string or object"; + case FieldType::TwoAccountArrayField: + return "length-2 array of Accounts"; + default: + Throw( + "unknown type " + std::to_string(static_cast(typeID))); + } +} + class LedgerEntry_test : public beast::unit_test::suite { void checkErrorValue( Json::Value const& jv, std::string const& err, - std::string const& msg) + std::string const& msg, + source_location const location = source_location::current()) { if (BEAST_EXPECT(jv.isMember(jss::status))) - BEAST_EXPECT(jv[jss::status] == "error"); + BEAST_EXPECTS( + jv[jss::status] == "error", std::to_string(location.line())); if (BEAST_EXPECT(jv.isMember(jss::error))) - BEAST_EXPECT(jv[jss::error] == err); + BEAST_EXPECTS( + jv[jss::error] == err, + "Expected error " + err + ", received " + + jv[jss::error].asString() + ", at line " + + std::to_string(location.line()) + ", " + + jv.toStyledString()); if (msg.empty()) { - BEAST_EXPECT( + BEAST_EXPECTS( jv[jss::error_message] == Json::nullValue || - jv[jss::error_message] == ""); + jv[jss::error_message] == "", + "Expected no error message, received \"" + + jv[jss::error_message].asString() + "\", at line " + + std::to_string(location.line()) + ", " + + jv.toStyledString()); } else if (BEAST_EXPECT(jv.isMember(jss::error_message))) - BEAST_EXPECT(jv[jss::error_message] == msg); + BEAST_EXPECTS( + jv[jss::error_message] == msg, + "Expected error message \"" + msg + "\", received \"" + + jv[jss::error_message].asString() + "\", at line " + + std::to_string(location.line()) + ", " + + jv.toStyledString()); } - // Corrupt a valid address by replacing the 10th character with '!'. - // '!' is not part of the ripple alphabet. - std::string - makeBadAddress(std::string good) + std::vector + getBadValues(FieldType fieldType) { - std::string ret = std::move(good); - ret.replace(10, 1, 1, '!'); - return ret; + static Json::Value const injectObject = []() { + Json::Value obj(Json::objectValue); + obj[jss::account] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU"; + obj[jss::ledger_index] = "validated"; + return obj; + }(); + static Json::Value const injectArray = []() { + Json::Value arr(Json::arrayValue); + arr[0u] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU"; + arr[1u] = "validated"; + return arr; + }(); + static std::array const allBadValues = { + "", // 0 + true, // 1 + 1, // 2 + "1", // 3 + -1, // 4 + 1.1, // 5 + "-1", // 6 + "abcdef", // 7 + "ABCDEF", // 8 + "12KK", // 9 + "0123456789ABCDEFGH", // 10 + "rJxKV9e9p6wiPw!!!!xrJ4X1n98LosPL1sgcJW", // 11 + "rPSTrR5yEr11uMkfsz1kHCp9jK4aoa3Avv", // 12 + "n9K2isxwTxcSHJKxMkJznDoWXAUs7NNy49H9Fknz1pC7oHAH3kH9", // 13 + "USD", // 14 + "USDollars", // 15 + "5233D68B4D44388F98559DE42903767803EFA7C1F8D01413FC16EE6B01403D" + "6D", // 16 + Json::arrayValue, // 17 + Json::objectValue, // 18 + injectObject, // 19 + injectArray // 20 + }; + + auto remove = + [&](std::vector indices) -> std::vector { + std::unordered_set indexSet( + indices.begin(), indices.end()); + std::vector values; + values.reserve(allBadValues.size() - indexSet.size()); + for (std::size_t i = 0; i < allBadValues.size(); ++i) + { + if (indexSet.find(i) == indexSet.end()) + { + values.push_back(allBadValues[i]); + } + } + return values; + }; + + static auto const& badUInt32Values = remove({2, 3}); + static auto const& badUInt64Values = remove({2, 3}); + static auto const& badHashValues = remove({2, 3, 7, 8, 16}); + static auto const& badAccountValues = remove({12}); + static auto const& badBlobValues = remove({3, 7, 8, 16}); + static auto const& badCurrencyValues = remove({14}); + static auto const& badArrayValues = remove({17, 20}); + static auto const& badIndexValues = remove({12, 16, 18, 19}); + + switch (fieldType) + { + case FieldType::UInt32Field: + return badUInt32Values; + case FieldType::UInt64Field: + return badUInt64Values; + case FieldType::HashField: + return badHashValues; + case FieldType::AccountField: + return badAccountValues; + case FieldType::BlobField: + return badBlobValues; + case FieldType::CurrencyField: + return badCurrencyValues; + case FieldType::ArrayField: + case FieldType::TwoAccountArrayField: + return badArrayValues; + case FieldType::HashOrObjectField: + return badIndexValues; + default: + Throw( + "unknown type " + + std::to_string(static_cast(fieldType))); + } + } + + Json::Value + getCorrectValue(Json::StaticString fieldName) + { + static Json::Value const twoAccountArray = []() { + Json::Value arr(Json::arrayValue); + arr[0u] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU"; + arr[1u] = "r4MrUGTdB57duTnRs6KbsRGQXgkseGb1b5"; + return arr; + }(); + + auto const typeID = getFieldType(fieldName); + switch (typeID) + { + case FieldType::UInt32Field: + return 1; + case FieldType::UInt64Field: + return 1; + case FieldType::HashField: + return "5233D68B4D44388F98559DE42903767803EFA7C1F8D01413FC16EE6" + "B01403D6D"; + case FieldType::AccountField: + return "r4MrUGTdB57duTnRs6KbsRGQXgkseGb1b5"; + case FieldType::BlobField: + return "ABCDEF"; + case FieldType::CurrencyField: + return "USD"; + case FieldType::ArrayField: + return Json::arrayValue; + case FieldType::HashOrObjectField: + return "5233D68B4D44388F98559DE42903767803EFA7C1F8D01413FC16EE6" + "B01403D6D"; + case FieldType::TwoAccountArrayField: + return twoAccountArray; + default: + Throw( + "unknown type " + + std::to_string(static_cast(typeID))); + } + } + + void + testMalformedField( + test::jtx::Env& env, + Json::Value correctRequest, + Json::StaticString const fieldName, + FieldType const typeID, + std::string const& expectedError, + bool required = true, + source_location const location = source_location::current()) + { + forAllApiVersions([&, this](unsigned apiVersion) { + if (required) + { + correctRequest.removeMember(fieldName); + Json::Value const jrr = env.rpc( + apiVersion, + "json", + "ledger_entry", + to_string(correctRequest))[jss::result]; + if (apiVersion < 2u) + checkErrorValue(jrr, "unknownOption", "", location); + else + checkErrorValue( + jrr, + "invalidParams", + "No ledger_entry params provided.", + location); + } + auto tryField = [&](Json::Value fieldValue) -> void { + correctRequest[fieldName] = fieldValue; + Json::Value const jrr = env.rpc( + apiVersion, + "json", + "ledger_entry", + to_string(correctRequest))[jss::result]; + auto const expectedErrMsg = + RPC::expected_field_message(fieldName, getTypeName(typeID)); + checkErrorValue(jrr, expectedError, expectedErrMsg, location); + }; + + auto const& badValues = getBadValues(typeID); + for (auto const& value : badValues) + { + tryField(value); + } + if (required) + { + tryField(Json::nullValue); + } + }); + } + + void + testMalformedSubfield( + test::jtx::Env& env, + Json::Value correctRequest, + Json::StaticString parentFieldName, + Json::StaticString fieldName, + FieldType typeID, + std::string const& expectedError, + bool required = true, + source_location const location = source_location::current()) + { + forAllApiVersions([&, this](unsigned apiVersion) { + if (required) + { + correctRequest[parentFieldName].removeMember(fieldName); + Json::Value const jrr = env.rpc( + apiVersion, + "json", + "ledger_entry", + to_string(correctRequest))[jss::result]; + checkErrorValue( + jrr, + "malformedRequest", + RPC::missing_field_message(fieldName.c_str()), + location); + + correctRequest[parentFieldName][fieldName] = Json::nullValue; + Json::Value const jrr2 = env.rpc( + apiVersion, + "json", + "ledger_entry", + to_string(correctRequest))[jss::result]; + checkErrorValue( + jrr2, + "malformedRequest", + RPC::missing_field_message(fieldName.c_str()), + location); + } + auto tryField = [&](Json::Value fieldValue) -> void { + correctRequest[parentFieldName][fieldName] = fieldValue; + + Json::Value const jrr = env.rpc( + apiVersion, + "json", + "ledger_entry", + to_string(correctRequest))[jss::result]; + checkErrorValue( + jrr, + expectedError, + RPC::expected_field_message(fieldName, getTypeName(typeID)), + location); + }; + + auto const& badValues = getBadValues(typeID); + for (auto const& value : badValues) + { + tryField(value); + } + }); + } + + // No subfields + void + runLedgerEntryTest( + test::jtx::Env& env, + Json::StaticString const& parentField, + source_location const location = source_location::current()) + { + testMalformedField( + env, + Json::Value{}, + parentField, + FieldType::HashField, + "malformedRequest", + true, + location); + } + + struct Subfield + { + Json::StaticString fieldName; + std::string malformedErrorMsg; + bool required = true; + }; + + void + runLedgerEntryTest( + test::jtx::Env& env, + Json::StaticString const& parentField, + std::vector const& subfields, + source_location const location = source_location::current()) + { + testMalformedField( + env, + Json::Value{}, + parentField, + FieldType::HashOrObjectField, + "malformedRequest", + true, + location); + + Json::Value correctOutput; + correctOutput[parentField] = Json::objectValue; + for (auto const& subfield : subfields) + { + correctOutput[parentField][subfield.fieldName] = + getCorrectValue(subfield.fieldName); + } + + for (auto const& subfield : subfields) + { + auto const fieldType = getFieldType(subfield.fieldName); + testMalformedSubfield( + env, + correctOutput, + parentField, + subfield.fieldName, + fieldType, + subfield.malformedErrorMsg, + subfield.required, + location); + } } void @@ -76,7 +471,6 @@ class LedgerEntry_test : public beast::unit_test::suite Account const alice{"alice"}; env.fund(XRP(10000), alice); env.close(); - { // Missing ledger_entry ledger_hash Json::Value jvParams; @@ -88,6 +482,33 @@ class LedgerEntry_test : public beast::unit_test::suite "json", "ledger_entry", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound"); } + { + // Missing ledger_entry ledger_hash + Json::Value jvParams; + jvParams[jss::account_root] = alice.human(); + auto const typeId = FieldType::HashField; + + forAllApiVersions([&, this](unsigned apiVersion) { + auto tryField = [&](Json::Value fieldValue) -> void { + jvParams[jss::ledger_hash] = fieldValue; + Json::Value const jrr = env.rpc( + apiVersion, + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; + auto const expectedErrMsg = fieldValue.isString() + ? "ledgerHashMalformed" + : "ledgerHashNotString"; + checkErrorValue(jrr, "invalidParams", expectedErrMsg); + }; + + auto const& badValues = getBadValues(typeId); + for (auto const& value : badValues) + { + tryField(value); + } + }); + } { // ask for an zero index @@ -95,17 +516,38 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_index] = "validated"; jvParams[jss::index] = "00000000000000000000000000000000000000000000000000000000000000" - "0000"; + "00"; auto const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); } + + forAllApiVersions([&, this](unsigned apiVersion) { + // "features" is not an option supported by ledger_entry. + { + Json::Value jvParams = Json::objectValue; + jvParams[jss::features] = + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + "AAAAAAAAAA"; + jvParams[jss::api_version] = apiVersion; + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + + if (apiVersion < 2u) + checkErrorValue(jrr, "unknownOption", ""); + else + checkErrorValue( + jrr, + "invalidParams", + "No ledger_entry params provided."); + } + }); } void testLedgerEntryAccountRoot() { - testcase("ledger_entry Request AccountRoot"); + testcase("AccountRoot"); using namespace test::jtx; auto cfg = envconfig(); @@ -176,13 +618,26 @@ class LedgerEntry_test : public beast::unit_test::suite BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000"); } { - // Request using a corrupted AccountID. + // Check alias Json::Value jvParams; - jvParams[jss::account_root] = makeBadAddress(alice.human()); + jvParams[jss::account] = alice.human(); jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); + BEAST_EXPECT(jrr.isMember(jss::node)); + BEAST_EXPECT(jrr[jss::node][jss::Account] == alice.human()); + BEAST_EXPECT(jrr[jss::node][sfBalance.jsonName] == "10000000000"); + accountRootIndex = jrr[jss::index].asString(); + } + { + // Check malformed cases + Json::Value jvParams; + testMalformedField( + env, + jvParams, + jss::account_root, + FieldType::AccountField, + "malformedAddress"); } { // Request an account that is not in the ledger. @@ -191,14 +646,14 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "entryNotFound", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); } } void testLedgerEntryCheck() { - testcase("ledger_entry Request Check"); + testcase("Check"); using namespace test::jtx; Env env{*this}; Account const alice{"alice"}; @@ -238,14 +693,19 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "unexpectedLedgerType", ""); + checkErrorValue( + jrr, "unexpectedLedgerType", "Unexpected ledger type."); + } + { + // Check malformed cases + runLedgerEntryTest(env, jss::check); } } void testLedgerEntryCredentials() { - testcase("ledger_entry credentials"); + testcase("Credentials"); using namespace test::jtx; @@ -287,163 +747,33 @@ class LedgerEntry_test : public beast::unit_test::suite jss::Credential); } - { - // Fail, index not a hash - auto const jv = credentials::ledgerEntry(env, ""); - checkErrorValue(jv[jss::result], "malformedRequest", ""); - } - { // Fail, credential doesn't exist auto const jv = credentials::ledgerEntry( env, "48004829F915654A81B11C4AB8218D96FED67F209B58328A72314FB6EA288B" "E4"); - checkErrorValue(jv[jss::result], "entryNotFound", ""); + checkErrorValue( + jv[jss::result], "entryNotFound", "Entry not found."); } { - // Fail, invalid subject - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = 42; - jv[jss::credential][jss::issuer] = issuer.human(); - jv[jss::credential][jss::credential_type] = - strHex(std::string_view(credType)); - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, invalid issuer - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = alice.human(); - jv[jss::credential][jss::issuer] = 42; - jv[jss::credential][jss::credential_type] = - strHex(std::string_view(credType)); - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, invalid credentials type - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = alice.human(); - jv[jss::credential][jss::issuer] = issuer.human(); - jv[jss::credential][jss::credential_type] = 42; - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, empty subject - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = ""; - jv[jss::credential][jss::issuer] = issuer.human(); - jv[jss::credential][jss::credential_type] = - strHex(std::string_view(credType)); - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, empty issuer - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = alice.human(); - jv[jss::credential][jss::issuer] = ""; - jv[jss::credential][jss::credential_type] = - strHex(std::string_view(credType)); - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, empty credentials type - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = alice.human(); - jv[jss::credential][jss::issuer] = issuer.human(); - jv[jss::credential][jss::credential_type] = ""; - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, no subject - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::issuer] = issuer.human(); - jv[jss::credential][jss::credential_type] = - strHex(std::string_view(credType)); - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, no issuer - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = alice.human(); - jv[jss::credential][jss::credential_type] = - strHex(std::string_view(credType)); - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, no credentials type - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = alice.human(); - jv[jss::credential][jss::issuer] = issuer.human(); - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, not AccountID subject - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = "wehsdbvasbdfvj"; - jv[jss::credential][jss::issuer] = issuer.human(); - jv[jss::credential][jss::credential_type] = - strHex(std::string_view(credType)); - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, not AccountID issuer - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = alice.human(); - jv[jss::credential][jss::issuer] = "c4p93ugndfbsiu"; - jv[jss::credential][jss::credential_type] = - strHex(std::string_view(credType)); - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, credentials type isn't hex encoded - Json::Value jv; - jv[jss::ledger_index] = jss::validated; - jv[jss::credential][jss::subject] = alice.human(); - jv[jss::credential][jss::issuer] = issuer.human(); - jv[jss::credential][jss::credential_type] = "12KK"; - auto const jrr = env.rpc("json", "ledger_entry", to_string(jv)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); + // Check all malformed cases + runLedgerEntryTest( + env, + jss::credential, + { + {jss::subject, "malformedRequest"}, + {jss::issuer, "malformedRequest"}, + {jss::credential_type, "malformedRequest"}, + }); } } void testLedgerEntryDelegate() { - testcase("ledger_entry Delegate"); + testcase("Delegate"); using namespace test::jtx; @@ -482,78 +812,23 @@ class LedgerEntry_test : public beast::unit_test::suite BEAST_EXPECT(jrr[jss::node][sfAccount.jsonName] == alice.human()); BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == bob.human()); } + { - // Malformed request: delegate neither object nor string. - Json::Value jvParams; - jvParams[jss::delegate] = 5; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed request: delegate not hex string. - Json::Value jvParams; - jvParams[jss::delegate] = "0123456789ABCDEFG"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed request: account not a string - Json::Value jvParams; - jvParams[jss::delegate][jss::account] = 5; - jvParams[jss::delegate][jss::authorize] = bob.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); - } - { - // Malformed request: authorize not a string - Json::Value jvParams; - jvParams[jss::delegate][jss::account] = alice.human(); - jvParams[jss::delegate][jss::authorize] = 5; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); - } - { - // this lambda function is used test malformed account and authroize - auto testMalformedAccount = - [&](std::optional const& account, - std::optional const& authorize, - std::string const& error) { - Json::Value jvParams; - jvParams[jss::ledger_hash] = ledgerHash; - if (account) - jvParams[jss::delegate][jss::account] = *account; - if (authorize) - jvParams[jss::delegate][jss::authorize] = *authorize; - auto const jrr = env.rpc( - "json", - "ledger_entry", - to_string(jvParams))[jss::result]; - checkErrorValue(jrr, error, ""); - }; - // missing account - testMalformedAccount(std::nullopt, bob.human(), "malformedRequest"); - // missing authorize - testMalformedAccount( - alice.human(), std::nullopt, "malformedRequest"); - // malformed account - testMalformedAccount("-", bob.human(), "malformedAddress"); - // malformed authorize - testMalformedAccount(alice.human(), "-", "malformedAddress"); + // Check all malformed cases + runLedgerEntryTest( + env, + jss::delegate, + { + {jss::account, "malformedAddress"}, + {jss::authorize, "malformedAddress"}, + }); } } void testLedgerEntryDepositPreauth() { - testcase("ledger_entry Deposit Preauth"); + testcase("Deposit Preauth"); using namespace test::jtx; @@ -600,91 +875,21 @@ class LedgerEntry_test : public beast::unit_test::suite BEAST_EXPECT(jrr[jss::node][sfAuthorize.jsonName] == becky.human()); } { - // Malformed request: deposit_preauth neither object nor string. - Json::Value jvParams; - jvParams[jss::deposit_preauth] = -5; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed request: deposit_preauth not hex string. - Json::Value jvParams; - jvParams[jss::deposit_preauth] = "0123456789ABCDEFG"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed request: missing [jss::deposit_preauth][jss::owner] - Json::Value jvParams; - jvParams[jss::deposit_preauth][jss::authorized] = becky.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed request: [jss::deposit_preauth][jss::owner] not string. - Json::Value jvParams; - jvParams[jss::deposit_preauth][jss::owner] = 7; - jvParams[jss::deposit_preauth][jss::authorized] = becky.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed: missing [jss::deposit_preauth][jss::authorized] - Json::Value jvParams; - jvParams[jss::deposit_preauth][jss::owner] = alice.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed: [jss::deposit_preauth][jss::authorized] not string. - Json::Value jvParams; - jvParams[jss::deposit_preauth][jss::owner] = alice.human(); - jvParams[jss::deposit_preauth][jss::authorized] = 47; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed: [jss::deposit_preauth][jss::owner] is malformed. - Json::Value jvParams; - jvParams[jss::deposit_preauth][jss::owner] = - "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas"; - - jvParams[jss::deposit_preauth][jss::authorized] = becky.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedOwner", ""); - } - { - // Malformed: [jss::deposit_preauth][jss::authorized] is malformed. - Json::Value jvParams; - jvParams[jss::deposit_preauth][jss::owner] = alice.human(); - jvParams[jss::deposit_preauth][jss::authorized] = - "rP6P9ypfAmc!pw8SZHNwM4nvZHFXDraQas"; - - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAuthorized", ""); + // test all missing/malformed field cases + runLedgerEntryTest( + env, + jss::deposit_preauth, + { + {jss::owner, "malformedOwner"}, + {jss::authorized, "malformedAuthorized", false}, + }); } } void testLedgerEntryDepositPreauthCred() { - testcase("ledger_entry Deposit Preauth with credentials"); + testcase("Deposit Preauth with credentials"); using namespace test::jtx; @@ -739,19 +944,30 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_index] = jss::validated; jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); + auto tryField = [&](Json::Value fieldValue) -> void { + Json::Value arr = Json::arrayValue; + Json::Value jo; + jo[jss::issuer] = fieldValue; + jo[jss::credential_type] = strHex(std::string_view(credType)); + arr.append(jo); + jvParams[jss::deposit_preauth][jss::authorized_credentials] = + arr; - Json::Value jo; - jo[jss::issuer] = to_string(xrpAccount()); - jo[jss::credential_type] = strHex(std::string_view(credType)); - arr.append(std::move(jo)); - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + auto const expectedErrMsg = fieldValue.isNull() + ? RPC::missing_field_message(jss::issuer.c_str()) + : RPC::expected_field_message(jss::issuer, "AccountID"); + checkErrorValue( + jrr, "malformedAuthorizedCredentials", expectedErrMsg); + }; + + auto const& badValues = getBadValues(FieldType::AccountField); + for (auto const& value : badValues) + { + tryField(value); + } + tryField(Json::nullValue); } { @@ -773,7 +989,10 @@ class LedgerEntry_test : public beast::unit_test::suite auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams)); checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); + jrr[jss::result], + "malformedAuthorizedCredentials", + RPC::expected_field_message( + jss::authorized_credentials, "array")); } { @@ -782,20 +1001,31 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_index] = jss::validated; jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); + auto tryField = [&](Json::Value fieldValue) -> void { + Json::Value arr = Json::arrayValue; + Json::Value jo; + jo[jss::issuer] = issuer.human(); + jo[jss::credential_type] = fieldValue; + arr.append(jo); + jvParams[jss::deposit_preauth][jss::authorized_credentials] = + arr; - Json::Value jo; - jo[jss::issuer] = issuer.human(); - jo[jss::credential_type] = ""; - arr.append(std::move(jo)); + Json::Value const jrr = env.rpc( + "json", "ledger_entry", to_string(jvParams))[jss::result]; + auto const expectedErrMsg = fieldValue.isNull() + ? RPC::missing_field_message(jss::credential_type.c_str()) + : RPC::expected_field_message( + jss::credential_type, "hex string"); + checkErrorValue( + jrr, "malformedAuthorizedCredentials", expectedErrMsg); + }; - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); + auto const& badValues = getBadValues(FieldType::BlobField); + for (auto const& value : badValues) + { + tryField(value); + } + tryField(Json::nullValue); } { @@ -817,7 +1047,11 @@ class LedgerEntry_test : public beast::unit_test::suite auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); + checkErrorValue( + jrr[jss::result], + "malformedRequest", + "Must have exactly one of `authorized` and " + "`authorized_credentials`."); } { @@ -825,11 +1059,14 @@ class LedgerEntry_test : public beast::unit_test::suite Json::Value jvParams; jvParams[jss::ledger_index] = jss::validated; jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - jvParams[jss::deposit_preauth][jss::authorized_credentials] = 42; - - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); + testMalformedSubfield( + env, + jvParams, + jss::deposit_preauth, + jss::authorized_credentials, + FieldType::ArrayField, + "malformedAuthorizedCredentials", + false); } { @@ -846,7 +1083,9 @@ class LedgerEntry_test : public beast::unit_test::suite auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams)); checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); + jrr[jss::result], + "malformedAuthorizedCredentials", + "Invalid field 'authorized_credentials', not array."); } { @@ -865,7 +1104,9 @@ class LedgerEntry_test : public beast::unit_test::suite auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams)); checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); + jrr[jss::result], + "malformedAuthorizedCredentials", + "Invalid field 'authorized_credentials', not array."); } { @@ -879,13 +1120,14 @@ class LedgerEntry_test : public beast::unit_test::suite auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams)); checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); + jrr[jss::result], + "malformedAuthorizedCredentials", + "Invalid field 'authorized_credentials', not array."); } { // Failed, authorized_credentials is too long - - static std::string_view const credTypes[] = { + static std::array const credTypes = { "cred1", "cred2", "cred3", @@ -908,205 +1150,27 @@ class LedgerEntry_test : public beast::unit_test::suite auto& arr( jvParams[jss::deposit_preauth][jss::authorized_credentials]); - for (unsigned i = 0; i < sizeof(credTypes) / sizeof(credTypes[0]); - ++i) + for (auto cred : credTypes) { Json::Value jo; jo[jss::issuer] = issuer.human(); - jo[jss::credential_type] = - strHex(std::string_view(credTypes[i])); + jo[jss::credential_type] = strHex(std::string_view(cred)); arr.append(std::move(jo)); } auto const jrr = env.rpc("json", "ledger_entry", to_string(jvParams)); checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); - } - - { - // Failed, issuer is not set - Json::Value jvParams; - jvParams[jss::ledger_index] = jss::validated; - jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); - - Json::Value jo; - jo[jss::credential_type] = strHex(std::string_view(credType)); - arr.append(std::move(jo)); - - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); - } - - { - // Failed, issuer isn't string - Json::Value jvParams; - jvParams[jss::ledger_index] = jss::validated; - jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); - - Json::Value jo; - jo[jss::issuer] = 42; - jo[jss::credential_type] = strHex(std::string_view(credType)); - arr.append(std::move(jo)); - - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); - } - - { - // Failed, issuer is an array - Json::Value jvParams; - jvParams[jss::ledger_index] = jss::validated; - jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); - - Json::Value jo; - Json::Value payload = Json::arrayValue; - payload.append(42); - jo[jss::issuer] = std::move(payload); - jo[jss::credential_type] = strHex(std::string_view(credType)); - arr.append(std::move(jo)); - - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); - } - - { - // Failed, issuer isn't valid encoded account - Json::Value jvParams; - jvParams[jss::ledger_index] = jss::validated; - jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); - - Json::Value jo; - jo[jss::issuer] = "invalid_account"; - jo[jss::credential_type] = strHex(std::string_view(credType)); - arr.append(std::move(jo)); - - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); - } - - { - // Failed, credential_type is not set - Json::Value jvParams; - jvParams[jss::ledger_index] = jss::validated; - jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); - - Json::Value jo; - jo[jss::issuer] = issuer.human(); - arr.append(std::move(jo)); - - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); - } - - { - // Failed, credential_type isn't string - Json::Value jvParams; - jvParams[jss::ledger_index] = jss::validated; - jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); - - Json::Value jo; - jo[jss::issuer] = issuer.human(); - jo[jss::credential_type] = 42; - arr.append(std::move(jo)); - - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); - } - - { - // Failed, credential_type is an array - Json::Value jvParams; - jvParams[jss::ledger_index] = jss::validated; - jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); - - Json::Value jo; - jo[jss::issuer] = issuer.human(); - Json::Value payload = Json::arrayValue; - payload.append(42); - jo[jss::credential_type] = std::move(payload); - arr.append(std::move(jo)); - - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); - } - - { - // Failed, credential_type isn't hex encoded - Json::Value jvParams; - jvParams[jss::ledger_index] = jss::validated; - jvParams[jss::deposit_preauth][jss::owner] = bob.human(); - - jvParams[jss::deposit_preauth][jss::authorized_credentials] = - Json::arrayValue; - auto& arr( - jvParams[jss::deposit_preauth][jss::authorized_credentials]); - - Json::Value jo; - jo[jss::issuer] = issuer.human(); - jo[jss::credential_type] = "12KK"; - arr.append(std::move(jo)); - - auto const jrr = - env.rpc("json", "ledger_entry", to_string(jvParams)); - checkErrorValue( - jrr[jss::result], "malformedAuthorizedCredentials", ""); + jrr[jss::result], + "malformedAuthorizedCredentials", + "Invalid field 'authorized_credentials', not array."); } } void testLedgerEntryDirectory() { - testcase("ledger_entry Request Directory"); + testcase("Directory"); using namespace test::jtx; Env env{*this}; Account const alice{"alice"}; @@ -1188,39 +1252,48 @@ class LedgerEntry_test : public beast::unit_test::suite BEAST_EXPECT(jrr[jss::node][sfIndexes.jsonName].size() == 2); } { - // Null directory argument. + // Bad directory argument. Json::Value jvParams; - jvParams[jss::directory] = Json::nullValue; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + testMalformedField( + env, + jvParams, + jss::directory, + FieldType::HashOrObjectField, + "malformedRequest"); } { // Non-integer sub_index. Json::Value jvParams; jvParams[jss::directory] = Json::objectValue; jvParams[jss::directory][jss::dir_root] = dirRootIndex; - jvParams[jss::directory][jss::sub_index] = 1.5; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + testMalformedSubfield( + env, + jvParams, + jss::directory, + jss::sub_index, + FieldType::UInt64Field, + "malformedRequest", + false); } { // Malformed owner entry. Json::Value jvParams; jvParams[jss::directory] = Json::objectValue; - std::string const badAddress = makeBadAddress(alice.human()); - jvParams[jss::directory][jss::owner] = badAddress; jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); + testMalformedSubfield( + env, + jvParams, + jss::directory, + jss::owner, + FieldType::AccountField, + "malformedAddress", + false); } { - // Malformed directory object. Specify both dir_root and owner. + // Malformed directory object. Specifies both dir_root and owner. Json::Value jvParams; jvParams[jss::directory] = Json::objectValue; jvParams[jss::directory][jss::owner] = alice.human(); @@ -1228,7 +1301,10 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + checkErrorValue( + jrr, + "malformedRequest", + "Must have exactly one of `owner` and `dir_root` fields."); } { // Incomplete directory object. Missing both dir_root and owner. @@ -1238,14 +1314,17 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + checkErrorValue( + jrr, + "malformedRequest", + "Must have exactly one of `owner` and `dir_root` fields."); } } void testLedgerEntryEscrow() { - testcase("ledger_entry Request Escrow"); + testcase("Escrow"); using namespace test::jtx; Env env{*this}; Account const alice{"alice"}; @@ -1296,56 +1375,18 @@ class LedgerEntry_test : public beast::unit_test::suite jrr[jss::node][jss::Amount] == XRP(333).value().getText()); } { - // Malformed owner entry. - Json::Value jvParams; - jvParams[jss::escrow] = Json::objectValue; - - std::string const badAddress = makeBadAddress(alice.human()); - jvParams[jss::escrow][jss::owner] = badAddress; - jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedOwner", ""); - } - { - // Missing owner. - Json::Value jvParams; - jvParams[jss::escrow] = Json::objectValue; - jvParams[jss::escrow][jss::seq] = env.seq(alice) - 1; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Missing sequence. - Json::Value jvParams; - jvParams[jss::escrow] = Json::objectValue; - jvParams[jss::escrow][jss::owner] = alice.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Non-integer sequence. - Json::Value jvParams; - jvParams[jss::escrow] = Json::objectValue; - jvParams[jss::escrow][jss::owner] = alice.human(); - jvParams[jss::escrow][jss::seq] = - std::to_string(env.seq(alice) - 1); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + // Malformed escrow fields + runLedgerEntryTest( + env, + jss::escrow, + {{jss::owner, "malformedOwner"}, {jss::seq, "malformedSeq"}}); } } void testLedgerEntryOffer() { - testcase("ledger_entry Request Offer"); + testcase("Offer"); using namespace test::jtx; Env env{*this}; Account const alice{"alice"}; @@ -1379,56 +1420,21 @@ class LedgerEntry_test : public beast::unit_test::suite "json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::node][jss::TakerGets] == "322000000"); } - { - // Malformed account entry. - Json::Value jvParams; - jvParams[jss::offer] = Json::objectValue; - std::string const badAddress = makeBadAddress(alice.human()); - jvParams[jss::offer][jss::account] = badAddress; - jvParams[jss::offer][jss::seq] = env.seq(alice) - 1; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); - } { - // Malformed offer object. Missing account member. - Json::Value jvParams; - jvParams[jss::offer] = Json::objectValue; - jvParams[jss::offer][jss::seq] = env.seq(alice) - 1; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed offer object. Missing seq member. - Json::Value jvParams; - jvParams[jss::offer] = Json::objectValue; - jvParams[jss::offer][jss::account] = alice.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed offer object. Non-integral seq member. - Json::Value jvParams; - jvParams[jss::offer] = Json::objectValue; - jvParams[jss::offer][jss::account] = alice.human(); - jvParams[jss::offer][jss::seq] = std::to_string(env.seq(alice) - 1); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + // Malformed offer fields + runLedgerEntryTest( + env, + jss::offer, + {{jss::account, "malformedAddress"}, + {jss::seq, "malformedRequest"}}); } } void testLedgerEntryPayChan() { - testcase("ledger_entry Request Pay Chan"); + testcase("Pay Chan"); using namespace test::jtx; using namespace std::literals::chrono_literals; Env env{*this}; @@ -1478,14 +1484,19 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "entryNotFound", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); + } + + { + // Malformed paychan field + runLedgerEntryTest(env, jss::payment_channel); } } void testLedgerEntryRippleState() { - testcase("ledger_entry Request RippleState"); + testcase("RippleState"); using namespace test::jtx; Env env{*this}; Account const alice{"alice"}; @@ -1521,36 +1532,14 @@ class LedgerEntry_test : public beast::unit_test::suite jrr[jss::node][sfHighLimit.jsonName][jss::value] == "999"); } { - // ripple_state is not an object. - Json::Value jvParams; - jvParams[fieldName] = "ripple_state"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state.currency is missing. - Json::Value jvParams; - jvParams[fieldName] = Json::objectValue; - jvParams[fieldName][jss::accounts] = Json::arrayValue; - jvParams[fieldName][jss::accounts][0u] = alice.human(); - jvParams[fieldName][jss::accounts][1u] = gw.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state accounts is not an array. - Json::Value jvParams; - jvParams[fieldName] = Json::objectValue; - jvParams[fieldName][jss::accounts] = 2; - jvParams[fieldName][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + // test basic malformed scenarios + runLedgerEntryTest( + env, + fieldName, + { + {jss::accounts, "malformedRequest"}, + {jss::currency, "malformedCurrency"}, + }); } { // ripple_state one of the accounts is missing. @@ -1562,7 +1551,11 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + checkErrorValue( + jrr, + "malformedRequest", + "Invalid field 'accounts', not length-2 array of " + "Accounts."); } { // ripple_state more than 2 accounts. @@ -1576,33 +1569,60 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + checkErrorValue( + jrr, + "malformedRequest", + "Invalid field 'accounts', not length-2 array of " + "Accounts."); } { - // ripple_state account[0] is not a string. + // ripple_state account[0] / account[1] is not an account. Json::Value jvParams; jvParams[fieldName] = Json::objectValue; - jvParams[fieldName][jss::accounts] = Json::arrayValue; - jvParams[fieldName][jss::accounts][0u] = 44; - jvParams[fieldName][jss::accounts][1u] = gw.human(); - jvParams[fieldName][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state account[1] is not a string. - Json::Value jvParams; - jvParams[fieldName] = Json::objectValue; - jvParams[fieldName][jss::accounts] = Json::arrayValue; - jvParams[fieldName][jss::accounts][0u] = alice.human(); - jvParams[fieldName][jss::accounts][1u] = 21; - jvParams[fieldName][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + auto tryField = [&](Json::Value badAccount) -> void { + { + // account[0] + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = badAccount; + jvParams[fieldName][jss::accounts][1u] = gw.human(); + jvParams[fieldName][jss::currency] = "USD"; + + Json::Value const jrr = env.rpc( + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; + checkErrorValue( + jrr, + "malformedAddress", + RPC::expected_field_message( + jss::accounts, "array of Accounts")); + } + + { + // account[1] + jvParams[fieldName][jss::accounts] = Json::arrayValue; + jvParams[fieldName][jss::accounts][0u] = alice.human(); + jvParams[fieldName][jss::accounts][1u] = badAccount; + jvParams[fieldName][jss::currency] = "USD"; + + Json::Value const jrr = env.rpc( + "json", + "ledger_entry", + to_string(jvParams))[jss::result]; + checkErrorValue( + jrr, + "malformedAddress", + RPC::expected_field_message( + jss::accounts, "array of Accounts")); + } + }; + + auto const& badValues = getBadValues(FieldType::AccountField); + for (auto const& value : badValues) + { + tryField(value); + } + tryField(Json::nullValue); } { // ripple_state account[0] == account[1]. @@ -1615,48 +1635,10 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // ripple_state malformed account[0]. - Json::Value jvParams; - jvParams[fieldName] = Json::objectValue; - jvParams[fieldName][jss::accounts] = Json::arrayValue; - jvParams[fieldName][jss::accounts][0u] = - makeBadAddress(alice.human()); - jvParams[fieldName][jss::accounts][1u] = gw.human(); - jvParams[fieldName][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); - } - { - // ripple_state malformed account[1]. - Json::Value jvParams; - jvParams[fieldName] = Json::objectValue; - jvParams[fieldName][jss::accounts] = Json::arrayValue; - jvParams[fieldName][jss::accounts][0u] = alice.human(); - jvParams[fieldName][jss::accounts][1u] = - makeBadAddress(gw.human()); - jvParams[fieldName][jss::currency] = "USD"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); - } - { - // ripple_state malformed currency. - Json::Value jvParams; - jvParams[fieldName] = Json::objectValue; - jvParams[fieldName][jss::accounts] = Json::arrayValue; - jvParams[fieldName][jss::accounts][0u] = alice.human(); - jvParams[fieldName][jss::accounts][1u] = gw.human(); - jvParams[fieldName][jss::currency] = "USDollars"; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedCurrency", ""); + checkErrorValue( + jrr, + "malformedRequest", + "Cannot have a trustline to self."); } } } @@ -1664,7 +1646,7 @@ class LedgerEntry_test : public beast::unit_test::suite void testLedgerEntryTicket() { - testcase("ledger_entry Request Ticket"); + testcase("Ticket"); using namespace test::jtx; Env env{*this}; env.close(); @@ -1686,7 +1668,7 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "entryNotFound", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); } { // First real ticket requested by index. @@ -1721,7 +1703,7 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "entryNotFound", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); } { // Request a ticket using an account root entry. @@ -1730,59 +1712,26 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "unexpectedLedgerType", ""); + checkErrorValue( + jrr, "unexpectedLedgerType", "Unexpected ledger type."); } - { - // Malformed account entry. - Json::Value jvParams; - jvParams[jss::ticket] = Json::objectValue; - std::string const badAddress = makeBadAddress(env.master.human()); - jvParams[jss::ticket][jss::account] = badAddress; - jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedAddress", ""); - } { - // Malformed ticket object. Missing account member. - Json::Value jvParams; - jvParams[jss::ticket] = Json::objectValue; - jvParams[jss::ticket][jss::ticket_seq] = env.seq(env.master) - 1; - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed ticket object. Missing seq member. - Json::Value jvParams; - jvParams[jss::ticket] = Json::objectValue; - jvParams[jss::ticket][jss::account] = env.master.human(); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); - } - { - // Malformed ticket object. Non-integral seq member. - Json::Value jvParams; - jvParams[jss::ticket] = Json::objectValue; - jvParams[jss::ticket][jss::account] = env.master.human(); - jvParams[jss::ticket][jss::ticket_seq] = - std::to_string(env.seq(env.master) - 1); - jvParams[jss::ledger_hash] = ledgerHash; - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "malformedRequest", ""); + // test basic malformed scenarios + runLedgerEntryTest( + env, + jss::ticket, + { + {jss::account, "malformedAddress"}, + {jss::ticket_seq, "malformedRequest"}, + }); } } void testLedgerEntryDID() { - testcase("ledger_entry Request DID"); + testcase("DID"); using namespace test::jtx; using namespace std::literals::chrono_literals; Env env{*this}; @@ -1826,230 +1775,17 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "entryNotFound", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); } - } - - void - testLedgerEntryInvalidParams(unsigned int apiVersion) - { - testcase( - "ledger_entry Request With Invalid Parameters v" + - std::to_string(apiVersion)); - using namespace test::jtx; - Env env{*this}; - - std::string const ledgerHash{to_string(env.closed()->info().hash)}; - - auto makeParams = [&apiVersion](std::function f) { - Json::Value params; - params[jss::api_version] = apiVersion; - f(params); - return params; - }; - // "features" is not an option supported by ledger_entry. { - auto const jvParams = - makeParams([&ledgerHash](Json::Value& jvParams) { - jvParams[jss::features] = ledgerHash; - jvParams[jss::ledger_hash] = ledgerHash; - }); - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - if (apiVersion < 2u) - checkErrorValue(jrr, "unknownOption", ""); - else - checkErrorValue(jrr, "invalidParams", ""); - } - Json::Value const injectObject = []() { - Json::Value obj(Json::objectValue); - obj[jss::account] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU"; - obj[jss::ledger_index] = "validated"; - return obj; - }(); - Json::Value const injectArray = []() { - Json::Value arr(Json::arrayValue); - arr[0u] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU"; - arr[1u] = "validated"; - return arr; - }(); - - // invalid input for fields that can handle an object, but can't handle - // an array - for (auto const& field : - {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm}) - { - auto const jvParams = - makeParams([&field, &injectArray](Json::Value& jvParams) { - jvParams[field] = injectArray; - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - if (apiVersion < 2u) - checkErrorValue(jrr, "internal", "Internal error."); - else - checkErrorValue(jrr, "invalidParams", ""); - } - // Fields that can handle objects just fine - for (auto const& field : - {jss::directory, jss::escrow, jss::offer, jss::ticket, jss::amm}) - { - auto const jvParams = - makeParams([&field, &injectObject](Json::Value& jvParams) { - jvParams[field] = injectObject; - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - checkErrorValue(jrr, "malformedRequest", ""); - } - - for (auto const& inject : {injectObject, injectArray}) - { - // invalid input for fields that can't handle an object or an array - for (auto const& field : - {jss::index, - jss::account_root, - jss::check, - jss::payment_channel}) - { - auto const jvParams = - makeParams([&field, &inject](Json::Value& jvParams) { - jvParams[field] = inject; - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - if (apiVersion < 2u) - checkErrorValue(jrr, "internal", "Internal error."); - else - checkErrorValue(jrr, "invalidParams", ""); - } - // directory sub-fields - for (auto const& field : {jss::dir_root, jss::owner}) - { - auto const jvParams = - makeParams([&field, &inject](Json::Value& jvParams) { - jvParams[jss::directory][field] = inject; - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - if (apiVersion < 2u) - checkErrorValue(jrr, "internal", "Internal error."); - else - checkErrorValue(jrr, "invalidParams", ""); - } - // escrow sub-fields - { - auto const jvParams = - makeParams([&inject](Json::Value& jvParams) { - jvParams[jss::escrow][jss::owner] = inject; - jvParams[jss::escrow][jss::seq] = 99; - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - if (apiVersion < 2u) - checkErrorValue(jrr, "internal", "Internal error."); - else - checkErrorValue(jrr, "invalidParams", ""); - } - // offer sub-fields - { - auto const jvParams = - makeParams([&inject](Json::Value& jvParams) { - jvParams[jss::offer][jss::account] = inject; - jvParams[jss::offer][jss::seq] = 99; - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - if (apiVersion < 2u) - checkErrorValue(jrr, "internal", "Internal error."); - else - checkErrorValue(jrr, "invalidParams", ""); - } - // ripple_state sub-fields - { - auto const jvParams = - makeParams([&inject](Json::Value& jvParams) { - Json::Value rs(Json::objectValue); - rs[jss::currency] = "FOO"; - rs[jss::accounts] = Json::Value(Json::arrayValue); - rs[jss::accounts][0u] = - "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU"; - rs[jss::accounts][1u] = - "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv"; - rs[jss::currency] = inject; - jvParams[jss::ripple_state] = std::move(rs); - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - if (apiVersion < 2u) - checkErrorValue(jrr, "internal", "Internal error."); - else - checkErrorValue(jrr, "invalidParams", ""); - } - // ticket sub-fields - { - auto const jvParams = - makeParams([&inject](Json::Value& jvParams) { - jvParams[jss::ticket][jss::account] = inject; - jvParams[jss::ticket][jss::ticket_seq] = 99; - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - if (apiVersion < 2u) - checkErrorValue(jrr, "internal", "Internal error."); - else - checkErrorValue(jrr, "invalidParams", ""); - } - - // Fields that can handle malformed inputs just fine - for (auto const& field : {jss::nft_page, jss::deposit_preauth}) - { - auto const jvParams = - makeParams([&field, &inject](Json::Value& jvParams) { - jvParams[field] = inject; - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - checkErrorValue(jrr, "malformedRequest", ""); - } - // Subfields of deposit_preauth that can handle malformed inputs - // fine - for (auto const& field : {jss::owner, jss::authorized}) - { - auto const jvParams = - makeParams([&field, &inject](Json::Value& jvParams) { - auto pa = Json::Value(Json::objectValue); - pa[jss::owner] = "rhigTLJJyXXSRUyRCQtqi1NoAZZzZnS4KU"; - pa[jss::authorized] = - "rKssEq6pg1KbqEqAFnua5mFAL6Ggpsh2wv"; - pa[field] = inject; - jvParams[jss::deposit_preauth] = std::move(pa); - }); - - Json::Value const jrr = env.rpc( - "json", "ledger_entry", to_string(jvParams))[jss::result]; - - checkErrorValue(jrr, "malformedRequest", ""); - } + // Malformed DID index + Json::Value jvParams; + testMalformedField( + env, + jvParams, + jss::did, + FieldType::AccountField, + "malformedAddress"); } } @@ -2068,28 +1804,16 @@ class LedgerEntry_test : public beast::unit_test::suite {.owner = owner, .fee = static_cast(env.current()->fees().base.drops())}); - // Malformed document id - auto res = Oracle::ledgerEntry(env, owner, NoneTag); - BEAST_EXPECT(res[jss::error].asString() == "invalidParams"); - std::vector invalid = {-1, 1.2, "", "Invalid"}; - for (auto const& v : invalid) { - auto const res = Oracle::ledgerEntry(env, owner, v); - BEAST_EXPECT(res[jss::error].asString() == "malformedDocumentID"); + // test basic malformed scenarios + runLedgerEntryTest( + env, + jss::oracle, + { + {jss::account, "malformedAccount"}, + {jss::oracle_document_id, "malformedDocumentID"}, + }); } - // Missing document id - res = Oracle::ledgerEntry(env, owner, std::nullopt); - BEAST_EXPECT(res[jss::error].asString() == "malformedRequest"); - - // Missing account - res = Oracle::ledgerEntry(env, std::nullopt, 1); - BEAST_EXPECT(res[jss::error].asString() == "malformedRequest"); - - // Malformed account - std::string malfAccount = to_string(owner.id()); - malfAccount.replace(10, 1, 1, '!'); - res = Oracle::ledgerEntry(env, malfAccount, 1); - BEAST_EXPECT(res[jss::error].asString() == "malformedAddress"); } void @@ -2144,7 +1868,7 @@ class LedgerEntry_test : public beast::unit_test::suite void testLedgerEntryMPT() { - testcase("ledger_entry Request MPT"); + testcase("MPT"); using namespace test::jtx; using namespace std::literals::chrono_literals; Env env{*this}; @@ -2185,7 +1909,7 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "entryNotFound", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); } { // Request the MPToken using its owner + mptIssuanceID. @@ -2210,14 +1934,24 @@ class LedgerEntry_test : public beast::unit_test::suite jvParams[jss::ledger_hash] = ledgerHash; Json::Value const jrr = env.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "entryNotFound", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); + } + { + // Malformed MPTIssuance index + Json::Value jvParams; + testMalformedField( + env, + jvParams, + jss::mptoken, + FieldType::HashOrObjectField, + "malformedRequest"); } } void testLedgerEntryPermissionedDomain() { - testcase("ledger_entry PermissionedDomain"); + testcase("PermissionedDomain"); using namespace test::jtx; @@ -2278,73 +2012,25 @@ class LedgerEntry_test : public beast::unit_test::suite "12F1F1F1F180D67377B2FAB292A31C922470326268D2B9B74CD1E582645B9A" "DE"; auto const jrr = env.rpc("json", "ledger_entry", to_string(params)); - checkErrorValue(jrr[jss::result], "entryNotFound", ""); + checkErrorValue( + jrr[jss::result], "entryNotFound", "Entry not found."); } - { - // Fail, invalid permissioned domain index - Json::Value params; - params[jss::ledger_index] = jss::validated; - params[jss::permissioned_domain] = "NotAHexString"; - auto const jrr = env.rpc("json", "ledger_entry", to_string(params)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, permissioned domain is not an object - Json::Value params; - params[jss::ledger_index] = jss::validated; - params[jss::permissioned_domain] = 10; - auto const jrr = env.rpc("json", "ledger_entry", to_string(params)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); - } - - { - // Fail, invalid account - Json::Value params; - params[jss::ledger_index] = jss::validated; - params[jss::permissioned_domain][jss::account] = 1; - params[jss::permissioned_domain][jss::seq] = seq; - auto const jrr = env.rpc("json", "ledger_entry", to_string(params)); - checkErrorValue(jrr[jss::result], "malformedAddress", ""); - } - - { - // Fail, account is an object - Json::Value params; - params[jss::ledger_index] = jss::validated; - params[jss::permissioned_domain][jss::account] = - Json::Value{Json::ValueType::objectValue}; - params[jss::permissioned_domain][jss::seq] = seq; - auto const jrr = env.rpc("json", "ledger_entry", to_string(params)); - checkErrorValue(jrr[jss::result], "malformedAddress", ""); - } - - { - // Fail, no account - Json::Value params; - params[jss::ledger_index] = jss::validated; - params[jss::permissioned_domain][jss::account] = ""; - params[jss::permissioned_domain][jss::seq] = seq; - auto const jrr = env.rpc("json", "ledger_entry", to_string(params)); - checkErrorValue(jrr[jss::result], "malformedAddress", ""); - } - - { - // Fail, invalid sequence - Json::Value params; - params[jss::ledger_index] = jss::validated; - params[jss::permissioned_domain][jss::account] = alice.human(); - params[jss::permissioned_domain][jss::seq] = "12g"; - auto const jrr = env.rpc("json", "ledger_entry", to_string(params)); - checkErrorValue(jrr[jss::result], "malformedRequest", ""); + // test basic malformed scenarios + runLedgerEntryTest( + env, + jss::permissioned_domain, + { + {jss::account, "malformedAddress"}, + {jss::seq, "malformedRequest"}, + }); } } void testLedgerEntryCLI() { - testcase("ledger_entry command-line"); + testcase("command-line"); using namespace test::jtx; Env env{*this}; @@ -2391,9 +2077,6 @@ public: testLedgerEntryMPT(); testLedgerEntryPermissionedDomain(); testLedgerEntryCLI(); - - forAllApiVersions(std::bind_front( - &LedgerEntry_test::testLedgerEntryInvalidParams, this)); } }; @@ -2444,7 +2127,6 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, BEAST_EXPECT(jrr.isMember(jss::node)); auto r = jrr[jss::node]; - // std::cout << to_string(r) << '\n'; BEAST_EXPECT(r.isMember(jss::Account)); BEAST_EXPECT(r[jss::Account] == mcDoor.human()); @@ -2486,7 +2168,7 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, Json::Value const jrr = mcEnv.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "entryNotFound", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); } { // create two claim ids and verify that the bridge counter was @@ -2500,7 +2182,6 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, Json::Value jvParams; jvParams[jss::bridge_account] = mcDoor.human(); jvParams[jss::bridge] = jvb; - // std::cout << to_string(jvParams) << '\n'; Json::Value const jrr = mcEnv.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; @@ -2536,13 +2217,11 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, jvParams[jss::xchain_owned_claim_id] = jvXRPBridgeRPC; jvParams[jss::xchain_owned_claim_id][jss::xchain_owned_claim_id] = 1; - // std::cout << to_string(jvParams) << '\n'; Json::Value const jrr = scEnv.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::node)); auto r = jrr[jss::node]; - // std::cout << to_string(r) << '\n'; BEAST_EXPECT(r.isMember(jss::Account)); BEAST_EXPECT(r[jss::Account] == scAlice.human()); @@ -2563,7 +2242,6 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, BEAST_EXPECT(jrr.isMember(jss::node)); auto r = jrr[jss::node]; - // std::cout << to_string(r) << '\n'; BEAST_EXPECT(r.isMember(jss::Account)); BEAST_EXPECT(r[jss::Account] == scBob.human()); @@ -2622,10 +2300,8 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, jvXRPBridgeRPC; jvParams[jss::xchain_owned_create_account_claim_id] [jss::xchain_owned_create_account_claim_id] = 1; - // std::cout << to_string(jvParams) << '\n'; Json::Value const jrr = scEnv.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - // std::cout << to_string(jrr) << '\n'; BEAST_EXPECT(jrr.isMember(jss::node)); auto r = jrr[jss::node]; @@ -2694,10 +2370,9 @@ class LedgerEntry_XChain_test : public beast::unit_test::suite, jvXRPBridgeRPC; jvParams[jss::xchain_owned_create_account_claim_id] [jss::xchain_owned_create_account_claim_id] = 1; - // std::cout << to_string(jvParams) << '\n'; Json::Value const jrr = scEnv.rpc( "json", "ledger_entry", to_string(jvParams))[jss::result]; - checkErrorValue(jrr, "entryNotFound", ""); + checkErrorValue(jrr, "entryNotFound", "Entry not found."); } } diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index b98f31340a..52a69eb79e 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -190,7 +190,7 @@ getAccountObjects( auto& jvObjects = (jvResult[jss::account_objects] = Json::arrayValue); - // this is a mutable version of limit, used to seemlessly switch + // this is a mutable version of limit, used to seamlessly switch // to iterating directory entries when nftokenpages are exhausted uint32_t mlimit = limit; @@ -373,7 +373,7 @@ ledgerFromRequest(T& ledger, JsonContext& context) indexValue = legacyLedger; } - if (hashValue) + if (!hashValue.isNull()) { if (!hashValue.isString()) return {rpcINVALID_PARAMS, "ledgerHashNotString"}; @@ -384,6 +384,9 @@ ledgerFromRequest(T& ledger, JsonContext& context) return getLedger(ledger, ledgerHash, context); } + if (!indexValue.isConvertibleTo(Json::stringValue)) + return {rpcINVALID_PARAMS, "ledgerIndexMalformed"}; + auto const index = indexValue.asString(); if (index == "current" || index.empty()) @@ -395,11 +398,11 @@ ledgerFromRequest(T& ledger, JsonContext& context) if (index == "closed") return getLedger(ledger, LedgerShortcut::CLOSED, context); - std::uint32_t iVal; - if (beast::lexicalCastChecked(iVal, index)) - return getLedger(ledger, iVal, context); + std::uint32_t val; + if (!beast::lexicalCastChecked(val, index)) + return {rpcINVALID_PARAMS, "ledgerIndexMalformed"}; - return {rpcINVALID_PARAMS, "ledgerIndexMalformed"}; + return getLedger(ledger, val, context); } } // namespace @@ -586,7 +589,7 @@ getLedger(T& ledger, LedgerShortcut shortcut, Context& context) return Status::OK; } -// Explicit instantiaion of above three functions +// Explicit instantiation of above three functions template Status getLedger<>(std::shared_ptr&, uint32_t, Context&); diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index fb82788907..61a7e2fb2c 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -38,50 +39,57 @@ namespace ripple { -static std::optional -parseIndex(Json::Value const& params, Json::Value& jvResult) +static Expected +parseObjectID( + Json::Value const& params, + Json::StaticString const fieldName, + std::string const& expectedType = "hex string or object") { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) + if (auto const uNodeIndex = LedgerEntryHelpers::parse(params)) { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return *uNodeIndex; } - - return uNodeIndex; + return LedgerEntryHelpers::invalidFieldError( + "malformedRequest", fieldName, expectedType); } -static std::optional -parseAccountRoot(Json::Value const& params, Json::Value& jvResult) +static Expected +parseIndex(Json::Value const& params, Json::StaticString const fieldName) { - auto const account = parseBase58(params.asString()); - if (!account || account->isZero()) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } - - return keylet::account(*account).key; + return parseObjectID(params, fieldName, "hex string"); } -static std::optional -parseAMM(Json::Value const& params, Json::Value& jvResult) +static Expected +parseAccountRoot(Json::Value const& params, Json::StaticString const fieldName) +{ + if (auto const account = LedgerEntryHelpers::parse(params)) + { + return keylet::account(*account).key; + } + + return LedgerEntryHelpers::invalidFieldError( + "malformedAddress", fieldName, "AccountID"); +} + +static Expected +parseAmendments(Json::Value const& params, Json::StaticString const fieldName) +{ + return parseObjectID(params, fieldName, "hex string"); +} + +static Expected +parseAMM(Json::Value const& params, Json::StaticString const fieldName) { if (!params.isObject()) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(params, fieldName); } - if (!params.isMember(jss::asset) || !params.isMember(jss::asset2)) + if (auto const value = + LedgerEntryHelpers::hasRequired(params, {jss::asset, jss::asset2}); + !value) { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return Unexpected(value.error()); } try @@ -92,135 +100,136 @@ parseAMM(Json::Value const& params, Json::Value& jvResult) } catch (std::runtime_error const&) { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return LedgerEntryHelpers::malformedError("malformedRequest", ""); } } -static std::optional -parseBridge(Json::Value const& params, Json::Value& jvResult) +static Expected +parseBridge(Json::Value const& params, Json::StaticString const fieldName) { - // return the keylet for the specified bridge or nullopt if the - // request is malformed - auto const maybeKeylet = [&]() -> std::optional { - try - { - if (!params.isMember(jss::bridge_account)) - return std::nullopt; - - auto const& jsBridgeAccount = params[jss::bridge_account]; - if (!jsBridgeAccount.isString()) - { - return std::nullopt; - } - - auto const account = - parseBase58(jsBridgeAccount.asString()); - if (!account || account->isZero()) - { - return std::nullopt; - } - - // This may throw and is the reason for the `try` block. The - // try block has a larger scope so the `bridge` variable - // doesn't need to be an optional. - STXChainBridge const bridge(params[jss::bridge]); - STXChainBridge::ChainType const chainType = - STXChainBridge::srcChain(account == bridge.lockingChainDoor()); - - if (account != bridge.door(chainType)) - return std::nullopt; - - return keylet::bridge(bridge, chainType); - } - catch (...) - { - return std::nullopt; - } - }(); - - if (maybeKeylet) + if (!params.isMember(jss::bridge)) { - return maybeKeylet->key; + return Unexpected(LedgerEntryHelpers::missingFieldError(jss::bridge)); } - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + if (params[jss::bridge].isString()) + { + return parseObjectID(params, fieldName); + } + + auto const bridge = + LedgerEntryHelpers::parseBridgeFields(params[jss::bridge]); + if (!bridge) + return Unexpected(bridge.error()); + + auto const account = LedgerEntryHelpers::requiredAccountID( + params, jss::bridge_account, "malformedBridgeAccount"); + if (!account) + return Unexpected(account.error()); + + STXChainBridge::ChainType const chainType = + STXChainBridge::srcChain(account.value() == bridge->lockingChainDoor()); + if (account.value() != bridge->door(chainType)) + return LedgerEntryHelpers::malformedError("malformedRequest", ""); + + return keylet::bridge(*bridge, chainType).key; } -static std::optional -parseCheck(Json::Value const& params, Json::Value& jvResult) +static Expected +parseCheck(Json::Value const& params, Json::StaticString const fieldName) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - return uNodeIndex; + return parseObjectID(params, fieldName, "hex string"); } -static std::optional -parseCredential(Json::Value const& cred, Json::Value& jvResult) +static Expected +parseCredential(Json::Value const& cred, Json::StaticString const fieldName) { - if (cred.isString()) + if (!cred.isObject()) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(cred.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(cred, fieldName); } - if ((!cred.isMember(jss::subject) || !cred[jss::subject].isString()) || - (!cred.isMember(jss::issuer) || !cred[jss::issuer].isString()) || - (!cred.isMember(jss::credential_type) || - !cred[jss::credential_type].isString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } + auto const subject = LedgerEntryHelpers::requiredAccountID( + cred, jss::subject, "malformedRequest"); + if (!subject) + return Unexpected(subject.error()); - auto const subject = parseBase58(cred[jss::subject].asString()); - auto const issuer = parseBase58(cred[jss::issuer].asString()); - auto const credType = strUnHex(cred[jss::credential_type].asString()); + auto const issuer = LedgerEntryHelpers::requiredAccountID( + cred, jss::issuer, "malformedRequest"); + if (!issuer) + return Unexpected(issuer.error()); - if (!subject || subject->isZero() || !issuer || issuer->isZero() || - !credType || credType->empty()) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } + auto const credType = LedgerEntryHelpers::requiredHexBlob( + cred, + jss::credential_type, + maxCredentialTypeLength, + "malformedRequest"); + if (!credType) + return Unexpected(credType.error()); return keylet::credential( *subject, *issuer, Slice(credType->data(), credType->size())) .key; } -static STArray +static Expected +parseDelegate(Json::Value const& params, Json::StaticString const fieldName) +{ + if (!params.isObject()) + { + return parseObjectID(params, fieldName); + } + + auto const account = LedgerEntryHelpers::requiredAccountID( + params, jss::account, "malformedAddress"); + if (!account) + return Unexpected(account.error()); + + auto const authorize = LedgerEntryHelpers::requiredAccountID( + params, jss::authorize, "malformedAddress"); + if (!authorize) + return Unexpected(authorize.error()); + + return keylet::delegate(*account, *authorize).key; +} + +static Expected parseAuthorizeCredentials(Json::Value const& jv) { + if (!jv.isArray()) + return LedgerEntryHelpers::invalidFieldError( + "malformedAuthorizedCredentials", + jss::authorized_credentials, + "array"); STArray arr(sfAuthorizeCredentials, jv.size()); for (auto const& jo : jv) { - if (!jo.isObject() || // - !jo.isMember(jss::issuer) || !jo[jss::issuer].isString() || - !jo.isMember(jss::credential_type) || - !jo[jss::credential_type].isString()) - return {}; + if (!jo.isObject()) + return LedgerEntryHelpers::invalidFieldError( + "malformedAuthorizedCredentials", + jss::authorized_credentials, + "array"); + if (auto const value = LedgerEntryHelpers::hasRequired( + jo, + {jss::issuer, jss::credential_type}, + "malformedAuthorizedCredentials"); + !value) + { + return Unexpected(value.error()); + } - auto const issuer = parseBase58(jo[jss::issuer].asString()); - if (!issuer || !*issuer) - return {}; + auto const issuer = LedgerEntryHelpers::requiredAccountID( + jo, jss::issuer, "malformedAuthorizedCredentials"); + if (!issuer) + return Unexpected(issuer.error()); - auto const credentialType = - strUnHex(jo[jss::credential_type].asString()); - if (!credentialType || credentialType->empty() || - credentialType->size() > maxCredentialTypeLength) - return {}; + auto const credentialType = LedgerEntryHelpers::requiredHexBlob( + jo, + jss::credential_type, + maxCredentialTypeLength, + "malformedAuthorizedCredentials"); + if (!credentialType) + return Unexpected(credentialType.error()); auto credential = STObject::makeInnerObject(sfCredential); credential.setAccountID(sfIssuer, *issuer); @@ -231,703 +240,450 @@ parseAuthorizeCredentials(Json::Value const& jv) return arr; } -static std::optional -parseDelegate(Json::Value const& params, Json::Value& jvResult) -{ - if (!params.isObject()) - { - uint256 uNodeIndex; - if (!params.isString() || !uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; - } - if (!params.isMember(jss::account) || !params.isMember(jss::authorize)) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - if (!params[jss::account].isString() || !params[jss::authorize].isString()) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } - auto const account = - parseBase58(params[jss::account].asString()); - if (!account) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } - auto const authorize = - parseBase58(params[jss::authorize].asString()); - if (!authorize) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } - return keylet::delegate(*account, *authorize).key; -} - -static std::optional -parseDepositPreauth(Json::Value const& dp, Json::Value& jvResult) +static Expected +parseDepositPreauth(Json::Value const& dp, Json::StaticString const fieldName) { if (!dp.isObject()) { - uint256 uNodeIndex; - if (!dp.isString() || !uNodeIndex.parseHex(dp.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(dp, fieldName); } - // clang-format off - if ( - (!dp.isMember(jss::owner) || !dp[jss::owner].isString()) || - (dp.isMember(jss::authorized) == dp.isMember(jss::authorized_credentials)) || - (dp.isMember(jss::authorized) && !dp[jss::authorized].isString()) || - (dp.isMember(jss::authorized_credentials) && !dp[jss::authorized_credentials].isArray()) - ) - // clang-format on + if ((dp.isMember(jss::authorized) == + dp.isMember(jss::authorized_credentials))) { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return LedgerEntryHelpers::malformedError( + "malformedRequest", + "Must have exactly one of `authorized` and " + "`authorized_credentials`."); } - auto const owner = parseBase58(dp[jss::owner].asString()); + auto const owner = + LedgerEntryHelpers::requiredAccountID(dp, jss::owner, "malformedOwner"); if (!owner) { - jvResult[jss::error] = "malformedOwner"; - return std::nullopt; + return Unexpected(owner.error()); } if (dp.isMember(jss::authorized)) { - auto const authorized = - parseBase58(dp[jss::authorized].asString()); - if (!authorized) + if (auto const authorized = + LedgerEntryHelpers::parse(dp[jss::authorized])) { - jvResult[jss::error] = "malformedAuthorized"; - return std::nullopt; + return keylet::depositPreauth(*owner, *authorized).key; } - return keylet::depositPreauth(*owner, *authorized).key; + return LedgerEntryHelpers::invalidFieldError( + "malformedAuthorized", jss::authorized, "AccountID"); } auto const& ac(dp[jss::authorized_credentials]); - STArray const arr = parseAuthorizeCredentials(ac); - - if (arr.empty() || (arr.size() > maxCredentialsArraySize)) + auto const arr = parseAuthorizeCredentials(ac); + if (!arr.has_value()) + return Unexpected(arr.error()); + if (arr->empty() || (arr->size() > maxCredentialsArraySize)) { - jvResult[jss::error] = "malformedAuthorizedCredentials"; - return std::nullopt; + return LedgerEntryHelpers::invalidFieldError( + "malformedAuthorizedCredentials", + jss::authorized_credentials, + "array"); } - auto const& sorted = credentials::makeSorted(arr); + auto const& sorted = credentials::makeSorted(arr.value()); if (sorted.empty()) { - jvResult[jss::error] = "malformedAuthorizedCredentials"; - return std::nullopt; + // TODO: this error message is bad/inaccurate + return LedgerEntryHelpers::invalidFieldError( + "malformedAuthorizedCredentials", + jss::authorized_credentials, + "array"); } - return keylet::depositPreauth(*owner, sorted).key; + return keylet::depositPreauth(*owner, std::move(sorted)).key; } -static std::optional -parseDID(Json::Value const& params, Json::Value& jvResult) +static Expected +parseDID(Json::Value const& params, Json::StaticString const fieldName) { - auto const account = parseBase58(params.asString()); - if (!account || account->isZero()) + auto const account = LedgerEntryHelpers::parse(params); + if (!account) { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; + return LedgerEntryHelpers::invalidFieldError( + "malformedAddress", fieldName, "AccountID"); } return keylet::did(*account).key; } -static std::optional -parseDirectory(Json::Value const& params, Json::Value& jvResult) +static Expected +parseDirectoryNode( + Json::Value const& params, + Json::StaticString const fieldName) { - if (params.isNull()) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - if (!params.isObject()) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(params, fieldName); } - if (params.isMember(jss::sub_index) && !params[jss::sub_index].isIntegral()) + if (params.isMember(jss::sub_index) && + (!params[jss::sub_index].isConvertibleTo(Json::uintValue) || + params[jss::sub_index].isBool())) { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return LedgerEntryHelpers::invalidFieldError( + "malformedRequest", jss::sub_index, "number"); } - std::uint64_t uSubIndex = - params.isMember(jss::sub_index) ? params[jss::sub_index].asUInt() : 0; + if (params.isMember(jss::owner) == params.isMember(jss::dir_root)) + { + return LedgerEntryHelpers::malformedError( + "malformedRequest", + "Must have exactly one of `owner` and `dir_root` fields."); + } + + std::uint64_t uSubIndex = params.get(jss::sub_index, 0).asUInt(); if (params.isMember(jss::dir_root)) { - uint256 uDirRoot; - - if (params.isMember(jss::owner)) + if (auto const uDirRoot = + LedgerEntryHelpers::parse(params[jss::dir_root])) { - // May not specify both dir_root and owner. - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return keylet::page(*uDirRoot, uSubIndex).key; } - if (!uDirRoot.parseHex(params[jss::dir_root].asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return keylet::page(uDirRoot, uSubIndex).key; + return LedgerEntryHelpers::invalidFieldError( + "malformedDirRoot", jss::dir_root, "hash"); } if (params.isMember(jss::owner)) { auto const ownerID = - parseBase58(params[jss::owner].asString()); - + LedgerEntryHelpers::parse(params[jss::owner]); if (!ownerID) { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; + return LedgerEntryHelpers::invalidFieldError( + "malformedAddress", jss::owner, "AccountID"); } return keylet::page(keylet::ownerDir(*ownerID), uSubIndex).key; } - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return LedgerEntryHelpers::malformedError("malformedRequest", ""); } -static std::optional -parseEscrow(Json::Value const& params, Json::Value& jvResult) +static Expected +parseEscrow(Json::Value const& params, Json::StaticString const fieldName) { if (!params.isObject()) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - return uNodeIndex; + return parseObjectID(params, fieldName); } - if (!params.isMember(jss::owner) || !params.isMember(jss::seq) || - !params[jss::seq].isIntegral()) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - auto const id = parseBase58(params[jss::owner].asString()); - + auto const id = LedgerEntryHelpers::requiredAccountID( + params, jss::owner, "malformedOwner"); if (!id) - { - jvResult[jss::error] = "malformedOwner"; - return std::nullopt; - } + return Unexpected(id.error()); + auto const seq = + LedgerEntryHelpers::requiredUInt32(params, jss::seq, "malformedSeq"); + if (!seq) + return Unexpected(seq.error()); - return keylet::escrow(*id, params[jss::seq].asUInt()).key; + return keylet::escrow(*id, *seq).key; } -static std::optional -parseMPToken(Json::Value const& mptJson, Json::Value& jvResult) +static Expected +parseFeeSettings(Json::Value const& params, Json::StaticString const fieldName) { - if (!mptJson.isObject()) - { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(mptJson.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; - } - - if (!mptJson.isMember(jss::mpt_issuance_id) || - !mptJson.isMember(jss::account)) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - try - { - auto const mptIssuanceIdStr = mptJson[jss::mpt_issuance_id].asString(); - - uint192 mptIssuanceID; - if (!mptIssuanceID.parseHex(mptIssuanceIdStr)) - Throw("Cannot parse mpt_issuance_id"); - - auto const account = - parseBase58(mptJson[jss::account].asString()); - - if (!account || account->isZero()) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } - - return keylet::mptoken(mptIssuanceID, *account).key; - } - catch (std::runtime_error const&) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } + return parseObjectID(params, fieldName, "hex string"); } -static std::optional +static Expected +parseLedgerHashes(Json::Value const& params, Json::StaticString const fieldName) +{ + return parseObjectID(params, fieldName, "hex string"); +} + +static Expected +parseMPToken(Json::Value const& params, Json::StaticString const fieldName) +{ + if (!params.isObject()) + { + return parseObjectID(params, fieldName); + } + + auto const mptIssuanceID = LedgerEntryHelpers::requiredUInt192( + params, jss::mpt_issuance_id, "malformedMPTIssuanceID"); + if (!mptIssuanceID) + return Unexpected(mptIssuanceID.error()); + + auto const account = LedgerEntryHelpers::requiredAccountID( + params, jss::account, "malformedAccount"); + if (!account) + return Unexpected(account.error()); + + return keylet::mptoken(*mptIssuanceID, *account).key; +} + +static Expected parseMPTokenIssuance( - Json::Value const& unparsedMPTIssuanceID, - Json::Value& jvResult) + Json::Value const& params, + Json::StaticString const fieldName) { - if (unparsedMPTIssuanceID.isString()) - { - uint192 mptIssuanceID; - if (!mptIssuanceID.parseHex(unparsedMPTIssuanceID.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } + auto const mptIssuanceID = LedgerEntryHelpers::parse(params); + if (!mptIssuanceID) + return LedgerEntryHelpers::invalidFieldError( + "malformedMPTokenIssuance", fieldName, "Hash192"); - return keylet::mptIssuance(mptIssuanceID).key; - } - - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return keylet::mptIssuance(*mptIssuanceID).key; } -static std::optional -parseNFTokenPage(Json::Value const& params, Json::Value& jvResult) +static Expected +parseNFTokenOffer(Json::Value const& params, Json::StaticString const fieldName) { - if (params.isString()) - { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; - } - - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return parseObjectID(params, fieldName, "hex string"); } -static std::optional -parseOffer(Json::Value const& params, Json::Value& jvResult) +static Expected +parseNFTokenPage(Json::Value const& params, Json::StaticString const fieldName) +{ + return parseObjectID(params, fieldName, "hex string"); +} + +static Expected +parseNegativeUNL(Json::Value const& params, Json::StaticString const fieldName) +{ + return parseObjectID(params, fieldName, "hex string"); +} + +static Expected +parseOffer(Json::Value const& params, Json::StaticString const fieldName) { if (!params.isObject()) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(params, fieldName); } - if (!params.isMember(jss::account) || !params.isMember(jss::seq) || - !params[jss::seq].isIntegral()) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - auto const id = parseBase58(params[jss::account].asString()); + auto const id = LedgerEntryHelpers::requiredAccountID( + params, jss::account, "malformedAddress"); if (!id) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } + return Unexpected(id.error()); - return keylet::offer(*id, params[jss::seq].asUInt()).key; + auto const seq = LedgerEntryHelpers::requiredUInt32( + params, jss::seq, "malformedRequest"); + if (!seq) + return Unexpected(seq.error()); + + return keylet::offer(*id, *seq).key; } -static std::optional -parseOracle(Json::Value const& params, Json::Value& jvResult) +static Expected +parseOracle(Json::Value const& params, Json::StaticString const fieldName) { if (!params.isObject()) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(params, fieldName); } - if (!params.isMember(jss::oracle_document_id) || - !params.isMember(jss::account)) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } + auto const id = LedgerEntryHelpers::requiredAccountID( + params, jss::account, "malformedAccount"); + if (!id) + return Unexpected(id.error()); - auto const& oracle = params; - auto const documentID = [&]() -> std::optional { - auto const id = oracle[jss::oracle_document_id]; - if (id.isUInt() || (id.isInt() && id.asInt() >= 0)) - return std::make_optional(id.asUInt()); + auto const seq = LedgerEntryHelpers::requiredUInt32( + params, jss::oracle_document_id, "malformedDocumentID"); + if (!seq) + return Unexpected(seq.error()); - if (id.isString()) - { - std::uint32_t v; - if (beast::lexicalCastChecked(v, id.asString())) - return std::make_optional(v); - } - - return std::nullopt; - }(); - - auto const account = - parseBase58(oracle[jss::account].asString()); - if (!account || account->isZero()) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } - - if (!documentID) - { - jvResult[jss::error] = "malformedDocumentID"; - return std::nullopt; - } - - return keylet::oracle(*account, *documentID).key; + return keylet::oracle(*id, *seq).key; } -static std::optional -parsePaymentChannel(Json::Value const& params, Json::Value& jvResult) +static Expected +parsePayChannel(Json::Value const& params, Json::StaticString const fieldName) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - return uNodeIndex; + return parseObjectID(params, fieldName, "hex string"); } -static std::optional -parsePermissionedDomains(Json::Value const& pd, Json::Value& jvResult) +static Expected +parsePermissionedDomain( + Json::Value const& pd, + Json::StaticString const fieldName) { if (pd.isString()) { - auto const index = parseIndex(pd, jvResult); - return index; + return parseObjectID(pd, fieldName); } if (!pd.isObject()) { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return LedgerEntryHelpers::invalidFieldError( + "malformedRequest", fieldName, "hex string or object"); } - if (!pd.isMember(jss::account)) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - if (!pd[jss::account].isString()) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } - - if (!pd.isMember(jss::seq) || - (pd[jss::seq].isInt() && pd[jss::seq].asInt() < 0) || - (!pd[jss::seq].isInt() && !pd[jss::seq].isUInt())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - auto const account = parseBase58(pd[jss::account].asString()); + auto const account = LedgerEntryHelpers::requiredAccountID( + pd, jss::account, "malformedAddress"); if (!account) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } + return Unexpected(account.error()); + + auto const seq = + LedgerEntryHelpers::requiredUInt32(pd, jss::seq, "malformedRequest"); + if (!seq) + return Unexpected(seq.error()); return keylet::permissionedDomain(*account, pd[jss::seq].asUInt()).key; } -static std::optional -parseRippleState(Json::Value const& jvRippleState, Json::Value& jvResult) +static Expected +parseRippleState( + Json::Value const& jvRippleState, + Json::StaticString const fieldName) { Currency uCurrency; - if (!jvRippleState.isObject() || !jvRippleState.isMember(jss::currency) || - !jvRippleState.isMember(jss::accounts) || - !jvRippleState[jss::accounts].isArray() || - 2 != jvRippleState[jss::accounts].size() || - !jvRippleState[jss::accounts][0u].isString() || - !jvRippleState[jss::accounts][1u].isString() || - (jvRippleState[jss::accounts][0u].asString() == - jvRippleState[jss::accounts][1u].asString())) + if (!jvRippleState.isObject()) { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return parseObjectID(jvRippleState, fieldName); + } + + if (auto const value = LedgerEntryHelpers::hasRequired( + jvRippleState, {jss::currency, jss::accounts}); + !value) + { + return Unexpected(value.error()); + } + + if (!jvRippleState[jss::accounts].isArray() || + jvRippleState[jss::accounts].size() != 2) + { + return LedgerEntryHelpers::invalidFieldError( + "malformedRequest", jss::accounts, "length-2 array of Accounts"); } auto const id1 = - parseBase58(jvRippleState[jss::accounts][0u].asString()); + LedgerEntryHelpers::parse(jvRippleState[jss::accounts][0u]); auto const id2 = - parseBase58(jvRippleState[jss::accounts][1u].asString()); + LedgerEntryHelpers::parse(jvRippleState[jss::accounts][1u]); if (!id1 || !id2) { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; + return LedgerEntryHelpers::invalidFieldError( + "malformedAddress", jss::accounts, "array of Accounts"); + } + if (id1 == id2) + { + return LedgerEntryHelpers::malformedError( + "malformedRequest", "Cannot have a trustline to self."); } - if (!to_currency(uCurrency, jvRippleState[jss::currency].asString())) + if (!jvRippleState[jss::currency].isString() || + jvRippleState[jss::currency] == "" || + !to_currency(uCurrency, jvRippleState[jss::currency].asString())) { - jvResult[jss::error] = "malformedCurrency"; - return std::nullopt; + return LedgerEntryHelpers::invalidFieldError( + "malformedCurrency", jss::currency, "Currency"); } return keylet::line(*id1, *id2, uCurrency).key; } -static std::optional -parseTicket(Json::Value const& params, Json::Value& jvResult) +static Expected +parseSignerList(Json::Value const& params, Json::StaticString const fieldName) +{ + return parseObjectID(params, fieldName, "hex string"); +} + +static Expected +parseTicket(Json::Value const& params, Json::StaticString const fieldName) { if (!params.isObject()) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(params, fieldName); } - if (!params.isMember(jss::account) || !params.isMember(jss::ticket_seq) || - !params[jss::ticket_seq].isIntegral()) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - auto const id = parseBase58(params[jss::account].asString()); + auto const id = LedgerEntryHelpers::requiredAccountID( + params, jss::account, "malformedAddress"); if (!id) - { - jvResult[jss::error] = "malformedAddress"; - return std::nullopt; - } + return Unexpected(id.error()); - return getTicketIndex(*id, params[jss::ticket_seq].asUInt()); + auto const seq = LedgerEntryHelpers::requiredUInt32( + params, jss::ticket_seq, "malformedRequest"); + if (!seq) + return Unexpected(seq.error()); + + return getTicketIndex(*id, *seq); } -static std::optional -parseVault(Json::Value const& params, Json::Value& jvResult) +static Expected +parseVault(Json::Value const& params, Json::StaticString const fieldName) { if (!params.isObject()) { - uint256 uNodeIndex; - if (!uNodeIndex.parseHex(params.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(params, fieldName); } - if (!params.isMember(jss::owner) || !params.isMember(jss::seq) || - !(params[jss::seq].isInt() || params[jss::seq].isUInt()) || - params[jss::seq].asDouble() <= 0.0 || - params[jss::seq].asDouble() > double(Json::Value::maxUInt)) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - - auto const id = parseBase58(params[jss::owner].asString()); + auto const id = LedgerEntryHelpers::requiredAccountID( + params, jss::owner, "malformedOwner"); if (!id) - { - jvResult[jss::error] = "malformedOwner"; - return std::nullopt; - } + return Unexpected(id.error()); - return keylet::vault(*id, params[jss::seq].asUInt()).key; + auto const seq = LedgerEntryHelpers::requiredUInt32( + params, jss::seq, "malformedRequest"); + if (!seq) + return Unexpected(seq.error()); + + return keylet::vault(*id, *seq).key; } -static std::optional -parseXChainOwnedClaimID(Json::Value const& claim_id, Json::Value& jvResult) +static Expected +parseXChainOwnedClaimID( + Json::Value const& claim_id, + Json::StaticString const fieldName) { - if (claim_id.isString()) + if (!claim_id.isObject()) { - uint256 uNodeIndex; - // we accept a node id as specifier of a xchain claim id - if (!uNodeIndex.parseHex(claim_id.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(claim_id, fieldName); } - if (!claim_id.isObject() || - !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) && - claim_id[sfIssuingChainDoor.getJsonName()].isString()) || - !(claim_id.isMember(sfLockingChainDoor.getJsonName()) && - claim_id[sfLockingChainDoor.getJsonName()].isString()) || - !claim_id.isMember(sfIssuingChainIssue.getJsonName()) || - !claim_id.isMember(sfLockingChainIssue.getJsonName()) || - !claim_id.isMember(jss::xchain_owned_claim_id)) + auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id); + if (!bridge_spec) + return Unexpected(bridge_spec.error()); + + auto const seq = LedgerEntryHelpers::requiredUInt32( + claim_id, jss::xchain_owned_claim_id, "malformedXChainOwnedClaimID"); + if (!seq) { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return Unexpected(seq.error()); } - // if not specified with a node id, a claim_id is specified by - // four strings defining the bridge (locking_chain_door, - // locking_chain_issue, issuing_chain_door, issuing_chain_issue) - // and the claim id sequence number. - auto const lockingChainDoor = parseBase58( - claim_id[sfLockingChainDoor.getJsonName()].asString()); - auto const issuingChainDoor = parseBase58( - claim_id[sfIssuingChainDoor.getJsonName()].asString()); - Issue lockingChainIssue, issuingChainIssue; - bool valid = lockingChainDoor && issuingChainDoor; - - if (valid) - { - try - { - lockingChainIssue = - issueFromJson(claim_id[sfLockingChainIssue.getJsonName()]); - issuingChainIssue = - issueFromJson(claim_id[sfIssuingChainIssue.getJsonName()]); - } - catch (std::runtime_error const& ex) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - } - - if (valid && claim_id[jss::xchain_owned_claim_id].isIntegral()) - { - auto const seq = claim_id[jss::xchain_owned_claim_id].asUInt(); - - STXChainBridge bridge_spec( - *lockingChainDoor, - lockingChainIssue, - *issuingChainDoor, - issuingChainIssue); - Keylet keylet = keylet::xChainClaimID(bridge_spec, seq); - return keylet.key; - } - - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + Keylet keylet = keylet::xChainClaimID(*bridge_spec, *seq); + return keylet.key; } -static std::optional +static Expected parseXChainOwnedCreateAccountClaimID( Json::Value const& claim_id, - Json::Value& jvResult) + Json::StaticString const fieldName) { - if (claim_id.isString()) + if (!claim_id.isObject()) { - uint256 uNodeIndex; - // we accept a node id as specifier of a xchain create account - // claim_id - if (!uNodeIndex.parseHex(claim_id.asString())) - { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; - } - return uNodeIndex; + return parseObjectID(claim_id, fieldName); } - if (!claim_id.isObject() || - !(claim_id.isMember(sfIssuingChainDoor.getJsonName()) && - claim_id[sfIssuingChainDoor.getJsonName()].isString()) || - !(claim_id.isMember(sfLockingChainDoor.getJsonName()) && - claim_id[sfLockingChainDoor.getJsonName()].isString()) || - !claim_id.isMember(sfIssuingChainIssue.getJsonName()) || - !claim_id.isMember(sfLockingChainIssue.getJsonName()) || - !claim_id.isMember(jss::xchain_owned_create_account_claim_id)) + auto const bridge_spec = LedgerEntryHelpers::parseBridgeFields(claim_id); + if (!bridge_spec) + return Unexpected(bridge_spec.error()); + + auto const seq = LedgerEntryHelpers::requiredUInt32( + claim_id, + jss::xchain_owned_create_account_claim_id, + "malformedXChainOwnedCreateAccountClaimID"); + if (!seq) { - jvResult[jss::error] = "malformedRequest"; - return std::nullopt; + return Unexpected(seq.error()); } - // if not specified with a node id, a create account claim_id is - // specified by four strings defining the bridge - // (locking_chain_door, locking_chain_issue, issuing_chain_door, - // issuing_chain_issue) and the create account claim id sequence - // number. - auto const lockingChainDoor = parseBase58( - claim_id[sfLockingChainDoor.getJsonName()].asString()); - auto const issuingChainDoor = parseBase58( - claim_id[sfIssuingChainDoor.getJsonName()].asString()); - Issue lockingChainIssue, issuingChainIssue; - bool valid = lockingChainDoor && issuingChainDoor; - if (valid) - { - try - { - lockingChainIssue = - issueFromJson(claim_id[sfLockingChainIssue.getJsonName()]); - issuingChainIssue = - issueFromJson(claim_id[sfIssuingChainIssue.getJsonName()]); - } - catch (std::runtime_error const& ex) - { - valid = false; - jvResult[jss::error] = "malformedRequest"; - } - } - - if (valid && - claim_id[jss::xchain_owned_create_account_claim_id].isIntegral()) - { - auto const seq = - claim_id[jss::xchain_owned_create_account_claim_id].asUInt(); - - STXChainBridge bridge_spec( - *lockingChainDoor, - lockingChainIssue, - *issuingChainDoor, - issuingChainIssue); - Keylet keylet = keylet::xChainCreateAccountClaimID(bridge_spec, seq); - return keylet.key; - } - - return std::nullopt; + Keylet keylet = keylet::xChainCreateAccountClaimID(*bridge_spec, *seq); + return keylet.key; } -using FunctionType = - std::function(Json::Value const&, Json::Value&)>; +using FunctionType = Expected (*)( + Json::Value const&, + Json::StaticString const); struct LedgerEntry { @@ -944,50 +700,49 @@ struct LedgerEntry Json::Value doLedgerEntry(RPC::JsonContext& context) { + static auto ledgerEntryParsers = std::to_array({ +#pragma push_macro("LEDGER_ENTRY") +#undef LEDGER_ENTRY + +#define LEDGER_ENTRY(tag, value, name, rpcName, fields) \ + {jss::rpcName, parse##name, tag}, + +#include + +#undef LEDGER_ENTRY +#pragma pop_macro("LEDGER_ENTRY") + {jss::index, parseIndex, ltANY}, + // aliases + {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, + {jss::ripple_state, parseRippleState, ltRIPPLE_STATE}, + }); + + auto hasMoreThanOneMember = [&]() { + int count = 0; + + for (auto const& ledgerEntry : ledgerEntryParsers) + { + if (context.params.isMember(ledgerEntry.fieldName)) + { + count++; + if (count > 1) // Early exit if more than one is found + return true; + } + } + return false; // Return false if <= 1 is found + }(); + + if (hasMoreThanOneMember) + { + return RPC::make_param_error("Too many fields provided."); + } + std::shared_ptr lpLedger; auto jvResult = RPC::lookupLedger(lpLedger, context); if (!lpLedger) return jvResult; - static auto ledgerEntryParsers = std::to_array({ - {jss::index, parseIndex, ltANY}, - {jss::account_root, parseAccountRoot, ltACCOUNT_ROOT}, - // TODO: add amendments - {jss::amm, parseAMM, ltAMM}, - {jss::bridge, parseBridge, ltBRIDGE}, - {jss::check, parseCheck, ltCHECK}, - {jss::credential, parseCredential, ltCREDENTIAL}, - {jss::delegate, parseDelegate, ltDELEGATE}, - {jss::deposit_preauth, parseDepositPreauth, ltDEPOSIT_PREAUTH}, - {jss::did, parseDID, ltDID}, - {jss::directory, parseDirectory, ltDIR_NODE}, - {jss::escrow, parseEscrow, ltESCROW}, - // TODO: add fee, hashes - {jss::mpt_issuance, parseMPTokenIssuance, ltMPTOKEN_ISSUANCE}, - {jss::mptoken, parseMPToken, ltMPTOKEN}, - // TODO: add NFT Offers - {jss::nft_page, parseNFTokenPage, ltNFTOKEN_PAGE}, - // TODO: add NegativeUNL - {jss::offer, parseOffer, ltOFFER}, - {jss::oracle, parseOracle, ltORACLE}, - {jss::payment_channel, parsePaymentChannel, ltPAYCHAN}, - {jss::permissioned_domain, - parsePermissionedDomains, - ltPERMISSIONED_DOMAIN}, - {jss::ripple_state, parseRippleState, ltRIPPLE_STATE}, - // This is an alias, since the `ledger_data` filter uses jss::state - {jss::state, parseRippleState, ltRIPPLE_STATE}, - {jss::ticket, parseTicket, ltTICKET}, - {jss::xchain_owned_claim_id, - parseXChainOwnedClaimID, - ltXCHAIN_OWNED_CLAIM_ID}, - {jss::xchain_owned_create_account_claim_id, - parseXChainOwnedCreateAccountClaimID, - ltXCHAIN_OWNED_CREATE_ACCOUNT_CLAIM_ID}, - {jss::vault, parseVault, ltVAULT}, - }); - uint256 uNodeIndex; LedgerEntryType expectedType = ltANY; @@ -1006,34 +761,33 @@ doLedgerEntry(RPC::JsonContext& context) Json::Value const& params = ledgerEntry.fieldName == jss::bridge ? context.params : context.params[ledgerEntry.fieldName]; - uNodeIndex = ledgerEntry.parseFunction(params, jvResult) - .value_or(beast::zero); - if (jvResult.isMember(jss::error)) - { - return jvResult; - } + auto const result = + ledgerEntry.parseFunction(params, ledgerEntry.fieldName); + if (!result) + return result.error(); + + uNodeIndex = result.value(); found = true; break; } } - if (!found) { if (context.apiVersion < 2u) + { jvResult[jss::error] = "unknownOption"; - else - jvResult[jss::error] = "invalidParams"; - return jvResult; + return jvResult; + } + return RPC::make_param_error("No ledger_entry params provided."); } } catch (Json::error& e) { if (context.apiVersion > 1u) { - // For apiVersion 2 onwards, any parsing failures that throw this - // exception return an invalidParam error. - jvResult[jss::error] = "invalidParams"; - return jvResult; + // For apiVersion 2 onwards, any parsing failures that throw + // this exception return an invalidParam error. + return RPC::make_error(rpcINVALID_PARAMS); } else throw; @@ -1041,8 +795,7 @@ doLedgerEntry(RPC::JsonContext& context) if (uNodeIndex.isZero()) { - jvResult[jss::error] = "entryNotFound"; - return jvResult; + return RPC::make_error(rpcENTRY_NOT_FOUND); } auto const sleNode = lpLedger->read(keylet::unchecked(uNodeIndex)); @@ -1054,14 +807,12 @@ doLedgerEntry(RPC::JsonContext& context) if (!sleNode) { // Not found. - jvResult[jss::error] = "entryNotFound"; - return jvResult; + return RPC::make_error(rpcENTRY_NOT_FOUND); } if ((expectedType != ltANY) && (expectedType != sleNode->getType())) { - jvResult[jss::error] = "unexpectedLedgerType"; - return jvResult; + return RPC::make_error(rpcUNEXPECTED_LEDGER_TYPE); } if (bNodeBinary) @@ -1091,7 +842,7 @@ doLedgerEntryGrpc( grpc::Status status = grpc::Status::OK; std::shared_ptr ledger; - if (auto const status = RPC::ledgerFromRequest(ledger, context)) + if (auto status = RPC::ledgerFromRequest(ledger, context)) { grpc::Status errorStatus; if (status.toErrorCode() == rpcINVALID_PARAMS) diff --git a/src/xrpld/rpc/handlers/LedgerEntryHelpers.h b/src/xrpld/rpc/handlers/LedgerEntryHelpers.h new file mode 100644 index 0000000000..12b99dbbff --- /dev/null +++ b/src/xrpld/rpc/handlers/LedgerEntryHelpers.h @@ -0,0 +1,299 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012-2025 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace ripple { + +namespace LedgerEntryHelpers { + +Unexpected +missingFieldError( + Json::StaticString const field, + std::optional err = std::nullopt) +{ + Json::Value json = Json::objectValue; + auto error = RPC::missing_field_message(std::string(field.c_str())); + json[jss::error] = err.value_or("malformedRequest"); + json[jss::error_code] = rpcINVALID_PARAMS; + json[jss::error_message] = std::move(error); + return Unexpected(json); +} + +Unexpected +invalidFieldError( + std::string const& err, + Json::StaticString const field, + std::string const& type) +{ + Json::Value json = Json::objectValue; + auto error = RPC::expected_field_message(field, type); + json[jss::error] = err; + json[jss::error_code] = rpcINVALID_PARAMS; + json[jss::error_message] = std::move(error); + return Unexpected(json); +} + +Unexpected +malformedError(std::string const& err, std::string const& message) +{ + Json::Value json = Json::objectValue; + json[jss::error] = err; + json[jss::error_code] = rpcINVALID_PARAMS; + json[jss::error_message] = message; + return Unexpected(json); +} + +Expected +hasRequired( + Json::Value const& params, + std::initializer_list fields, + std::optional err = std::nullopt) +{ + for (auto const field : fields) + { + if (!params.isMember(field) || params[field].isNull()) + { + return missingFieldError(field, err); + } + } + return true; +} + +template +std::optional +parse(Json::Value const& param); + +template +Expected +required( + Json::Value const& params, + Json::StaticString const fieldName, + std::string const& err, + std::string const& expectedType) +{ + if (!params.isMember(fieldName) || params[fieldName].isNull()) + { + return missingFieldError(fieldName); + } + if (auto obj = parse(params[fieldName])) + { + return *obj; + } + return invalidFieldError(err, fieldName, expectedType); +} + +template <> +std::optional +parse(Json::Value const& param) +{ + if (!param.isString()) + return std::nullopt; + + auto const account = parseBase58(param.asString()); + if (!account || account->isZero()) + { + return std::nullopt; + } + + return account; +} + +Expected +requiredAccountID( + Json::Value const& params, + Json::StaticString const fieldName, + std::string const& err) +{ + return required(params, fieldName, err, "AccountID"); +} + +std::optional +parseHexBlob(Json::Value const& param, std::size_t maxLength) +{ + if (!param.isString()) + return std::nullopt; + + auto const blob = strUnHex(param.asString()); + if (!blob || blob->empty() || blob->size() > maxLength) + return std::nullopt; + + return blob; +} + +Expected +requiredHexBlob( + Json::Value const& params, + Json::StaticString const fieldName, + std::size_t maxLength, + std::string const& err) +{ + if (!params.isMember(fieldName) || params[fieldName].isNull()) + { + return missingFieldError(fieldName); + } + if (auto blob = parseHexBlob(params[fieldName], maxLength)) + { + return *blob; + } + return invalidFieldError(err, fieldName, "hex string"); +} + +template <> +std::optional +parse(Json::Value const& param) +{ + if (param.isUInt() || (param.isInt() && param.asInt() >= 0)) + return param.asUInt(); + + if (param.isString()) + { + std::uint32_t v; + if (beast::lexicalCastChecked(v, param.asString())) + return v; + } + + return std::nullopt; +} + +Expected +requiredUInt32( + Json::Value const& params, + Json::StaticString const fieldName, + std::string const& err) +{ + return required(params, fieldName, err, "number"); +} + +template <> +std::optional +parse(Json::Value const& param) +{ + uint256 uNodeIndex; + if (!param.isString() || !uNodeIndex.parseHex(param.asString())) + { + return std::nullopt; + } + + return uNodeIndex; +} + +Expected +requiredUInt256( + Json::Value const& params, + Json::StaticString const fieldName, + std::string const& err) +{ + return required(params, fieldName, err, "Hash256"); +} + +template <> +std::optional +parse(Json::Value const& param) +{ + uint192 field; + if (!param.isString() || !field.parseHex(param.asString())) + { + return std::nullopt; + } + + return field; +} + +Expected +requiredUInt192( + Json::Value const& params, + Json::StaticString const fieldName, + std::string const& err) +{ + return required(params, fieldName, err, "Hash192"); +} + +Expected +parseBridgeFields(Json::Value const& params) +{ + if (auto const value = hasRequired( + params, + {jss::LockingChainDoor, + jss::LockingChainIssue, + jss::IssuingChainDoor, + jss::IssuingChainIssue}); + !value) + { + return Unexpected(value.error()); + } + + auto const lockingChainDoor = requiredAccountID( + params, jss::LockingChainDoor, "malformedLockingChainDoor"); + if (!lockingChainDoor) + { + return Unexpected(lockingChainDoor.error()); + } + + auto const issuingChainDoor = requiredAccountID( + params, jss::IssuingChainDoor, "malformedIssuingChainDoor"); + if (!issuingChainDoor) + { + return Unexpected(issuingChainDoor.error()); + } + + Issue lockingChainIssue; + try + { + lockingChainIssue = issueFromJson(params[jss::LockingChainIssue]); + } + catch (std::runtime_error const& ex) + { + return invalidFieldError( + "malformedIssue", jss::LockingChainIssue, "Issue"); + } + + Issue issuingChainIssue; + try + { + issuingChainIssue = issueFromJson(params[jss::IssuingChainIssue]); + } + catch (std::runtime_error const& ex) + { + return invalidFieldError( + "malformedIssue", jss::IssuingChainIssue, "Issue"); + } + + return STXChainBridge( + *lockingChainDoor, + lockingChainIssue, + *issuingChainDoor, + issuingChainIssue); +} + +} // namespace LedgerEntryHelpers + +} // namespace ripple From 2e6f00aef250a3c6fd88b680794c2d78e93fe361 Mon Sep 17 00:00:00 2001 From: Bronek Kozicki Date: Wed, 3 Sep 2025 09:25:52 +0100 Subject: [PATCH 32/66] Add required disable_ccache option (#5756) --- .github/workflows/build-test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 05b65edbfd..90e1d9853c 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -117,6 +117,8 @@ jobs: uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - name: Prepare runner uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5 + with: + disable_ccache: false - name: Check configuration (Windows) if: ${{ inputs.os == 'windows' }} From 724e9b1313bda87199efdb05fe452d5b18def35e Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 3 Sep 2025 11:24:07 +0100 Subject: [PATCH 33/66] chore: Use conan lockfile (#5751) * chore: Use conan lockfile * Add windows-specific dependencies as well * Add more info about lockfiles * Update lockfile to latest version * Update BUILD.md with conan install note --- .pre-commit-config.yaml | 3 ++- BUILD.md | 22 +++++++++++++++- conan.lock | 56 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 conan.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3bd60b76d0..223c324a8c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -60,5 +60,6 @@ repos: exclude: | (?x)^( external/.*| - .github/scripts/levelization/results/.*\.txt + .github/scripts/levelization/results/.*\.txt| + conan\.lock )$ diff --git a/BUILD.md b/BUILD.md index c8ec31f826..6b1594bb5e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -158,6 +158,10 @@ updated dependencies with the newer version. However, if we switch to a newer version that no longer requires a patch, no action is required on your part, as the new recipe will be automatically pulled from the official Conan Center. +> [!NOTE] +> You might need to add `--lockfile=""` to your `conan install` command +> to avoid automatic use of the existing `conan.lock` file when you run `conan export` manually on your machine + ### Conan profile tweaks #### Missing compiler version @@ -466,6 +470,21 @@ tools.build:cxxflags=['-DBOOST_ASIO_DISABLE_CONCEPTS'] The location of `rippled` binary in your build directory depends on your CMake generator. Pass `--help` to see the rest of the command line options. +#### Conan lockfile + +To achieve reproducible dependencies, we use [Conan lockfile](https://docs.conan.io/2/tutorial/versioning/lockfiles.html). + +The `conan.lock` file in the repository contains a "snapshot" of the current dependencies. +It is implicitly used when running `conan` commands, you don't need to specify it. + +You have to update this file every time you add a new dependency or change a revision or version of an existing dependency. + +To do that, run the following command in the repository root: + +```bash +conan lock create . -o '&:jemalloc=True' -o '&:rocksdb=True' +``` + ## Coverage report The coverage report is intended for developers using compilers GCC @@ -564,7 +583,8 @@ After any updates or changes to dependencies, you may need to do the following: ``` 3. Re-run [conan export](#patched-recipes) if needed. -4. Re-run [conan install](#build-and-test). +4. [Regenerate lockfile](#conan-lockfile). +5. Re-run [conan install](#build-and-test). ### `protobuf/port_def.inc` file not found diff --git a/conan.lock b/conan.lock new file mode 100644 index 0000000000..0f11f086b4 --- /dev/null +++ b/conan.lock @@ -0,0 +1,56 @@ +{ + "version": "0.5", + "requires": [ + "zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497", + "xxhash/0.8.3#681d36a0a6111fc56e5e45ea182c19cc%1756234289.683", + "sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1756234266.869", + "soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1756234262.318", + "snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246", + "rocksdb/10.0.1#85537f46e538974d67da0c3977de48ac%1756234304.347", + "re2/20230301#dfd6e2bf050eb90ddd8729cfb4c844a4%1756234257.976", + "protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614", + "openssl/3.5.2#0c5a5e15ae569f45dff57adcf1770cf7%1756234259.61", + "nudb/2.0.9#c62cfd501e57055a7e0d8ee3d5e5427d%1756234237.107", + "lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1756234228.999", + "libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1756223727.64", + "libbacktrace/cci.20210118#a7691bfccd8caaf66309df196790a5a1%1756230911.03", + "libarchive/3.8.1#5cf685686322e906cb42706ab7e099a8%1756234256.696", + "jemalloc/5.3.0#e951da9cf599e956cebc117880d2d9f8%1729241615.244", + "grpc/1.50.1#02291451d1e17200293a409410d1c4e1%1756234248.958", + "doctest/2.4.11#a4211dfc329a16ba9f280f9574025659%1756234220.819", + "date/3.0.4#f74bbba5a08fa388256688743136cb6f%1756234217.493", + "c-ares/1.34.5#b78b91e7cfb1f11ce777a285bbf169c6%1756234217.915", + "bzip2/1.0.8#00b4a4658791c1f06914e087f0e792f5%1756234261.716", + "boost/1.88.0#8852c0b72ce8271fb8ff7c53456d4983%1756223752.326", + "abseil/20230802.1#f0f91485b111dc9837a68972cb19ca7b%1756234220.907" + ], + "build_requires": [ + "zlib/1.3.1#b8bc2603263cf7eccbd6e17e66b0ed76%1756234269.497", + "strawberryperl/5.32.1.1#707032463aa0620fa17ec0d887f5fe41%1756234281.733", + "protobuf/3.21.12#d927114e28de9f4691a6bbcdd9a529d1%1756234251.614", + "nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1756234232.901", + "msys2/cci.latest#5b73b10144f73cc5bfe0572ed9be39e1%1751977009.857", + "m4/1.4.19#b38ced39a01e31fef5435bc634461fd2%1700758725.451", + "cmake/3.31.8#dde3bde00bb843687e55aea5afa0e220%1756234232.89", + "b2/5.3.3#107c15377719889654eb9a162a673975%1756234226.28", + "automake/1.16.5#b91b7c384c3deaa9d535be02da14d04f%1755524470.56", + "autoconf/2.71#51077f068e61700d65bb05541ea1e4b0%1731054366.86" + ], + "python_requires": [], + "overrides": { + "protobuf/3.21.12": [ + null, + "protobuf/3.21.12" + ], + "lz4/1.9.4": [ + "lz4/1.10.0" + ], + "boost/1.83.0": [ + "boost/1.88.0" + ], + "sqlite3/3.44.2": [ + "sqlite3/3.49.1" + ] + }, + "config_requires": [] +} \ No newline at end of file From c38f2a3f2e44c00885c703fba035fb606c7a61c5 Mon Sep 17 00:00:00 2001 From: Jingchen Date: Wed, 3 Sep 2025 17:08:02 +0100 Subject: [PATCH 34/66] Fix coverage parameter (#5760) --- cmake/CodeCoverage.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index 09ec3b9569..ec601de453 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -218,12 +218,12 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") endif() - check_cxx_compiler_flag(-fprofile-update HAVE_cxx_fprofile_update) + check_cxx_compiler_flag(-fprofile-update=atomic HAVE_cxx_fprofile_update) if(HAVE_cxx_fprofile_update) set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-update=atomic") endif() - check_c_compiler_flag(-fprofile-update HAVE_c_fprofile_update) + check_c_compiler_flag(-fprofile-update=atomic HAVE_c_fprofile_update) if(HAVE_c_fprofile_update) set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-update=atomic") endif() From cf5f65b68eadf86be7b0f571dc0437830ddb4234 Mon Sep 17 00:00:00 2001 From: Bronek Kozicki Date: Thu, 4 Sep 2025 09:54:24 +0100 Subject: [PATCH 35/66] Add `Scale` to SingleAssetVault (#5652) * Add and Scale to VaultCreate * Add round-trip calculation to VaultDeposit VaultWithdraw and VaultClawback * Implement Number::truncate() for VaultClawback * Add rounding to DepositWithdraw * Disallow zero shares withdraw or deposit with tecPRECISION_LOSS * Return tecPATH_DRY on overflow when converting shares/assets * Remove empty shares MPToken in clawback or withdraw (except for vault owner) * Implicitly create shares MPToken for vault owner in VaultCreate * Review feedback: defensive checks in shares/assets calculations --------- Co-authored-by: Ed Hennis --- include/xrpl/basics/Number.h | 18 + include/xrpl/protocol/Protocol.h | 7 + .../xrpl/protocol/detail/ledger_entries.macro | 1 + .../xrpl/protocol/detail/transactions.macro | 1 + src/test/app/Vault_test.cpp | 1427 +++++++++++++++-- src/test/basics/Number_test.cpp | 25 + src/test/jtx/Account.h | 4 + src/test/jtx/impl/Account.cpp | 8 + src/xrpld/app/tx/detail/InvariantCheck.cpp | 6 + src/xrpld/app/tx/detail/VaultClawback.cpp | 173 +- src/xrpld/app/tx/detail/VaultCreate.cpp | 55 +- src/xrpld/app/tx/detail/VaultDelete.cpp | 22 +- src/xrpld/app/tx/detail/VaultDeposit.cpp | 85 +- src/xrpld/app/tx/detail/VaultSet.cpp | 6 +- src/xrpld/app/tx/detail/VaultWithdraw.cpp | 134 +- src/xrpld/ledger/View.h | 39 +- src/xrpld/ledger/detail/View.cpp | 79 +- 17 files changed, 1869 insertions(+), 221 deletions(-) diff --git a/include/xrpl/basics/Number.h b/include/xrpl/basics/Number.h index 9ee05bfb45..41c60d30a1 100644 --- a/include/xrpl/basics/Number.h +++ b/include/xrpl/basics/Number.h @@ -150,6 +150,24 @@ public: return (mantissa_ < 0) ? -1 : (mantissa_ ? 1 : 0); } + Number + truncate() const noexcept + { + if (exponent_ >= 0 || mantissa_ == 0) + return *this; + + Number ret = *this; + while (ret.exponent_ < 0 && ret.mantissa_ != 0) + { + ret.exponent_ += 1; + ret.mantissa_ /= rep(10); + } + // We are guaranteed that normalize() will never throw an exception + // because exponent is either negative or zero at this point. + ret.normalize(); + return ret; + } + friend constexpr bool operator>(Number const& x, Number const& y) noexcept { diff --git a/include/xrpl/protocol/Protocol.h b/include/xrpl/protocol/Protocol.h index 898fd06fbd..a0fcfee34c 100644 --- a/include/xrpl/protocol/Protocol.h +++ b/include/xrpl/protocol/Protocol.h @@ -122,6 +122,13 @@ std::size_t constexpr maxDataPayloadLength = 256; /** Vault withdrawal policies */ std::uint8_t constexpr vaultStrategyFirstComeFirstServe = 1; +/** Default IOU scale factor for a Vault */ +std::uint8_t constexpr vaultDefaultIOUScale = 6; +/** Maximum scale factor for a Vault. The number is chosen to ensure that +1 IOU can be always converted to shares. +10^19 > maxMPTokenAmount (2^64-1) > 10^18 */ +std::uint8_t constexpr vaultMaximumIOUScale = 18; + /** Maximum recursion depth for vault shares being put as an asset inside * another vault; counted from 0 */ std::uint8_t constexpr maxAssetCheckDepth = 5; diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index 967fb37b94..ac9ebc6069 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -499,6 +499,7 @@ LEDGER_ENTRY(ltVAULT, 0x0084, Vault, vault, ({ {sfLossUnrealized, soeREQUIRED}, {sfShareMPTID, soeREQUIRED}, {sfWithdrawalPolicy, soeREQUIRED}, + {sfScale, soeDEFAULT}, // no SharesTotal ever (use MPTIssuance.sfOutstandingAmount) // no PermissionedDomainID ever (use MPTIssuance.sfDomainID) })) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 89e9a16df5..1131e24f61 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -483,6 +483,7 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, Delegation::delegatable, ({ {sfDomainID, soeOPTIONAL}, {sfWithdrawalPolicy, soeOPTIONAL}, {sfData, soeOPTIONAL}, + {sfScale, soeOPTIONAL}, })) /** This transaction updates a single asset vault. */ diff --git a/src/test/app/Vault_test.cpp b/src/test/app/Vault_test.cpp index 7add8b3eda..2216ff6421 100644 --- a/src/test/app/Vault_test.cpp +++ b/src/test/app/Vault_test.cpp @@ -19,11 +19,14 @@ #include #include +#include +#include #include #include #include +#include #include #include #include @@ -73,12 +76,32 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); BEAST_EXPECT(env.le(keylet)); + std::uint64_t const scale = asset.raw().holds() ? 1 : 1e6; - auto const share = [&env, keylet = keylet, this]() -> PrettyAsset { + auto const [share, vaultAccount] = + [&env, + keylet = keylet, + asset, + this]() -> std::tuple { auto const vault = env.le(keylet); BEAST_EXPECT(vault != nullptr); - return MPTIssue(vault->at(sfShareMPTID)); + if (asset.raw().holds() && !asset.raw().native()) + BEAST_EXPECT(vault->at(sfScale) == 6); + else + BEAST_EXPECT(vault->at(sfScale) == 0); + auto const shares = + env.le(keylet::mptIssuance(vault->at(sfShareMPTID))); + BEAST_EXPECT(shares != nullptr); + if (asset.raw().holds() && !asset.raw().native()) + BEAST_EXPECT(shares->at(sfAssetScale) == 6); + else + BEAST_EXPECT(shares->at(sfAssetScale) == 0); + return { + MPTIssue(vault->at(sfShareMPTID)), + Account("vault", vault->at(sfAccount))}; }(); + auto const shares = share.raw().get(); + env.memoize(vaultAccount); // Several 3rd party accounts which cannot receive funds Account alice{"alice"}; @@ -96,6 +119,7 @@ class Vault_test : public beast::unit_test::suite .id = keylet.key, .amount = asset(10000)}); env(tx, ter(tecINSUFFICIENT_FUNDS)); + env.close(); } { @@ -105,6 +129,9 @@ class Vault_test : public beast::unit_test::suite .id = keylet.key, .amount = asset(50)}); env(tx); + env.close(); + BEAST_EXPECT( + env.balance(depositor, shares) == share(50 * scale)); } { @@ -114,12 +141,16 @@ class Vault_test : public beast::unit_test::suite .id = keylet.key, .amount = asset(50)}); env(tx); + env.close(); + BEAST_EXPECT( + env.balance(depositor, shares) == share(100 * scale)); } { testcase(prefix + " fail to delete non-empty vault"); auto tx = vault.del({.owner = owner, .id = keylet.key}); env(tx, ter(tecHAS_OBLIGATIONS)); + env.close(); } { @@ -127,6 +158,7 @@ class Vault_test : public beast::unit_test::suite auto tx = vault.set({.owner = issuer, .id = keylet.key}); tx[sfAssetsMaximum] = asset(50).number(); env(tx, ter(tecNO_PERMISSION)); + env.close(); } { @@ -135,6 +167,7 @@ class Vault_test : public beast::unit_test::suite auto tx = vault.set({.owner = owner, .id = keylet.key}); tx[sfAssetsMaximum] = asset(50).number(); env(tx, ter(tecLIMIT_EXCEEDED)); + env.close(); } { @@ -142,6 +175,7 @@ class Vault_test : public beast::unit_test::suite auto tx = vault.set({.owner = owner, .id = keylet.key}); tx[sfAssetsMaximum] = asset(150).number(); env(tx); + env.close(); } { @@ -149,6 +183,7 @@ class Vault_test : public beast::unit_test::suite auto tx = vault.set({.owner = owner, .id = keylet.key}); tx[sfData] = "0"; env(tx); + env.close(); } { @@ -156,6 +191,7 @@ class Vault_test : public beast::unit_test::suite auto tx = vault.set({.owner = owner, .id = keylet.key}); tx[sfDomainID] = to_string(base_uint<256>(42ul)); env(tx, ter{tecNO_PERMISSION}); + env.close(); } { @@ -165,6 +201,7 @@ class Vault_test : public beast::unit_test::suite .id = keylet.key, .amount = asset(100)}); env(tx, ter(tecLIMIT_EXCEEDED)); + env.close(); } { @@ -172,6 +209,7 @@ class Vault_test : public beast::unit_test::suite auto tx = vault.set({.owner = owner, .id = keylet.key}); tx[sfAssetsMaximum] = asset(0).number(); env(tx); + env.close(); } { @@ -190,6 +228,9 @@ class Vault_test : public beast::unit_test::suite .id = keylet.key, .amount = asset(100)}); env(tx); + env.close(); + BEAST_EXPECT( + env.balance(depositor, shares) == share(200 * scale)); } { @@ -202,6 +243,12 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(10)}); env(tx, code); + env.close(); + if (!asset.raw().native()) + { + BEAST_EXPECT( + env.balance(depositor, shares) == share(190 * scale)); + } } { @@ -211,6 +258,30 @@ class Vault_test : public beast::unit_test::suite auto tx = vault.clawback( {.issuer = issuer, .id = keylet.key, .holder = depositor}); env(tx, code); + env.close(); + if (!asset.raw().native()) + { + BEAST_EXPECT(env.balance(depositor, shares) == share(0)); + + { + auto tx = vault.clawback( + {.issuer = issuer, + .id = keylet.key, + .holder = depositor, + .amount = asset(10)}); + env(tx, ter{tecPRECISION_LOSS}); + env.close(); + } + + { + auto tx = vault.withdraw( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(10)}); + env(tx, ter{tecPRECISION_LOSS}); + env.close(); + } + } } if (!asset.raw().native()) @@ -221,6 +292,9 @@ class Vault_test : public beast::unit_test::suite .id = keylet.key, .amount = asset(200)}); env(tx); + env.close(); + BEAST_EXPECT( + env.balance(depositor, shares) == share(200 * scale)); } { @@ -232,6 +306,7 @@ class Vault_test : public beast::unit_test::suite .amount = asset(100)}); tx[sfDestination] = alice.human(); env(tx, ter{tecNO_PERMISSION}); + env.close(); } { @@ -242,6 +317,7 @@ class Vault_test : public beast::unit_test::suite .amount = asset(1000)}); tx[sfDestination] = "0"; env(tx, ter(temMALFORMED)); + env.close(); } { @@ -254,6 +330,7 @@ class Vault_test : public beast::unit_test::suite .amount = asset(1000)}); tx[sfDestinationTag] = "0"; env(tx, ter(temMALFORMED)); + env.close(); } if (!asset.raw().native()) @@ -267,6 +344,77 @@ class Vault_test : public beast::unit_test::suite tx[sfDestination] = erin.human(); env(tx, ter{asset.raw().holds() ? tecNO_LINE : tecNO_AUTH}); + env.close(); + } + + { + testcase( + prefix + + " fail to withdraw to 3rd party lsfRequireDestTag"); + auto tx = vault.withdraw( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(100)}); + tx[sfDestination] = dave.human(); + env(tx, ter{tecDST_TAG_NEEDED}); + env.close(); + } + + { + testcase(prefix + " withdraw to authorized 3rd party"); + auto tx = vault.withdraw( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(100)}); + tx[sfDestination] = charlie.human(); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(depositor, shares) == share(100 * scale)); + } + + { + testcase(prefix + " withdraw to issuer"); + auto tx = vault.withdraw( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(50)}); + tx[sfDestination] = issuer.human(); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(depositor, shares) == share(50 * scale)); + } + + { + testcase(prefix + " withdraw remaining assets"); + auto tx = vault.withdraw( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(50)}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(depositor, shares) == share(0)); + + if (!asset.raw().native()) + { + auto tx = vault.clawback( + {.issuer = issuer, + .id = keylet.key, + .holder = depositor, + .amount = asset(0)}); + env(tx, ter{tecPRECISION_LOSS}); + env.close(); + } + + { + auto tx = vault.withdraw( + {.depositor = depositor, + .id = keylet.key, + .amount = share(10)}); + env(tx, ter{tecINSUFFICIENT_FUNDS}); + env.close(); + } } if (!asset.raw().native() && asset.raw().holds()) @@ -280,10 +428,27 @@ class Vault_test : public beast::unit_test::suite auto tx = vault.deposit( {.depositor = erin, .id = keylet.key, .amount = asset(10)}); env(tx); - env(pay(erin, depositor, share(10))); + env.close(); + { + auto tx = pay(erin, depositor, share(10 * scale)); + + // depositor no longer has MPToken for shares + env(tx, ter{tecNO_AUTH}); + env.close(); + + // depositor will gain MPToken for shares again + env(vault.deposit( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(1)})); + env.close(); + + env(tx); + env.close(); + } testcase(prefix + " withdraw to authorized 3rd party"); - // Depositor withdraws shares, destined to Erin + // Depositor withdraws assets, destined to Erin tx = vault.withdraw( {.depositor = depositor, .id = keylet.key, @@ -292,52 +457,23 @@ class Vault_test : public beast::unit_test::suite env(tx); // Erin returns assets to issuer env(pay(erin, issuer, asset(10))); + env.close(); testcase(prefix + " fail to pay to unauthorized 3rd party"); env(trust(erin, asset(0))); + env.close(); + // Erin has MPToken but is no longer authorized to hold assets env(pay(depositor, erin, share(1)), ter{tecNO_LINE}); - } + env.close(); - { - testcase( - prefix + - " fail to withdraw to 3rd party lsfRequireDestTag"); - auto tx = vault.withdraw( + // Depositor withdraws remaining single asset + tx = vault.withdraw( {.depositor = depositor, .id = keylet.key, - .amount = asset(100)}); - tx[sfDestination] = dave.human(); - env(tx, ter{tecDST_TAG_NEEDED}); - } - - { - testcase(prefix + " withdraw to authorized 3rd party"); - auto tx = vault.withdraw( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(100)}); - tx[sfDestination] = charlie.human(); - env(tx); - } - - { - testcase(prefix + " withdraw to issuer"); - auto tx = vault.withdraw( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(50)}); - tx[sfDestination] = issuer.human(); - env(tx); - } - - { - testcase(prefix + " withdraw remaining assets"); - auto tx = vault.withdraw( - {.depositor = depositor, - .id = keylet.key, - .amount = asset(50)}); + .amount = asset(1)}); env(tx); + env.close(); } { @@ -740,6 +876,61 @@ class Vault_test : public beast::unit_test::suite } }); + testCase([&](Env& env, + Account const&, + Account const& owner, + Asset const& asset, + Vault& vault) { + testcase("create with Scale"); + + { + auto [tx, keylet] = + vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 255; + env(tx, ter(temMALFORMED)); + } + + { + auto [tx, keylet] = + vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 19; + env(tx, ter(temMALFORMED)); + } + + // accepted range from 0 to 18 + { + auto [tx, keylet] = + vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 18; + env(tx); + env.close(); + auto const sleVault = env.le(keylet); + BEAST_EXPECT(sleVault); + BEAST_EXPECT((*sleVault)[sfScale] == 18); + } + + { + auto [tx, keylet] = + vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 0; + env(tx); + env.close(); + auto const sleVault = env.le(keylet); + BEAST_EXPECT(sleVault); + BEAST_EXPECT((*sleVault)[sfScale] == 0); + } + + { + auto [tx, keylet] = + vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + auto const sleVault = env.le(keylet); + BEAST_EXPECT(sleVault); + BEAST_EXPECT((*sleVault)[sfScale] == 6); + } + }); + testCase([&](Env& env, Account const&, Account const& owner, @@ -1105,6 +1296,32 @@ class Vault_test : public beast::unit_test::suite testcase("non-existing domain"); env(tx, ter{tecOBJECT_NOT_FOUND}); }); + + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + testcase("cannot set Scale=0"); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 0; + env(tx, ter{temMALFORMED}); + }); + + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + testcase("cannot set Scale=1"); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 1; + env(tx, ter{temMALFORMED}); + }); } void @@ -1221,21 +1438,65 @@ class Vault_test : public beast::unit_test::suite { using namespace test::jtx; - Env env{*this, testable_amendments() | featureSingleAssetVault}; - Account issuer{"issuer"}; - Account owner{"owner"}; - Account depositor{"depositor"}; - env.fund(XRP(1000), issuer, owner, depositor); - env.close(); - Vault vault{env}; + auto testCase = [this](std::function test) { + Env env{*this, testable_amendments() | featureSingleAssetVault}; + Account issuer{"issuer"}; + Account owner{"owner"}; + Account depositor{"depositor"}; + env.fund(XRP(1000), issuer, owner, depositor); + env.close(); + Vault vault{env}; + MPTTester mptt{env, issuer, mptInitNoFund}; + // Locked because that is the default flag. + mptt.create(); + Asset asset = mptt.issuanceID(); - MPTTester mptt{env, issuer, mptInitNoFund}; + test(env, issuer, owner, depositor, asset, vault); + }; - // Locked because that is the default flag. - mptt.create(); - Asset asset = mptt.issuanceID(); - auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); - env(tx, ter(tecNO_AUTH)); + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + testcase("MPT no authorization"); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx, ter(tecNO_AUTH)); + }); + + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + testcase("MPT cannot set Scale=0"); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 0; + env(tx, ter{temMALFORMED}); + }); + + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + Asset const& asset, + Vault& vault) { + testcase("MPT cannot set Scale=1"); + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = 1; + env(tx, ter{temMALFORMED}); + }); } void @@ -1285,7 +1546,7 @@ class Vault_test : public beast::unit_test::suite jvVault[jss::result][jss::vault][sfAssetsTotal] == "100"); BEAST_EXPECT( jvVault[jss::result][jss::vault][jss::shares] - [sfOutstandingAmount] == "100"); + [sfOutstandingAmount] == "100000000"); // Vault pseudo-account return parseBase58( @@ -1324,7 +1585,7 @@ class Vault_test : public beast::unit_test::suite jvVault[jss::result][jss::vault][sfAssetsTotal] == "50"); BEAST_EXPECT( jvVault[jss::result][jss::vault][jss::shares] - [sfOutstandingAmount] == "50"); + [sfOutstandingAmount] == "50000000"); } { @@ -1508,6 +1769,10 @@ class Vault_test : public beast::unit_test::suite env(tx); env.close(); + // Clawback removed shares MPToken + auto const mptSle = env.le(keylet::mptoken(share, depositor.id())); + BEAST_EXPECT(mptSle == nullptr); + // Can delete empty vault, even if global lock tx = vault.del({.owner = owner, .id = keylet.key}); env(tx); @@ -1597,11 +1862,14 @@ class Vault_test : public beast::unit_test::suite vault.create({.owner = owner, .asset = asset}); env(tx); env.close(); + auto v = env.le(keylet); + BEAST_EXPECT(v); + MPTID share = (*v)[sfShareMPTID]; tx = vault.deposit( {.depositor = depositor, .id = keylet.key, - .amount = asset(1000)}); + .amount = asset(1000)}); // all assets held by depositor env(tx); env.close(); @@ -1629,8 +1897,14 @@ class Vault_test : public beast::unit_test::suite tx = vault.withdraw( {.depositor = depositor, .id = keylet.key, - .amount = asset(100)}); + .amount = asset(1000)}); env(tx); + env.close(); + + // Withdraw removed shares MPToken + auto const mptSle = + env.le(keylet::mptoken(share, depositor.id())); + BEAST_EXPECT(mptSle == nullptr); } }, {.requireAuth = false}); @@ -1702,6 +1976,96 @@ class Vault_test : public beast::unit_test::suite env(vault.del({.owner = owner, .id = keylet.key})); }); + testCase([this]( + Env& env, + Account const& issuer, + Account const& owner, + Account const& depositor, + PrettyAsset const& asset, + Vault& vault, + MPTTester& mptt) { + testcase("MPT vault owner can receive shares unless unauthorized"); + + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + env(tx); + env.close(); + + tx = vault.deposit( + {.depositor = depositor, + .id = keylet.key, + .amount = asset(1000)}); + env(tx); + env.close(); + + auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID { + auto const vault = env.le(keylet); + return vault->at(sfShareMPTID); + }(keylet); + PrettyAsset shares = MPTIssue(issuanceId); + + { + // owner has MPToken for shares they did not explicitly create + env(pay(depositor, owner, shares(1))); + env.close(); + + tx = vault.withdraw( + {.depositor = owner, + .id = keylet.key, + .amount = shares(1)}); + env(tx); + env.close(); + + // owner's MPToken for vault shares not destroyed by withdraw + env(pay(depositor, owner, shares(1))); + env.close(); + + tx = vault.clawback( + {.issuer = issuer, + .id = keylet.key, + .holder = owner, + .amount = asset(0)}); + env(tx); + env.close(); + + // owner's MPToken for vault shares not destroyed by clawback + env(pay(depositor, owner, shares(1))); + env.close(); + + // pay back, so we can destroy owner's MPToken now + env(pay(owner, depositor, shares(1))); + env.close(); + + { + // explicitly destroy vault owners MPToken with zero balance + Json::Value jv; + jv[sfAccount] = owner.human(); + jv[sfMPTokenIssuanceID] = to_string(issuanceId); + jv[sfFlags] = tfMPTUnauthorize; + jv[sfTransactionType] = jss::MPTokenAuthorize; + env(jv); + env.close(); + } + + // owner no longer has MPToken for vault shares + tx = pay(depositor, owner, shares(1)); + env(tx, ter{tecNO_AUTH}); + env.close(); + + // destroy all remaining shares, so we can delete vault + tx = vault.clawback( + {.issuer = issuer, + .id = keylet.key, + .holder = depositor, + .amount = asset(0)}); + env(tx); + env.close(); + + // will soft fail destroying MPToken for vault owner + env(vault.del({.owner = owner, .id = keylet.key})); + env.close(); + } + }); + testCase( [this]( Env& env, @@ -1771,6 +2135,8 @@ class Vault_test : public beast::unit_test::suite // Withdrawal to other (authorized) accounts works tx[sfDestination] = issuer.human(); env(tx); + env.close(); + tx[sfDestination] = owner.human(); env(tx); env.close(); @@ -1792,6 +2158,7 @@ class Vault_test : public beast::unit_test::suite .holder = depositor, .amount = asset(800)}); env(tx); + env.close(); env(vault.del({.owner = owner, .id = keylet.key})); }); @@ -1901,18 +2268,16 @@ class Vault_test : public beast::unit_test::suite using namespace test::jtx; auto testCase = - [&, this]( - std::function vaultAccount, - Vault& vault, - PrettyAsset const& asset, - std::function issuanceId, - std::function vaultBalance)> - test) { + [&, + this](std::function vaultAccount, + Vault& vault, + PrettyAsset const& asset, + std::function issuanceId)> test) { Env env{*this, testable_amendments() | featureSingleAssetVault}; Account const owner{"owner"}; Account const issuer{"issuer"}; @@ -1929,33 +2294,13 @@ class Vault_test : public beast::unit_test::suite env(rate(issuer, 1.25)); env.close(); - auto const [tx, keylet] = - vault.create({.owner = owner, .asset = asset}); - env(tx); - env.close(); - auto const vaultAccount = - [&env](ripple::Keylet keylet) -> AccountID { - return env.le(keylet)->at(sfAccount); + [&env](ripple::Keylet keylet) -> Account { + return Account("vault", env.le(keylet)->at(sfAccount)); }; auto const issuanceId = [&env](ripple::Keylet keylet) -> MPTID { return env.le(keylet)->at(sfShareMPTID); }; - auto const vaultBalance = // - [&env, &vaultAccount, issue = asset.raw().get()]( - ripple::Keylet keylet) -> PrettyAmount { - auto const account = vaultAccount(keylet); - auto const sle = env.le(keylet::line(account, issue)); - if (sle == nullptr) - return { - STAmount(issue, 0), - env.lookup(issue.account).name()}; - auto amount = sle->getFieldAmount(sfBalance); - amount.setIssuer(issue.account); - if (account > issue.account) - amount.negate(); - return {amount, env.lookup(issue.account).name()}; - }; test( env, @@ -1965,8 +2310,7 @@ class Vault_test : public beast::unit_test::suite vaultAccount, vault, asset, - issuanceId, - vaultBalance); + issuanceId); }; testCase([&, this]( @@ -2029,8 +2373,7 @@ class Vault_test : public beast::unit_test::suite auto vaultAccount, Vault& vault, PrettyAsset const& asset, - auto issuanceId, - auto) { + auto issuanceId) { testcase("IOU frozen trust line to vault account"); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); @@ -2101,7 +2444,9 @@ class Vault_test : public beast::unit_test::suite env.close(); env(vault.withdraw( - {.depositor = owner, .id = keylet.key, .amount = share(50)})); + {.depositor = owner, + .id = keylet.key, + .amount = share(50'000'000)})); env(vault.del({.owner = owner, .id = keylet.key})); env.close(); @@ -2112,11 +2457,10 @@ class Vault_test : public beast::unit_test::suite Account const& owner, Account const& issuer, Account const& charlie, - auto, + auto vaultAccount, Vault& vault, PrettyAsset const& asset, - auto issuanceId, - auto vaultBalance) { + auto issuanceId) { testcase("IOU transfer fees not applied"); auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); @@ -2132,7 +2476,8 @@ class Vault_test : public beast::unit_test::suite // transfer fees ignored on deposit BEAST_EXPECT(env.balance(owner, issue) == asset(100)); - BEAST_EXPECT(vaultBalance(keylet) == asset(100)); + BEAST_EXPECT( + env.balance(vaultAccount(keylet), issue) == asset(100)); { auto tx = vault.clawback( @@ -2146,20 +2491,22 @@ class Vault_test : public beast::unit_test::suite // transfer fees ignored on clawback BEAST_EXPECT(env.balance(owner, issue) == asset(100)); - BEAST_EXPECT(vaultBalance(keylet) == asset(50)); + BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(50)); env(vault.withdraw( - {.depositor = owner, .id = keylet.key, .amount = share(20)})); + {.depositor = owner, + .id = keylet.key, + .amount = share(20'000'000)})); // transfer fees ignored on withdraw BEAST_EXPECT(env.balance(owner, issue) == asset(120)); - BEAST_EXPECT(vaultBalance(keylet) == asset(30)); + BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(30)); { auto tx = vault.withdraw( {.depositor = owner, .id = keylet.key, - .amount = share(30)}); + .amount = share(30'000'000)}); tx[sfDestination] = charlie.human(); env(tx); } @@ -2167,7 +2514,7 @@ class Vault_test : public beast::unit_test::suite // transfer fees ignored on withdraw to 3rd party BEAST_EXPECT(env.balance(owner, issue) == asset(120)); BEAST_EXPECT(env.balance(charlie, issue) == asset(30)); - BEAST_EXPECT(vaultBalance(keylet) == asset(0)); + BEAST_EXPECT(env.balance(vaultAccount(keylet), issue) == asset(0)); env(vault.del({.owner = owner, .id = keylet.key})); env.close(); @@ -2843,6 +3190,870 @@ class Vault_test : public beast::unit_test::suite env(tx, ter{terADDRESS_COLLISION}); } + void + testScaleIOU() + { + using namespace test::jtx; + + struct Data + { + Account const& owner; + Account const& issuer; + Account const& depositor; + Account const& vaultAccount; + MPTIssue shares; + PrettyAsset const& share; + Vault& vault; + ripple::Keylet keylet; + Issue assets; + PrettyAsset const& asset; + std::function)> peek; + }; + + auto testCase = [&, this]( + std::uint8_t scale, + std::function test) { + Env env{*this, testable_amendments() | featureSingleAssetVault}; + Account const owner{"owner"}; + Account const issuer{"issuer"}; + Account const depositor{"depositor"}; + Vault vault{env}; + env.fund(XRP(1000), issuer, owner, depositor); + env(fset(issuer, asfAllowTrustLineClawback)); + env.close(); + + PrettyAsset const asset = issuer["IOU"]; + env.trust(asset(1000), owner); + env.trust(asset(1000), depositor); + env(pay(issuer, owner, asset(200))); + env(pay(issuer, depositor, asset(200))); + env.close(); + + auto [tx, keylet] = vault.create({.owner = owner, .asset = asset}); + tx[sfScale] = scale; + env(tx); + + auto const [vaultAccount, issuanceId] = + [&env](ripple::Keylet keylet) -> std::tuple { + auto const vault = env.le(keylet); + return { + Account("vault", vault->at(sfAccount)), + vault->at(sfShareMPTID)}; + }(keylet); + MPTIssue shares(issuanceId); + env.memoize(vaultAccount); + + auto const peek = + [=, &env, this](std::function fn) -> bool { + return env.app().openLedger().modify( + [&](OpenView& view, beast::Journal j) -> bool { + Sandbox sb(&view, tapNONE); + auto vault = sb.peek(keylet::vault(keylet.key)); + if (!BEAST_EXPECT(vault != nullptr)) + return false; + auto shares = sb.peek( + keylet::mptIssuance(vault->at(sfShareMPTID))); + if (!BEAST_EXPECT(shares != nullptr)) + return false; + if (fn(*vault, *shares)) + { + sb.update(vault); + sb.update(shares); + sb.apply(view); + return true; + } + return false; + }); + }; + + test( + env, + {.owner = owner, + .issuer = issuer, + .depositor = depositor, + .vaultAccount = vaultAccount, + .shares = shares, + .share = PrettyAsset(shares), + .vault = vault, + .keylet = keylet, + .assets = asset.raw().get(), + .asset = asset, + .peek = peek}); + }; + + testCase(18, [&, this](Env& env, Data d) { + testcase("Scale deposit overflow on first deposit"); + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = d.asset(10)}); + env(tx, ter{tecPATH_DRY}); + env.close(); + }); + + testCase(18, [&, this](Env& env, Data d) { + testcase("Scale deposit overflow on second deposit"); + + { + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = d.asset(5)}); + env(tx); + env.close(); + } + + { + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = d.asset(10)}); + env(tx, ter{tecPATH_DRY}); + env.close(); + } + }); + + testCase(18, [&, this](Env& env, Data d) { + testcase("Scale deposit overflow on total shares"); + + { + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = d.asset(5)}); + env(tx); + env.close(); + } + + { + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = d.asset(5)}); + env(tx, ter{tecPATH_DRY}); + env.close(); + } + }); + + testCase(1, [&, this](Env& env, Data d) { + testcase("Scale deposit exact"); + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = d.asset(1)}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(10)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - 1)); + }); + + testCase(1, [&, this](Env& env, Data d) { + testcase("Scale deposit insignificant amount"); + + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(9, -2))}); + env(tx, ter{tecPRECISION_LOSS}); + }); + + testCase(1, [&, this](Env& env, Data d) { + testcase("Scale deposit exact, using full precision"); + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(15, -1))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(15)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(15, -1))); + }); + + testCase(1, [&, this](Env& env, Data d) { + testcase("Scale deposit exact, truncating from .5"); + + auto const start = env.balance(d.depositor, d.assets).number(); + // Each of the cases below will transfer exactly 1.2 IOU to the + // vault and receive 12 shares in exchange + { + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(125, -2))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(12, -1))); + } + + { + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(1201, -3))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(24)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(24, -1))); + } + + { + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(1299, -3))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(36)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(36, -1))); + } + }); + + testCase(1, [&, this](Env& env, Data d) { + testcase("Scale deposit exact, truncating from .01"); + + auto const start = env.balance(d.depositor, d.assets).number(); + // round to 12 + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(1201, -3))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(12, -1))); + + { + // round to 6 + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(69, -2))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(18, -1))); + } + }); + + testCase(1, [&, this](Env& env, Data d) { + testcase("Scale deposit exact, truncating from .99"); + + auto const start = env.balance(d.depositor, d.assets).number(); + // round to 12 + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(1299, -3))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(12)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(12, -1))); + + { + // round to 6 + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(62, -2))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(18)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(18, -1))); + } + }); + + testCase(1, [&, this](Env& env, Data d) { + // initial setup: deposit 100 IOU, receive 1000 shares + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(100, 0))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(1000, 0))); + + { + testcase("Scale redeem exact"); + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 100 * 100 / 1000 = 100 * 0.1 = 10 + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.share, Number(100, 0))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(900)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(10, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(90, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(900, 0))); + } + + { + testcase("Scale redeem with rounding"); + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5 + + auto const start = env.balance(d.depositor, d.assets).number(); + d.peek([](SLE& vault, auto&) -> bool { + vault[sfAssetsAvailable] = Number(1); + return true; + }); + + // Note, this transaction fails first (because of above change + // in the open ledger) but then succeeds when the ledger is + // closed (because a modification like above is not persistent), + // which is why the checks below are expected to pass. + auto tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.share, Number(25, 0))}); + env(tx, ter{tecINSUFFICIENT_FUNDS}); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(900 - 25)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(900 - 25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(900 - 25, 0))); + } + + { + testcase("Scale redeem exact"); + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 87.5 * 21 / 875 = 87.5 * 0.024 = 2.1 + + auto const start = env.balance(d.depositor, d.assets).number(); + + tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.share, Number(21, 0))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(875 - 21)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(21, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(875 - 21, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(875 - 21, 0))); + } + + { + testcase("Scale redeem rest"); + auto const rest = env.balance(d.depositor, d.shares).number(); + + tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.share, rest)}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets).number() == 0); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares).number() == 0); + } + }); + + testCase(18, [&, this](Env& env, Data d) { + testcase("Scale withdraw overflow"); + + { + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = d.asset(5)}); + env(tx); + env.close(); + } + + { + auto tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(10, 0))}); + env(tx, ter{tecPATH_DRY}); + env.close(); + } + }); + + testCase(1, [&, this](Env& env, Data d) { + // initial setup: deposit 100 IOU, receive 1000 shares + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(100, 0))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(1000, 0))); + + { + testcase("Scale withdraw exact"); + // assetsToSharesWithdraw: + // shares = sharesTotal * (assets / assetsTotal) + // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100 + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 100 * 100 / 1000 = 100 * 0.1 = 10 + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(10, 0))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(900)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(10, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(90, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(900, 0))); + } + + { + testcase("Scale withdraw insignificant amount"); + auto tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(4, -2))}); + env(tx, ter{tecPRECISION_LOSS}); + } + + { + testcase("Scale withdraw with rounding assets"); + // assetsToSharesWithdraw: + // shares = sharesTotal * (assets / assetsTotal) + // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25 + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5 + + auto const start = env.balance(d.depositor, d.assets).number(); + d.peek([](SLE& vault, auto&) -> bool { + vault[sfAssetsAvailable] = Number(1); + return true; + }); + + // Note, this transaction fails first (because of above change + // in the open ledger) but then succeeds when the ledger is + // closed (because a modification like above is not persistent), + // which is why the checks below are expected to pass. + auto tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(25, -1))}); + env(tx, ter{tecINSUFFICIENT_FUNDS}); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(900 - 25)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(900 - 25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(900 - 25, 0))); + } + + { + testcase("Scale withdraw with rounding shares up"); + // assetsToSharesWithdraw: + // shares = sharesTotal * (assets / assetsTotal) + // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5 + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8 + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(375, -2))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(875 - 38)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(38, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(875 - 38, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(875 - 38, 0))); + } + + { + testcase("Scale withdraw with rounding shares down"); + // assetsToSharesWithdraw: + // shares = sharesTotal * (assets / assetsTotal) + // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2 + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7 + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(372, -2))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(837 - 37)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(37, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(837 - 37, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(837 - 37, 0))); + } + + { + testcase("Scale withdraw tiny amount"); + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(9, -2))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(800 - 1)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start + Number(1, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(800 - 1, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(800 - 1, 0))); + } + + { + testcase("Scale withdraw rest"); + auto const rest = + env.balance(d.vaultAccount, d.assets).number(); + + tx = d.vault.withdraw( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, rest)}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets).number() == 0); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares).number() == 0); + } + }); + + testCase(18, [&, this](Env& env, Data d) { + testcase("Scale clawback overflow"); + + { + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = d.asset(5)}); + env(tx); + env.close(); + } + + { + auto tx = d.vault.clawback( + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, Number(10, 0))}); + env(tx, ter{tecPATH_DRY}); + env.close(); + } + }); + + testCase(1, [&, this](Env& env, Data d) { + // initial setup: deposit 100 IOU, receive 1000 shares + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.deposit( + {.depositor = d.depositor, + .id = d.keylet.key, + .amount = STAmount(d.asset, Number(100, 0))}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares) == d.share(1000)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start - Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(100, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(1000, 0))); + { + testcase("Scale clawback exact"); + // assetsToSharesWithdraw: + // shares = sharesTotal * (assets / assetsTotal) + // shares = 1000 * 10 / 100 = 1000 * 0.1 = 100 + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 100 * 100 / 1000 = 100 * 0.1 = 10 + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.clawback( + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, Number(10, 0))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(900)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start)); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(90, 0))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(900, 0))); + } + + { + testcase("Scale clawback insignificant amount"); + auto tx = d.vault.clawback( + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, Number(4, -2))}); + env(tx, ter{tecPRECISION_LOSS}); + } + + { + testcase("Scale clawback with rounding assets"); + // assetsToSharesWithdraw: + // shares = sharesTotal * (assets / assetsTotal) + // shares = 900 * 2.5 / 90 = 900 * 0.02777... = 25 + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 90 * 25 / 900 = 90 * 0.02777... = 2.5 + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.clawback( + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, Number(25, -1))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(900 - 25)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start)); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(900 - 25, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(900 - 25, 0))); + } + + { + testcase("Scale clawback with rounding shares up"); + // assetsToSharesWithdraw: + // shares = sharesTotal * (assets / assetsTotal) + // shares = 875 * 3.75 / 87.5 = 875 * 0.042857... = 37.5 + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 87.5 * 38 / 875 = 87.5 * 0.043428... = 3.8 + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.clawback( + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, Number(375, -2))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(875 - 38)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start)); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(875 - 38, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(875 - 38, 0))); + } + + { + testcase("Scale clawback with rounding shares down"); + // assetsToSharesWithdraw: + // shares = sharesTotal * (assets / assetsTotal) + // shares = 837 * 3.72 / 83.7 = 837 * 0.04444... = 37.2 + // sharesToAssetsWithdraw: + // assets = assetsTotal * (shares / sharesTotal) + // assets = 83.7 * 37 / 837 = 83.7 * 0.044205... = 3.7 + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.clawback( + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, Number(372, -2))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(837 - 37)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start)); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(837 - 37, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(837 - 37, 0))); + } + + { + testcase("Scale clawback tiny amount"); + + auto const start = env.balance(d.depositor, d.assets).number(); + auto tx = d.vault.clawback( + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, Number(9, -2))}); + env(tx); + env.close(); + BEAST_EXPECT( + env.balance(d.depositor, d.shares) == d.share(800 - 1)); + BEAST_EXPECT( + env.balance(d.depositor, d.assets) == + STAmount(d.asset, start)); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets) == + STAmount(d.asset, Number(800 - 1, -1))); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares) == + STAmount(d.share, Number(800 - 1, 0))); + } + + { + testcase("Scale clawback rest"); + auto const rest = + env.balance(d.vaultAccount, d.assets).number(); + d.peek([](SLE& vault, auto&) -> bool { + vault[sfAssetsAvailable] = Number(5); + return true; + }); + + // Note, this transaction yields two different results: + // * in the open ledger, with AssetsAvailable = 5 + // * when the ledger is closed with unmodified AssetsAvailable + // because a modification like above is not persistent. + tx = d.vault.clawback( + {.issuer = d.issuer, + .id = d.keylet.key, + .holder = d.depositor, + .amount = STAmount(d.asset, rest)}); + env(tx); + env.close(); + BEAST_EXPECT(env.balance(d.depositor, d.shares).number() == 0); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.assets).number() == 0); + BEAST_EXPECT( + env.balance(d.vaultAccount, d.shares).number() == 0); + } + }); + } + void testRPC() { @@ -2943,7 +4154,8 @@ class Vault_test : public beast::unit_test::suite issuance, sfFlags, int(lsfMPTCanEscrow | lsfMPTCanTrade | lsfMPTCanTransfer))); - BEAST_EXPECT(checkString(issuance, sfOutstandingAmount, "50")); + BEAST_EXPECT( + checkString(issuance, sfOutstandingAmount, "50000000")); } }; @@ -3326,6 +4538,7 @@ public: testWithDomainCheckXRP(); testNonTransferableShares(); testFailedPseudoAccount(); + testScaleIOU(); testRPC(); } }; diff --git a/src/test/basics/Number_test.cpp b/src/test/basics/Number_test.cpp index 964cfe9614..f24c0b35e1 100644 --- a/src/test/basics/Number_test.cpp +++ b/src/test/basics/Number_test.cpp @@ -720,6 +720,30 @@ public: BEAST_EXPECT(res2 == STAmount{7518784}); } + void + test_truncate() + { + BEAST_EXPECT(Number(25, +1).truncate() == Number(250, 0)); + BEAST_EXPECT(Number(25, 0).truncate() == Number(25, 0)); + BEAST_EXPECT(Number(25, -1).truncate() == Number(2, 0)); + BEAST_EXPECT(Number(25, -2).truncate() == Number(0, 0)); + BEAST_EXPECT(Number(99, -2).truncate() == Number(0, 0)); + + BEAST_EXPECT(Number(-25, +1).truncate() == Number(-250, 0)); + BEAST_EXPECT(Number(-25, 0).truncate() == Number(-25, 0)); + BEAST_EXPECT(Number(-25, -1).truncate() == Number(-2, 0)); + BEAST_EXPECT(Number(-25, -2).truncate() == Number(0, 0)); + BEAST_EXPECT(Number(-99, -2).truncate() == Number(0, 0)); + + BEAST_EXPECT(Number(0, 0).truncate() == Number(0, 0)); + BEAST_EXPECT(Number(0, 30000).truncate() == Number(0, 0)); + BEAST_EXPECT(Number(0, -30000).truncate() == Number(0, 0)); + BEAST_EXPECT(Number(100, -30000).truncate() == Number(0, 0)); + BEAST_EXPECT(Number(100, -30000).truncate() == Number(0, 0)); + BEAST_EXPECT(Number(-100, -30000).truncate() == Number(0, 0)); + BEAST_EXPECT(Number(-100, -30000).truncate() == Number(0, 0)); + } + void run() override { @@ -740,6 +764,7 @@ public: test_stream(); test_inc_dec(); test_toSTAmount(); + test_truncate(); } }; diff --git a/src/test/jtx/Account.h b/src/test/jtx/Account.h index d91bb4a383..940960051a 100644 --- a/src/test/jtx/Account.h +++ b/src/test/jtx/Account.h @@ -74,6 +74,10 @@ public: /** @} */ + /** Create an Account from an account ID. Should only be used when the + * secret key is unavailable, such as for pseudo-accounts. */ + explicit Account(std::string name, AccountID const& id); + enum AcctStringType { base58Seed, other }; /** Create an account from a base58 seed string. Throws on invalid seed. */ Account(AcctStringType stringType, std::string base58SeedStr); diff --git a/src/test/jtx/impl/Account.cpp b/src/test/jtx/impl/Account.cpp index b61048e66f..fe901848f8 100644 --- a/src/test/jtx/impl/Account.cpp +++ b/src/test/jtx/impl/Account.cpp @@ -86,6 +86,14 @@ Account::Account(AcctStringType stringType, std::string base58SeedStr) { } +Account::Account(std::string name, AccountID const& id) + : Account(name, randomKeyPair(KeyType::secp256k1), privateCtorTag{}) +{ + // override the randomly generated values + id_ = id; + human_ = toBase58(id_); +} + IOU Account::operator[](std::string const& s) const { diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index d93378d3cd..da0dfc117f 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -1510,6 +1510,12 @@ ValidMPTIssuance::finalize( if (tx.getTxnType() == ttESCROW_FINISH) return true; + + if ((tx.getTxnType() == ttVAULT_CLAWBACK || + tx.getTxnType() == ttVAULT_WITHDRAW) && + mptokensDeleted_ == 1 && mptokensCreated_ == 0 && + mptIssuancesCreated_ == 0 && mptIssuancesDeleted_ == 0) + return true; } if (mptIssuancesCreated_ != 0) diff --git a/src/xrpld/app/tx/detail/VaultClawback.cpp b/src/xrpld/app/tx/detail/VaultClawback.cpp index f9bd0c7629..87740da179 100644 --- a/src/xrpld/app/tx/detail/VaultClawback.cpp +++ b/src/xrpld/app/tx/detail/VaultClawback.cpp @@ -21,8 +21,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -151,7 +153,7 @@ VaultClawback::doApply() if (!vault) return tefINTERNAL; // LCOV_EXCL_LINE - auto const mptIssuanceID = (*vault)[sfShareMPTID]; + auto const mptIssuanceID = *((*vault)[sfShareMPTID]); auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); if (!sleIssuance) { @@ -161,68 +163,169 @@ VaultClawback::doApply() // LCOV_EXCL_STOP } - Asset const asset = vault->at(sfAsset); + Asset const vaultAsset = vault->at(sfAsset); STAmount const amount = [&]() -> STAmount { auto const maybeAmount = tx[~sfAmount]; if (maybeAmount) return *maybeAmount; - return {sfAmount, asset, 0}; + return {sfAmount, vaultAsset, 0}; }(); XRPL_ASSERT( - amount.asset() == asset, + amount.asset() == vaultAsset, "ripple::VaultClawback::doApply : matching asset"); + auto assetsAvailable = vault->at(sfAssetsAvailable); + auto assetsTotal = vault->at(sfAssetsTotal); + [[maybe_unused]] auto const lossUnrealized = vault->at(sfLossUnrealized); + XRPL_ASSERT( + lossUnrealized <= (assetsTotal - assetsAvailable), + "ripple::VaultClawback::doApply : loss and assets do balance"); + AccountID holder = tx[sfHolder]; - STAmount assets, shares; - if (amount == beast::zero) + MPTIssue const share{mptIssuanceID}; + STAmount sharesDestroyed = {share}; + STAmount assetsRecovered; + try { - Asset share = *(*vault)[sfShareMPTID]; - shares = accountHolds( - view(), - holder, - share, - FreezeHandling::fhIGNORE_FREEZE, - AuthHandling::ahIGNORE_AUTH, - j_); - assets = sharesToAssetsWithdraw(vault, sleIssuance, shares); + if (amount == beast::zero) + { + sharesDestroyed = accountHolds( + view(), + holder, + share, + FreezeHandling::fhIGNORE_FREEZE, + AuthHandling::ahIGNORE_AUTH, + j_); + + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, sharesDestroyed); + if (!maybeAssets) + return tecINTERNAL; // LCOV_EXCL_LINE + assetsRecovered = *maybeAssets; + } + else + { + assetsRecovered = amount; + { + auto const maybeShares = + assetsToSharesWithdraw(vault, sleIssuance, assetsRecovered); + if (!maybeShares) + return tecINTERNAL; // LCOV_EXCL_LINE + sharesDestroyed = *maybeShares; + } + + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, sharesDestroyed); + if (!maybeAssets) + return tecINTERNAL; // LCOV_EXCL_LINE + assetsRecovered = *maybeAssets; + } + + // Clamp to maximum. + if (assetsRecovered > *assetsAvailable) + { + assetsRecovered = *assetsAvailable; + // Note, it is important to truncate the number of shares, otherwise + // the corresponding assets might breach the AssetsAvailable + { + auto const maybeShares = assetsToSharesWithdraw( + vault, sleIssuance, assetsRecovered, TruncateShares::yes); + if (!maybeShares) + return tecINTERNAL; // LCOV_EXCL_LINE + sharesDestroyed = *maybeShares; + } + + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, sharesDestroyed); + if (!maybeAssets) + return tecINTERNAL; // LCOV_EXCL_LINE + assetsRecovered = *maybeAssets; + if (assetsRecovered > *assetsAvailable) + { + // LCOV_EXCL_START + JLOG(j_.error()) + << "VaultClawback: invalid rounding of shares."; + return tecINTERNAL; + // LCOV_EXCL_STOP + } + } } - else + catch (std::overflow_error const&) { - assets = amount; - shares = assetsToSharesWithdraw(vault, sleIssuance, assets); + // It's easy to hit this exception from Number with large enough Scale + // so we avoid spamming the log and only use debug here. + JLOG(j_.debug()) // + << "VaultClawback: overflow error with" + << " scale=" << (int)vault->at(sfScale).value() // + << ", assetsTotal=" << vault->at(sfAssetsTotal).value() + << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount) + << ", amount=" << amount.value(); + return tecPATH_DRY; } - // Clamp to maximum. - Number maxAssets = *vault->at(sfAssetsAvailable); - if (assets > maxAssets) - { - assets = maxAssets; - shares = assetsToSharesWithdraw(vault, sleIssuance, assets); - } + if (sharesDestroyed == beast::zero) + return tecPRECISION_LOSS; - if (shares == beast::zero) - return tecINSUFFICIENT_FUNDS; - - vault->at(sfAssetsTotal) -= assets; - vault->at(sfAssetsAvailable) -= assets; + assetsTotal -= assetsRecovered; + assetsAvailable -= assetsRecovered; view().update(vault); auto const& vaultAccount = vault->at(sfAccount); // Transfer shares from holder to vault. - if (auto ter = accountSend( - view(), holder, vaultAccount, shares, j_, WaiveTransferFee::Yes)) + if (auto const ter = accountSend( + view(), + holder, + vaultAccount, + sharesDestroyed, + j_, + WaiveTransferFee::Yes); + !isTesSuccess(ter)) return ter; + // Try to remove MPToken for shares, if the holder balance is zero. Vault + // pseudo-account will never set lsfMPTAuthorized, so we ignore flags. + // Keep MPToken if holder is the vault owner. + if (holder != vault->at(sfOwner)) + { + if (auto const ter = + removeEmptyHolding(view(), holder, sharesDestroyed.asset(), j_); + isTesSuccess(ter)) + { + JLOG(j_.debug()) // + << "VaultClawback: removed empty MPToken for vault shares" + << " MPTID=" << to_string(mptIssuanceID) // + << " account=" << toBase58(holder); + } + else if (ter != tecHAS_OBLIGATIONS) + { + // LCOV_EXCL_START + JLOG(j_.error()) // + << "VaultClawback: failed to remove MPToken for vault shares" + << " MPTID=" << to_string(mptIssuanceID) // + << " account=" << toBase58(holder) // + << " with result: " << transToken(ter); + return ter; + // LCOV_EXCL_STOP + } + // else quietly ignore, holder balance is not zero + } + // Transfer assets from vault to issuer. - if (auto ter = accountSend( - view(), vaultAccount, account_, assets, j_, WaiveTransferFee::Yes)) + if (auto const ter = accountSend( + view(), + vaultAccount, + account_, + assetsRecovered, + j_, + WaiveTransferFee::Yes); + !isTesSuccess(ter)) return ter; // Sanity check if (accountHolds( view(), vaultAccount, - assets.asset(), + assetsRecovered.asset(), FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, j_) < beast::zero) diff --git a/src/xrpld/app/tx/detail/VaultCreate.cpp b/src/xrpld/app/tx/detail/VaultCreate.cpp index cb6a994e7e..0b5cdd4fc0 100644 --- a/src/xrpld/app/tx/detail/VaultCreate.cpp +++ b/src/xrpld/app/tx/detail/VaultCreate.cpp @@ -25,8 +25,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -84,6 +86,16 @@ VaultCreate::preflight(PreflightContext const& ctx) return temMALFORMED; } + if (auto const scale = ctx.tx[~sfScale]) + { + auto const vaultAsset = ctx.tx[sfAsset]; + if (vaultAsset.holds() || vaultAsset.native()) + return temMALFORMED; + + if (scale > vaultMaximumIOUScale) + return temMALFORMED; + } + return preflight2(ctx); } @@ -97,8 +109,8 @@ VaultCreate::calculateBaseFee(ReadView const& view, STTx const& tx) TER VaultCreate::preclaim(PreclaimContext const& ctx) { - auto vaultAsset = ctx.tx[sfAsset]; - auto account = ctx.tx[sfAccount]; + auto const vaultAsset = ctx.tx[sfAsset]; + auto const account = ctx.tx[sfAccount]; if (vaultAsset.native()) ; // No special checks for XRP @@ -148,7 +160,7 @@ VaultCreate::preclaim(PreclaimContext const& ctx) return tecOBJECT_NOT_FOUND; } - auto sequence = ctx.tx.getSeqValue(); + auto const sequence = ctx.tx.getSeqValue(); if (auto const accountId = pseudoAccountAddress( ctx.view, keylet::vault(account, sequence).key); accountId == beast::zero) @@ -165,8 +177,8 @@ VaultCreate::doApply() // we can consider downgrading them to `tef` or `tem`. auto const& tx = ctx_.tx; - auto sequence = tx.getSeqValue(); - auto owner = view().peek(keylet::account(account_)); + auto const sequence = tx.getSeqValue(); + auto const owner = view().peek(keylet::account(account_)); if (owner == nullptr) return tefINTERNAL; // LCOV_EXCL_LINE @@ -190,6 +202,10 @@ VaultCreate::doApply() !isTesSuccess(ter)) return ter; + std::uint8_t const scale = (asset.holds() || asset.native()) + ? 0 + : ctx_.tx[~sfScale].value_or(vaultDefaultIOUScale); + auto txFlags = tx.getFlags(); std::uint32_t mptFlags = 0; if ((txFlags & tfVaultShareNonTransferable) == 0) @@ -209,12 +225,13 @@ VaultCreate::doApply() .account = pseudoId->value(), .sequence = 1, .flags = mptFlags, + .assetScale = scale, .metadata = tx[~sfMPTokenMetadata], .domainId = tx[~sfDomainID], }); if (!maybeShare) return maybeShare.error(); // LCOV_EXCL_LINE - auto& share = *maybeShare; + auto const& mptIssuanceID = *maybeShare; vault->setFieldIssue(sfAsset, STIssue{sfAsset, asset}); vault->at(sfFlags) = txFlags & tfVaultPrivate; @@ -227,7 +244,7 @@ VaultCreate::doApply() // Leave default values for AssetTotal and AssetAvailable, both zero. if (auto value = tx[~sfAssetsMaximum]) vault->at(sfAssetsMaximum) = *value; - vault->at(sfShareMPTID) = share; + vault->at(sfShareMPTID) = mptIssuanceID; if (auto value = tx[~sfData]) vault->at(sfData) = *value; // Required field, default to vaultStrategyFirstComeFirstServe @@ -235,9 +252,31 @@ VaultCreate::doApply() vault->at(sfWithdrawalPolicy) = *value; else vault->at(sfWithdrawalPolicy) = vaultStrategyFirstComeFirstServe; - // No `LossUnrealized`. + if (scale) + vault->at(sfScale) = scale; view().insert(vault); + // Explicitly create MPToken for the vault owner + if (auto const err = authorizeMPToken( + view(), mPriorBalance, mptIssuanceID, account_, ctx_.journal); + !isTesSuccess(err)) + return err; + + // If the vault is private, set the authorized flag for the vault owner + if (txFlags & tfVaultPrivate) + { + if (auto const err = authorizeMPToken( + view(), + mPriorBalance, + mptIssuanceID, + pseudoId, + ctx_.journal, + {}, + account_); + !isTesSuccess(err)) + return err; + } + return tesSUCCESS; } diff --git a/src/xrpld/app/tx/detail/VaultDelete.cpp b/src/xrpld/app/tx/detail/VaultDelete.cpp index 7861e9e9b6..d4b74ae1d5 100644 --- a/src/xrpld/app/tx/detail/VaultDelete.cpp +++ b/src/xrpld/app/tx/detail/VaultDelete.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -128,7 +129,8 @@ VaultDelete::doApply() // Destroy the share issuance. Do not use MPTokenIssuanceDestroy for this, // no special logic needed. First run few checks, duplicated from preclaim. - auto const mpt = view().peek(keylet::mptIssuance(vault->at(sfShareMPTID))); + auto const shareMPTID = *vault->at(sfShareMPTID); + auto const mpt = view().peek(keylet::mptIssuance(shareMPTID)); if (!mpt) { // LCOV_EXCL_START @@ -137,6 +139,24 @@ VaultDelete::doApply() // LCOV_EXCL_STOP } + // Try to remove MPToken for vault shares for the vault owner if it exists. + if (auto const mptoken = view().peek(keylet::mptoken(shareMPTID, account_))) + { + if (auto const ter = + removeEmptyHolding(view(), account_, MPTIssue(shareMPTID), j_); + !isTesSuccess(ter)) + { + // LCOV_EXCL_START + JLOG(j_.error()) // + << "VaultDelete: failed to remove vault owner's MPToken" + << " MPTID=" << to_string(shareMPTID) // + << " account=" << toBase58(account_) // + << " with result: " << transToken(ter); + return ter; + // LCOV_EXCL_STOP + } + } + if (!view().dirRemove( keylet::ownerDir(pseudoID), (*mpt)[sfOwnerNode], mpt->key(), false)) { diff --git a/src/xrpld/app/tx/detail/VaultDeposit.cpp b/src/xrpld/app/tx/detail/VaultDeposit.cpp index db1fc3bbfe..5cdcb43e20 100644 --- a/src/xrpld/app/tx/detail/VaultDeposit.cpp +++ b/src/xrpld/app/tx/detail/VaultDeposit.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -138,7 +139,7 @@ VaultDeposit::preclaim(PreclaimContext const& ctx) if (isFrozen(ctx.view, account, vaultShare)) return tecLOCKED; - if (vault->isFlag(tfVaultPrivate) && account != vault->at(sfOwner)) + if (vault->isFlag(lsfVaultPrivate) && account != vault->at(sfOwner)) { auto const maybeDomainID = sleIssuance->at(~sfDomainID); // Since this is a private vault and the account is not its owner, we @@ -183,7 +184,7 @@ VaultDeposit::doApply() if (!vault) return tefINTERNAL; // LCOV_EXCL_LINE - auto const assets = ctx_.tx[sfAmount]; + auto const amount = ctx_.tx[sfAmount]; // Make sure the depositor can hold shares. auto const mptIssuanceID = (*vault)[sfShareMPTID]; auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); @@ -197,14 +198,14 @@ VaultDeposit::doApply() auto const& vaultAccount = vault->at(sfAccount); // Note, vault owner is always authorized - if ((vault->getFlags() & tfVaultPrivate) && account_ != vault->at(sfOwner)) + if (vault->isFlag(lsfVaultPrivate) && account_ != vault->at(sfOwner)) { if (auto const err = enforceMPTokenAuthorization( ctx_.view(), mptIssuanceID, account_, mPriorBalance, j_); !isTesSuccess(err)) return err; } - else + else // !vault->isFlag(lsfVaultPrivate) || account_ == vault->at(sfOwner) { // No authorization needed, but must ensure there is MPToken auto sleMpt = view().read(keylet::mptoken(mptIssuanceID, account_)); @@ -221,8 +222,12 @@ VaultDeposit::doApply() } // If the vault is private, set the authorized flag for the vault owner - if (vault->isFlag(tfVaultPrivate)) + if (vault->isFlag(lsfVaultPrivate)) { + // This follows from the reverse of the outer enclosing if condition + XRPL_ASSERT( + account_ == vault->at(sfOwner), + "ripple::VaultDeposit::doApply : account is owner"); if (auto const err = authorizeMPToken( view(), mPriorBalance, // priorBalance @@ -237,14 +242,52 @@ VaultDeposit::doApply() } } - // Compute exchange before transferring any amounts. - auto const shares = assetsToSharesDeposit(vault, sleIssuance, assets); + STAmount sharesCreated = {vault->at(sfShareMPTID)}, assetsDeposited; + try + { + // Compute exchange before transferring any amounts. + { + auto const maybeShares = + assetsToSharesDeposit(vault, sleIssuance, amount); + if (!maybeShares) + return tecINTERNAL; // LCOV_EXCL_LINE + sharesCreated = *maybeShares; + } + if (sharesCreated == beast::zero) + return tecPRECISION_LOSS; + + auto const maybeAssets = + sharesToAssetsDeposit(vault, sleIssuance, sharesCreated); + if (!maybeAssets) + return tecINTERNAL; // LCOV_EXCL_LINE + else if (*maybeAssets > amount) + { + // LCOV_EXCL_START + JLOG(j_.error()) << "VaultDeposit: would take more than offered."; + return tecINTERNAL; + // LCOV_EXCL_STOP + } + assetsDeposited = *maybeAssets; + } + catch (std::overflow_error const&) + { + // It's easy to hit this exception from Number with large enough Scale + // so we avoid spamming the log and only use debug here. + JLOG(j_.debug()) // + << "VaultDeposit: overflow error with" + << " scale=" << (int)vault->at(sfScale).value() // + << ", assetsTotal=" << vault->at(sfAssetsTotal).value() + << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount) + << ", amount=" << amount; + return tecPATH_DRY; + } + XRPL_ASSERT( - shares.asset() != assets.asset(), + sharesCreated.asset() != assetsDeposited.asset(), "ripple::VaultDeposit::doApply : assets are not shares"); - vault->at(sfAssetsTotal) += assets; - vault->at(sfAssetsAvailable) += assets; + vault->at(sfAssetsTotal) += assetsDeposited; + vault->at(sfAssetsAvailable) += assetsDeposited; view().update(vault); // A deposit must not push the vault over its limit. @@ -253,15 +296,21 @@ VaultDeposit::doApply() return tecLIMIT_EXCEEDED; // Transfer assets from depositor to vault. - if (auto ter = accountSend( - view(), account_, vaultAccount, assets, j_, WaiveTransferFee::Yes)) + if (auto const ter = accountSend( + view(), + account_, + vaultAccount, + assetsDeposited, + j_, + WaiveTransferFee::Yes); + !isTesSuccess(ter)) return ter; // Sanity check if (accountHolds( view(), account_, - assets.asset(), + assetsDeposited.asset(), FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, j_) < beast::zero) @@ -273,8 +322,14 @@ VaultDeposit::doApply() } // Transfer shares from vault to depositor. - if (auto ter = accountSend( - view(), vaultAccount, account_, shares, j_, WaiveTransferFee::Yes)) + if (auto const ter = accountSend( + view(), + vaultAccount, + account_, + sharesCreated, + j_, + WaiveTransferFee::Yes); + !isTesSuccess(ter)) return ter; return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/VaultSet.cpp b/src/xrpld/app/tx/detail/VaultSet.cpp index a13ce6d10e..4750f89be2 100644 --- a/src/xrpld/app/tx/detail/VaultSet.cpp +++ b/src/xrpld/app/tx/detail/VaultSet.cpp @@ -108,7 +108,7 @@ VaultSet::preclaim(PreclaimContext const& ctx) if (auto const domain = ctx.tx[~sfDomainID]) { // We can only set domain if private flag was originally set - if ((vault->getFlags() & tfVaultPrivate) == 0) + if (!vault->isFlag(lsfVaultPrivate)) { JLOG(ctx.j.debug()) << "VaultSet: vault is not private"; return tecNO_PERMISSION; @@ -175,9 +175,9 @@ VaultSet::doApply() { if (*domainId != beast::zero) { - // In VaultSet::preclaim we enforce that tfVaultPrivate must have + // In VaultSet::preclaim we enforce that lsfVaultPrivate must have // been set in the vault. We currently do not support making such a - // vault public (i.e. removal of tfVaultPrivate flag). The + // vault public (i.e. removal of lsfVaultPrivate flag). The // sfDomainID flag must be set in the MPTokenIssuance object and can // be freely updated. sleIssuance->setFieldH256(sfDomainID, *domainId); diff --git a/src/xrpld/app/tx/detail/VaultWithdraw.cpp b/src/xrpld/app/tx/detail/VaultWithdraw.cpp index 09a9fd14e1..0ceaabbfde 100644 --- a/src/xrpld/app/tx/detail/VaultWithdraw.cpp +++ b/src/xrpld/app/tx/detail/VaultWithdraw.cpp @@ -177,7 +177,7 @@ VaultWithdraw::doApply() if (!vault) return tefINTERNAL; // LCOV_EXCL_LINE - auto const mptIssuanceID = (*vault)[sfShareMPTID]; + auto const mptIssuanceID = *((*vault)[sfShareMPTID]); auto const sleIssuance = view().read(keylet::mptIssuance(mptIssuanceID)); if (!sleIssuance) { @@ -192,24 +192,57 @@ VaultWithdraw::doApply() // to deposit into it, and this means you are also indefinitely authorized // to withdraw from it. - auto amount = ctx_.tx[sfAmount]; - auto const asset = vault->at(sfAsset); - auto const share = MPTIssue(mptIssuanceID); - STAmount shares, assets; - if (amount.asset() == asset) + auto const amount = ctx_.tx[sfAmount]; + Asset const vaultAsset = vault->at(sfAsset); + MPTIssue const share{mptIssuanceID}; + STAmount sharesRedeemed = {share}; + STAmount assetsWithdrawn; + try { - // Fixed assets, variable shares. - assets = amount; - shares = assetsToSharesWithdraw(vault, sleIssuance, assets); + if (amount.asset() == vaultAsset) + { + // Fixed assets, variable shares. + { + auto const maybeShares = + assetsToSharesWithdraw(vault, sleIssuance, amount); + if (!maybeShares) + return tecINTERNAL; // LCOV_EXCL_LINE + sharesRedeemed = *maybeShares; + } + + if (sharesRedeemed == beast::zero) + return tecPRECISION_LOSS; + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed); + if (!maybeAssets) + return tecINTERNAL; // LCOV_EXCL_LINE + assetsWithdrawn = *maybeAssets; + } + else if (amount.asset() == share) + { + // Fixed shares, variable assets. + sharesRedeemed = amount; + auto const maybeAssets = + sharesToAssetsWithdraw(vault, sleIssuance, sharesRedeemed); + if (!maybeAssets) + return tecINTERNAL; // LCOV_EXCL_LINE + assetsWithdrawn = *maybeAssets; + } + else + return tefINTERNAL; // LCOV_EXCL_LINE } - else if (amount.asset() == share) + catch (std::overflow_error const&) { - // Fixed shares, variable assets. - shares = amount; - assets = sharesToAssetsWithdraw(vault, sleIssuance, shares); + // It's easy to hit this exception from Number with large enough Scale + // so we avoid spamming the log and only use debug here. + JLOG(j_.debug()) // + << "VaultWithdraw: overflow error with" + << " scale=" << (int)vault->at(sfScale).value() // + << ", assetsTotal=" << vault->at(sfAssetsTotal).value() + << ", sharesTotal=" << sleIssuance->at(sfOutstandingAmount) + << ", amount=" << amount.value(); + return tecPATH_DRY; } - else - return tefINTERNAL; // LCOV_EXCL_LINE if (accountHolds( view(), @@ -217,31 +250,72 @@ VaultWithdraw::doApply() share, FreezeHandling::fhZERO_IF_FROZEN, AuthHandling::ahIGNORE_AUTH, - j_) < shares) + j_) < sharesRedeemed) { JLOG(j_.debug()) << "VaultWithdraw: account doesn't hold enough shares"; return tecINSUFFICIENT_FUNDS; } - // The vault must have enough assets on hand. The vault may hold assets that - // it has already pledged. That is why we look at AssetAvailable instead of - // the pseudo-account balance. - if (*vault->at(sfAssetsAvailable) < assets) + auto assetsAvailable = vault->at(sfAssetsAvailable); + auto assetsTotal = vault->at(sfAssetsTotal); + [[maybe_unused]] auto const lossUnrealized = vault->at(sfLossUnrealized); + XRPL_ASSERT( + lossUnrealized <= (assetsTotal - assetsAvailable), + "ripple::VaultWithdraw::doApply : loss and assets do balance"); + + // The vault must have enough assets on hand. The vault may hold assets + // that it has already pledged. That is why we look at AssetAvailable + // instead of the pseudo-account balance. + if (*assetsAvailable < assetsWithdrawn) { JLOG(j_.debug()) << "VaultWithdraw: vault doesn't hold enough assets"; return tecINSUFFICIENT_FUNDS; } - vault->at(sfAssetsTotal) -= assets; - vault->at(sfAssetsAvailable) -= assets; + assetsTotal -= assetsWithdrawn; + assetsAvailable -= assetsWithdrawn; view().update(vault); auto const& vaultAccount = vault->at(sfAccount); // Transfer shares from depositor to vault. - if (auto ter = accountSend( - view(), account_, vaultAccount, shares, j_, WaiveTransferFee::Yes)) + if (auto const ter = accountSend( + view(), + account_, + vaultAccount, + sharesRedeemed, + j_, + WaiveTransferFee::Yes); + !isTesSuccess(ter)) return ter; + // Try to remove MPToken for shares, if the account balance is zero. Vault + // pseudo-account will never set lsfMPTAuthorized, so we ignore flags. + // Keep MPToken if holder is the vault owner. + if (account_ != vault->at(sfOwner)) + { + if (auto const ter = removeEmptyHolding( + view(), account_, sharesRedeemed.asset(), j_); + isTesSuccess(ter)) + { + JLOG(j_.debug()) // + << "VaultWithdraw: removed empty MPToken for vault shares" + << " MPTID=" << to_string(mptIssuanceID) // + << " account=" << toBase58(account_); + } + else if (ter != tecHAS_OBLIGATIONS) + { + // LCOV_EXCL_START + JLOG(j_.error()) // + << "VaultWithdraw: failed to remove MPToken for vault shares" + << " MPTID=" << to_string(mptIssuanceID) // + << " account=" << toBase58(account_) // + << " with result: " << transToken(ter); + return ter; + // LCOV_EXCL_STOP + } + // else quietly ignore, account balance is not zero + } + auto const dstAcct = [&]() -> AccountID { if (ctx_.tx.isFieldPresent(sfDestination)) return ctx_.tx.getAccountID(sfDestination); @@ -249,15 +323,21 @@ VaultWithdraw::doApply() }(); // Transfer assets from vault to depositor or destination account. - if (auto ter = accountSend( - view(), vaultAccount, dstAcct, assets, j_, WaiveTransferFee::Yes)) + if (auto const ter = accountSend( + view(), + vaultAccount, + dstAcct, + assetsWithdrawn, + j_, + WaiveTransferFee::Yes); + !isTesSuccess(ter)) return ter; // Sanity check if (accountHolds( view(), vaultAccount, - assets.asset(), + assetsWithdrawn.asset(), FreezeHandling::fhIGNORE_FREEZE, AuthHandling::ahIGNORE_AUTH, j_) < beast::zero) diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index 07f6945dd4..cfd3599f78 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -912,28 +912,41 @@ deleteAMMTrustLine( std::optional const& ammAccountID, beast::Journal j); -// From the perspective of a vault, -// return the number of shares to give the depositor -// when they deposit a fixed amount of assets. -[[nodiscard]] STAmount +// From the perspective of a vault, return the number of shares to give the +// depositor when they deposit a fixed amount of assets. Since shares are MPT +// this number is integral and always truncated in this calculation. +[[nodiscard]] std::optional assetsToSharesDeposit( std::shared_ptr const& vault, std::shared_ptr const& issuance, STAmount const& assets); -// From the perspective of a vault, -// return the number of shares to demand from the depositor -// when they ask to withdraw a fixed amount of assets. -[[nodiscard]] STAmount +// From the perspective of a vault, return the number of assets to take from +// depositor when they receive a fixed amount of shares. Note, since shares are +// MPT, they are always an integral number. +[[nodiscard]] std::optional +sharesToAssetsDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares); + +enum class TruncateShares : bool { no = false, yes = true }; + +// From the perspective of a vault, return the number of shares to demand from +// the depositor when they ask to withdraw a fixed amount of assets. Since +// shares are MPT this number is integral, and it will be rounded to nearest +// unless explicitly requested to be truncated instead. +[[nodiscard]] std::optional assetsToSharesWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, - STAmount const& assets); + STAmount const& assets, + TruncateShares truncate = TruncateShares::no); -// From the perspective of a vault, -// return the number of assets to give the depositor -// when they redeem a fixed amount of shares. -[[nodiscard]] STAmount +// From the perspective of a vault, return the number of assets to give the +// depositor when they redeem a fixed amount of shares. Note, since shares are +// MPT, they are always an integral number. +[[nodiscard]] std::optional sharesToAssetsWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 4f8a29d15c..708d5b29f7 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -2793,58 +2793,113 @@ rippleCredit( saAmount.asset().value()); } -[[nodiscard]] STAmount +[[nodiscard]] std::optional assetsToSharesDeposit( std::shared_ptr const& vault, std::shared_ptr const& issuance, STAmount const& assets) { + XRPL_ASSERT( + !assets.negative(), + "ripple::assetsToSharesDeposit : non-negative assets"); XRPL_ASSERT( assets.asset() == vault->at(sfAsset), "ripple::assetsToSharesDeposit : assets and vault match"); - Number assetTotal = vault->at(sfAssetsTotal); - STAmount shares{vault->at(sfShareMPTID), static_cast(assets)}; + if (assets.negative() || assets.asset() != vault->at(sfAsset)) + return std::nullopt; // LCOV_EXCL_LINE + + Number const assetTotal = vault->at(sfAssetsTotal); + STAmount shares{vault->at(sfShareMPTID)}; if (assetTotal == 0) - return shares; - Number shareTotal = issuance->at(sfOutstandingAmount); - shares = shareTotal * (assets / assetTotal); + return STAmount{ + shares.asset(), + Number(assets.mantissa(), assets.exponent() + vault->at(sfScale)) + .truncate()}; + + Number const shareTotal = issuance->at(sfOutstandingAmount); + shares = (shareTotal * (assets / assetTotal)).truncate(); return shares; } -[[nodiscard]] STAmount +[[nodiscard]] std::optional +sharesToAssetsDeposit( + std::shared_ptr const& vault, + std::shared_ptr const& issuance, + STAmount const& shares) +{ + XRPL_ASSERT( + !shares.negative(), + "ripple::sharesToAssetsDeposit : non-negative shares"); + XRPL_ASSERT( + shares.asset() == vault->at(sfShareMPTID), + "ripple::sharesToAssetsDeposit : shares and vault match"); + if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) + return std::nullopt; // LCOV_EXCL_LINE + + Number const assetTotal = vault->at(sfAssetsTotal); + STAmount assets{vault->at(sfAsset)}; + if (assetTotal == 0) + return STAmount{ + assets.asset(), + shares.mantissa(), + shares.exponent() - vault->at(sfScale), + false}; + + Number const shareTotal = issuance->at(sfOutstandingAmount); + assets = assetTotal * (shares / shareTotal); + return assets; +} + +[[nodiscard]] std::optional assetsToSharesWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, - STAmount const& assets) + STAmount const& assets, + TruncateShares truncate) { + XRPL_ASSERT( + !assets.negative(), + "ripple::assetsToSharesDeposit : non-negative assets"); XRPL_ASSERT( assets.asset() == vault->at(sfAsset), "ripple::assetsToSharesWithdraw : assets and vault match"); + if (assets.negative() || assets.asset() != vault->at(sfAsset)) + return std::nullopt; // LCOV_EXCL_LINE + Number assetTotal = vault->at(sfAssetsTotal); assetTotal -= vault->at(sfLossUnrealized); STAmount shares{vault->at(sfShareMPTID)}; if (assetTotal == 0) return shares; - Number shareTotal = issuance->at(sfOutstandingAmount); - shares = shareTotal * (assets / assetTotal); + Number const shareTotal = issuance->at(sfOutstandingAmount); + Number result = shareTotal * (assets / assetTotal); + if (truncate == TruncateShares::yes) + result = result.truncate(); + shares = result; return shares; } -[[nodiscard]] STAmount +[[nodiscard]] std::optional sharesToAssetsWithdraw( std::shared_ptr const& vault, std::shared_ptr const& issuance, STAmount const& shares) { + XRPL_ASSERT( + !shares.negative(), + "ripple::sharesToAssetsDeposit : non-negative shares"); XRPL_ASSERT( shares.asset() == vault->at(sfShareMPTID), "ripple::sharesToAssetsWithdraw : shares and vault match"); + if (shares.negative() || shares.asset() != vault->at(sfShareMPTID)) + return std::nullopt; // LCOV_EXCL_LINE + Number assetTotal = vault->at(sfAssetsTotal); assetTotal -= vault->at(sfLossUnrealized); STAmount assets{vault->at(sfAsset)}; if (assetTotal == 0) return assets; - Number shareTotal = issuance->at(sfOutstandingAmount); + Number const shareTotal = issuance->at(sfOutstandingAmount); assets = assetTotal * (shares / shareTotal); return assets; } From 811c98082144fb19ca21d321f2318e51c54af4c8 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 4 Sep 2025 16:27:30 +0100 Subject: [PATCH 36/66] ci: Use cleanup-workspace action (#5763) * ci: Use cleanup-workspace action * Use latest version --- .github/workflows/build-test.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 90e1d9853c..40399539b8 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -102,16 +102,9 @@ jobs: echo 'CMake target: ${{ matrix.cmake_target }}' echo 'Config name: ${{ matrix.config_name }}' - - name: Clean workspace (MacOS) - if: ${{ inputs.os == 'macos' }} - run: | - WORKSPACE=${{ github.workspace }} - echo "Cleaning workspace '${WORKSPACE}'." - if [ -z "${WORKSPACE}" ] || [ "${WORKSPACE}" = "/" ]; then - echo "Invalid working directory '${WORKSPACE}'." - exit 1 - fi - find "${WORKSPACE}" -depth 1 | xargs rm -rfv + - name: Cleanup workspace + if: ${{ runner.os == 'macOS' }} + uses: XRPLF/actions/.github/actions/cleanup-workspace@3f044c7478548e3c32ff68980eeb36ece02b364e - name: Checkout repository uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 From 3865dde0b89a778b3bd7d0ebf6c368bbd83ce6c6 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 4 Sep 2025 20:26:57 +0100 Subject: [PATCH 37/66] fix: Add missing info to notify-clio workflow (#5761) * Add missing info to notify-clio workflow, as conan_ref --- .github/workflows/notify-clio.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/notify-clio.yml b/.github/workflows/notify-clio.yml index f7e10de7af..b0e5f35787 100644 --- a/.github/workflows/notify-clio.yml +++ b/.github/workflows/notify-clio.yml @@ -50,6 +50,10 @@ jobs: echo "channel=pr_${{ github.event.pull_request.number }}" >> "${GITHUB_OUTPUT}" echo 'Extracting version.' echo "version=$(cat src/libxrpl/protocol/BuildInfo.cpp | grep "versionString =" | awk -F '"' '{print $2}')" >> "${GITHUB_OUTPUT}" + - name: Calculate conan reference + id: conan_ref + run: | + echo "conan_ref=${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/@${{ steps.generate.outputs.channel }}" >> "${GITHUB_OUTPUT}" - name: Add Conan remote run: | echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." @@ -61,10 +65,9 @@ jobs: - name: Upload package run: | conan export --user=${{ steps.generate.outputs.user }} --channel=${{ steps.generate.outputs.channel }} . - conan upload --confirm --check --remote=${{ inputs.conan_remote_name }} xrpl/${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }} + conan upload --confirm --check --remote=${{ inputs.conan_remote_name }} xrpl/${{ steps.conan_ref.outputs.conan_ref }} outputs: - channel: ${{ steps.generate.outputs.channel }} - version: ${{ steps.generate.outputs.version }} + conan_ref: ${{ steps.conan_ref.outputs.conan_ref }} notify: needs: upload @@ -76,5 +79,5 @@ jobs: run: | gh api --method POST -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" \ /repos/xrplf/clio/dispatches -f "event_type=check_libxrpl" \ - -F "client_payload[version]=${{ needs.upload.outputs.version }}@${{ needs.upload.outputs.user }}/${{ needs.upload.outputs.channel }}" \ - -F "client_payload[pr]=${{ github.event.pull_request.number }}" + -F "client_payload[conan_ref]=${{ needs.upload.outputs.conan_ref }}" \ + -F "client_payload[pr_url]=${{ github.event.pull_request.html_url }}" From b0f4174e4790a96de39bd05c19807627b39d2cd8 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 4 Sep 2025 21:30:54 +0100 Subject: [PATCH 38/66] chore: Use tooling provided by pre-commit (#5753) --- .github/workflows/check-format.yml | 35 +---------------- .pre-commit-config.yaml | 60 +++++++++--------------------- 2 files changed, 19 insertions(+), 76 deletions(-) diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml index 359e3e634b..c63589017d 100644 --- a/.github/workflows/check-format.yml +++ b/.github/workflows/check-format.yml @@ -17,41 +17,10 @@ jobs: runs-on: ubuntu-latest container: ghcr.io/xrplf/ci/tools-rippled-pre-commit steps: - # The $GITHUB_WORKSPACE and ${{ github.workspace }} might not point to the - # same directory for jobs running in containers. The actions/checkout step - # is *supposed* to checkout into $GITHUB_WORKSPACE and then add it to - # safe.directory (see instructions at https://github.com/actions/checkout) - # but that is apparently not happening for some container images. We - # therefore preemptively add both directories to safe.directory. See also - # https://github.com/actions/runner/issues/2058 for more details. - - name: Configure git safe.directory - run: | - git config --global --add safe.directory $GITHUB_WORKSPACE - git config --global --add safe.directory ${{ github.workspace }} - name: Checkout repository uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - - name: Check configuration - run: | - echo 'Checking path.' - echo ${PATH} | tr ':' '\n' - - echo 'Checking environment variables.' - env | sort - - echo 'Checking pre-commit version.' - pre-commit --version - - echo 'Checking clang-format version.' - clang-format --version - - echo 'Checking NPM version.' - npm --version - - echo 'Checking Node.js version.' - node --version - - echo 'Checking prettier version.' - prettier --version + - name: Prepare runner + uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5 - name: Format code run: pre-commit run --show-diff-on-failure --color=always --all-files - name: Check for differences diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 223c324a8c..85568a8b2e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,18 +1,5 @@ # To run pre-commit hooks, first install pre-commit: # - `pip install pre-commit==${PRE_COMMIT_VERSION}` -# - `pip install pre-commit-hooks==${PRE_COMMIT_HOOKS_VERSION}` -# -# Depending on your system, you can use `brew install` or `apt install` as well -# for installing the pre-commit package, but `pip` is needed to install the -# hooks; you can also use `pipx` if you prefer. -# Next, install the required formatters: -# - `pip install clang-format==${CLANG_VERSION}` -# - `npm install prettier@${PRETTIER_VERSION}` -# -# See https://github.com/XRPLF/ci/blob/main/.github/workflows/tools-rippled.yml -# for the versions used in the CI pipeline. You will need to have the exact same -# versions of the tools installed on your system to produce the same results as -# the pipeline. # # Then, run the following command to install the git hook scripts: # - `pre-commit install` @@ -20,42 +7,29 @@ # - `pre-commit run --all-files` # To manually run a specific hook, use: # - `pre-commit run --all-files` -# To run the hooks against only the files changed in the current commit, use: +# To run the hooks against only the staged files, use: # - `pre-commit run` repos: - - repo: local + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: 3e8a8703264a2f4a69428a0aa4dcb512790b2c8c # frozen: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: mixed-line-ending + - id: check-merge-conflict + args: [--assume-in-merge] + + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: 7d85583be209cb547946c82fbe51f4bc5dd1d017 # frozen: v18.1.8 hooks: - id: clang-format - name: clang-format - language: system - entry: clang-format -i - files: '\.(cpp|hpp|h|ipp|proto)$' - - id: trailing-whitespace - name: trailing-whitespace - entry: trailing-whitespace-fixer - language: system - types: [text] - - id: end-of-file - name: end-of-file - entry: end-of-file-fixer - language: system - types: [text] - - id: mixed-line-ending - name: mixed-line-ending - entry: mixed-line-ending - language: system - types: [text] - - id: check-merge-conflict - name: check-merge-conflict - entry: check-merge-conflict --assume-in-merge - language: system - types: [text] - - repo: local + args: [--style=file] + "types_or": [c++, c, proto] + + - repo: https://github.com/rbubley/mirrors-prettier + rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2 hooks: - id: prettier - name: prettier - language: system - entry: prettier --ignore-unknown --write exclude: | (?x)^( From 6bf83380387a0f8a779598ee629e6f49b66fec73 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Thu, 4 Sep 2025 18:32:23 -0400 Subject: [PATCH 39/66] chore: Add `conan.lock` to workflow file checks (#5769) * Add conan.lock to workflow file checks * Add conan.lock to on-trigger.yml --- .github/workflows/on-pr.yml | 1 + .github/workflows/on-trigger.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index a4bbfd0997..8008aebcbb 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -75,6 +75,7 @@ jobs: tests/** CMakeLists.txt conanfile.py + conan.lock - name: Check whether to run # This step determines whether the rest of the workflow should # run. The rest of the workflow will run if this job runs AND at diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index 7732b814ad..dcd592a1f7 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -32,6 +32,7 @@ on: - "tests/**" - "CMakeLists.txt" - "conanfile.py" + - "conan.lock" # Run at 06:32 UTC on every day of the week from Monday through Friday. This # will force all dependencies to be rebuilt, which is useful to verify that From cb52c9af001316a906665a10283f6f5dd271ebb3 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Fri, 5 Sep 2025 14:08:17 +0100 Subject: [PATCH 40/66] fix: Remove extra @ in notify-clio.yml (#5771) --- .github/workflows/notify-clio.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/notify-clio.yml b/.github/workflows/notify-clio.yml index b0e5f35787..0c85bc9302 100644 --- a/.github/workflows/notify-clio.yml +++ b/.github/workflows/notify-clio.yml @@ -53,7 +53,7 @@ jobs: - name: Calculate conan reference id: conan_ref run: | - echo "conan_ref=${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/@${{ steps.generate.outputs.channel }}" >> "${GITHUB_OUTPUT}" + echo "conan_ref=${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }}" >> "${GITHUB_OUTPUT}" - name: Add Conan remote run: | echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." From 9fe0a154f15df0e6314317b1b5952353470c0765 Mon Sep 17 00:00:00 2001 From: tzchenxixi Date: Mon, 8 Sep 2025 21:13:32 +0800 Subject: [PATCH 41/66] chore: remove redundant word in comment (#5752) --- src/xrpld/app/paths/Pathfinder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xrpld/app/paths/Pathfinder.h b/src/xrpld/app/paths/Pathfinder.h index ea3928dff4..b6c8bb8b2d 100644 --- a/src/xrpld/app/paths/Pathfinder.h +++ b/src/xrpld/app/paths/Pathfinder.h @@ -166,7 +166,7 @@ private: int addFlags, std::function const& continueCallback); - // Compute the liquidity for a path. Return tesSUCCESS if it has has enough + // Compute the liquidity for a path. Return tesSUCCESS if it has enough // liquidity to be worth keeping, otherwise an error. TER getPathLiquidity( From 6d40b882a4684d6926d0b23ad0e68c847e19e1ef Mon Sep 17 00:00:00 2001 From: Bronek Kozicki Date: Mon, 8 Sep 2025 14:54:50 +0100 Subject: [PATCH 42/66] Switch on-trigger to minimal build (#5773) --- .github/workflows/on-trigger.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index dcd592a1f7..d005d43d33 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -112,7 +112,7 @@ jobs: dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build == 'true' }} dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload == 'true' }} os: ${{ matrix.os }} - strategy_matrix: "all" + strategy_matrix: "minimal" secrets: codecov_token: ${{ secrets.CODECOV_TOKEN }} conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} From 9ebeb413e45b64b738c9c2ee1367b1e7674859be Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Mon, 8 Sep 2025 16:15:59 +0100 Subject: [PATCH 43/66] feat: Implement separate upload workflow (#5762) * feat: Implement separate upload workflow * Use cleanup-workspace * Name some workflows reusable * Add dependencies --- .github/actions/build-deps/action.yml | 31 +------- .github/actions/build-test/action.yml | 1 + .github/actions/setup-conan/action.yml | 43 ++++++++++ .github/scripts/strategy-matrix/generate.py | 0 .github/workflows/build-test.yml | 62 ++------------- .github/workflows/notify-clio.yml | 19 +++-- .github/workflows/on-pr.yml | 26 +------ .github/workflows/on-trigger.yml | 36 --------- .../workflows/reusable-strategy-matrix.yml | 38 +++++++++ .../reusable-upload-conan-deps-os.yml | 78 +++++++++++++++++++ .github/workflows/upload-conan-deps.yml | 62 +++++++++++++++ 11 files changed, 243 insertions(+), 153 deletions(-) create mode 100644 .github/actions/setup-conan/action.yml mode change 100644 => 100755 .github/scripts/strategy-matrix/generate.py create mode 100644 .github/workflows/reusable-strategy-matrix.yml create mode 100644 .github/workflows/reusable-upload-conan-deps-os.yml create mode 100644 .github/workflows/upload-conan-deps.yml diff --git a/.github/actions/build-deps/action.yml b/.github/actions/build-deps/action.yml index ba4f4e9e2f..c3b405e70f 100644 --- a/.github/actions/build-deps/action.yml +++ b/.github/actions/build-deps/action.yml @@ -1,7 +1,5 @@ -# This action installs and optionally uploads Conan dependencies to a remote -# repository. The dependencies will only be uploaded if the credentials are -# provided. name: Build Conan dependencies +description: "Install Conan dependencies, optionally forcing a rebuild of all dependencies." # Note that actions do not support 'type' and all inputs are strings, see # https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs. @@ -12,28 +10,10 @@ inputs: build_type: description: 'The build type to use ("Debug", "Release").' required: true - conan_remote_name: - description: "The name of the Conan remote to use." - required: true - conan_remote_url: - description: "The URL of the Conan endpoint to use." - required: true - conan_remote_username: - description: "The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded." - required: false - default: "" - conan_remote_password: - description: "The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded." - required: false - default: "" force_build: description: 'Force building of all dependencies ("true", "false").' required: false default: "false" - force_upload: - description: 'Force uploading of all dependencies ("true", "false").' - required: false - default: "false" runs: using: composite @@ -51,12 +31,3 @@ runs: --options:host '&:xrpld=True' \ --settings:all build_type=${{ inputs.build_type }} \ --format=json .. - - name: Upload Conan dependencies - if: ${{ inputs.conan_remote_username != '' && inputs.conan_remote_password != '' }} - shell: bash - working-directory: ${{ inputs.build_dir }} - run: | - echo "Logging into Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." - conan remote login ${{ inputs.conan_remote_name }} "${{ inputs.conan_remote_username }}" --password "${{ inputs.conan_remote_password }}" - echo 'Uploading dependencies.' - conan upload '*' --confirm --check ${{ inputs.force_upload == 'true' && '--force' || '' }} --remote=${{ inputs.conan_remote_name }} diff --git a/.github/actions/build-test/action.yml b/.github/actions/build-test/action.yml index ee945dcf38..cf1bac16f7 100644 --- a/.github/actions/build-test/action.yml +++ b/.github/actions/build-test/action.yml @@ -1,6 +1,7 @@ # This action build and tests the binary. The Conan dependencies must have # already been installed (see the build-deps action). name: Build and Test +description: "Build and test the binary." # Note that actions do not support 'type' and all inputs are strings, see # https://docs.github.com/en/actions/reference/workflows-and-actions/metadata-syntax#inputs. diff --git a/.github/actions/setup-conan/action.yml b/.github/actions/setup-conan/action.yml new file mode 100644 index 0000000000..d31809dc94 --- /dev/null +++ b/.github/actions/setup-conan/action.yml @@ -0,0 +1,43 @@ +name: Setup Conan +description: "Set up Conan configuration, profile, and remote." + +inputs: + conan_remote_name: + description: "The name of the Conan remote to use." + required: false + default: xrplf + conan_remote_url: + description: "The URL of the Conan endpoint to use." + required: false + default: https://conan.ripplex.io + +runs: + using: composite + + steps: + - name: Set up Conan configuration + shell: bash + run: | + echo 'Installing configuration.' + cat conan/global.conf ${{ runner.os == 'Linux' && '>>' || '>' }} $(conan config home)/global.conf + + echo 'Conan configuration:' + conan config show '*' + + - name: Set up Conan profile + shell: bash + run: | + echo 'Installing profile.' + conan config install conan/profiles/default -tf $(conan config home)/profiles/ + + echo 'Conan profile:' + conan profile show + + - name: Set up Conan remote + shell: bash + run: | + echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." + conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }} + + echo 'Listing Conan remotes.' + conan remote list diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py old mode 100644 new mode 100755 diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 40399539b8..69ff986f98 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -13,14 +13,6 @@ on: required: false type: string default: ".build" - conan_remote_name: - description: "The name of the Conan remote to use." - required: true - type: string - conan_remote_url: - description: "The URL of the Conan endpoint to use." - required: true - type: string dependencies_force_build: description: "Force building of all dependencies." required: false @@ -45,12 +37,6 @@ on: codecov_token: description: "The Codecov token to use for uploading coverage reports." required: false - conan_remote_username: - description: "The username for logging into the Conan remote. If not provided, the dependencies will not be uploaded." - required: false - conan_remote_password: - description: "The password for logging into the Conan remote. If not provided, the dependencies will not be uploaded." - required: false concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.os }} @@ -63,20 +49,10 @@ defaults: jobs: # Generate the strategy matrix to be used by the following job. generate-matrix: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - - name: Set up Python - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 - with: - python-version: 3.13 - - name: Generate strategy matrix - working-directory: .github/scripts/strategy-matrix - id: generate - run: python generate.py ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} --config=${{ inputs.os }}.json >> "${GITHUB_OUTPUT}" - outputs: - matrix: ${{ steps.generate.outputs.matrix }} + uses: ./.github/workflows/reusable-strategy-matrix.yml + with: + os: ${{ inputs.os }} + strategy_matrix: ${{ inputs.strategy_matrix }} # Build and test the binary. build-test: @@ -148,40 +124,16 @@ jobs: echo 'Checking nproc version.' nproc --version - - name: Set up Conan configuration - run: | - echo 'Installing configuration.' - cat conan/global.conf ${{ inputs.os == 'linux' && '>>' || '>' }} $(conan config home)/global.conf - - echo 'Conan configuration:' - conan config show '*' - - name: Set up Conan profile - run: | - echo 'Installing profile.' - conan config install conan/profiles/default -tf $(conan config home)/profiles/ - - echo 'Conan profile:' - conan profile show - - name: Set up Conan remote - shell: bash - run: | - echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." - conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }} - - echo 'Listing Conan remotes.' - conan remote list + - name: Setup Conan + uses: ./.github/actions/setup-conan - name: Build dependencies uses: ./.github/actions/build-deps with: build_dir: ${{ inputs.build_dir }} build_type: ${{ matrix.build_type }} - conan_remote_name: ${{ inputs.conan_remote_name }} - conan_remote_url: ${{ inputs.conan_remote_url }} - conan_remote_username: ${{ secrets.conan_remote_username }} - conan_remote_password: ${{ secrets.conan_remote_password }} force_build: ${{ inputs.dependencies_force_build }} - force_upload: ${{ inputs.dependencies_force_upload }} + - name: Build and test binary uses: ./.github/actions/build-test with: diff --git a/.github/workflows/notify-clio.yml b/.github/workflows/notify-clio.yml index 0c85bc9302..692904ff12 100644 --- a/.github/workflows/notify-clio.yml +++ b/.github/workflows/notify-clio.yml @@ -9,12 +9,14 @@ on: inputs: conan_remote_name: description: "The name of the Conan remote to use." - required: true + required: false type: string + default: xrplf conan_remote_url: description: "The URL of the Conan endpoint to use." - required: true + required: false type: string + default: https://conan.ripplex.io secrets: clio_notify_token: description: "The GitHub token to notify Clio about new versions." @@ -54,12 +56,13 @@ jobs: id: conan_ref run: | echo "conan_ref=${{ steps.generate.outputs.version }}@${{ steps.generate.outputs.user }}/${{ steps.generate.outputs.channel }}" >> "${GITHUB_OUTPUT}" - - name: Add Conan remote - run: | - echo "Adding Conan remote '${{ inputs.conan_remote_name }}' at ${{ inputs.conan_remote_url }}." - conan remote add --index 0 --force ${{ inputs.conan_remote_name }} ${{ inputs.conan_remote_url }} - echo 'Listing Conan remotes.' - conan remote list + + - name: Set up Conan + uses: ./.github/actions/setup-conan + with: + conan_remote_name: ${{ inputs.conan_remote_name }} + conan_remote_url: ${{ inputs.conan_remote_url }} + - name: Log into Conan remote run: conan remote login ${{ inputs.conan_remote_name }} "${{ secrets.conan_remote_username }}" --password "${{ secrets.conan_remote_password }}" - name: Upload package diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 8008aebcbb..f72b8a9121 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -23,10 +23,6 @@ defaults: run: shell: bash -env: - CONAN_REMOTE_NAME: xrplf - CONAN_REMOTE_URL: https://conan.ripplex.io - jobs: # This job determines whether the rest of the workflow should run. It runs # when the PR is not a draft (which should also cover merge-group) or @@ -105,40 +101,22 @@ jobs: if: needs.should-run.outputs.go == 'true' uses: ./.github/workflows/check-levelization.yml - # This job works around the limitation that GitHub Actions does not support - # using environment variables as inputs for reusable workflows. - generate-outputs: - needs: should-run - if: needs.should-run.outputs.go == 'true' - runs-on: ubuntu-latest - steps: - - name: No-op - run: true - outputs: - conan_remote_name: ${{ env.CONAN_REMOTE_NAME }} - conan_remote_url: ${{ env.CONAN_REMOTE_URL }} - build-test: - needs: generate-outputs + needs: should-run uses: ./.github/workflows/build-test.yml strategy: matrix: os: [linux, macos, windows] with: - conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} - conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} os: ${{ matrix.os }} secrets: codecov_token: ${{ secrets.CODECOV_TOKEN }} notify-clio: needs: - - generate-outputs + - should-run - build-test uses: ./.github/workflows/notify-clio.yml - with: - conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} - conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} secrets: clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }} conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index d005d43d33..14884391ef 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -66,54 +66,18 @@ defaults: run: shell: bash -env: - CONAN_REMOTE_NAME: xrplf - CONAN_REMOTE_URL: https://conan.ripplex.io - jobs: check-missing-commits: if: ${{ github.event_name == 'push' && github.ref_type == 'branch' && contains(fromJSON('["develop", "release"]'), github.ref_name) }} uses: ./.github/workflows/check-missing-commits.yml - # This job works around the limitation that GitHub Actions does not support - # using environment variables as inputs for reusable workflows. It also sets - # outputs that depend on the event that triggered the workflow. - generate-outputs: - runs-on: ubuntu-latest - steps: - - name: Check inputs and set outputs - id: generate - run: | - if [[ '${{ github.event_name }}' == 'push' ]]; then - echo 'dependencies_force_build=false' >> "${GITHUB_OUTPUT}" - echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}" - elif [[ '${{ github.event_name }}' == 'schedule' ]]; then - echo 'dependencies_force_build=true' >> "${GITHUB_OUTPUT}" - echo 'dependencies_force_upload=false' >> "${GITHUB_OUTPUT}" - else - echo 'dependencies_force_build=${{ inputs.dependencies_force_build }}' >> "${GITHUB_OUTPUT}" - echo 'dependencies_force_upload=${{ inputs.dependencies_force_upload }}' >> "${GITHUB_OUTPUT}" - fi - outputs: - conan_remote_name: ${{ env.CONAN_REMOTE_NAME }} - conan_remote_url: ${{ env.CONAN_REMOTE_URL }} - dependencies_force_build: ${{ steps.generate.outputs.dependencies_force_build }} - dependencies_force_upload: ${{ steps.generate.outputs.dependencies_force_upload }} - build-test: - needs: generate-outputs uses: ./.github/workflows/build-test.yml strategy: matrix: os: [linux, macos, windows] with: - conan_remote_name: ${{ needs.generate-outputs.outputs.conan_remote_name }} - conan_remote_url: ${{ needs.generate-outputs.outputs.conan_remote_url }} - dependencies_force_build: ${{ needs.generate-outputs.outputs.dependencies_force_build == 'true' }} - dependencies_force_upload: ${{ needs.generate-outputs.outputs.dependencies_force_upload == 'true' }} os: ${{ matrix.os }} strategy_matrix: "minimal" secrets: codecov_token: ${{ secrets.CODECOV_TOKEN }} - conan_remote_username: ${{ secrets.CONAN_REMOTE_USERNAME }} - conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml new file mode 100644 index 0000000000..5c84b51de1 --- /dev/null +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -0,0 +1,38 @@ +name: Generate strategy matrix + +on: + workflow_call: + inputs: + os: + description: 'The operating system to use for the build ("linux", "macos", "windows").' + required: true + type: string + strategy_matrix: + # TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations. + description: 'The strategy matrix to use for generating the configurations ("minimal", "all").' + required: false + type: string + default: "minimal" + outputs: + matrix: + description: "The generated strategy matrix." + value: ${{ jobs.generate-matrix.outputs.matrix }} + +jobs: + generate-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.generate.outputs.matrix }} + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 + with: + python-version: 3.13 + + - name: Generate strategy matrix + working-directory: .github/scripts/strategy-matrix + id: generate + run: ./generate.py ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} --config=${{ inputs.os }}.json >> "${GITHUB_OUTPUT}" diff --git a/.github/workflows/reusable-upload-conan-deps-os.yml b/.github/workflows/reusable-upload-conan-deps-os.yml new file mode 100644 index 0000000000..787cf6a39e --- /dev/null +++ b/.github/workflows/reusable-upload-conan-deps-os.yml @@ -0,0 +1,78 @@ +name: Upload Conan Dependencies + +on: + workflow_call: + inputs: + build_dir: + description: "The directory where to build." + required: false + type: string + default: ".build" + conan_remote_name: + description: "The name of the Conan remote to use." + required: false + type: string + default: xrplf + + os: + description: 'The operating system to use for the build ("linux", "macos", "windows").' + required: true + type: string + force_source_build: + description: "Force source build of all dependencies" + required: true + type: boolean + force_upload: + description: "Force upload of all dependencies" + required: true + type: boolean + secrets: + CONAN_USERNAME: + required: true + CONAN_PASSWORD: + required: true + +jobs: + generate-matrix: + uses: ./.github/workflows/reusable-strategy-matrix.yml + with: + os: ${{ inputs.os }} + strategy_matrix: all + + upload-conan-deps: + needs: + - generate-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} + runs-on: ${{ matrix.architecture.runner }} + container: ${{ inputs.os == 'linux' && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }} + + steps: + - name: Cleanup workspace + if: ${{ runner.os == 'macOS' }} + uses: XRPLF/actions/.github/actions/cleanup-workspace@3f044c7478548e3c32ff68980eeb36ece02b364e + + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Prepare runner + uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5 + with: + disable_ccache: false + + - name: Setup Conan + uses: ./.github/actions/setup-conan + + - name: Build dependencies + uses: ./.github/actions/build-deps + with: + build_dir: ${{ inputs.build_dir }} + build_type: ${{ matrix.build_type }} + force_build: ${{ inputs.force_source_build }} + + - name: Login to Conan + if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' + run: conan remote login -p ${{ secrets.CONAN_PASSWORD }} ${{ inputs.conan_remote_name }} ${{ secrets.CONAN_USERNAME }} + + - name: Upload Conan packages + if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' + run: conan upload "*" -r=${{ inputs.conan_remote_name }} --confirm ${{ inputs.force_upload == 'true' && '--force' || '' }} diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml new file mode 100644 index 0000000000..5a6adc99be --- /dev/null +++ b/.github/workflows/upload-conan-deps.yml @@ -0,0 +1,62 @@ +name: Upload Conan Dependencies + +on: + schedule: + - cron: "0 9 * * 1-5" + workflow_dispatch: + inputs: + force_source_build: + description: "Force source build of all dependencies" + required: false + default: false + type: boolean + force_upload: + description: "Force upload of all dependencies" + required: false + default: false + type: boolean + pull_request: + branches: [develop] + paths: + - .github/workflows/upload-conan-deps.yml + + - .github/workflows/reusable-strategy-matrix.yml + - .github/workflows/reusable-upload-conan-deps-os.yml + + - .github/actions/build-deps/action.yml + - ".github/scripts/strategy-matrix/**" + + - conanfile.py + - conan.lock + push: + branches: [develop] + paths: + - .github/workflows/upload-conan-deps.yml + + - .github/workflows/reusable-strategy-matrix.yml + - .github/workflows/reusable-upload-conan-deps-os.yml + + - .github/actions/build-deps/action.yml + - ".github/scripts/strategy-matrix/**" + + - conanfile.py + - conan.lock + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + run-upload-conan-deps: + strategy: + fail-fast: true + matrix: + os: ["linux", "macos", "windows"] + uses: ./.github/workflows/reusable-upload-conan-deps-os.yml + with: + force_source_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }} + force_upload: ${{ github.event.inputs.force_upload == 'true' }} + os: ${{ matrix.os }} + secrets: + CONAN_USERNAME: ${{ secrets.CONAN_REMOTE_USERNAME }} + CONAN_PASSWORD: ${{ secrets.CONAN_REMOTE_PASSWORD }} From bcde2790a412e647f15bb8e4df6c778c4050bd67 Mon Sep 17 00:00:00 2001 From: Wo Jake Date: Tue, 9 Sep 2025 02:03:20 +0800 Subject: [PATCH 44/66] Update old links & descriptions in README.md (#4701) --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index fe7daa38bc..dbc5ab078e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The [XRP Ledger](https://xrpl.org/) is a decentralized cryptographic ledger powe ## XRP -[XRP](https://xrpl.org/xrp.html) is a public, counterparty-free asset native to the XRP Ledger, and is designed to bridge the many different currencies in use worldwide. XRP is traded on the open-market and is available for anyone to access. The XRP Ledger was created in 2012 with a finite supply of 100 billion units of XRP. +[XRP](https://xrpl.org/xrp.html) is a public, counterparty-free crypto-asset native to the XRP Ledger, and is designed as a gas token for network services and to bridge different currencies. XRP is traded on the open-market and is available for anyone to access. The XRP Ledger was created in 2012 with a finite supply of 100 billion units of XRP. ## rippled @@ -23,19 +23,19 @@ If you are interested in running an **API Server** (including a **Full History S - **[Censorship-Resistant Transaction Processing][]:** No single party decides which transactions succeed or fail, and no one can "roll back" a transaction after it completes. As long as those who choose to participate in the network keep it healthy, they can settle transactions in seconds. - **[Fast, Efficient Consensus Algorithm][]:** The XRP Ledger's consensus algorithm settles transactions in 4 to 5 seconds, processing at a throughput of up to 1500 transactions per second. These properties put XRP at least an order of magnitude ahead of other top digital assets. -- **[Finite XRP Supply][]:** When the XRP Ledger began, 100 billion XRP were created, and no more XRP will ever be created. The available supply of XRP decreases slowly over time as small amounts are destroyed to pay transaction costs. -- **[Responsible Software Governance][]:** A team of full-time, world-class developers at Ripple maintain and continually improve the XRP Ledger's underlying software with contributions from the open-source community. Ripple acts as a steward for the technology and an advocate for its interests, and builds constructive relationships with governments and financial institutions worldwide. +- **[Finite XRP Supply][]:** When the XRP Ledger began, 100 billion XRP were created, and no more XRP will ever be created. The available supply of XRP decreases slowly over time as small amounts are destroyed to pay transaction fees. +- **[Responsible Software Governance][]:** A team of full-time developers at Ripple & other organizations maintain and continually improve the XRP Ledger's underlying software with contributions from the open-source community. Ripple acts as a steward for the technology and an advocate for its interests. - **[Secure, Adaptable Cryptography][]:** The XRP Ledger relies on industry standard digital signature systems like ECDSA (the same scheme used by Bitcoin) but also supports modern, efficient algorithms like Ed25519. The extensible nature of the XRP Ledger's software makes it possible to add and disable algorithms as the state of the art in cryptography advances. -- **[Modern Features for Smart Contracts][]:** Features like Escrow, Checks, and Payment Channels support cutting-edge financial applications including the [Interledger Protocol](https://interledger.org/). This toolbox of advanced features comes with safety features like a process for amending the network and separate checks against invariant constraints. +- **[Modern Features][]:** Features like Escrow, Checks, and Payment Channels support financial applications atop of the XRP Ledger. This toolbox of advanced features comes with safety features like a process for amending the network and separate checks against invariant constraints. - **[On-Ledger Decentralized Exchange][]:** In addition to all the features that make XRP useful on its own, the XRP Ledger also has a fully-functional accounting system for tracking and trading obligations denominated in any way users want, and an exchange built into the protocol. The XRP Ledger can settle long, cross-currency payment paths and exchanges of multiple currencies in atomic transactions, bridging gaps of trust with XRP. -[Censorship-Resistant Transaction Processing]: https://xrpl.org/xrp-ledger-overview.html#censorship-resistant-transaction-processing -[Fast, Efficient Consensus Algorithm]: https://xrpl.org/xrp-ledger-overview.html#fast-efficient-consensus-algorithm -[Finite XRP Supply]: https://xrpl.org/xrp-ledger-overview.html#finite-xrp-supply -[Responsible Software Governance]: https://xrpl.org/xrp-ledger-overview.html#responsible-software-governance -[Secure, Adaptable Cryptography]: https://xrpl.org/xrp-ledger-overview.html#secure-adaptable-cryptography -[Modern Features for Smart Contracts]: https://xrpl.org/xrp-ledger-overview.html#modern-features-for-smart-contracts -[On-Ledger Decentralized Exchange]: https://xrpl.org/xrp-ledger-overview.html#on-ledger-decentralized-exchange +[Censorship-Resistant Transaction Processing]: https://xrpl.org/transaction-censorship-detection.html#transaction-censorship-detection +[Fast, Efficient Consensus Algorithm]: https://xrpl.org/consensus-research.html#consensus-research +[Finite XRP Supply]: https://xrpl.org/what-is-xrp.html +[Responsible Software Governance]: https://xrpl.org/contribute-code.html#contribute-code-to-the-xrp-ledger +[Secure, Adaptable Cryptography]: https://xrpl.org/cryptographic-keys.html#cryptographic-keys +[Modern Features]: https://xrpl.org/use-specialized-payment-types.html +[On-Ledger Decentralized Exchange]: https://xrpl.org/decentralized-exchange.html#decentralized-exchange ## Source Code From da4c8c9550157dd91950aa774d13659104609e2a Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 9 Sep 2025 16:25:41 +0100 Subject: [PATCH 45/66] ci: Only run build-test/notify-clio if should-run indicates to (#5777) - Fixes an issue introduced by #5762 which removed the transitive `should-run` check from these two jobs. --- .github/workflows/on-pr.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index f72b8a9121..4aa9ca5869 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -103,6 +103,7 @@ jobs: build-test: needs: should-run + if: needs.should-run.outputs.go == 'true' uses: ./.github/workflows/build-test.yml strategy: matrix: @@ -116,6 +117,7 @@ jobs: needs: - should-run - build-test + if: needs.should-run.outputs.go == 'true' uses: ./.github/workflows/notify-clio.yml secrets: clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }} From f1eaa6a2641238cf1c4098f4a006e5ab04a3afee Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Tue, 9 Sep 2025 11:57:28 -0400 Subject: [PATCH 46/66] enable fixAMMClawbackRounding (#5750) --- include/xrpl/protocol/detail/features.macro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index e2725d1fc0..264fad7fc2 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -34,7 +34,7 @@ XRPL_FIX (PriceOracleOrder, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo) -XRPL_FIX (AMMClawbackRounding, Supported::no, VoteBehavior::DefaultNo) +XRPL_FIX (AMMClawbackRounding, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (AMMv1_3, Supported::yes, VoteBehavior::DefaultNo) From 148f669a2562a1870503969946286885a5981835 Mon Sep 17 00:00:00 2001 From: Ed Hennis Date: Tue, 9 Sep 2025 14:07:04 -0400 Subject: [PATCH 47/66] chore: "passed" fails if any previous jobs fail or are cancelled (#5776) For the purposes of being able to merge a PR, Github Actions jobs count as passed if they ran and passed, or were skipped. With this change, if any of the jobs that "passed" depends on fail or are cancelled, then "passed" will fail. If they all succeed or are skipped, then "passed" is skipped, which does not prevent a merge. This saves spinning up a runner in the usual case where things work, and will simplify our branch protection rules, so that only "passed" will need to be checked. --- .github/workflows/on-pr.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 4aa9ca5869..f194bd1e37 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -125,11 +125,12 @@ jobs: conan_remote_password: ${{ secrets.CONAN_REMOTE_PASSWORD }} passed: + if: failure() || cancelled() needs: - build-test - check-format - check-levelization runs-on: ubuntu-latest steps: - - name: No-op - run: true + - name: Fail + run: false From e67e0395df2d7e3ead98163920112b7772bed325 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 9 Sep 2025 20:47:06 +0100 Subject: [PATCH 48/66] ci: Limit number of parallel jobs in "upload-conan-deps" (#5781) - This should prevent Artifactory from being overloaded by too many requests at a time. - Uses "max-parallel" to limit the build job to 10 simultaneous instances. - Only run the minimal matrix on PRs. --- .github/scripts/strategy-matrix/generate.py | 43 +++++++--- .../workflows/reusable-strategy-matrix.yml | 4 +- .../reusable-upload-conan-deps-os.yml | 78 ------------------- .github/workflows/upload-conan-deps.yml | 56 +++++++++---- 4 files changed, 75 insertions(+), 106 deletions(-) delete mode 100644 .github/workflows/reusable-upload-conan-deps-os.yml diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index 9743d5a4e3..b6f6601291 100755 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -2,7 +2,17 @@ import argparse import itertools import json -import re +from pathlib import Path +from dataclasses import dataclass + +THIS_DIR = Path(__file__).parent.resolve() + +@dataclass +class Config: + architecture: list[dict] + os: list[dict] + build_type: list[str] + cmake_args: list[str] ''' Generate a strategy matrix for GitHub Actions CI. @@ -18,9 +28,9 @@ We will further set additional CMake arguments as follows: - Certain Debian Bookworm configurations will change the reference fee, enable codecov, and enable voidstar in PRs. ''' -def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict], build_type: list[str], cmake_args: list[str]) -> dict: +def generate_strategy_matrix(all: bool, config: Config) -> list: configurations = [] - for architecture, os, build_type, cmake_args in itertools.product(architecture, os, build_type, cmake_args): + for architecture, os, build_type, cmake_args in itertools.product(config.architecture, config.os, config.build_type, config.cmake_args): # The default CMake target is 'all' for Linux and MacOS and 'install' # for Windows, but it can get overridden for certain configurations. cmake_target = 'install' if os["distro_name"] == 'windows' else 'all' @@ -158,21 +168,30 @@ def generate_strategy_matrix(all: bool, architecture: list[dict], os: list[dict] 'architecture': architecture, }) - return {'include': configurations} + return configurations + + +def read_config(file: Path) -> Config: + config = json.loads(file.read_text()) + if config['architecture'] is None or config['os'] is None or config['build_type'] is None or config['cmake_args'] is None: + raise Exception('Invalid configuration file.') + + return Config(**config) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-a', '--all', help='Set to generate all configurations (generally used when merging a PR) or leave unset to generate a subset of configurations (generally used when committing to a PR).', action="store_true") - parser.add_argument('-c', '--config', help='Path to the JSON file containing the strategy matrix configurations.', required=True, type=str) + parser.add_argument('-c', '--config', help='Path to the JSON file containing the strategy matrix configurations.', required=False, type=Path) args = parser.parse_args() - # Load the JSON configuration file. - config = None - with open(args.config, 'r') as f: - config = json.load(f) - if config['architecture'] is None or config['os'] is None or config['build_type'] is None or config['cmake_args'] is None: - raise Exception('Invalid configuration file.') + matrix = [] + if args.config is None or args.config == '': + matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "linux.json")) + matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "macos.json")) + matrix += generate_strategy_matrix(args.all, read_config(THIS_DIR / "windows.json")) + else: + matrix += generate_strategy_matrix(args.all, read_config(args.config)) # Generate the strategy matrix. - print(f'matrix={json.dumps(generate_strategy_matrix(args.all, config['architecture'], config['os'], config['build_type'], config['cmake_args']))}') + print(f'matrix={json.dumps({"include": matrix})}') diff --git a/.github/workflows/reusable-strategy-matrix.yml b/.github/workflows/reusable-strategy-matrix.yml index 5c84b51de1..20a90fc2e3 100644 --- a/.github/workflows/reusable-strategy-matrix.yml +++ b/.github/workflows/reusable-strategy-matrix.yml @@ -5,7 +5,7 @@ on: inputs: os: description: 'The operating system to use for the build ("linux", "macos", "windows").' - required: true + required: false type: string strategy_matrix: # TODO: Support additional strategies, e.g. "ubuntu" for generating all Ubuntu configurations. @@ -35,4 +35,4 @@ jobs: - name: Generate strategy matrix working-directory: .github/scripts/strategy-matrix id: generate - run: ./generate.py ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} --config=${{ inputs.os }}.json >> "${GITHUB_OUTPUT}" + run: ./generate.py ${{ inputs.strategy_matrix == 'all' && '--all' || '' }} ${{ inputs.os != '' && format('--config={0}.json', inputs.os) || '' }} >> "${GITHUB_OUTPUT}" diff --git a/.github/workflows/reusable-upload-conan-deps-os.yml b/.github/workflows/reusable-upload-conan-deps-os.yml deleted file mode 100644 index 787cf6a39e..0000000000 --- a/.github/workflows/reusable-upload-conan-deps-os.yml +++ /dev/null @@ -1,78 +0,0 @@ -name: Upload Conan Dependencies - -on: - workflow_call: - inputs: - build_dir: - description: "The directory where to build." - required: false - type: string - default: ".build" - conan_remote_name: - description: "The name of the Conan remote to use." - required: false - type: string - default: xrplf - - os: - description: 'The operating system to use for the build ("linux", "macos", "windows").' - required: true - type: string - force_source_build: - description: "Force source build of all dependencies" - required: true - type: boolean - force_upload: - description: "Force upload of all dependencies" - required: true - type: boolean - secrets: - CONAN_USERNAME: - required: true - CONAN_PASSWORD: - required: true - -jobs: - generate-matrix: - uses: ./.github/workflows/reusable-strategy-matrix.yml - with: - os: ${{ inputs.os }} - strategy_matrix: all - - upload-conan-deps: - needs: - - generate-matrix - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} - runs-on: ${{ matrix.architecture.runner }} - container: ${{ inputs.os == 'linux' && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }} - - steps: - - name: Cleanup workspace - if: ${{ runner.os == 'macOS' }} - uses: XRPLF/actions/.github/actions/cleanup-workspace@3f044c7478548e3c32ff68980eeb36ece02b364e - - - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - - name: Prepare runner - uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5 - with: - disable_ccache: false - - - name: Setup Conan - uses: ./.github/actions/setup-conan - - - name: Build dependencies - uses: ./.github/actions/build-deps - with: - build_dir: ${{ inputs.build_dir }} - build_type: ${{ matrix.build_type }} - force_build: ${{ inputs.force_source_build }} - - - name: Login to Conan - if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' - run: conan remote login -p ${{ secrets.CONAN_PASSWORD }} ${{ inputs.conan_remote_name }} ${{ secrets.CONAN_USERNAME }} - - - name: Upload Conan packages - if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' - run: conan upload "*" -r=${{ inputs.conan_remote_name }} --confirm ${{ inputs.force_upload == 'true' && '--force' || '' }} diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index 5a6adc99be..f63482761d 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -21,7 +21,6 @@ on: - .github/workflows/upload-conan-deps.yml - .github/workflows/reusable-strategy-matrix.yml - - .github/workflows/reusable-upload-conan-deps-os.yml - .github/actions/build-deps/action.yml - ".github/scripts/strategy-matrix/**" @@ -34,7 +33,6 @@ on: - .github/workflows/upload-conan-deps.yml - .github/workflows/reusable-strategy-matrix.yml - - .github/workflows/reusable-upload-conan-deps-os.yml - .github/actions/build-deps/action.yml - ".github/scripts/strategy-matrix/**" @@ -47,16 +45,46 @@ concurrency: cancel-in-progress: true jobs: - run-upload-conan-deps: - strategy: - fail-fast: true - matrix: - os: ["linux", "macos", "windows"] - uses: ./.github/workflows/reusable-upload-conan-deps-os.yml + generate-matrix: + uses: ./.github/workflows/reusable-strategy-matrix.yml with: - force_source_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }} - force_upload: ${{ github.event.inputs.force_upload == 'true' }} - os: ${{ matrix.os }} - secrets: - CONAN_USERNAME: ${{ secrets.CONAN_REMOTE_USERNAME }} - CONAN_PASSWORD: ${{ secrets.CONAN_REMOTE_PASSWORD }} + strategy_matrix: ${{ github.event_name == 'pull_request' && 'minimal' || 'all' }} + + run-upload-conan-deps: + needs: + - generate-matrix + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} + max-parallel: 10 + runs-on: ${{ matrix.architecture.runner }} + container: ${{ contains(matrix.architecture.platform, 'linux') && format('ghcr.io/xrplf/ci/{0}-{1}:{2}-{3}', matrix.os.distro_name, matrix.os.distro_version, matrix.os.compiler_name, matrix.os.compiler_version) || null }} + + steps: + - name: Cleanup workspace + if: ${{ runner.os == 'macOS' }} + uses: XRPLF/actions/.github/actions/cleanup-workspace@3f044c7478548e3c32ff68980eeb36ece02b364e + + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 + - name: Prepare runner + uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5 + with: + disable_ccache: false + + - name: Setup Conan + uses: ./.github/actions/setup-conan + + - name: Build dependencies + uses: ./.github/actions/build-deps + with: + build_dir: .build + build_type: ${{ matrix.build_type }} + force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }} + + - name: Login to Conan + if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' + run: conan remote login -p ${{ secrets.CONAN_PASSWORD }} ${{ inputs.conan_remote_name }} ${{ secrets.CONAN_USERNAME }} + + - name: Upload Conan packages + if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' + run: conan upload "*" -r=${{ inputs.conan_remote_name }} --confirm ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }} From e5f7a8442d763870db531070c587fc0aec70a5d8 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 9 Sep 2025 21:21:12 +0100 Subject: [PATCH 49/66] ci: Change `upload-conan-deps` workflow is run (#5782) - Don't run upload-conan-deps in PRs, unless the PR changes the workflow file. - Change cron schedule for uploading Conan dependencies to run after work hours for most dev. --- .github/workflows/upload-conan-deps.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index f63482761d..07e9a60dbd 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -2,7 +2,7 @@ name: Upload Conan Dependencies on: schedule: - - cron: "0 9 * * 1-5" + - cron: "0 3 * * 2-6" workflow_dispatch: inputs: force_source_build: @@ -18,15 +18,8 @@ on: pull_request: branches: [develop] paths: + # This allows testing changes to the upload workflow in a PR - .github/workflows/upload-conan-deps.yml - - - .github/workflows/reusable-strategy-matrix.yml - - - .github/actions/build-deps/action.yml - - ".github/scripts/strategy-matrix/**" - - - conanfile.py - - conan.lock push: branches: [develop] paths: From f6426ca1832d61e3b752124673eddf61553b7b56 Mon Sep 17 00:00:00 2001 From: Bronek Kozicki Date: Tue, 9 Sep 2025 22:23:07 +0100 Subject: [PATCH 50/66] Switch CI pipeline bookworm:gcc-13 from arm64 to amd64 (#5779) --- .github/scripts/strategy-matrix/generate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/strategy-matrix/generate.py b/.github/scripts/strategy-matrix/generate.py index b6f6601291..ac39803fff 100755 --- a/.github/scripts/strategy-matrix/generate.py +++ b/.github/scripts/strategy-matrix/generate.py @@ -45,7 +45,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list: # Only generate a subset of configurations in PRs. if not all: # Debian: - # - Bookworm using GCC 13: Release and Unity on linux/arm64, set + # - Bookworm using GCC 13: Release and Unity on linux/amd64, set # the reference fee to 500. # - Bookworm using GCC 15: Debug and no Unity on linux/amd64, enable # code coverage (which will be done below). @@ -57,7 +57,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list: if os['distro_name'] == 'debian': skip = True if os['distro_version'] == 'bookworm': - if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-13' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/arm64': + if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-13' and build_type == 'Release' and '-Dunity=ON' in cmake_args and architecture['platform'] == 'linux/amd64': cmake_args = f'-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}' skip = False if f'{os['compiler_name']}-{os['compiler_version']}' == 'gcc-15' and build_type == 'Debug' and '-Dunity=OFF' in cmake_args and architecture['platform'] == 'linux/amd64': From cdbe70b2a7d54e85cf2e5eddd55e93bc206a5ef6 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 10 Sep 2025 08:35:58 +0100 Subject: [PATCH 51/66] ci: Use default conan install format (#5784) --- .github/actions/build-deps/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build-deps/action.yml b/.github/actions/build-deps/action.yml index c3b405e70f..351d8a6361 100644 --- a/.github/actions/build-deps/action.yml +++ b/.github/actions/build-deps/action.yml @@ -30,4 +30,4 @@ runs: --options:host '&:tests=True' \ --options:host '&:xrpld=True' \ --settings:all build_type=${{ inputs.build_type }} \ - --format=json .. + .. From 3d92375d127acb61ca7b931d561e5cca7762cabf Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Wed, 10 Sep 2025 09:20:45 +0100 Subject: [PATCH 52/66] ci: Add missing dependencies to workflows (#5783) --- .github/workflows/on-pr.yml | 2 ++ .github/workflows/on-trigger.yml | 2 ++ .github/workflows/upload-conan-deps.yml | 1 + 3 files changed, 5 insertions(+) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index f194bd1e37..c480cc5476 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -60,8 +60,10 @@ jobs: # Keep the paths below in sync with those in `on-trigger.yml`. .github/actions/build-deps/** .github/actions/build-test/** + .github/actions/setup-conan/** .github/scripts/strategy-matrix/** .github/workflows/build-test.yml + .github/workflows/reusable-strategy-matrix.yml .codecov.yml cmake/** conan/** diff --git a/.github/workflows/on-trigger.yml b/.github/workflows/on-trigger.yml index 14884391ef..7c17621d67 100644 --- a/.github/workflows/on-trigger.yml +++ b/.github/workflows/on-trigger.yml @@ -21,8 +21,10 @@ on: # Keep the paths below in sync with those in `on-pr.yml`. - ".github/actions/build-deps/**" - ".github/actions/build-test/**" + - ".github/actions/setup-conan/**" - ".github/scripts/strategy-matrix/**" - ".github/workflows/build-test.yml" + - ".github/workflows/reusable-strategy-matrix.yml" - ".codecov.yml" - "cmake/**" - "conan/**" diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index 07e9a60dbd..5af72a9e41 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -28,6 +28,7 @@ on: - .github/workflows/reusable-strategy-matrix.yml - .github/actions/build-deps/action.yml + - .github/actions/setup-conan/action.yml - ".github/scripts/strategy-matrix/**" - conanfile.py From 61d628d654fd03135a47bf0dea28f56b03a8bd22 Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Wed, 10 Sep 2025 13:47:33 -0400 Subject: [PATCH 53/66] fix: Add restrictions to Permission Delegation: fixDelegateV1_1 (#5650) - Amendment: fixDelegateV1_1 - In DelegateSet, disallow invalid PermissionValues like 0, and transaction values when the transaction's amendment is not enabled. Acts as if the transaction doesn't exist, which is the same thing older versions without the amendment will do. - Payment burn/mint should disallow DEX currency exchange. - Support MPT for Payment burn/mint. --- include/xrpl/protocol/Permissions.h | 7 +- include/xrpl/protocol/TxFormats.h | 2 +- include/xrpl/protocol/detail/features.macro | 1 + .../xrpl/protocol/detail/transactions.macro | 332 ++++++++++++--- include/xrpl/protocol/jss.h | 2 +- src/libxrpl/protocol/Permissions.cpp | 42 +- src/libxrpl/protocol/TxFormats.cpp | 2 +- src/test/app/AMMClawback_test.cpp | 3 +- src/test/app/Delegate_test.cpp | 390 +++++++++++++++++- src/xrpld/app/tx/detail/DelegateSet.cpp | 22 +- src/xrpld/app/tx/detail/Payment.cpp | 27 +- src/xrpld/app/tx/detail/applySteps.cpp | 4 +- 12 files changed, 732 insertions(+), 102 deletions(-) diff --git a/include/xrpl/protocol/Permissions.h b/include/xrpl/protocol/Permissions.h index 67f3eea8d7..cf49ff7382 100644 --- a/include/xrpl/protocol/Permissions.h +++ b/include/xrpl/protocol/Permissions.h @@ -20,6 +20,8 @@ #ifndef RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED #define RIPPLE_PROTOCOL_PERMISSION_H_INCLUDED +#include +#include #include #include @@ -53,6 +55,8 @@ class Permission private: Permission(); + std::unordered_map txFeatureMap_; + std::unordered_map delegatableTx_; std::unordered_map @@ -80,7 +84,8 @@ public: getGranularTxType(GranularPermissionType const& gpType) const; bool - isDelegatable(std::uint32_t const& permissionValue) const; + isDelegatable(std::uint32_t const& permissionValue, Rules const& rules) + const; // for tx level permission, permission value is equal to tx type plus one uint32_t diff --git a/include/xrpl/protocol/TxFormats.h b/include/xrpl/protocol/TxFormats.h index 70b721a3d7..d17eea7644 100644 --- a/include/xrpl/protocol/TxFormats.h +++ b/include/xrpl/protocol/TxFormats.h @@ -59,7 +59,7 @@ enum TxType : std::uint16_t #pragma push_macro("TRANSACTION") #undef TRANSACTION -#define TRANSACTION(tag, value, name, delegatable, fields) tag = value, +#define TRANSACTION(tag, value, ...) tag = value, #include diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 264fad7fc2..9aacbbe3d9 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -32,6 +32,7 @@ // If you add an amendment here, then do not forget to increment `numFeatures` // in include/xrpl/protocol/Feature.h. +XRPL_FIX (DelegateV1_1, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (PriceOracleOrder, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (AMMClawbackRounding, Supported::yes, VoteBehavior::DefaultNo) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index 1131e24f61..bfbc18aa1b 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -22,14 +22,17 @@ #endif /** - * TRANSACTION(tag, value, name, delegatable, fields) + * TRANSACTION(tag, value, name, delegatable, amendments, fields) * * You must define a transactor class in the `ripple` namespace named `name`, * and include its header in `src/xrpld/app/tx/detail/applySteps.cpp`. */ /** This transaction type executes a payment. */ -TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegatable, ({ +TRANSACTION(ttPAYMENT, 0, Payment, + Delegation::delegatable, + uint256{}, + ({ {sfDestination, soeREQUIRED}, {sfAmount, soeREQUIRED, soeMPTSupported}, {sfSendMax, soeOPTIONAL, soeMPTSupported}, @@ -42,7 +45,10 @@ TRANSACTION(ttPAYMENT, 0, Payment, Delegation::delegatable, ({ })) /** This transaction type creates an escrow object. */ -TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegatable, ({ +TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, + Delegation::delegatable, + uint256{}, + ({ {sfDestination, soeREQUIRED}, {sfAmount, soeREQUIRED, soeMPTSupported}, {sfCondition, soeOPTIONAL}, @@ -52,7 +58,10 @@ TRANSACTION(ttESCROW_CREATE, 1, EscrowCreate, Delegation::delegatable, ({ })) /** This transaction type completes an existing escrow. */ -TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegatable, ({ +TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, + Delegation::delegatable, + uint256{}, + ({ {sfOwner, soeREQUIRED}, {sfOfferSequence, soeREQUIRED}, {sfFulfillment, soeOPTIONAL}, @@ -62,7 +71,10 @@ TRANSACTION(ttESCROW_FINISH, 2, EscrowFinish, Delegation::delegatable, ({ /** This transaction type adjusts various account settings. */ -TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegatable, ({ +TRANSACTION(ttACCOUNT_SET, 3, AccountSet, + Delegation::notDelegatable, + uint256{}, + ({ {sfEmailHash, soeOPTIONAL}, {sfWalletLocator, soeOPTIONAL}, {sfWalletSize, soeOPTIONAL}, @@ -76,20 +88,29 @@ TRANSACTION(ttACCOUNT_SET, 3, AccountSet, Delegation::notDelegatable, ({ })) /** This transaction type cancels an existing escrow. */ -TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, Delegation::delegatable, ({ +TRANSACTION(ttESCROW_CANCEL, 4, EscrowCancel, + Delegation::delegatable, + uint256{}, + ({ {sfOwner, soeREQUIRED}, {sfOfferSequence, soeREQUIRED}, })) /** This transaction type sets or clears an account's "regular key". */ -TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, Delegation::notDelegatable, ({ +TRANSACTION(ttREGULAR_KEY_SET, 5, SetRegularKey, + Delegation::notDelegatable, + uint256{}, + ({ {sfRegularKey, soeOPTIONAL}, })) // 6 deprecated /** This transaction type creates an offer to trade one asset for another. */ -TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegatable, ({ +TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, + Delegation::delegatable, + uint256{}, + ({ {sfTakerPays, soeREQUIRED}, {sfTakerGets, soeREQUIRED}, {sfExpiration, soeOPTIONAL}, @@ -98,14 +119,20 @@ TRANSACTION(ttOFFER_CREATE, 7, OfferCreate, Delegation::delegatable, ({ })) /** This transaction type cancels existing offers to trade one asset for another. */ -TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, Delegation::delegatable, ({ +TRANSACTION(ttOFFER_CANCEL, 8, OfferCancel, + Delegation::delegatable, + uint256{}, + ({ {sfOfferSequence, soeREQUIRED}, })) // 9 deprecated /** This transaction type creates a new set of tickets. */ -TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegatable, ({ +TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, + Delegation::delegatable, + featureTicketBatch, + ({ {sfTicketCount, soeREQUIRED}, })) @@ -114,13 +141,19 @@ TRANSACTION(ttTICKET_CREATE, 10, TicketCreate, Delegation::delegatable, ({ /** This transaction type modifies the signer list associated with an account. */ // The SignerEntries are optional because a SignerList is deleted by // setting the SignerQuorum to zero and omitting SignerEntries. -TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, Delegation::notDelegatable, ({ +TRANSACTION(ttSIGNER_LIST_SET, 12, SignerListSet, + Delegation::notDelegatable, + uint256{}, + ({ {sfSignerQuorum, soeREQUIRED}, {sfSignerEntries, soeOPTIONAL}, })) /** This transaction type creates a new unidirectional XRP payment channel. */ -TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegatable, ({ +TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, + Delegation::delegatable, + uint256{}, + ({ {sfDestination, soeREQUIRED}, {sfAmount, soeREQUIRED}, {sfSettleDelay, soeREQUIRED}, @@ -130,14 +163,20 @@ TRANSACTION(ttPAYCHAN_CREATE, 13, PaymentChannelCreate, Delegation::delegatable, })) /** This transaction type funds an existing unidirectional XRP payment channel. */ -TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, Delegation::delegatable, ({ +TRANSACTION(ttPAYCHAN_FUND, 14, PaymentChannelFund, + Delegation::delegatable, + uint256{}, + ({ {sfChannel, soeREQUIRED}, {sfAmount, soeREQUIRED}, {sfExpiration, soeOPTIONAL}, })) /** This transaction type submits a claim against an existing unidirectional payment channel. */ -TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegatable, ({ +TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, + Delegation::delegatable, + uint256{}, + ({ {sfChannel, soeREQUIRED}, {sfAmount, soeOPTIONAL}, {sfBalance, soeOPTIONAL}, @@ -147,7 +186,10 @@ TRANSACTION(ttPAYCHAN_CLAIM, 15, PaymentChannelClaim, Delegation::delegatable, ( })) /** This transaction type creates a new check. */ -TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegatable, ({ +TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, + Delegation::delegatable, + featureChecks, + ({ {sfDestination, soeREQUIRED}, {sfSendMax, soeREQUIRED}, {sfExpiration, soeOPTIONAL}, @@ -156,19 +198,28 @@ TRANSACTION(ttCHECK_CREATE, 16, CheckCreate, Delegation::delegatable, ({ })) /** This transaction type cashes an existing check. */ -TRANSACTION(ttCHECK_CASH, 17, CheckCash, Delegation::delegatable, ({ +TRANSACTION(ttCHECK_CASH, 17, CheckCash, + Delegation::delegatable, + featureChecks, + ({ {sfCheckID, soeREQUIRED}, {sfAmount, soeOPTIONAL}, {sfDeliverMin, soeOPTIONAL}, })) /** This transaction type cancels an existing check. */ -TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, Delegation::delegatable, ({ +TRANSACTION(ttCHECK_CANCEL, 18, CheckCancel, + Delegation::delegatable, + featureChecks, + ({ {sfCheckID, soeREQUIRED}, })) /** This transaction type grants or revokes authorization to transfer funds. */ -TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, Delegation::delegatable, ({ +TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, + Delegation::delegatable, + featureDepositPreauth, + ({ {sfAuthorize, soeOPTIONAL}, {sfUnauthorize, soeOPTIONAL}, {sfAuthorizeCredentials, soeOPTIONAL}, @@ -176,14 +227,20 @@ TRANSACTION(ttDEPOSIT_PREAUTH, 19, DepositPreauth, Delegation::delegatable, ({ })) /** This transaction type modifies a trustline between two accounts. */ -TRANSACTION(ttTRUST_SET, 20, TrustSet, Delegation::delegatable, ({ +TRANSACTION(ttTRUST_SET, 20, TrustSet, + Delegation::delegatable, + uint256{}, + ({ {sfLimitAmount, soeOPTIONAL}, {sfQualityIn, soeOPTIONAL}, {sfQualityOut, soeOPTIONAL}, })) /** This transaction type deletes an existing account. */ -TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegatable, ({ +TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, + Delegation::notDelegatable, + uint256{}, + ({ {sfDestination, soeREQUIRED}, {sfDestinationTag, soeOPTIONAL}, {sfCredentialIDs, soeOPTIONAL}, @@ -192,7 +249,10 @@ TRANSACTION(ttACCOUNT_DELETE, 21, AccountDelete, Delegation::notDelegatable, ({ // 22 reserved /** This transaction mints a new NFT. */ -TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, Delegation::delegatable, ({ +TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, + Delegation::delegatable, + featureNonFungibleTokensV1, + ({ {sfNFTokenTaxon, soeREQUIRED}, {sfTransferFee, soeOPTIONAL}, {sfIssuer, soeOPTIONAL}, @@ -203,13 +263,19 @@ TRANSACTION(ttNFTOKEN_MINT, 25, NFTokenMint, Delegation::delegatable, ({ })) /** This transaction burns (i.e. destroys) an existing NFT. */ -TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, Delegation::delegatable, ({ +TRANSACTION(ttNFTOKEN_BURN, 26, NFTokenBurn, + Delegation::delegatable, + featureNonFungibleTokensV1, + ({ {sfNFTokenID, soeREQUIRED}, {sfOwner, soeOPTIONAL}, })) /** This transaction creates a new offer to buy or sell an NFT. */ -TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, Delegation::delegatable, ({ +TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, + Delegation::delegatable, + featureNonFungibleTokensV1, + ({ {sfNFTokenID, soeREQUIRED}, {sfAmount, soeREQUIRED}, {sfDestination, soeOPTIONAL}, @@ -218,25 +284,37 @@ TRANSACTION(ttNFTOKEN_CREATE_OFFER, 27, NFTokenCreateOffer, Delegation::delegata })) /** This transaction cancels an existing offer to buy or sell an existing NFT. */ -TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, Delegation::delegatable, ({ +TRANSACTION(ttNFTOKEN_CANCEL_OFFER, 28, NFTokenCancelOffer, + Delegation::delegatable, + featureNonFungibleTokensV1, + ({ {sfNFTokenOffers, soeREQUIRED}, })) /** This transaction accepts an existing offer to buy or sell an existing NFT. */ -TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, Delegation::delegatable, ({ +TRANSACTION(ttNFTOKEN_ACCEPT_OFFER, 29, NFTokenAcceptOffer, + Delegation::delegatable, + featureNonFungibleTokensV1, + ({ {sfNFTokenBuyOffer, soeOPTIONAL}, {sfNFTokenSellOffer, soeOPTIONAL}, {sfNFTokenBrokerFee, soeOPTIONAL}, })) /** This transaction claws back issued tokens. */ -TRANSACTION(ttCLAWBACK, 30, Clawback, Delegation::delegatable, ({ +TRANSACTION(ttCLAWBACK, 30, Clawback, + Delegation::delegatable, + featureClawback, + ({ {sfAmount, soeREQUIRED, soeMPTSupported}, {sfHolder, soeOPTIONAL}, })) /** This transaction claws back tokens from an AMM pool. */ -TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, Delegation::delegatable, ({ +TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, + Delegation::delegatable, + featureAMMClawback, + ({ {sfHolder, soeREQUIRED}, {sfAsset, soeREQUIRED}, {sfAsset2, soeREQUIRED}, @@ -244,14 +322,20 @@ TRANSACTION(ttAMM_CLAWBACK, 31, AMMClawback, Delegation::delegatable, ({ })) /** This transaction type creates an AMM instance */ -TRANSACTION(ttAMM_CREATE, 35, AMMCreate, Delegation::delegatable, ({ +TRANSACTION(ttAMM_CREATE, 35, AMMCreate, + Delegation::delegatable, + featureAMM, + ({ {sfAmount, soeREQUIRED}, {sfAmount2, soeREQUIRED}, {sfTradingFee, soeREQUIRED}, })) /** This transaction type deposits into an AMM instance */ -TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, Delegation::delegatable, ({ +TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, + Delegation::delegatable, + featureAMM, + ({ {sfAsset, soeREQUIRED}, {sfAsset2, soeREQUIRED}, {sfAmount, soeOPTIONAL}, @@ -262,7 +346,10 @@ TRANSACTION(ttAMM_DEPOSIT, 36, AMMDeposit, Delegation::delegatable, ({ })) /** This transaction type withdraws from an AMM instance */ -TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, Delegation::delegatable, ({ +TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, + Delegation::delegatable, + featureAMM, + ({ {sfAsset, soeREQUIRED}, {sfAsset2, soeREQUIRED}, {sfAmount, soeOPTIONAL}, @@ -272,14 +359,20 @@ TRANSACTION(ttAMM_WITHDRAW, 37, AMMWithdraw, Delegation::delegatable, ({ })) /** This transaction type votes for the trading fee */ -TRANSACTION(ttAMM_VOTE, 38, AMMVote, Delegation::delegatable, ({ +TRANSACTION(ttAMM_VOTE, 38, AMMVote, + Delegation::delegatable, + featureAMM, + ({ {sfAsset, soeREQUIRED}, {sfAsset2, soeREQUIRED}, {sfTradingFee, soeREQUIRED}, })) /** This transaction type bids for the auction slot */ -TRANSACTION(ttAMM_BID, 39, AMMBid, Delegation::delegatable, ({ +TRANSACTION(ttAMM_BID, 39, AMMBid, + Delegation::delegatable, + featureAMM, + ({ {sfAsset, soeREQUIRED}, {sfAsset2, soeREQUIRED}, {sfBidMin, soeOPTIONAL}, @@ -288,20 +381,29 @@ TRANSACTION(ttAMM_BID, 39, AMMBid, Delegation::delegatable, ({ })) /** This transaction type deletes AMM in the empty state */ -TRANSACTION(ttAMM_DELETE, 40, AMMDelete, Delegation::delegatable, ({ +TRANSACTION(ttAMM_DELETE, 40, AMMDelete, + Delegation::delegatable, + featureAMM, + ({ {sfAsset, soeREQUIRED}, {sfAsset2, soeREQUIRED}, })) /** This transactions creates a crosschain sequence number */ -TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID, Delegation::delegatable, ({ +TRANSACTION(ttXCHAIN_CREATE_CLAIM_ID, 41, XChainCreateClaimID, + Delegation::delegatable, + featureXChainBridge, + ({ {sfXChainBridge, soeREQUIRED}, {sfSignatureReward, soeREQUIRED}, {sfOtherChainSource, soeREQUIRED}, })) /** This transactions initiates a crosschain transaction */ -TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, Delegation::delegatable, ({ +TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, + Delegation::delegatable, + featureXChainBridge, + ({ {sfXChainBridge, soeREQUIRED}, {sfXChainClaimID, soeREQUIRED}, {sfAmount, soeREQUIRED}, @@ -309,7 +411,10 @@ TRANSACTION(ttXCHAIN_COMMIT, 42, XChainCommit, Delegation::delegatable, ({ })) /** This transaction completes a crosschain transaction */ -TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, Delegation::delegatable, ({ +TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, + Delegation::delegatable, + featureXChainBridge, + ({ {sfXChainBridge, soeREQUIRED}, {sfXChainClaimID, soeREQUIRED}, {sfDestination, soeREQUIRED}, @@ -318,7 +423,10 @@ TRANSACTION(ttXCHAIN_CLAIM, 43, XChainClaim, Delegation::delegatable, ({ })) /** This transaction initiates a crosschain account create transaction */ -TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, Delegation::delegatable, ({ +TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, + Delegation::delegatable, + featureXChainBridge, + ({ {sfXChainBridge, soeREQUIRED}, {sfDestination, soeREQUIRED}, {sfAmount, soeREQUIRED}, @@ -326,7 +434,10 @@ TRANSACTION(ttXCHAIN_ACCOUNT_CREATE_COMMIT, 44, XChainAccountCreateCommit, Deleg })) /** This transaction adds an attestation to a claim */ -TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, Delegation::delegatable, ({ +TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, + Delegation::delegatable, + featureXChainBridge, + ({ {sfXChainBridge, soeREQUIRED}, {sfAttestationSignerAccount, soeREQUIRED}, @@ -342,7 +453,10 @@ TRANSACTION(ttXCHAIN_ADD_CLAIM_ATTESTATION, 45, XChainAddClaimAttestation, Deleg })) /** This transaction adds an attestation to an account */ -TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateAttestation, Delegation::delegatable, ({ +TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateAttestation, + Delegation::delegatable, + featureXChainBridge, + ({ {sfXChainBridge, soeREQUIRED}, {sfAttestationSignerAccount, soeREQUIRED}, @@ -359,31 +473,46 @@ TRANSACTION(ttXCHAIN_ADD_ACCOUNT_CREATE_ATTESTATION, 46, XChainAddAccountCreateA })) /** This transaction modifies a sidechain */ -TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge, Delegation::delegatable, ({ +TRANSACTION(ttXCHAIN_MODIFY_BRIDGE, 47, XChainModifyBridge, + Delegation::delegatable, + featureXChainBridge, + ({ {sfXChainBridge, soeREQUIRED}, {sfSignatureReward, soeOPTIONAL}, {sfMinAccountCreateAmount, soeOPTIONAL}, })) /** This transactions creates a sidechain */ -TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, Delegation::delegatable, ({ +TRANSACTION(ttXCHAIN_CREATE_BRIDGE, 48, XChainCreateBridge, + Delegation::delegatable, + featureXChainBridge, + ({ {sfXChainBridge, soeREQUIRED}, {sfSignatureReward, soeREQUIRED}, {sfMinAccountCreateAmount, soeOPTIONAL}, })) /** This transaction type creates or updates a DID */ -TRANSACTION(ttDID_SET, 49, DIDSet, Delegation::delegatable, ({ +TRANSACTION(ttDID_SET, 49, DIDSet, + Delegation::delegatable, + featureDID, + ({ {sfDIDDocument, soeOPTIONAL}, {sfURI, soeOPTIONAL}, {sfData, soeOPTIONAL}, })) /** This transaction type deletes a DID */ -TRANSACTION(ttDID_DELETE, 50, DIDDelete, Delegation::delegatable, ({})) +TRANSACTION(ttDID_DELETE, 50, DIDDelete, + Delegation::delegatable, + featureDID, + ({})) /** This transaction type creates an Oracle instance */ -TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegatable, ({ +TRANSACTION(ttORACLE_SET, 51, OracleSet, + Delegation::delegatable, + featurePriceOracle, + ({ {sfOracleDocumentID, soeREQUIRED}, {sfProvider, soeOPTIONAL}, {sfURI, soeOPTIONAL}, @@ -393,18 +522,27 @@ TRANSACTION(ttORACLE_SET, 51, OracleSet, Delegation::delegatable, ({ })) /** This transaction type deletes an Oracle instance */ -TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, Delegation::delegatable, ({ +TRANSACTION(ttORACLE_DELETE, 52, OracleDelete, + Delegation::delegatable, + featurePriceOracle, + ({ {sfOracleDocumentID, soeREQUIRED}, })) /** This transaction type fixes a problem in the ledger state */ -TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, Delegation::delegatable, ({ +TRANSACTION(ttLEDGER_STATE_FIX, 53, LedgerStateFix, + Delegation::delegatable, + fixNFTokenPageLinks, + ({ {sfLedgerFixType, soeREQUIRED}, {sfOwner, soeOPTIONAL}, })) /** This transaction type creates a MPTokensIssuance instance */ -TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, Delegation::delegatable, ({ +TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, + Delegation::delegatable, + featureMPTokensV1, + ({ {sfAssetScale, soeOPTIONAL}, {sfTransferFee, soeOPTIONAL}, {sfMaximumAmount, soeOPTIONAL}, @@ -413,25 +551,37 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, Delegation::de })) /** This transaction type destroys a MPTokensIssuance instance */ -TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, Delegation::delegatable, ({ +TRANSACTION(ttMPTOKEN_ISSUANCE_DESTROY, 55, MPTokenIssuanceDestroy, + Delegation::delegatable, + featureMPTokensV1, + ({ {sfMPTokenIssuanceID, soeREQUIRED}, })) /** This transaction type sets flags on a MPTokensIssuance or MPToken instance */ -TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, Delegation::delegatable, ({ +TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, + Delegation::delegatable, + featureMPTokensV1, + ({ {sfMPTokenIssuanceID, soeREQUIRED}, {sfHolder, soeOPTIONAL}, {sfDomainID, soeOPTIONAL}, })) /** This transaction type authorizes a MPToken instance */ -TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, Delegation::delegatable, ({ +TRANSACTION(ttMPTOKEN_AUTHORIZE, 57, MPTokenAuthorize, + Delegation::delegatable, + featureMPTokensV1, + ({ {sfMPTokenIssuanceID, soeREQUIRED}, {sfHolder, soeOPTIONAL}, })) /** This transaction type create an Credential instance */ -TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegatable, ({ +TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, + Delegation::delegatable, + featureCredentials, + ({ {sfSubject, soeREQUIRED}, {sfCredentialType, soeREQUIRED}, {sfExpiration, soeOPTIONAL}, @@ -439,44 +589,65 @@ TRANSACTION(ttCREDENTIAL_CREATE, 58, CredentialCreate, Delegation::delegatable, })) /** This transaction type accept an Credential object */ -TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, Delegation::delegatable, ({ +TRANSACTION(ttCREDENTIAL_ACCEPT, 59, CredentialAccept, + Delegation::delegatable, + featureCredentials, + ({ {sfIssuer, soeREQUIRED}, {sfCredentialType, soeREQUIRED}, })) /** This transaction type delete an Credential object */ -TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, Delegation::delegatable, ({ +TRANSACTION(ttCREDENTIAL_DELETE, 60, CredentialDelete, + Delegation::delegatable, + featureCredentials, + ({ {sfSubject, soeOPTIONAL}, {sfIssuer, soeOPTIONAL}, {sfCredentialType, soeREQUIRED}, })) /** This transaction type modify a NFToken */ -TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, Delegation::delegatable, ({ +TRANSACTION(ttNFTOKEN_MODIFY, 61, NFTokenModify, + Delegation::delegatable, + featureDynamicNFT, + ({ {sfNFTokenID, soeREQUIRED}, {sfOwner, soeOPTIONAL}, {sfURI, soeOPTIONAL}, })) /** This transaction type creates or modifies a Permissioned Domain */ -TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, Delegation::delegatable, ({ +TRANSACTION(ttPERMISSIONED_DOMAIN_SET, 62, PermissionedDomainSet, + Delegation::delegatable, + featurePermissionedDomains, + ({ {sfDomainID, soeOPTIONAL}, {sfAcceptedCredentials, soeREQUIRED}, })) /** This transaction type deletes a Permissioned Domain */ -TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, Delegation::delegatable, ({ +TRANSACTION(ttPERMISSIONED_DOMAIN_DELETE, 63, PermissionedDomainDelete, + Delegation::delegatable, + featurePermissionedDomains, + ({ {sfDomainID, soeREQUIRED}, })) /** This transaction type delegates authorized account specified permissions */ -TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, Delegation::notDelegatable, ({ +TRANSACTION(ttDELEGATE_SET, 64, DelegateSet, + Delegation::notDelegatable, + featurePermissionDelegation, + ({ {sfAuthorize, soeREQUIRED}, {sfPermissions, soeREQUIRED}, })) /** This transaction creates a single asset vault. */ -TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, Delegation::delegatable, ({ +TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, + Delegation::delegatable, + featureSingleAssetVault, + ({ {sfAsset, soeREQUIRED, soeMPTSupported}, {sfAssetsMaximum, soeOPTIONAL}, {sfMPTokenMetadata, soeOPTIONAL}, @@ -487,7 +658,10 @@ TRANSACTION(ttVAULT_CREATE, 65, VaultCreate, Delegation::delegatable, ({ })) /** This transaction updates a single asset vault. */ -TRANSACTION(ttVAULT_SET, 66, VaultSet, Delegation::delegatable, ({ +TRANSACTION(ttVAULT_SET, 66, VaultSet, + Delegation::delegatable, + featureSingleAssetVault, + ({ {sfVaultID, soeREQUIRED}, {sfAssetsMaximum, soeOPTIONAL}, {sfDomainID, soeOPTIONAL}, @@ -495,18 +669,27 @@ TRANSACTION(ttVAULT_SET, 66, VaultSet, Delegation::delegatable, ({ })) /** This transaction deletes a single asset vault. */ -TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, Delegation::delegatable, ({ +TRANSACTION(ttVAULT_DELETE, 67, VaultDelete, + Delegation::delegatable, + featureSingleAssetVault, + ({ {sfVaultID, soeREQUIRED}, })) /** This transaction trades assets for shares with a vault. */ -TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, Delegation::delegatable, ({ +TRANSACTION(ttVAULT_DEPOSIT, 68, VaultDeposit, + Delegation::delegatable, + featureSingleAssetVault, + ({ {sfVaultID, soeREQUIRED}, {sfAmount, soeREQUIRED, soeMPTSupported}, })) /** This transaction trades shares for assets with a vault. */ -TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, Delegation::delegatable, ({ +TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, + Delegation::delegatable, + featureSingleAssetVault, + ({ {sfVaultID, soeREQUIRED}, {sfAmount, soeREQUIRED, soeMPTSupported}, {sfDestination, soeOPTIONAL}, @@ -514,14 +697,20 @@ TRANSACTION(ttVAULT_WITHDRAW, 69, VaultWithdraw, Delegation::delegatable, ({ })) /** This transaction claws back tokens from a vault. */ -TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, Delegation::delegatable, ({ +TRANSACTION(ttVAULT_CLAWBACK, 70, VaultClawback, + Delegation::delegatable, + featureSingleAssetVault, + ({ {sfVaultID, soeREQUIRED}, {sfHolder, soeREQUIRED}, {sfAmount, soeOPTIONAL, soeMPTSupported}, })) /** This transaction type batches together transactions. */ -TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, ({ +TRANSACTION(ttBATCH, 71, Batch, + Delegation::notDelegatable, + featureBatch, + ({ {sfRawTransactions, soeREQUIRED}, {sfBatchSigners, soeOPTIONAL}, })) @@ -530,7 +719,10 @@ TRANSACTION(ttBATCH, 71, Batch, Delegation::notDelegatable, ({ For details, see: https://xrpl.org/amendments.html */ -TRANSACTION(ttAMENDMENT, 100, EnableAmendment, Delegation::notDelegatable, ({ +TRANSACTION(ttAMENDMENT, 100, EnableAmendment, + Delegation::notDelegatable, + uint256{}, + ({ {sfLedgerSequence, soeREQUIRED}, {sfAmendment, soeREQUIRED}, })) @@ -538,7 +730,10 @@ TRANSACTION(ttAMENDMENT, 100, EnableAmendment, Delegation::notDelegatable, ({ /** This system-generated transaction type is used to update the network's fee settings. For details, see: https://xrpl.org/fee-voting.html */ -TRANSACTION(ttFEE, 101, SetFee, Delegation::notDelegatable, ({ +TRANSACTION(ttFEE, 101, SetFee, + Delegation::notDelegatable, + uint256{}, + ({ {sfLedgerSequence, soeOPTIONAL}, // Old version uses raw numbers {sfBaseFee, soeOPTIONAL}, @@ -555,7 +750,10 @@ TRANSACTION(ttFEE, 101, SetFee, Delegation::notDelegatable, ({ For details, see: https://xrpl.org/negative-unl.html */ -TRANSACTION(ttUNL_MODIFY, 102, UNLModify, Delegation::notDelegatable, ({ +TRANSACTION(ttUNL_MODIFY, 102, UNLModify, + Delegation::notDelegatable, + uint256{}, + ({ {sfUNLModifyDisabling, soeREQUIRED}, {sfLedgerSequence, soeREQUIRED}, {sfUNLModifyValidator, soeREQUIRED}, diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 68d2497aca..d847cf6012 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -710,7 +710,7 @@ JSS(write_load); // out: GetCounts #pragma push_macro("TRANSACTION") #undef TRANSACTION -#define TRANSACTION(tag, value, name, delegatable, fields) JSS(name); +#define TRANSACTION(tag, value, name, ...) JSS(name); #include diff --git a/src/libxrpl/protocol/Permissions.cpp b/src/libxrpl/protocol/Permissions.cpp index ca8cb26f36..781799f128 100644 --- a/src/libxrpl/protocol/Permissions.cpp +++ b/src/libxrpl/protocol/Permissions.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include #include @@ -25,11 +26,24 @@ namespace ripple { Permission::Permission() { + txFeatureMap_ = { +#pragma push_macro("TRANSACTION") +#undef TRANSACTION + +#define TRANSACTION(tag, value, name, delegatable, amendment, ...) \ + {value, amendment}, + +#include + +#undef TRANSACTION +#pragma pop_macro("TRANSACTION") + }; + delegatableTx_ = { #pragma push_macro("TRANSACTION") #undef TRANSACTION -#define TRANSACTION(tag, value, name, delegatable, fields) {value, delegatable}, +#define TRANSACTION(tag, value, name, delegatable, ...) {value, delegatable}, #include @@ -118,7 +132,9 @@ Permission::getGranularTxType(GranularPermissionType const& gpType) const } bool -Permission::isDelegatable(std::uint32_t const& permissionValue) const +Permission::isDelegatable( + std::uint32_t const& permissionValue, + Rules const& rules) const { auto const granularPermission = getGranularName(static_cast(permissionValue)); @@ -126,7 +142,27 @@ Permission::isDelegatable(std::uint32_t const& permissionValue) const // granular permissions are always allowed to be delegated return true; - auto const it = delegatableTx_.find(permissionValue - 1); + auto const txType = permissionToTxType(permissionValue); + auto const it = delegatableTx_.find(txType); + + if (rules.enabled(fixDelegateV1_1)) + { + if (it == delegatableTx_.end()) + return false; + + auto const txFeaturesIt = txFeatureMap_.find(txType); + XRPL_ASSERT( + txFeaturesIt != txFeatureMap_.end(), + "ripple::Permissions::isDelegatable : tx exists in txFeatureMap_"); + + // fixDelegateV1_1: Delegation is only allowed if the required amendment + // for the transaction is enabled. For transactions that do not require + // an amendment, delegation is always allowed. + if (txFeaturesIt->second != uint256{} && + !rules.enabled(txFeaturesIt->second)) + return false; + } + if (it != delegatableTx_.end() && it->second == Delegation::notDelegatable) return false; diff --git a/src/libxrpl/protocol/TxFormats.cpp b/src/libxrpl/protocol/TxFormats.cpp index 5edffeb666..c10c023ee9 100644 --- a/src/libxrpl/protocol/TxFormats.cpp +++ b/src/libxrpl/protocol/TxFormats.cpp @@ -55,7 +55,7 @@ TxFormats::TxFormats() #undef TRANSACTION #define UNWRAP(...) __VA_ARGS__ -#define TRANSACTION(tag, value, name, delegatable, fields) \ +#define TRANSACTION(tag, value, name, delegatable, amendment, fields) \ add(jss::name, tag, UNWRAP fields, commonFields); #include diff --git a/src/test/app/AMMClawback_test.cpp b/src/test/app/AMMClawback_test.cpp index 9564911664..707113fe32 100644 --- a/src/test/app/AMMClawback_test.cpp +++ b/src/test/app/AMMClawback_test.cpp @@ -2442,8 +2442,7 @@ class AMMClawback_test : public beast::unit_test::suite void run() override { - FeatureBitset const all{ - jtx::testable_amendments() | fixAMMClawbackRounding}; + FeatureBitset const all = jtx::testable_amendments(); testInvalidRequest(); testFeatureDisabled(all - featureAMMClawback); diff --git a/src/test/app/Delegate_test.cpp b/src/test/app/Delegate_test.cpp index 44cb6a54b6..ea5e073a55 100644 --- a/src/test/app/Delegate_test.cpp +++ b/src/test/app/Delegate_test.cpp @@ -16,6 +16,7 @@ //============================================================================== #include +#include #include #include @@ -139,12 +140,12 @@ class Delegate_test : public beast::unit_test::suite } void - testInvalidRequest() + testInvalidRequest(FeatureBitset features) { testcase("test invalid DelegateSet"); using namespace jtx; - Env env(*this); + Env env(*this, features); Account gw{"gateway"}; Account alice{"alice"}; Account bob{"bob"}; @@ -216,22 +217,17 @@ class Delegate_test : public beast::unit_test::suite } // non-delegatable transaction + auto const res = features[fixDelegateV1_1] ? ter(temMALFORMED) + : ter(tecNO_PERMISSION); { - env(delegate::set(gw, alice, {"SetRegularKey"}), - ter(tecNO_PERMISSION)); - env(delegate::set(gw, alice, {"AccountSet"}), - ter(tecNO_PERMISSION)); - env(delegate::set(gw, alice, {"SignerListSet"}), - ter(tecNO_PERMISSION)); - env(delegate::set(gw, alice, {"DelegateSet"}), - ter(tecNO_PERMISSION)); - env(delegate::set(gw, alice, {"SetRegularKey"}), - ter(tecNO_PERMISSION)); - env(delegate::set(gw, alice, {"EnableAmendment"}), - ter(tecNO_PERMISSION)); - env(delegate::set(gw, alice, {"UNLModify"}), ter(tecNO_PERMISSION)); - env(delegate::set(gw, alice, {"SetFee"}), ter(tecNO_PERMISSION)); - env(delegate::set(gw, alice, {"Batch"}), ter(tecNO_PERMISSION)); + env(delegate::set(gw, alice, {"SetRegularKey"}), res); + env(delegate::set(gw, alice, {"AccountSet"}), res); + env(delegate::set(gw, alice, {"SignerListSet"}), res); + env(delegate::set(gw, alice, {"DelegateSet"}), res); + env(delegate::set(gw, alice, {"EnableAmendment"}), res); + env(delegate::set(gw, alice, {"UNLModify"}), res); + env(delegate::set(gw, alice, {"SetFee"}), res); + env(delegate::set(gw, alice, {"Batch"}), res); } } @@ -536,7 +532,7 @@ class Delegate_test : public beast::unit_test::suite } void - testPaymentGranular() + testPaymentGranular(FeatureBitset features) { testcase("test payment granular"); using namespace jtx; @@ -706,6 +702,158 @@ class Delegate_test : public beast::unit_test::suite env.require(balance(alice, USD(50))); BEAST_EXPECT(env.balance(bob, USD) == USD(0)); } + + // disallow cross currency payment with only PaymentBurn/PaymentMint + // permission + { + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const gw{"gateway"}; + Account const carol{"carol"}; + auto const USD = gw["USD"]; + + env.fund(XRP(10000), alice, bob, carol, gw); + env.close(); + env.trust(USD(50000), alice); + env.trust(USD(50000), bob); + env.trust(USD(50000), carol); + env(pay(gw, alice, USD(10000))); + env(pay(gw, bob, USD(10000))); + env(pay(gw, carol, USD(10000))); + env.close(); + + auto const result = features[fixDelegateV1_1] + ? static_cast(tecNO_DELEGATE_PERMISSION) + : static_cast(tesSUCCESS); + auto const offerCount = features[fixDelegateV1_1] ? 1 : 0; + + // PaymentMint + { + env(offer(carol, XRP(100), USD(501))); + BEAST_EXPECT(expectOffers(env, carol, 1)); + env(delegate::set(gw, bob, {"PaymentMint"})); + env.close(); + + // post-amendment: fixDelegateV1_1 + // bob can not send cross currency payment on behalf of the gw, + // even with PaymentMint permission and gw being the issuer. + env(pay(gw, alice, USD(5000)), + path(~USD), + sendmax(XRP(1001)), + txflags(tfPartialPayment), + delegate::as(bob), + ter(result)); + BEAST_EXPECT(expectOffers(env, carol, offerCount)); + + // succeed with direct payment + env(pay(gw, alice, USD(100)), delegate::as(bob)); + env.close(); + } + + // PaymentBurn + { + env(offer(bob, XRP(100), USD(501))); + BEAST_EXPECT(expectOffers(env, bob, 1)); + env(delegate::set(alice, bob, {"PaymentBurn"})); + env.close(); + + // post-amendment: fixDelegateV1_1 + // bob can not send cross currency payment on behalf of alice, + // even with PaymentBurn permission and gw being the issuer. + env(pay(alice, gw, USD(5000)), + path(~USD), + sendmax(XRP(1001)), + txflags(tfPartialPayment), + delegate::as(bob), + ter(result)); + BEAST_EXPECT(expectOffers(env, bob, offerCount)); + + // succeed with direct payment + env(pay(alice, gw, USD(100)), delegate::as(bob)); + env.close(); + } + } + + // PaymentMint and PaymentBurn for MPT + { + std::string logs; + Env env(*this, features, std::make_unique(&logs)); + Account const alice{"alice"}; + Account const bob{"bob"}; + Account const gw{"gateway"}; + + MPTTester mpt(env, gw, {.holders = {alice, bob}}); + mpt.create( + {.ownerCount = 1, .holderCount = 0, .flags = tfMPTCanTransfer}); + + mpt.authorize({.account = alice}); + mpt.authorize({.account = bob}); + + auto const MPT = mpt["MPT"]; + env(pay(gw, alice, MPT(500))); + env(pay(gw, bob, MPT(500))); + env.close(); + auto aliceMPT = env.balance(alice, MPT); + auto bobMPT = env.balance(bob, MPT); + + // PaymentMint + { + env(delegate::set(gw, bob, {"PaymentMint"})); + env.close(); + + if (!features[fixDelegateV1_1]) + { + // pre-amendment: PaymentMint is not supported for MPT + env(pay(gw, alice, MPT(50)), + delegate::as(bob), + ter(tefEXCEPTION)); + } + else + { + env(pay(gw, alice, MPT(50)), delegate::as(bob)); + BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT + MPT(50)); + BEAST_EXPECT(env.balance(bob, MPT) == bobMPT); + aliceMPT = env.balance(alice, MPT); + } + } + + // PaymentBurn + { + env(delegate::set(alice, bob, {"PaymentBurn"})); + env.close(); + + if (!features[fixDelegateV1_1]) + { + // pre-amendment: PaymentBurn is not supported for MPT + env(pay(alice, gw, MPT(50)), + delegate::as(bob), + ter(tefEXCEPTION)); + } + else + { + env(pay(alice, gw, MPT(50)), delegate::as(bob)); + BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT - MPT(50)); + BEAST_EXPECT(env.balance(bob, MPT) == bobMPT); + aliceMPT = env.balance(alice, MPT); + } + } + + // Payment transaction for MPT is allowed for both pre and post + // amendment + { + env(delegate::set( + alice, bob, {"PaymentBurn", "PaymentMint", "Payment"})); + env.close(); + env(pay(alice, gw, MPT(50)), delegate::as(bob)); + BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT - MPT(50)); + BEAST_EXPECT(env.balance(bob, MPT) == bobMPT); + aliceMPT = env.balance(alice, MPT); + env(pay(alice, bob, MPT(100)), delegate::as(bob)); + BEAST_EXPECT(env.balance(alice, MPT) == aliceMPT - MPT(100)); + BEAST_EXPECT(env.balance(bob, MPT) == bobMPT + MPT(100)); + } + } } void @@ -1476,18 +1624,216 @@ class Delegate_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(edward) == edwardBalance); } + void + testPermissionValue(FeatureBitset features) + { + testcase("test permission value"); + using namespace jtx; + + Env env(*this, features); + + Account alice{"alice"}; + Account bob{"bob"}; + env.fund(XRP(100000), alice, bob); + env.close(); + + auto buildRequest = [&](auto value) -> Json::Value { + Json::Value jv; + jv[jss::TransactionType] = jss::DelegateSet; + jv[jss::Account] = alice.human(); + jv[sfAuthorize.jsonName] = bob.human(); + + Json::Value permissionsJson(Json::arrayValue); + Json::Value permissionValue; + permissionValue[sfPermissionValue.jsonName] = value; + Json::Value permissionObj; + permissionObj[sfPermission.jsonName] = permissionValue; + permissionsJson.append(permissionObj); + jv[sfPermissions.jsonName] = permissionsJson; + + return jv; + }; + + // invalid permission value. + // neither granular permission nor transaction level permission + for (auto value : {0, 100000, 54321}) + { + auto jv = buildRequest(value); + if (!features[fixDelegateV1_1]) + env(jv); + else + env(jv, ter(temMALFORMED)); + } + } + + void + testTxReqireFeatures(FeatureBitset features) + { + testcase("test delegate disabled tx"); + using namespace jtx; + + // map of tx and required feature. + // non-delegatable tx are not included. + // NFTokenMint, NFTokenBurn, NFTokenCreateOffer, NFTokenCancelOffer, + // NFTokenAcceptOffer are not included, they are tested separately. + std::unordered_map txRequiredFeatures{ + {"TicketCreate", featureTicketBatch}, + {"CheckCreate", featureChecks}, + {"CheckCash", featureChecks}, + {"CheckCancel", featureChecks}, + {"DepositPreauth", featureDepositPreauth}, + {"Clawback", featureClawback}, + {"AMMClawback", featureAMMClawback}, + {"AMMCreate", featureAMM}, + {"AMMDeposit", featureAMM}, + {"AMMWithdraw", featureAMM}, + {"AMMVote", featureAMM}, + {"AMMBid", featureAMM}, + {"AMMDelete", featureAMM}, + {"XChainCreateClaimID", featureXChainBridge}, + {"XChainCommit", featureXChainBridge}, + {"XChainClaim", featureXChainBridge}, + {"XChainAccountCreateCommit", featureXChainBridge}, + {"XChainAddClaimAttestation", featureXChainBridge}, + {"XChainAddAccountCreateAttestation", featureXChainBridge}, + {"XChainModifyBridge", featureXChainBridge}, + {"XChainCreateBridge", featureXChainBridge}, + {"DIDSet", featureDID}, + {"DIDDelete", featureDID}, + {"OracleSet", featurePriceOracle}, + {"OracleDelete", featurePriceOracle}, + {"LedgerStateFix", fixNFTokenPageLinks}, + {"MPTokenIssuanceCreate", featureMPTokensV1}, + {"MPTokenIssuanceDestroy", featureMPTokensV1}, + {"MPTokenIssuanceSet", featureMPTokensV1}, + {"MPTokenAuthorize", featureMPTokensV1}, + {"CredentialCreate", featureCredentials}, + {"CredentialAccept", featureCredentials}, + {"CredentialDelete", featureCredentials}, + {"NFTokenModify", featureDynamicNFT}, + {"PermissionedDomainSet", featurePermissionedDomains}, + {"PermissionedDomainDelete", featurePermissionedDomains}, + {"VaultCreate", featureSingleAssetVault}, + {"VaultSet", featureSingleAssetVault}, + {"VaultDelete", featureSingleAssetVault}, + {"VaultDeposit", featureSingleAssetVault}, + {"VaultWithdraw", featureSingleAssetVault}, + {"VaultClawback", featureSingleAssetVault}}; + + // fixDelegateV1_1 post-amendment: can not delegate tx if any + // required feature disabled. + { + auto txAmendmentDisabled = [&](FeatureBitset features, + std::string const& tx) { + BEAST_EXPECT(txRequiredFeatures.contains(tx)); + + Env env(*this, features - txRequiredFeatures[tx]); + + Account const alice{"alice"}; + Account const bob{"bob"}; + env.fund(XRP(100000), alice, bob); + env.close(); + + if (!features[fixDelegateV1_1]) + env(delegate::set(alice, bob, {tx})); + else + env(delegate::set(alice, bob, {tx}), ter(temMALFORMED)); + }; + + for (auto const& tx : txRequiredFeatures) + txAmendmentDisabled(features, tx.first); + } + + // if all the required features in txRequiredFeatures are enabled, will + // succeed + { + auto txAmendmentEnabled = [&](std::string const& tx) { + Env env(*this, features); + + Account const alice{"alice"}; + Account const bob{"bob"}; + env.fund(XRP(100000), alice, bob); + env.close(); + + env(delegate::set(alice, bob, {tx})); + }; + + for (auto const& tx : txRequiredFeatures) + txAmendmentEnabled(tx.first); + } + + // NFTokenMint, NFTokenBurn, NFTokenCreateOffer, NFTokenCancelOffer, and + // NFTokenAcceptOffer are tested separately. Since + // featureNonFungibleTokensV1_1 includes the functionality of + // featureNonFungibleTokensV1, fixNFTokenNegOffer, and fixNFTokenDirV1, + // both featureNonFungibleTokensV1_1 and featureNonFungibleTokensV1 need + // to be disabled to block these transactions from being delegated. + { + Env env( + *this, + features - featureNonFungibleTokensV1 - + featureNonFungibleTokensV1_1); + + Account const alice{"alice"}; + Account const bob{"bob"}; + env.fund(XRP(100000), alice, bob); + env.close(); + + for (auto const tx : + {"NFTokenMint", + "NFTokenBurn", + "NFTokenCreateOffer", + "NFTokenCancelOffer", + "NFTokenAcceptOffer"}) + { + if (!features[fixDelegateV1_1]) + env(delegate::set(alice, bob, {tx})); + else + env(delegate::set(alice, bob, {tx}), ter(temMALFORMED)); + } + } + + // NFTokenMint, NFTokenBurn, NFTokenCreateOffer, NFTokenCancelOffer, and + // NFTokenAcceptOffer are allowed to be delegated if either + // featureNonFungibleTokensV1 or featureNonFungibleTokensV1_1 is + // enabled. + { + for (auto const feature : + {featureNonFungibleTokensV1, featureNonFungibleTokensV1_1}) + { + Env env(*this, features - feature); + Account const alice{"alice"}; + Account const bob{"bob"}; + env.fund(XRP(100000), alice, bob); + env.close(); + + for (auto const tx : + {"NFTokenMint", + "NFTokenBurn", + "NFTokenCreateOffer", + "NFTokenCancelOffer", + "NFTokenAcceptOffer"}) + env(delegate::set(alice, bob, {tx})); + } + } + } + void run() override { + FeatureBitset const all = jtx::testable_amendments(); + testFeatureDisabled(); testDelegateSet(); - testInvalidRequest(); + testInvalidRequest(all); + testInvalidRequest(all - fixDelegateV1_1); testReserve(); testFee(); testSequence(); testAccountDelete(); testDelegateTransaction(); - testPaymentGranular(); + testPaymentGranular(all); + testPaymentGranular(all - fixDelegateV1_1); testTrustSetGranular(); testAccountSetGranular(); testMPTokenIssuanceSetGranular(); @@ -1495,6 +1841,10 @@ class Delegate_test : public beast::unit_test::suite testSingleSignBadSecret(); testMultiSign(); testMultiSignQuorumNotMet(); + testPermissionValue(all); + testPermissionValue(all - fixDelegateV1_1); + testTxReqireFeatures(all); + testTxReqireFeatures(all - fixDelegateV1_1); } }; BEAST_DEFINE_TESTSUITE(Delegate, app, ripple); diff --git a/src/xrpld/app/tx/detail/DelegateSet.cpp b/src/xrpld/app/tx/detail/DelegateSet.cpp index 708cdf0dc2..ddeb01b399 100644 --- a/src/xrpld/app/tx/detail/DelegateSet.cpp +++ b/src/xrpld/app/tx/detail/DelegateSet.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include namespace ripple { @@ -51,6 +50,11 @@ DelegateSet::preflight(PreflightContext const& ctx) { if (!permissionSet.insert(permission[sfPermissionValue]).second) return temMALFORMED; + + if (ctx.rules.enabled(fixDelegateV1_1) && + !Permission::getInstance().isDelegatable( + permission[sfPermissionValue], ctx.rules)) + return temMALFORMED; } return preflight2(ctx); @@ -68,9 +72,21 @@ DelegateSet::preclaim(PreclaimContext const& ctx) auto const& permissions = ctx.tx.getFieldArray(sfPermissions); for (auto const& permission : permissions) { - auto const permissionValue = permission[sfPermissionValue]; - if (!Permission::getInstance().isDelegatable(permissionValue)) + if (!ctx.view.rules().enabled(fixDelegateV1_1) && + !Permission::getInstance().isDelegatable( + permission[sfPermissionValue], ctx.view.rules())) + { + // Before fixDelegateV1_1: + // - The check was performed during preclaim. + // - Transactions from amendments not yet enabled could still be + // delegated. + // + // After fixDelegateV1_1: + // - The check is performed during preflight. + // - Transactions from amendments not yet enabled can no longer be + // delegated. return tecNO_PERMISSION; + } } return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/Payment.cpp b/src/xrpld/app/tx/detail/Payment.cpp index 386b170ed1..784330b203 100644 --- a/src/xrpld/app/tx/detail/Payment.cpp +++ b/src/xrpld/app/tx/detail/Payment.cpp @@ -265,8 +265,33 @@ Payment::checkPermission(ReadView const& view, STTx const& tx) loadGranularPermission(sle, ttPAYMENT, granularPermissions); auto const& dstAmount = tx.getFieldAmount(sfAmount); - auto const& amountIssue = dstAmount.issue(); + // post-amendment: disallow cross currency payments for PaymentMint and + // PaymentBurn + if (view.rules().enabled(fixDelegateV1_1)) + { + auto const& amountAsset = dstAmount.asset(); + if (tx.isFieldPresent(sfSendMax) && + tx[sfSendMax].asset() != amountAsset) + return tecNO_DELEGATE_PERMISSION; + if (granularPermissions.contains(PaymentMint) && !isXRP(amountAsset) && + amountAsset.getIssuer() == tx[sfAccount]) + return tesSUCCESS; + + if (granularPermissions.contains(PaymentBurn) && !isXRP(amountAsset) && + amountAsset.getIssuer() == tx[sfDestination]) + return tesSUCCESS; + + return tecNO_DELEGATE_PERMISSION; + } + + // Calling dstAmount.issue() in the next line would throw if it holds MPT. + // That exception would be caught in preclaim and returned as tefEXCEPTION. + // This check is just a cleaner, more explicit way to get the same result. + if (dstAmount.holds()) + return tefEXCEPTION; + + auto const& amountIssue = dstAmount.issue(); if (granularPermissions.contains(PaymentMint) && !isXRP(amountIssue) && amountIssue.account == tx[sfAccount]) return tesSUCCESS; diff --git a/src/xrpld/app/tx/detail/applySteps.cpp b/src/xrpld/app/tx/detail/applySteps.cpp index 34259ebef0..03ef7244f8 100644 --- a/src/xrpld/app/tx/detail/applySteps.cpp +++ b/src/xrpld/app/tx/detail/applySteps.cpp @@ -97,8 +97,8 @@ with_txn_type(TxType txnType, F&& f) #pragma push_macro("TRANSACTION") #undef TRANSACTION -#define TRANSACTION(tag, value, name, delegatable, fields) \ - case tag: \ +#define TRANSACTION(tag, value, name, ...) \ + case tag: \ return f.template operator()(); #include From fbd60fc0007d95caa3fbbfcfcbbfc097c25ed502 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 11 Sep 2025 13:58:11 +0100 Subject: [PATCH 54/66] ci: Use pre-commit reusable workflow (#5772) --- .github/workflows/check-format.yml | 44 ------------------------------ .github/workflows/on-pr.yml | 9 ------ .github/workflows/pre-commit.yml | 14 ++++++++++ 3 files changed, 14 insertions(+), 53 deletions(-) delete mode 100644 .github/workflows/check-format.yml create mode 100644 .github/workflows/pre-commit.yml diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml deleted file mode 100644 index c63589017d..0000000000 --- a/.github/workflows/check-format.yml +++ /dev/null @@ -1,44 +0,0 @@ -# This workflow checks if the code is properly formatted. -name: Check format - -# This workflow can only be triggered by other workflows. -on: workflow_call - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-format - cancel-in-progress: true - -defaults: - run: - shell: bash - -jobs: - pre-commit: - runs-on: ubuntu-latest - container: ghcr.io/xrplf/ci/tools-rippled-pre-commit - steps: - - name: Checkout repository - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0 - - name: Prepare runner - uses: XRPLF/actions/.github/actions/prepare-runner@638e0dc11ea230f91bd26622fb542116bb5254d5 - - name: Format code - run: pre-commit run --show-diff-on-failure --color=always --all-files - - name: Check for differences - env: - MESSAGE: | - One or more files did not conform to the formatting. Maybe you did - not run 'pre-commit' before committing, or your version of - 'clang-format' or 'prettier' has an incompatibility with the ones - used here (see the "Check configuration" step above). - - Run 'pre-commit run --all-files' in your repo, and then commit and - push the changes. - run: | - DIFF=$(git status --porcelain) - if [ -n "${DIFF}" ]; then - # Print the files that changed to give the contributor a hint about - # what to expect when running pre-commit on their own machine. - git status - echo "${MESSAGE}" - exit 1 - fi diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index c480cc5476..24f27d5162 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -50,12 +50,9 @@ jobs: files: | # These paths are unique to `on-pr.yml`. .github/scripts/levelization/** - .github/workflows/check-format.yml .github/workflows/check-levelization.yml .github/workflows/notify-clio.yml .github/workflows/on-pr.yml - .clang-format - .pre-commit-config.yaml # Keep the paths below in sync with those in `on-trigger.yml`. .github/actions/build-deps/** @@ -93,11 +90,6 @@ jobs: outputs: go: ${{ steps.go.outputs.go == 'true' }} - check-format: - needs: should-run - if: needs.should-run.outputs.go == 'true' - uses: ./.github/workflows/check-format.yml - check-levelization: needs: should-run if: needs.should-run.outputs.go == 'true' @@ -130,7 +122,6 @@ jobs: if: failure() || cancelled() needs: - build-test - - check-format - check-levelization runs-on: ubuntu-latest steps: diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000000..ead137308d --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,14 @@ +name: Run pre-commit hooks + +on: + pull_request: + push: + branches: [develop, release, master] + workflow_dispatch: + +jobs: + run-hooks: + uses: XRPLF/actions/.github/workflows/pre-commit.yml@af1b0f0d764cda2e5435f5ac97b240d4bd4d95d3 + with: + runs_on: ubuntu-latest + container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit" }' From e6f8bc720fdbbe63ee907f5ac429aa9cfc7e56c9 Mon Sep 17 00:00:00 2001 From: tequ Date: Thu, 11 Sep 2025 23:17:06 +0900 Subject: [PATCH 55/66] Add additional metadata to simulate response (#5754) --- src/test/rpc/Simulate_test.cpp | 104 ++++++++++++++++++++++++++++ src/xrpld/rpc/handlers/Simulate.cpp | 14 ++++ 2 files changed, 118 insertions(+) diff --git a/src/test/rpc/Simulate_test.cpp b/src/test/rpc/Simulate_test.cpp index 5b3c0d2372..0a36a8a841 100644 --- a/src/test/rpc/Simulate_test.cpp +++ b/src/test/rpc/Simulate_test.cpp @@ -131,6 +131,32 @@ class Simulate_test : public beast::unit_test::suite std::to_string(env.current()->txCount())); } + void + testTxJsonMetadataField( + jtx::Env& env, + Json::Value const& tx, + std::function const& validate, + Json::Value const& expectedMetadataKey, + bool testSerialized = true) + { + env.close(); + + Json::Value params; + params[jss::tx_json] = tx; + validate( + env.rpc("json", "simulate", to_string(params)), + tx, + expectedMetadataKey); + validate(env.rpc("simulate", to_string(tx)), tx, expectedMetadataKey); + + BEAST_EXPECTS( + env.current()->txCount() == 0, + std::to_string(env.current()->txCount())); + } + Json::Value getJsonMetadata(Json::Value txResult) const { @@ -1186,6 +1212,83 @@ class Simulate_test : public beast::unit_test::suite } } + void + testSuccessfulTransactionAdditionalMetadata() + { + testcase("Successful transaction with additional metadata"); + + using namespace jtx; + Env env{*this, envconfig([&](std::unique_ptr cfg) { + cfg->NETWORK_ID = 1025; + return cfg; + })}; + + Account const alice("alice"); + + env.fund(XRP(10000), alice); + env.close(); + + { + auto validateOutput = [&](Json::Value const& resp, + Json::Value const& tx, + Json::Value const& expectedMetadataKey) { + auto result = resp[jss::result]; + + BEAST_EXPECT(result[jss::engine_result] == "tesSUCCESS"); + BEAST_EXPECT(result[jss::engine_result_code] == 0); + BEAST_EXPECT( + result[jss::engine_result_message] == + "The simulated transaction would have been applied."); + + if (BEAST_EXPECT( + result.isMember(jss::meta) || + result.isMember(jss::meta_blob))) + { + Json::Value const metadata = getJsonMetadata(result); + + BEAST_EXPECT(metadata[sfTransactionIndex.jsonName] == 0); + BEAST_EXPECT( + metadata[sfTransactionResult.jsonName] == "tesSUCCESS"); + BEAST_EXPECT( + metadata.isMember(expectedMetadataKey.asString())); + } + }; + + { + Json::Value tx; + tx[jss::Account] = env.master.human(); + tx[jss::TransactionType] = jss::Payment; + tx[sfDestination] = alice.human(); + tx[sfAmount] = "100"; + + // test delivered amount + testTxJsonMetadataField( + env, tx, validateOutput, jss::delivered_amount); + } + + { + Json::Value tx; + tx[jss::Account] = env.master.human(); + tx[jss::TransactionType] = jss::NFTokenMint; + tx[sfNFTokenTaxon] = 1; + + // test nft synthetic + testTxJsonMetadataField( + env, tx, validateOutput, jss::nftoken_id); + } + + { + Json::Value tx; + tx[jss::Account] = env.master.human(); + tx[jss::TransactionType] = jss::MPTokenIssuanceCreate; + + // test mpt issuance id + testTxJsonMetadataField( + env, tx, validateOutput, jss::mpt_issuance_id); + } + } + } + public: void run() override @@ -1202,6 +1305,7 @@ public: testMultisignedBadPubKey(); testDeleteExpiredCredentials(); testSuccessfulTransactionNetworkID(); + testSuccessfulTransactionAdditionalMetadata(); } }; diff --git a/src/xrpld/rpc/handlers/Simulate.cpp b/src/xrpld/rpc/handlers/Simulate.cpp index 3c175883c5..092b0b4562 100644 --- a/src/xrpld/rpc/handlers/Simulate.cpp +++ b/src/xrpld/rpc/handlers/Simulate.cpp @@ -24,10 +24,13 @@ #include #include #include +#include #include +#include #include #include +#include #include #include #include @@ -272,6 +275,17 @@ simulateTxn(RPC::JsonContext& context, std::shared_ptr transaction) else { jvResult[jss::meta] = result.metadata->getJson(JsonOptions::none); + RPC::insertDeliveredAmount( + jvResult[jss::meta], + view, + transaction->getSTransaction(), + *result.metadata); + RPC::insertNFTSyntheticInJson( + jvResult, transaction->getSTransaction(), *result.metadata); + RPC::insertMPTokenIssuanceID( + jvResult[jss::meta], + transaction->getSTransaction(), + *result.metadata); } } From 6fe0599cc26db01e4912b133a70b9d252281a568 Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Thu, 11 Sep 2025 10:49:26 -0400 Subject: [PATCH 56/66] refactor: clean up `CTID.h` (#5681) --- src/xrpld/rpc/CTID.h | 85 +++++++++++++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 21 deletions(-) diff --git a/src/xrpld/rpc/CTID.h b/src/xrpld/rpc/CTID.h index be531c536a..0e2b7e0d65 100644 --- a/src/xrpld/rpc/CTID.h +++ b/src/xrpld/rpc/CTID.h @@ -39,53 +39,96 @@ namespace RPC { // The Concise Transaction ID provides a way to identify a transaction // that includes which network the transaction was submitted to. +/** + * @brief Encodes ledger sequence, transaction index, and network ID into a CTID + * string. + * + * @param ledgerSeq Ledger sequence number (max 0x0FFF'FFFF). + * @param txnIndex Transaction index within the ledger (max 0xFFFF). + * @param networkID Network identifier (max 0xFFFF). + * @return Optional CTID string in uppercase hexadecimal, or std::nullopt if + * inputs are out of range. + */ inline std::optional encodeCTID(uint32_t ledgerSeq, uint32_t txnIndex, uint32_t networkID) noexcept { - if (ledgerSeq > 0x0FFF'FFFF || txnIndex > 0xFFFF || networkID > 0xFFFF) - return {}; + constexpr uint32_t maxLedgerSeq = 0x0FFF'FFFF; + constexpr uint32_t maxTxnIndex = 0xFFFF; + constexpr uint32_t maxNetworkID = 0xFFFF; + + if (ledgerSeq > maxLedgerSeq || txnIndex > maxTxnIndex || + networkID > maxNetworkID) + return std::nullopt; uint64_t ctidValue = - ((0xC000'0000ULL + static_cast(ledgerSeq)) << 32) + - (static_cast(txnIndex) << 16) + networkID; + ((0xC000'0000ULL + static_cast(ledgerSeq)) << 32) | + ((static_cast(txnIndex) << 16) | networkID); std::stringstream buffer; buffer << std::hex << std::uppercase << std::setfill('0') << std::setw(16) << ctidValue; - return {buffer.str()}; + return buffer.str(); } +/** + * @brief Decodes a CTID string or integer into its component parts. + * + * @tparam T Type of the CTID input (string, string_view, char*, integral). + * @param ctid CTID value to decode. + * @return Optional tuple of (ledgerSeq, txnIndex, networkID), or std::nullopt + * if invalid. + */ template inline std::optional> decodeCTID(T const ctid) noexcept { - uint64_t ctidValue{0}; + uint64_t ctidValue = 0; + if constexpr ( - std::is_same_v || std::is_same_v || - std::is_same_v || std::is_same_v) + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v) { std::string const ctidString(ctid); - if (ctidString.length() != 16) - return {}; + if (ctidString.size() != 16) + return std::nullopt; - if (!boost::regex_match(ctidString, boost::regex("^[0-9A-Fa-f]+$"))) - return {}; + static boost::regex const hexRegex("^[0-9A-Fa-f]{16}$"); + if (!boost::regex_match(ctidString, hexRegex)) + return std::nullopt; - ctidValue = std::stoull(ctidString, nullptr, 16); + try + { + ctidValue = std::stoull(ctidString, nullptr, 16); + } + // LCOV_EXCL_START + catch (...) + { + // should be impossible to hit given the length/regex check + return std::nullopt; + } + // LCOV_EXCL_STOP } else if constexpr (std::is_integral_v) - ctidValue = ctid; + { + ctidValue = static_cast(ctid); + } else - return {}; + { + return std::nullopt; + } - if ((ctidValue & 0xF000'0000'0000'0000ULL) != 0xC000'0000'0000'0000ULL) - return {}; + // Validate CTID prefix. + constexpr uint64_t ctidPrefixMask = 0xF000'0000'0000'0000ULL; + constexpr uint64_t ctidPrefix = 0xC000'0000'0000'0000ULL; + if ((ctidValue & ctidPrefixMask) != ctidPrefix) + return std::nullopt; - uint32_t ledger_seq = (ctidValue >> 32) & 0xFFFF'FFFUL; - uint16_t txn_index = (ctidValue >> 16) & 0xFFFFU; - uint16_t network_id = ctidValue & 0xFFFFU; - return {{ledger_seq, txn_index, network_id}}; + uint32_t ledgerSeq = static_cast((ctidValue >> 32) & 0x0FFF'FFFF); + uint16_t txnIndex = static_cast((ctidValue >> 16) & 0xFFFF); + uint16_t networkID = static_cast(ctidValue & 0xFFFF); + + return std::make_tuple(ledgerSeq, txnIndex, networkID); } } // namespace RPC From f69ad4eff66d26b8c57e6e1649850652abfbb3b9 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Thu, 11 Sep 2025 16:42:27 +0100 Subject: [PATCH 57/66] docs: Add remote to `conan lock create` command (#5770) * docs: Add remote to `conan lock create` command * Document error resolution for conan package issues * Update BUILD.md * Add more info about lockfiles --- BUILD.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index 6b1594bb5e..fd7a0b855d 100644 --- a/BUILD.md +++ b/BUILD.md @@ -132,7 +132,7 @@ higher index than the default Conan Center remote, so it is consulted first. You can do this by running: ```bash -conan remote add --index 0 xrplf "https://conan.ripplex.io" +conan remote add --index 0 xrplf https://conan.ripplex.io ``` Alternatively, you can pull the patched recipes into the repository and use them @@ -479,12 +479,24 @@ It is implicitly used when running `conan` commands, you don't need to specify i You have to update this file every time you add a new dependency or change a revision or version of an existing dependency. -To do that, run the following command in the repository root: +> [!NOTE] +> Conan uses local cache by default when creating a lockfile. +> +> To ensure, that lockfile creation works the same way on all developer machines, you should clear the local cache before creating a new lockfile. + +To create a new lockfile, run the following commands in the repository root: ```bash +conan remove '*' --confirm +rm conan.lock +# This ensure that xrplf remote is the first to be consulted +conan remote add --force --index 0 xrplf https://conan.ripplex.io conan lock create . -o '&:jemalloc=True' -o '&:rocksdb=True' ``` +> [!NOTE] +> If some dependencies are exclusive for some OS, you may need to run the last command for them adding `--profile:all `. + ## Coverage report The coverage report is intended for developers using compilers GCC @@ -586,6 +598,11 @@ After any updates or changes to dependencies, you may need to do the following: 4. [Regenerate lockfile](#conan-lockfile). 5. Re-run [conan install](#build-and-test). +#### ERROR: Package not resolved + +If you're seeing an error like `ERROR: Package 'snappy/1.1.10' not resolved: Unable to find 'snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1756234314.246' in remotes.`, +please add `xrplf` remote or re-run `conan export` for [patched recipes](#patched-recipes). + ### `protobuf/port_def.inc` file not found If `cmake --build .` results in an error due to a missing a protobuf file, then From 9bd1ce436aaed263190a422ae555ed49eba921e5 Mon Sep 17 00:00:00 2001 From: Jingchen Date: Fri, 12 Sep 2025 16:13:27 +0100 Subject: [PATCH 58/66] Fix code coverage error (#5765) * Fix the issue where COVERAGE_CXX_COMPILER_FLAGS is never used --- cmake/CodeCoverage.cmake | 112 ++++++++++++++++++----------------- cmake/RippledCov.cmake | 2 + cmake/RippledInterface.cmake | 4 -- 3 files changed, 61 insertions(+), 57 deletions(-) diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index ec601de453..c2b66c9cac 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -104,6 +104,11 @@ # 2025-08-28, Bronek Kozicki # - fix "At least one COMMAND must be given" CMake warning from policy CMP0175 # +# 2025-09-03, Jingchen Wu +# - remove the unused function append_coverage_compiler_flags and append_coverage_compiler_flags_to_target +# - add a new function add_code_coverage_to_target +# - remove some unused code +# # USAGE: # # 1. Copy this file into your cmake modules path. @@ -112,10 +117,8 @@ # using a CMake option() to enable it just optionally): # include(CodeCoverage) # -# 3. Append necessary compiler flags for all supported source files: -# append_coverage_compiler_flags() -# Or for specific target: -# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME) +# 3. Append necessary compiler flags and linker flags for all supported source files: +# add_code_coverage_to_target( ) # # 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og # @@ -204,67 +207,69 @@ endforeach() set(COVERAGE_COMPILER_FLAGS "-g --coverage" CACHE INTERNAL "") + +set(COVERAGE_CXX_COMPILER_FLAGS "") +set(COVERAGE_C_COMPILER_FLAGS "") +set(COVERAGE_CXX_LINKER_FLAGS "") +set(COVERAGE_C_LINKER_FLAGS "") + if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") include(CheckCXXCompilerFlag) include(CheckCCompilerFlag) + include(CheckLinkerFlag) + + set(COVERAGE_CXX_COMPILER_FLAGS ${COVERAGE_COMPILER_FLAGS}) + set(COVERAGE_C_COMPILER_FLAGS ${COVERAGE_COMPILER_FLAGS}) + set(COVERAGE_CXX_LINKER_FLAGS ${COVERAGE_COMPILER_FLAGS}) + set(COVERAGE_C_LINKER_FLAGS ${COVERAGE_COMPILER_FLAGS}) check_cxx_compiler_flag(-fprofile-abs-path HAVE_cxx_fprofile_abs_path) if(HAVE_cxx_fprofile_abs_path) - set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") + set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_CXX_COMPILER_FLAGS} -fprofile-abs-path") endif() check_c_compiler_flag(-fprofile-abs-path HAVE_c_fprofile_abs_path) if(HAVE_c_fprofile_abs_path) - set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") + set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_C_COMPILER_FLAGS} -fprofile-abs-path") + endif() + + check_linker_flag(CXX -fprofile-abs-path HAVE_cxx_linker_fprofile_abs_path) + if(HAVE_cxx_linker_fprofile_abs_path) + set(COVERAGE_CXX_LINKER_FLAGS "${COVERAGE_CXX_LINKER_FLAGS} -fprofile-abs-path") + endif() + + check_linker_flag(C -fprofile-abs-path HAVE_c_linker_fprofile_abs_path) + if(HAVE_c_linker_fprofile_abs_path) + set(COVERAGE_C_LINKER_FLAGS "${COVERAGE_C_LINKER_FLAGS} -fprofile-abs-path") endif() check_cxx_compiler_flag(-fprofile-update=atomic HAVE_cxx_fprofile_update) if(HAVE_cxx_fprofile_update) - set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-update=atomic") + set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_CXX_COMPILER_FLAGS} -fprofile-update=atomic") endif() check_c_compiler_flag(-fprofile-update=atomic HAVE_c_fprofile_update) if(HAVE_c_fprofile_update) - set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-update=atomic") + set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_C_COMPILER_FLAGS} -fprofile-update=atomic") endif() -endif() -set(CMAKE_Fortran_FLAGS_COVERAGE - ${COVERAGE_COMPILER_FLAGS} - CACHE STRING "Flags used by the Fortran compiler during coverage builds." - FORCE ) -set(CMAKE_CXX_FLAGS_COVERAGE - ${COVERAGE_COMPILER_FLAGS} - CACHE STRING "Flags used by the C++ compiler during coverage builds." - FORCE ) -set(CMAKE_C_FLAGS_COVERAGE - ${COVERAGE_COMPILER_FLAGS} - CACHE STRING "Flags used by the C compiler during coverage builds." - FORCE ) -set(CMAKE_EXE_LINKER_FLAGS_COVERAGE - "" - CACHE STRING "Flags used for linking binaries during coverage builds." - FORCE ) -set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE - "" - CACHE STRING "Flags used by the shared libraries linker during coverage builds." - FORCE ) -mark_as_advanced( - CMAKE_Fortran_FLAGS_COVERAGE - CMAKE_CXX_FLAGS_COVERAGE - CMAKE_C_FLAGS_COVERAGE - CMAKE_EXE_LINKER_FLAGS_COVERAGE - CMAKE_SHARED_LINKER_FLAGS_COVERAGE ) + check_linker_flag(CXX -fprofile-update=atomic HAVE_cxx_linker_fprofile_update) + if(HAVE_cxx_linker_fprofile_update) + set(COVERAGE_CXX_LINKER_FLAGS "${COVERAGE_CXX_LINKER_FLAGS} -fprofile-update=atomic") + endif() + + check_linker_flag(C -fprofile-update=atomic HAVE_c_linker_fprofile_update) + if(HAVE_c_linker_fprofile_update) + set(COVERAGE_C_LINKER_FLAGS "${COVERAGE_C_LINKER_FLAGS} -fprofile-update=atomic") + endif() + +endif() get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)) message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading") endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG) -if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") - link_libraries(gcov) -endif() - # Defines a target for running and collection code coverage information # Builds dependencies, runs the given executable and outputs reports. # NOTE! The executable should always have a ZERO as exit code otherwise @@ -454,18 +459,19 @@ function(setup_target_for_coverage_gcovr) ) endfunction() # setup_target_for_coverage_gcovr -function(append_coverage_compiler_flags) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) - set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) - message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") -endfunction() # append_coverage_compiler_flags +function(add_code_coverage_to_target name scope) + separate_arguments(COVERAGE_CXX_COMPILER_FLAGS NATIVE_COMMAND "${COVERAGE_CXX_COMPILER_FLAGS}") + separate_arguments(COVERAGE_C_COMPILER_FLAGS NATIVE_COMMAND "${COVERAGE_C_COMPILER_FLAGS}") + separate_arguments(COVERAGE_CXX_LINKER_FLAGS NATIVE_COMMAND "${COVERAGE_CXX_LINKER_FLAGS}") + separate_arguments(COVERAGE_C_LINKER_FLAGS NATIVE_COMMAND "${COVERAGE_C_LINKER_FLAGS}") -# Setup coverage for specific library -function(append_coverage_compiler_flags_to_target name) - separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}") - target_compile_options(${name} PRIVATE ${_flag_list}) - if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") - target_link_libraries(${name} PRIVATE gcov) - endif() -endfunction() + # Add compiler options to the target + target_compile_options(${name} ${scope} + $<$:${COVERAGE_CXX_COMPILER_FLAGS}> + $<$:${COVERAGE_C_COMPILER_FLAGS}>) + + target_link_libraries (${name} ${scope} + $<$:${COVERAGE_CXX_LINKER_FLAGS} gcov> + $<$:${COVERAGE_C_LINKER_FLAGS} gcov> + ) +endfunction() # add_code_coverage_to_target diff --git a/cmake/RippledCov.cmake b/cmake/RippledCov.cmake index 3c48bb1c14..847915a51a 100644 --- a/cmake/RippledCov.cmake +++ b/cmake/RippledCov.cmake @@ -36,3 +36,5 @@ setup_target_for_coverage_gcovr( EXCLUDE "src/test" "include/xrpl/beast/test" "include/xrpl/beast/unit_test" "${CMAKE_BINARY_DIR}/pb-xrpl.libpb" DEPENDENCIES rippled ) + +add_code_coverage_to_target(opts INTERFACE) diff --git a/cmake/RippledInterface.cmake b/cmake/RippledInterface.cmake index 85e2717271..375338c788 100644 --- a/cmake/RippledInterface.cmake +++ b/cmake/RippledInterface.cmake @@ -28,15 +28,11 @@ target_compile_options (opts $<$,$>:-Wsuggest-override> $<$:-Wno-maybe-uninitialized> $<$:-fno-omit-frame-pointer> - $<$,$>:-g --coverage -fprofile-abs-path> - $<$,$>:-g --coverage> $<$:-pg> $<$,$>:-p>) target_link_libraries (opts INTERFACE - $<$,$>:-g --coverage -fprofile-abs-path> - $<$,$>:-g --coverage> $<$:-pg> $<$,$>:-p>) From 406c26cc72a7a8bf4a27baa02d74da81be977dd0 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Fri, 12 Sep 2025 18:09:42 +0100 Subject: [PATCH 59/66] ci: Fix conan secrets in `upload-conan-deps` (#5785) - Accounts for some variables that were changed and missed when the reusable workflow was removed. --- .github/workflows/upload-conan-deps.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index 5af72a9e41..6b94815284 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -34,6 +34,10 @@ on: - conanfile.py - conan.lock +env: + CONAN_REMOTE_NAME: xrplf + CONAN_REMOTE_URL: https://conan.ripplex.io + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -67,6 +71,9 @@ jobs: - name: Setup Conan uses: ./.github/actions/setup-conan + with: + conan_remote_name: ${{ env.CONAN_REMOTE_NAME }} + conan_remote_url: ${{ env.CONAN_REMOTE_URL }} - name: Build dependencies uses: ./.github/actions/build-deps @@ -75,10 +82,10 @@ jobs: build_type: ${{ matrix.build_type }} force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }} - - name: Login to Conan + - name: Log into Conan remote if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' - run: conan remote login -p ${{ secrets.CONAN_PASSWORD }} ${{ inputs.conan_remote_name }} ${{ secrets.CONAN_USERNAME }} + run: conan remote login ${{ env.CONAN_REMOTE_NAME }} "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}" - name: Upload Conan packages if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' - run: conan upload "*" -r=${{ inputs.conan_remote_name }} --confirm ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }} + run: conan upload "*" -r=${{ env.CONAN_REMOTE_NAME }} --confirm ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }} From bd182c0a3e0c153ef15ce6721db57c40e2369ccd Mon Sep 17 00:00:00 2001 From: Jingchen Date: Mon, 15 Sep 2025 14:51:19 +0100 Subject: [PATCH 60/66] fix: Skip processing transaction batch if the batch is empty (#5670) Avoids an assertion failure in NetworkOPsImp::apply in the unlikely event that all incoming transactions are invalid. --- src/test/app/NetworkOPs_test.cpp | 80 ++++++++++++++++++++++++++++ src/xrpld/app/misc/NetworkOPs.cpp | 5 ++ src/xrpld/overlay/detail/PeerImp.cpp | 7 ++- 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 src/test/app/NetworkOPs_test.cpp diff --git a/src/test/app/NetworkOPs_test.cpp b/src/test/app/NetworkOPs_test.cpp new file mode 100644 index 0000000000..edea55105b --- /dev/null +++ b/src/test/app/NetworkOPs_test.cpp @@ -0,0 +1,80 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2020 Dev Null Productions + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +#include + +namespace ripple { +namespace test { + +class NetworkOPs_test : public beast::unit_test::suite +{ +public: + void + run() override + { + testAllBadHeldTransactions(); + } + + void + testAllBadHeldTransactions() + { + // All trasactions are already marked as SF_BAD, and we should be able + // to handle the case properly without an assertion failure + testcase("No valid transactions in batch"); + + std::string logs; + + { + using namespace jtx; + auto const alice = Account{"alice"}; + Env env{ + *this, + envconfig(), + std::make_unique(&logs), + beast::severities::kAll}; + env.memoize(env.master); + env.memoize(alice); + + auto const jtx = env.jt(ticket::create(alice, 1), seq(1), fee(10)); + + auto transacionId = jtx.stx->getTransactionID(); + env.app().getHashRouter().setFlags( + transacionId, HashRouterFlags::HELD); + + env(jtx, json(jss::Sequence, 1), ter(terNO_ACCOUNT)); + + env.app().getHashRouter().setFlags( + transacionId, HashRouterFlags::BAD); + + env.close(); + } + + BEAST_EXPECT( + logs.find("No transaction to process!") != std::string::npos); + } +}; + +BEAST_DEFINE_TESTSUITE(NetworkOPs, app, ripple); + +} // namespace test +} // namespace ripple diff --git a/src/xrpld/app/misc/NetworkOPs.cpp b/src/xrpld/app/misc/NetworkOPs.cpp index 403090c390..b9069442f8 100644 --- a/src/xrpld/app/misc/NetworkOPs.cpp +++ b/src/xrpld/app/misc/NetworkOPs.cpp @@ -1452,6 +1452,11 @@ NetworkOPsImp::processTransactionSet(CanonicalTXSet const& set) for (auto& t : transactions) mTransactions.push_back(std::move(t)); } + if (mTransactions.empty()) + { + JLOG(m_journal.debug()) << "No transaction to process!"; + return; + } doTransactionSyncBatch(lock, [&](std::unique_lock const&) { XRPL_ASSERT( diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 69f25e1eb4..2cd9432eb8 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -2880,6 +2880,9 @@ PeerImp::checkTransaction( (stx->getFieldU32(sfLastLedgerSequence) < app_.getLedgerMaster().getValidLedgerIndex())) { + JLOG(p_journal_.info()) + << "Marking transaction " << stx->getTransactionID() + << "as BAD because it's expired"; app_.getHashRouter().setFlags( stx->getTransactionID(), HashRouterFlags::BAD); charge(Resource::feeUselessData, "expired tx"); @@ -2936,7 +2939,7 @@ PeerImp::checkTransaction( { if (!validReason.empty()) { - JLOG(p_journal_.trace()) + JLOG(p_journal_.debug()) << "Exception checking transaction: " << validReason; } @@ -2963,7 +2966,7 @@ PeerImp::checkTransaction( { if (!reason.empty()) { - JLOG(p_journal_.trace()) + JLOG(p_journal_.debug()) << "Exception checking transaction: " << reason; } app_.getHashRouter().setFlags( From 37c377a1b6ab18055bd3c8b954c5de97aaee683b Mon Sep 17 00:00:00 2001 From: Denis Angell Date: Mon, 15 Sep 2025 16:48:47 +0200 Subject: [PATCH 61/66] Fix: EscrowTokenV1 (#5571) * resolves an accounting inconsistency in MPT escrows where transfer fees were not properly handled when unlocking escrowed tokens. --- include/xrpl/protocol/detail/features.macro | 1 + src/test/app/EscrowToken_test.cpp | 68 +++++++++++++++++++++ src/xrpld/app/tx/detail/Escrow.cpp | 9 ++- src/xrpld/ledger/View.h | 3 +- src/xrpld/ledger/detail/View.cpp | 42 ++++++++++--- 5 files changed, 113 insertions(+), 10 deletions(-) diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index 9aacbbe3d9..f04e9f3641 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -32,6 +32,7 @@ // If you add an amendment here, then do not forget to increment `numFeatures` // in include/xrpl/protocol/Feature.h. +XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (DelegateV1_1, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (PriceOracleOrder, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo) diff --git a/src/test/app/EscrowToken_test.cpp b/src/test/app/EscrowToken_test.cpp index e81064c825..28c9a5b167 100644 --- a/src/test/app/EscrowToken_test.cpp +++ b/src/test/app/EscrowToken_test.cpp @@ -3501,6 +3501,10 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT( transferRate.value == std::uint32_t(1'000'000'000 * 1.25)); + BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 125); + BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == 125); + BEAST_EXPECT(env.balance(gw, MPT) == MPT(20'000)); + // bob can finish escrow env(escrow::finish(bob, alice, seq1), escrow::condition(escrow::cb1), @@ -3510,6 +3514,15 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(alice, MPT) == preAlice - delta); BEAST_EXPECT(env.balance(bob, MPT) == MPT(10'100)); + + auto const escrowedWithFix = + env.current()->rules().enabled(fixTokenEscrowV1) ? 0 : 25; + auto const outstandingWithFix = + env.current()->rules().enabled(fixTokenEscrowV1) ? MPT(19'975) + : MPT(20'000); + BEAST_EXPECT(mptEscrowed(env, alice, MPT) == escrowedWithFix); + BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == escrowedWithFix); + BEAST_EXPECT(env.balance(gw, MPT) == outstandingWithFix); } // test locked rate: cancel @@ -3554,6 +3567,60 @@ struct EscrowToken_test : public beast::unit_test::suite BEAST_EXPECT(env.balance(alice, MPT) == preAlice); BEAST_EXPECT(env.balance(bob, MPT) == preBob); + BEAST_EXPECT(env.balance(gw, MPT) == MPT(20'000)); + BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 0); + BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == 0); + } + + // test locked rate: issuer is destination + { + Env env{*this, features}; + auto const baseFee = env.current()->fees().base; + auto const alice = Account("alice"); + auto const bob = Account("bob"); + auto const gw = Account("gw"); + + MPTTester mptGw(env, gw, {.holders = {alice, bob}}); + mptGw.create( + {.transferFee = 25000, + .ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanEscrow | tfMPTCanTransfer}); + mptGw.authorize({.account = alice}); + mptGw.authorize({.account = bob}); + auto const MPT = mptGw["MPT"]; + env(pay(gw, alice, MPT(10'000))); + env(pay(gw, bob, MPT(10'000))); + env.close(); + + // alice can create escrow w/ xfer rate + auto const preAlice = env.balance(alice, MPT); + auto const seq1 = env.seq(alice); + auto const delta = MPT(125); + env(escrow::create(alice, gw, MPT(125)), + escrow::condition(escrow::cb1), + escrow::finish_time(env.now() + 1s), + fee(baseFee * 150)); + env.close(); + auto const transferRate = escrow::rate(env, alice, seq1); + BEAST_EXPECT( + transferRate.value == std::uint32_t(1'000'000'000 * 1.25)); + + BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 125); + BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == 125); + BEAST_EXPECT(env.balance(gw, MPT) == MPT(20'000)); + + // bob can finish escrow + env(escrow::finish(gw, alice, seq1), + escrow::condition(escrow::cb1), + escrow::fulfillment(escrow::fb1), + fee(baseFee * 150)); + env.close(); + + BEAST_EXPECT(env.balance(alice, MPT) == preAlice - delta); + BEAST_EXPECT(mptEscrowed(env, alice, MPT) == 0); + BEAST_EXPECT(issuerMPTEscrowed(env, MPT) == 0); + BEAST_EXPECT(env.balance(gw, MPT) == MPT(19'875)); } } @@ -3878,6 +3945,7 @@ public: FeatureBitset const all{testable_amendments()}; testIOUWithFeats(all); testMPTWithFeats(all); + testMPTWithFeats(all - fixTokenEscrowV1); } }; diff --git a/src/xrpld/app/tx/detail/Escrow.cpp b/src/xrpld/app/tx/detail/Escrow.cpp index dd0ffac778..3b05aa0007 100644 --- a/src/xrpld/app/tx/detail/Escrow.cpp +++ b/src/xrpld/app/tx/detail/Escrow.cpp @@ -1007,8 +1007,13 @@ escrowUnlockApplyHelper( // compute balance to transfer finalAmt = amount.value() - xferFee; } - - return rippleUnlockEscrowMPT(view, sender, receiver, finalAmt, journal); + return rippleUnlockEscrowMPT( + view, + sender, + receiver, + finalAmt, + view.rules().enabled(fixTokenEscrowV1) ? amount : finalAmt, + journal); } TER diff --git a/src/xrpld/ledger/View.h b/src/xrpld/ledger/View.h index cfd3599f78..faad633e00 100644 --- a/src/xrpld/ledger/View.h +++ b/src/xrpld/ledger/View.h @@ -719,7 +719,8 @@ rippleUnlockEscrowMPT( ApplyView& view, AccountID const& uGrantorID, AccountID const& uGranteeID, - STAmount const& saAmount, + STAmount const& netAmount, + STAmount const& grossAmount, beast::Journal j); /** Calls static accountSendIOU if saAmount represents Issue. diff --git a/src/xrpld/ledger/detail/View.cpp b/src/xrpld/ledger/detail/View.cpp index 708d5b29f7..473efa58fb 100644 --- a/src/xrpld/ledger/detail/View.cpp +++ b/src/xrpld/ledger/detail/View.cpp @@ -3006,11 +3006,17 @@ rippleUnlockEscrowMPT( ApplyView& view, AccountID const& sender, AccountID const& receiver, - STAmount const& amount, + STAmount const& netAmount, + STAmount const& grossAmount, beast::Journal j) { - auto const issuer = amount.getIssuer(); - auto const mptIssue = amount.get(); + if (!view.rules().enabled(fixTokenEscrowV1)) + XRPL_ASSERT( + netAmount == grossAmount, + "ripple::rippleUnlockEscrowMPT : netAmount == grossAmount"); + + auto const& issuer = netAmount.getIssuer(); + auto const& mptIssue = netAmount.get(); auto const mptID = keylet::mptIssuance(mptIssue.getMptID()); auto sleIssuance = view.peek(mptID); if (!sleIssuance) @@ -3031,7 +3037,7 @@ rippleUnlockEscrowMPT( } // LCOV_EXCL_STOP auto const locked = sleIssuance->getFieldU64(sfLockedAmount); - auto const redeem = amount.mpt().value(); + auto const redeem = grossAmount.mpt().value(); // Underflow check for subtraction if (!canSubtract( @@ -3064,7 +3070,7 @@ rippleUnlockEscrowMPT( } // LCOV_EXCL_STOP auto current = sle->getFieldU64(sfMPTAmount); - auto delta = amount.mpt().value(); + auto delta = netAmount.mpt().value(); // Overflow check for addition if (!canAdd(STAmount(mptIssue, current), STAmount(mptIssue, delta))) @@ -3082,7 +3088,7 @@ rippleUnlockEscrowMPT( { // Decrease the Issuance OutstandingAmount auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); - auto const redeem = amount.mpt().value(); + auto const redeem = netAmount.mpt().value(); // Underflow check for subtraction if (!canSubtract( @@ -3126,7 +3132,7 @@ rippleUnlockEscrowMPT( } // LCOV_EXCL_STOP auto const locked = sle->getFieldU64(sfLockedAmount); - auto const delta = amount.mpt().value(); + auto const delta = grossAmount.mpt().value(); // Underflow check for subtraction if (!canSubtract(STAmount(mptIssue, locked), STAmount(mptIssue, delta))) @@ -3144,6 +3150,28 @@ rippleUnlockEscrowMPT( sle->setFieldU64(sfLockedAmount, newLocked); view.update(sle); } + + // Note: The gross amount is the amount that was locked, the net + // amount is the amount that is being unlocked. The difference is the fee + // that was charged for the transfer. If this difference is greater than + // zero, we need to update the outstanding amount. + auto const diff = grossAmount.mpt().value() - netAmount.mpt().value(); + if (diff != 0) + { + auto const outstanding = sleIssuance->getFieldU64(sfOutstandingAmount); + // Underflow check for subtraction + if (!canSubtract( + STAmount(mptIssue, outstanding), STAmount(mptIssue, diff))) + { // LCOV_EXCL_START + JLOG(j.error()) + << "rippleUnlockEscrowMPT: insufficient outstanding amount for " + << mptIssue.getMptID() << ": " << outstanding << " < " << diff; + return tecINTERNAL; + } // LCOV_EXCL_STOP + + sleIssuance->setFieldU64(sfOutstandingAmount, outstanding - diff); + view.update(sleIssuance); + } return tesSUCCESS; } From 4caebfbd0eed74283705f4a2723761bd345a56da Mon Sep 17 00:00:00 2001 From: Bart Date: Mon, 15 Sep 2025 12:26:08 -0400 Subject: [PATCH 62/66] refactor: Wrap GitHub CI conditionals in curly braces (#5796) This change wraps all GitHub conditionals in `${{ .. }}`, both for consistency and to reduce unexpected failures, because it was previously noticed that not all conditionals work without those curly braces. --- .github/workflows/on-pr.yml | 6 +++--- .github/workflows/upload-conan-deps.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 24f27d5162..23f65c14cb 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -92,12 +92,12 @@ jobs: check-levelization: needs: should-run - if: needs.should-run.outputs.go == 'true' + if: ${{ needs.should-run.outputs.go == 'true' }} uses: ./.github/workflows/check-levelization.yml build-test: needs: should-run - if: needs.should-run.outputs.go == 'true' + if: ${{ needs.should-run.outputs.go == 'true' }} uses: ./.github/workflows/build-test.yml strategy: matrix: @@ -111,7 +111,7 @@ jobs: needs: - should-run - build-test - if: needs.should-run.outputs.go == 'true' + if: ${{ needs.should-run.outputs.go == 'true' }} uses: ./.github/workflows/notify-clio.yml secrets: clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }} diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index 6b94815284..c52b3c89d3 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -83,9 +83,9 @@ jobs: force_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }} - name: Log into Conan remote - if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' + if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' }} run: conan remote login ${{ env.CONAN_REMOTE_NAME }} "${{ secrets.CONAN_REMOTE_USERNAME }}" --password "${{ secrets.CONAN_REMOTE_PASSWORD }}" - name: Upload Conan packages - if: github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' + if: ${{ github.repository_owner == 'XRPLF' && github.event_name != 'pull_request' && github.event_name != 'schedule' }} run: conan upload "*" -r=${{ env.CONAN_REMOTE_NAME }} --confirm ${{ github.event.inputs.force_upload == 'true' && '--force' || '' }} From 3e4e9a2ddce8b4c1fc536574cc6d4ab3dcbd6a23 Mon Sep 17 00:00:00 2001 From: Bart Date: Mon, 15 Sep 2025 13:28:47 -0400 Subject: [PATCH 63/66] Only notify clio for PRs targeting the release and master branches (#5794) Clio should only be notified when releases are about to be made, instead of for all PR, so this change only notifies Clio when a PR targets the release or master branch. --- .github/workflows/on-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/on-pr.yml b/.github/workflows/on-pr.yml index 23f65c14cb..9befd31e71 100644 --- a/.github/workflows/on-pr.yml +++ b/.github/workflows/on-pr.yml @@ -111,7 +111,7 @@ jobs: needs: - should-run - build-test - if: ${{ needs.should-run.outputs.go == 'true' }} + if: ${{ needs.should-run.outputs.go == 'true' && contains(fromJSON('["release", "master"]'), github.ref_name) }} uses: ./.github/workflows/notify-clio.yml secrets: clio_notify_token: ${{ secrets.CLIO_NOTIFY_TOKEN }} From ccb9f1e42d13bf83c9c953fb562938e3734a699e Mon Sep 17 00:00:00 2001 From: yinyiqian1 Date: Mon, 15 Sep 2025 15:42:36 -0400 Subject: [PATCH 64/66] Support DynamicMPT XLS-94d (#5705) * extends the functionality of the MPTokenIssuanceSet transaction, allowing the issuer to update fields or flags that were explicitly marked as mutable during creation. --- include/xrpl/protocol/LedgerFormats.h | 9 + include/xrpl/protocol/TxFlags.h | 33 + include/xrpl/protocol/detail/features.macro | 1 + .../xrpl/protocol/detail/ledger_entries.macro | 1 + include/xrpl/protocol/detail/sfields.macro | 1 + .../xrpl/protocol/detail/transactions.macro | 4 + src/test/app/MPToken_test.cpp | 920 +++++++++++++++++- src/test/jtx/impl/mpt.cpp | 93 +- src/test/jtx/mpt.h | 16 + .../app/tx/detail/MPTokenIssuanceCreate.cpp | 12 + .../app/tx/detail/MPTokenIssuanceCreate.h | 1 + .../app/tx/detail/MPTokenIssuanceSet.cpp | 150 ++- 12 files changed, 1215 insertions(+), 26 deletions(-) diff --git a/include/xrpl/protocol/LedgerFormats.h b/include/xrpl/protocol/LedgerFormats.h index e3efe8fec2..711754df94 100644 --- a/include/xrpl/protocol/LedgerFormats.h +++ b/include/xrpl/protocol/LedgerFormats.h @@ -188,6 +188,15 @@ enum LedgerSpecificFlags { lsfMPTCanTransfer = 0x00000020, lsfMPTCanClawback = 0x00000040, + lsfMPTCanMutateCanLock = 0x00000002, + lsfMPTCanMutateRequireAuth = 0x00000004, + lsfMPTCanMutateCanEscrow = 0x00000008, + lsfMPTCanMutateCanTrade = 0x00000010, + lsfMPTCanMutateCanTransfer = 0x00000020, + lsfMPTCanMutateCanClawback = 0x00000040, + lsfMPTCanMutateMetadata = 0x00010000, + lsfMPTCanMutateTransferFee = 0x00020000, + // ltMPTOKEN lsfMPTAuthorized = 0x00000002, diff --git a/include/xrpl/protocol/TxFlags.h b/include/xrpl/protocol/TxFlags.h index a37474b780..c376180ac0 100644 --- a/include/xrpl/protocol/TxFlags.h +++ b/include/xrpl/protocol/TxFlags.h @@ -151,6 +151,20 @@ constexpr std::uint32_t const tfMPTCanClawback = lsfMPTCanClawback; constexpr std::uint32_t const tfMPTokenIssuanceCreateMask = ~(tfUniversal | tfMPTCanLock | tfMPTRequireAuth | tfMPTCanEscrow | tfMPTCanTrade | tfMPTCanTransfer | tfMPTCanClawback); +// MPTokenIssuanceCreate MutableFlags: +// Indicating specific fields or flags may be changed after issuance. +constexpr std::uint32_t const tfMPTCanMutateCanLock = lsfMPTCanMutateCanLock; +constexpr std::uint32_t const tfMPTCanMutateRequireAuth = lsfMPTCanMutateRequireAuth; +constexpr std::uint32_t const tfMPTCanMutateCanEscrow = lsfMPTCanMutateCanEscrow; +constexpr std::uint32_t const tfMPTCanMutateCanTrade = lsfMPTCanMutateCanTrade; +constexpr std::uint32_t const tfMPTCanMutateCanTransfer = lsfMPTCanMutateCanTransfer; +constexpr std::uint32_t const tfMPTCanMutateCanClawback = lsfMPTCanMutateCanClawback; +constexpr std::uint32_t const tfMPTCanMutateMetadata = lsfMPTCanMutateMetadata; +constexpr std::uint32_t const tfMPTCanMutateTransferFee = lsfMPTCanMutateTransferFee; +constexpr std::uint32_t const tfMPTokenIssuanceCreateMutableMask = + ~(tfMPTCanMutateCanLock | tfMPTCanMutateRequireAuth | tfMPTCanMutateCanEscrow | tfMPTCanMutateCanTrade + | tfMPTCanMutateCanTransfer | tfMPTCanMutateCanClawback | tfMPTCanMutateMetadata | tfMPTCanMutateTransferFee); + // MPTokenAuthorize flags: constexpr std::uint32_t const tfMPTUnauthorize = 0x00000001; constexpr std::uint32_t const tfMPTokenAuthorizeMask = ~(tfUniversal | tfMPTUnauthorize); @@ -161,6 +175,25 @@ constexpr std::uint32_t const tfMPTUnlock = 0x00000002; constexpr std::uint32_t const tfMPTokenIssuanceSetMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock); constexpr std::uint32_t const tfMPTokenIssuanceSetPermissionMask = ~(tfUniversal | tfMPTLock | tfMPTUnlock); +// MPTokenIssuanceSet MutableFlags: +// Set or Clear flags. +constexpr std::uint32_t const tfMPTSetCanLock = 0x00000001; +constexpr std::uint32_t const tfMPTClearCanLock = 0x00000002; +constexpr std::uint32_t const tfMPTSetRequireAuth = 0x00000004; +constexpr std::uint32_t const tfMPTClearRequireAuth = 0x00000008; +constexpr std::uint32_t const tfMPTSetCanEscrow = 0x00000010; +constexpr std::uint32_t const tfMPTClearCanEscrow = 0x00000020; +constexpr std::uint32_t const tfMPTSetCanTrade = 0x00000040; +constexpr std::uint32_t const tfMPTClearCanTrade = 0x00000080; +constexpr std::uint32_t const tfMPTSetCanTransfer = 0x00000100; +constexpr std::uint32_t const tfMPTClearCanTransfer = 0x00000200; +constexpr std::uint32_t const tfMPTSetCanClawback = 0x00000400; +constexpr std::uint32_t const tfMPTClearCanClawback = 0x00000800; +constexpr std::uint32_t const tfMPTokenIssuanceSetMutableMask = ~(tfMPTSetCanLock | tfMPTClearCanLock | + tfMPTSetRequireAuth | tfMPTClearRequireAuth | tfMPTSetCanEscrow | tfMPTClearCanEscrow | + tfMPTSetCanTrade | tfMPTClearCanTrade | tfMPTSetCanTransfer | tfMPTClearCanTransfer | + tfMPTSetCanClawback | tfMPTClearCanClawback); + // MPTokenIssuanceDestroy flags: constexpr std::uint32_t const tfMPTokenIssuanceDestroyMask = ~tfUniversal; diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index f04e9f3641..a9f5d95624 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -32,6 +32,7 @@ // If you add an amendment here, then do not forget to increment `numFeatures` // in include/xrpl/protocol/Feature.h. +XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (DelegateV1_1, Supported::no, VoteBehavior::DefaultNo) XRPL_FIX (PriceOracleOrder, Supported::no, VoteBehavior::DefaultNo) diff --git a/include/xrpl/protocol/detail/ledger_entries.macro b/include/xrpl/protocol/detail/ledger_entries.macro index ac9ebc6069..1066986223 100644 --- a/include/xrpl/protocol/detail/ledger_entries.macro +++ b/include/xrpl/protocol/detail/ledger_entries.macro @@ -412,6 +412,7 @@ LEDGER_ENTRY(ltMPTOKEN_ISSUANCE, 0x007e, MPTokenIssuance, mpt_issuance, ({ {sfPreviousTxnID, soeREQUIRED}, {sfPreviousTxnLgrSeq, soeREQUIRED}, {sfDomainID, soeOPTIONAL}, + {sfMutableFlags, soeDEFAULT}, })) /** A ledger object which tracks MPToken diff --git a/include/xrpl/protocol/detail/sfields.macro b/include/xrpl/protocol/detail/sfields.macro index 537fcae479..96192324fd 100644 --- a/include/xrpl/protocol/detail/sfields.macro +++ b/include/xrpl/protocol/detail/sfields.macro @@ -114,6 +114,7 @@ TYPED_SFIELD(sfVoteWeight, UINT32, 48) TYPED_SFIELD(sfFirstNFTokenSequence, UINT32, 50) TYPED_SFIELD(sfOracleDocumentID, UINT32, 51) TYPED_SFIELD(sfPermissionValue, UINT32, 52) +TYPED_SFIELD(sfMutableFlags, UINT32, 53) // 64-bit integers (common) TYPED_SFIELD(sfIndexNext, UINT64, 1) diff --git a/include/xrpl/protocol/detail/transactions.macro b/include/xrpl/protocol/detail/transactions.macro index bfbc18aa1b..3aaa5a40a3 100644 --- a/include/xrpl/protocol/detail/transactions.macro +++ b/include/xrpl/protocol/detail/transactions.macro @@ -548,6 +548,7 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_CREATE, 54, MPTokenIssuanceCreate, {sfMaximumAmount, soeOPTIONAL}, {sfMPTokenMetadata, soeOPTIONAL}, {sfDomainID, soeOPTIONAL}, + {sfMutableFlags, soeOPTIONAL}, })) /** This transaction type destroys a MPTokensIssuance instance */ @@ -566,6 +567,9 @@ TRANSACTION(ttMPTOKEN_ISSUANCE_SET, 56, MPTokenIssuanceSet, {sfMPTokenIssuanceID, soeREQUIRED}, {sfHolder, soeOPTIONAL}, {sfDomainID, soeOPTIONAL}, + {sfMPTokenMetadata, soeOPTIONAL}, + {sfTransferFee, soeOPTIONAL}, + {sfMutableFlags, soeOPTIONAL}, })) /** This transaction type authorizes a MPToken instance */ diff --git a/src/test/app/MPToken_test.cpp b/src/test/app/MPToken_test.cpp index 6470962f2f..1410370c33 100644 --- a/src/test/app/MPToken_test.cpp +++ b/src/test/app/MPToken_test.cpp @@ -589,7 +589,8 @@ class MPToken_test : public beast::unit_test::suite .flags = 0x00000008, .err = temINVALID_FLAG}); - if (!features[featureSingleAssetVault]) + if (!features[featureSingleAssetVault] && + !features[featureDynamicMPT]) { // test invalid flags - nothing is being changed mptAlice.set( @@ -623,7 +624,8 @@ class MPToken_test : public beast::unit_test::suite .flags = 0x00000000, .err = temMALFORMED}); - if (!features[featurePermissionedDomains]) + if (!features[featurePermissionedDomains] || + !features[featureSingleAssetVault]) { // cannot set DomainID since PD is not enabled mptAlice.set( @@ -631,7 +633,7 @@ class MPToken_test : public beast::unit_test::suite .domainID = uint256(42), .err = temDISABLED}); } - else + else if (features[featureSingleAssetVault]) { // cannot set DomainID since Holder is set mptAlice.set( @@ -2738,6 +2740,882 @@ class MPToken_test : public beast::unit_test::suite } } + void + testInvalidCreateDynamic(FeatureBitset features) + { + testcase("invalid MPTokenIssuanceCreate for DynamicMPT"); + + using namespace test::jtx; + Account const alice("alice"); + + // Can not provide MutableFlags when DynamicMPT amendment is not enabled + { + Env env{*this, features - featureDynamicMPT}; + MPTTester mptAlice(env, alice); + mptAlice.create( + {.ownerCount = 0, .mutableFlags = 2, .err = temDISABLED}); + mptAlice.create( + {.ownerCount = 0, .mutableFlags = 0, .err = temDISABLED}); + } + + // MutableFlags contains invalid values + { + Env env{*this, features}; + MPTTester mptAlice(env, alice); + + // Value 1 is reserved for MPT lock. + mptAlice.create( + {.ownerCount = 0, .mutableFlags = 1, .err = temINVALID_FLAG}); + mptAlice.create( + {.ownerCount = 0, .mutableFlags = 17, .err = temINVALID_FLAG}); + mptAlice.create( + {.ownerCount = 0, + .mutableFlags = 65535, + .err = temINVALID_FLAG}); + + // MutableFlags can not be 0 + mptAlice.create( + {.ownerCount = 0, .mutableFlags = 0, .err = temINVALID_FLAG}); + } + } + + void + testInvalidSetDynamic(FeatureBitset features) + { + testcase("invalid MPTokenIssuanceSet for DynamicMPT"); + + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + + // Can not provide MutableFlags, MPTokenMetadata or TransferFee when + // DynamicMPT amendment is not enabled + { + Env env{*this, features - featureDynamicMPT}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + auto const mptID = makeMptID(env.seq(alice), alice); + + // MutableFlags is not allowed when DynamicMPT is not enabled + mptAlice.set( + {.account = alice, + .id = mptID, + .mutableFlags = 2, + .err = temDISABLED}); + mptAlice.set( + {.account = alice, + .id = mptID, + .mutableFlags = 0, + .err = temDISABLED}); + + // MPTokenMetadata is not allowed when DynamicMPT is not enabled + mptAlice.set( + {.account = alice, + .id = mptID, + .metadata = "test", + .err = temDISABLED}); + mptAlice.set( + {.account = alice, + .id = mptID, + .metadata = "", + .err = temDISABLED}); + + // TransferFee is not allowed when DynamicMPT is not enabled + mptAlice.set( + {.account = alice, + .id = mptID, + .transferFee = 100, + .err = temDISABLED}); + mptAlice.set( + {.account = alice, + .id = mptID, + .transferFee = 0, + .err = temDISABLED}); + } + + // Can not provide holder when MutableFlags, MPTokenMetadata or + // TransferFee is present + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + auto const mptID = makeMptID(env.seq(alice), alice); + + // Holder is not allowed when MutableFlags is present + mptAlice.set( + {.account = alice, + .holder = bob, + .id = mptID, + .mutableFlags = 2, + .err = temMALFORMED}); + + // Holder is not allowed when MPTokenMetadata is present + mptAlice.set( + {.account = alice, + .holder = bob, + .id = mptID, + .metadata = "test", + .err = temMALFORMED}); + + // Holder is not allowed when TransferFee is present + mptAlice.set( + {.account = alice, + .holder = bob, + .id = mptID, + .transferFee = 100, + .err = temMALFORMED}); + } + + // Can not set Flags when MutableFlags, MPTokenMetadata or + // TransferFee is present + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create( + {.ownerCount = 1, + .mutableFlags = tfMPTCanMutateMetadata | + tfMPTCanMutateCanLock | tfMPTCanMutateTransferFee}); + + // Setting flags is not allowed when MutableFlags is present + mptAlice.set( + {.account = alice, + .flags = tfMPTCanLock, + .mutableFlags = 2, + .err = temMALFORMED}); + + // Setting flags is not allowed when MPTokenMetadata is present + mptAlice.set( + {.account = alice, + .flags = tfMPTCanLock, + .metadata = "test", + .err = temMALFORMED}); + + // setting flags is not allowed when TransferFee is present + mptAlice.set( + {.account = alice, + .flags = tfMPTCanLock, + .transferFee = 100, + .err = temMALFORMED}); + } + + // Flags being 0 or tfFullyCanonicalSig is fine + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create( + {.transferFee = 10, + .ownerCount = 1, + .flags = tfMPTCanTransfer, + .mutableFlags = + tfMPTCanMutateTransferFee | tfMPTCanMutateMetadata}); + + mptAlice.set( + {.account = alice, + .flags = 0, + .transferFee = 100, + .metadata = "test"}); + mptAlice.set( + {.account = alice, + .flags = tfFullyCanonicalSig, + .transferFee = 200, + .metadata = "test2"}); + } + + // Invalid MutableFlags + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + auto const mptID = makeMptID(env.seq(alice), alice); + + for (auto const flags : {10000, 0, 5000}) + { + mptAlice.set( + {.account = alice, + .id = mptID, + .mutableFlags = flags, + .err = temINVALID_FLAG}); + } + } + + // Can not set and clear the same mutable flag + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + auto const mptID = makeMptID(env.seq(alice), alice); + + auto const flagCombinations = { + tfMPTSetCanLock | tfMPTClearCanLock, + tfMPTSetRequireAuth | tfMPTClearRequireAuth, + tfMPTSetCanEscrow | tfMPTClearCanEscrow, + tfMPTSetCanTrade | tfMPTClearCanTrade, + tfMPTSetCanTransfer | tfMPTClearCanTransfer, + tfMPTSetCanClawback | tfMPTClearCanClawback, + tfMPTSetCanLock | tfMPTClearCanLock | tfMPTClearCanTrade, + tfMPTSetCanTransfer | tfMPTClearCanTransfer | + tfMPTSetCanEscrow | tfMPTClearCanClawback}; + + for (auto const& mutableFlags : flagCombinations) + { + mptAlice.set( + {.account = alice, + .id = mptID, + .mutableFlags = mutableFlags, + .err = temINVALID_FLAG}); + } + } + + // Can not mutate flag which is not mutable + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create({.ownerCount = 1}); + + auto const mutableFlags = { + tfMPTSetCanLock, + tfMPTClearCanLock, + tfMPTSetRequireAuth, + tfMPTClearRequireAuth, + tfMPTSetCanEscrow, + tfMPTClearCanEscrow, + tfMPTSetCanTrade, + tfMPTClearCanTrade, + tfMPTSetCanTransfer, + tfMPTClearCanTransfer, + tfMPTSetCanClawback, + tfMPTClearCanClawback}; + + for (auto const& mutableFlag : mutableFlags) + { + mptAlice.set( + {.account = alice, + .mutableFlags = mutableFlag, + .err = tecNO_PERMISSION}); + } + } + + // Metadata exceeding max length + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create( + {.ownerCount = 1, .mutableFlags = tfMPTCanMutateMetadata}); + + std::string metadata(maxMPTokenMetadataLength + 1, 'a'); + mptAlice.set( + {.account = alice, .metadata = metadata, .err = temMALFORMED}); + } + + // Can not mutate metadata when it is not mutable + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create({.ownerCount = 1}); + mptAlice.set( + {.account = alice, + .metadata = "test", + .err = tecNO_PERMISSION}); + } + + // Transfer fee exceeding the max value + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + auto const mptID = makeMptID(env.seq(alice), alice); + + mptAlice.create( + {.ownerCount = 1, .mutableFlags = tfMPTCanMutateTransferFee}); + + mptAlice.set( + {.account = alice, + .id = mptID, + .transferFee = maxTransferFee + 1, + .err = temBAD_TRANSFER_FEE}); + } + + // Test setting non-zero transfer fee and clearing MPTCanTransfer at the + // same time + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create( + {.transferFee = 100, + .ownerCount = 1, + .flags = tfMPTCanTransfer, + .mutableFlags = + tfMPTCanMutateTransferFee | tfMPTCanMutateCanTransfer}); + + // Can not set non-zero transfer fee and clear MPTCanTransfer at the + // same time + mptAlice.set( + {.account = alice, + .mutableFlags = tfMPTClearCanTransfer, + .transferFee = 1, + .err = temMALFORMED}); + + // Can set transfer fee to zero and clear MPTCanTransfer at the same + // time. tfMPTCanTransfer will be cleared and TransferFee field will + // be removed. + mptAlice.set( + {.account = alice, + .mutableFlags = tfMPTClearCanTransfer, + .transferFee = 0}); + BEAST_EXPECT(!mptAlice.isTransferFeePresent()); + } + + // Can not set non-zero transfer fee when MPTCanTransfer is not set + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create( + {.ownerCount = 1, + .mutableFlags = + tfMPTCanMutateTransferFee | tfMPTCanMutateCanTransfer}); + + mptAlice.set( + {.account = alice, + .transferFee = 100, + .err = tecNO_PERMISSION}); + + // Can not set transfer fee even when trying to set MPTCanTransfer + // at the same time. MPTCanTransfer must be set first, then transfer + // fee can be set in a separate transaction. + mptAlice.set( + {.account = alice, + .mutableFlags = tfMPTSetCanTransfer, + .transferFee = 100, + .err = tecNO_PERMISSION}); + } + + // Can not mutate transfer fee when it is not mutable + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create( + {.transferFee = 10, + .ownerCount = 1, + .flags = tfMPTCanTransfer}); + + mptAlice.set( + {.account = alice, + .transferFee = 100, + .err = tecNO_PERMISSION}); + + mptAlice.set( + {.account = alice, .transferFee = 0, .err = tecNO_PERMISSION}); + } + + // Set some flags mutable. Can not mutate the others + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create( + {.ownerCount = 1, + .mutableFlags = tfMPTCanMutateCanTrade | + tfMPTCanMutateCanTransfer | tfMPTCanMutateMetadata}); + + // Can not mutate transfer fee + mptAlice.set( + {.account = alice, + .transferFee = 100, + .err = tecNO_PERMISSION}); + + auto const invalidFlags = { + tfMPTSetCanLock, + tfMPTClearCanLock, + tfMPTSetRequireAuth, + tfMPTClearRequireAuth, + tfMPTSetCanEscrow, + tfMPTClearCanEscrow, + tfMPTSetCanClawback, + tfMPTClearCanClawback}; + + // Can not mutate flags which are not mutable + for (auto const& mutableFlag : invalidFlags) + { + mptAlice.set( + {.account = alice, + .mutableFlags = mutableFlag, + .err = tecNO_PERMISSION}); + } + + // Can mutate MPTCanTrade + mptAlice.set({.account = alice, .mutableFlags = tfMPTSetCanTrade}); + mptAlice.set( + {.account = alice, .mutableFlags = tfMPTClearCanTrade}); + + // Can mutate MPTCanTransfer + mptAlice.set( + {.account = alice, .mutableFlags = tfMPTSetCanTransfer}); + mptAlice.set( + {.account = alice, .mutableFlags = tfMPTClearCanTransfer}); + + // Can mutate metadata + mptAlice.set({.account = alice, .metadata = "test"}); + mptAlice.set({.account = alice, .metadata = ""}); + } + } + + void + testMutateMPT(FeatureBitset features) + { + testcase("Mutate MPT"); + using namespace test::jtx; + + Account const alice("alice"); + + // Mutate metadata + { + Env env{*this, features}; + MPTTester mptAlice(env, alice); + mptAlice.create( + {.metadata = "test", + .ownerCount = 1, + .mutableFlags = tfMPTCanMutateMetadata}); + + std::vector metadatas = { + "mutate metadata", + "mutate metadata 2", + "mutate metadata 3", + "mutate metadata 3", + "test", + "mutate metadata"}; + + for (auto const& metadata : metadatas) + { + mptAlice.set({.account = alice, .metadata = metadata}); + BEAST_EXPECT(mptAlice.checkMetadata(metadata)); + } + + // Metadata being empty will remove the field + mptAlice.set({.account = alice, .metadata = ""}); + BEAST_EXPECT(!mptAlice.isMetadataPresent()); + } + + // Mutate transfer fee + { + Env env{*this, features}; + MPTTester mptAlice(env, alice); + mptAlice.create( + {.transferFee = 100, + .metadata = "test", + .ownerCount = 1, + .flags = tfMPTCanTransfer, + .mutableFlags = tfMPTCanMutateTransferFee}); + + for (std::uint16_t const fee : std::initializer_list{ + 1, 10, 100, 200, 500, 1000, maxTransferFee}) + { + mptAlice.set({.account = alice, .transferFee = fee}); + BEAST_EXPECT(mptAlice.checkTransferFee(fee)); + } + + // Setting TransferFee to zero will remove the field + mptAlice.set({.account = alice, .transferFee = 0}); + BEAST_EXPECT(!mptAlice.isTransferFeePresent()); + + // Set transfer fee again + mptAlice.set({.account = alice, .transferFee = 10}); + BEAST_EXPECT(mptAlice.checkTransferFee(10)); + } + + // Test flag toggling + { + auto testFlagToggle = [&](std::uint32_t createFlags, + std::uint32_t setFlags, + std::uint32_t clearFlags) { + Env env{*this, features}; + MPTTester mptAlice(env, alice); + + // Create the MPT object with the specified initial flags + mptAlice.create( + {.metadata = "test", + .ownerCount = 1, + .mutableFlags = createFlags}); + + // Set and clear the flag multiple times + mptAlice.set({.account = alice, .mutableFlags = setFlags}); + mptAlice.set({.account = alice, .mutableFlags = clearFlags}); + mptAlice.set({.account = alice, .mutableFlags = clearFlags}); + mptAlice.set({.account = alice, .mutableFlags = setFlags}); + mptAlice.set({.account = alice, .mutableFlags = setFlags}); + mptAlice.set({.account = alice, .mutableFlags = clearFlags}); + mptAlice.set({.account = alice, .mutableFlags = setFlags}); + mptAlice.set({.account = alice, .mutableFlags = clearFlags}); + }; + + testFlagToggle( + tfMPTCanMutateCanLock, tfMPTCanLock, tfMPTClearCanLock); + testFlagToggle( + tfMPTCanMutateRequireAuth, + tfMPTSetRequireAuth, + tfMPTClearRequireAuth); + testFlagToggle( + tfMPTCanMutateCanEscrow, + tfMPTSetCanEscrow, + tfMPTClearCanEscrow); + testFlagToggle( + tfMPTCanMutateCanTrade, tfMPTSetCanTrade, tfMPTClearCanTrade); + testFlagToggle( + tfMPTCanMutateCanTransfer, + tfMPTSetCanTransfer, + tfMPTClearCanTransfer); + testFlagToggle( + tfMPTCanMutateCanClawback, + tfMPTSetCanClawback, + tfMPTClearCanClawback); + } + } + + void + testMutateCanLock(FeatureBitset features) + { + testcase("Mutate MPTCanLock"); + using namespace test::jtx; + + Account const alice("alice"); + Account const bob("bob"); + + // Individual lock + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create( + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanLock | tfMPTCanTransfer, + .mutableFlags = tfMPTCanMutateCanLock | + tfMPTCanMutateCanTrade | tfMPTCanMutateTransferFee}); + mptAlice.authorize({.account = bob, .holderCount = 1}); + + // Lock bob's mptoken + mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock}); + + // Can mutate the mutable flags and fields + mptAlice.set({.account = alice, .mutableFlags = tfMPTClearCanLock}); + mptAlice.set({.account = alice, .mutableFlags = tfMPTSetCanLock}); + mptAlice.set({.account = alice, .mutableFlags = tfMPTClearCanLock}); + mptAlice.set({.account = alice, .mutableFlags = tfMPTSetCanTrade}); + mptAlice.set( + {.account = alice, .mutableFlags = tfMPTClearCanTrade}); + mptAlice.set({.account = alice, .transferFee = 200}); + } + + // Global lock + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create( + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanLock, + .mutableFlags = tfMPTCanMutateCanLock | + tfMPTCanMutateCanClawback | tfMPTCanMutateMetadata}); + mptAlice.authorize({.account = bob, .holderCount = 1}); + + // Lock issuance + mptAlice.set({.account = alice, .flags = tfMPTLock}); + + // Can mutate the mutable flags and fields + mptAlice.set({.account = alice, .mutableFlags = tfMPTClearCanLock}); + mptAlice.set({.account = alice, .mutableFlags = tfMPTSetCanLock}); + mptAlice.set({.account = alice, .mutableFlags = tfMPTClearCanLock}); + mptAlice.set( + {.account = alice, .mutableFlags = tfMPTSetCanClawback}); + mptAlice.set( + {.account = alice, .mutableFlags = tfMPTClearCanClawback}); + mptAlice.set({.account = alice, .metadata = "mutate"}); + } + + // Test lock and unlock after mutating MPTCanLock + { + Env env{*this, features}; + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create( + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanLock, + .mutableFlags = tfMPTCanMutateCanLock | + tfMPTCanMutateCanClawback | tfMPTCanMutateMetadata}); + mptAlice.authorize({.account = bob, .holderCount = 1}); + + // Can lock and unlock + mptAlice.set({.account = alice, .flags = tfMPTLock}); + mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock}); + mptAlice.set({.account = alice, .flags = tfMPTUnlock}); + mptAlice.set( + {.account = alice, .holder = bob, .flags = tfMPTUnlock}); + + // Clear lsfMPTCanLock + mptAlice.set({.account = alice, .mutableFlags = tfMPTClearCanLock}); + + // Can not lock or unlock + mptAlice.set( + {.account = alice, + .flags = tfMPTLock, + .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, + .flags = tfMPTUnlock, + .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, + .holder = bob, + .flags = tfMPTLock, + .err = tecNO_PERMISSION}); + mptAlice.set( + {.account = alice, + .holder = bob, + .flags = tfMPTUnlock, + .err = tecNO_PERMISSION}); + + // Set MPTCanLock again + mptAlice.set({.account = alice, .mutableFlags = tfMPTSetCanLock}); + + // Can lock and unlock again + mptAlice.set({.account = alice, .flags = tfMPTLock}); + mptAlice.set({.account = alice, .holder = bob, .flags = tfMPTLock}); + mptAlice.set({.account = alice, .flags = tfMPTUnlock}); + mptAlice.set( + {.account = alice, .holder = bob, .flags = tfMPTUnlock}); + } + } + + void + testMutateRequireAuth(FeatureBitset features) + { + testcase("Mutate MPTRequireAuth"); + using namespace test::jtx; + + Env env{*this, features}; + Account const alice("alice"); + Account const bob("bob"); + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + mptAlice.create( + {.ownerCount = 1, + .flags = tfMPTRequireAuth, + .mutableFlags = tfMPTCanMutateRequireAuth}); + + mptAlice.authorize({.account = bob}); + mptAlice.authorize({.account = alice, .holder = bob}); + + // Pay to bob + mptAlice.pay(alice, bob, 1000); + + // Unauthorize bob + mptAlice.authorize( + {.account = alice, .holder = bob, .flags = tfMPTUnauthorize}); + + // Can not pay to bob + mptAlice.pay(bob, alice, 100, tecNO_AUTH); + + // Clear RequireAuth + mptAlice.set({.account = alice, .mutableFlags = tfMPTClearRequireAuth}); + + // Can pay to bob + mptAlice.pay(alice, bob, 1000); + + // Set RequireAuth again + mptAlice.set({.account = alice, .mutableFlags = tfMPTSetRequireAuth}); + + // Can not pay to bob since he is not authorized + mptAlice.pay(bob, alice, 100, tecNO_AUTH); + + // Authorize bob again + mptAlice.authorize({.account = alice, .holder = bob}); + + // Can pay to bob again + mptAlice.pay(alice, bob, 100); + } + + void + testMutateCanEscrow(FeatureBitset features) + { + testcase("Mutate MPTCanEscrow"); + using namespace test::jtx; + using namespace std::literals; + + Env env{*this, features}; + auto const baseFee = env.current()->fees().base; + auto const alice = Account("alice"); + auto const bob = Account("bob"); + auto const carol = Account("carol"); + + MPTTester mptAlice(env, alice, {.holders = {carol, bob}}); + mptAlice.create( + {.ownerCount = 1, + .holderCount = 0, + .flags = tfMPTCanTransfer, + .mutableFlags = tfMPTCanMutateCanEscrow}); + mptAlice.authorize({.account = carol}); + mptAlice.authorize({.account = bob}); + + auto const MPT = mptAlice["MPT"]; + env(pay(alice, carol, MPT(10'000))); + env(pay(alice, bob, MPT(10'000))); + env.close(); + + // MPTCanEscrow is not enabled + env(escrow::create(carol, bob, MPT(3)), + escrow::condition(escrow::cb1), + escrow::finish_time(env.now() + 1s), + fee(baseFee * 150), + ter(tecNO_PERMISSION)); + + // MPTCanEscrow is enabled now + mptAlice.set({.account = alice, .mutableFlags = tfMPTSetCanEscrow}); + env(escrow::create(carol, bob, MPT(3)), + escrow::condition(escrow::cb1), + escrow::finish_time(env.now() + 1s), + fee(baseFee * 150)); + + // Clear MPTCanEscrow + mptAlice.set({.account = alice, .mutableFlags = tfMPTClearCanEscrow}); + env(escrow::create(carol, bob, MPT(3)), + escrow::condition(escrow::cb1), + escrow::finish_time(env.now() + 1s), + fee(baseFee * 150), + ter(tecNO_PERMISSION)); + } + + void + testMutateCanTransfer(FeatureBitset features) + { + testcase("Mutate MPTCanTransfer"); + + using namespace test::jtx; + Account const alice("alice"); + Account const bob("bob"); + Account const carol("carol"); + + { + Env env{*this, features}; + + MPTTester mptAlice(env, alice, {.holders = {bob, carol}}); + mptAlice.create( + {.ownerCount = 1, + .mutableFlags = + tfMPTCanMutateCanTransfer | tfMPTCanMutateTransferFee}); + + mptAlice.authorize({.account = bob}); + mptAlice.authorize({.account = carol}); + + // Pay to bob + mptAlice.pay(alice, bob, 1000); + + // Bob can not pay carol since MPTCanTransfer is not set + mptAlice.pay(bob, carol, 50, tecNO_AUTH); + + // Can not set non-zero transfer fee when MPTCanTransfer is not set + mptAlice.set( + {.account = alice, + .transferFee = 100, + .err = tecNO_PERMISSION}); + + // Can not set non-zero transfer fee even when trying to set + // MPTCanTransfer at the same time + mptAlice.set( + {.account = alice, + .mutableFlags = tfMPTSetCanTransfer, + .transferFee = 100, + .err = tecNO_PERMISSION}); + + // Alice sets MPTCanTransfer + mptAlice.set( + {.account = alice, .mutableFlags = tfMPTSetCanTransfer}); + + // Can set transfer fee now + BEAST_EXPECT(!mptAlice.isTransferFeePresent()); + mptAlice.set({.account = alice, .transferFee = 100}); + BEAST_EXPECT(mptAlice.isTransferFeePresent()); + + // Bob can pay carol + mptAlice.pay(bob, carol, 50); + + // Alice clears MPTCanTransfer + mptAlice.set( + {.account = alice, .mutableFlags = tfMPTClearCanTransfer}); + + // TransferFee field is removed when MPTCanTransfer is cleared + BEAST_EXPECT(!mptAlice.isTransferFeePresent()); + + // Bob can not pay + mptAlice.pay(bob, carol, 50, tecNO_AUTH); + } + + // Can set transfer fee to zero when MPTCanTransfer is not set, but + // tfMPTCanMutateTransferFee is set. + { + Env env{*this, features}; + + MPTTester mptAlice(env, alice, {.holders = {bob, carol}}); + mptAlice.create( + {.transferFee = 100, + .ownerCount = 1, + .flags = tfMPTCanTransfer, + .mutableFlags = + tfMPTCanMutateTransferFee | tfMPTCanMutateCanTransfer}); + + BEAST_EXPECT(mptAlice.checkTransferFee(100)); + + // Clear MPTCanTransfer and transfer fee is removed + mptAlice.set( + {.account = alice, .mutableFlags = tfMPTClearCanTransfer}); + BEAST_EXPECT(!mptAlice.isTransferFeePresent()); + + // Can still set transfer fee to zero, although it is already zero + mptAlice.set({.account = alice, .transferFee = 0}); + + // TransferFee field is still not present + BEAST_EXPECT(!mptAlice.isTransferFeePresent()); + } + } + + void + testMutateCanClawback(FeatureBitset features) + { + testcase("Mutate MPTCanClawback"); + + using namespace test::jtx; + Env env(*this, features); + Account const alice{"alice"}; + Account const bob{"bob"}; + + MPTTester mptAlice(env, alice, {.holders = {bob}}); + + mptAlice.create( + {.ownerCount = 1, + .holderCount = 0, + .mutableFlags = tfMPTCanMutateCanClawback}); + + // Bob creates an MPToken + mptAlice.authorize({.account = bob}); + + // Alice pays bob 100 tokens + mptAlice.pay(alice, bob, 100); + + // MPTCanClawback is not enabled + mptAlice.claw(alice, bob, 1, tecNO_PERMISSION); + + // Enable MPTCanClawback + mptAlice.set({.account = alice, .mutableFlags = tfMPTSetCanClawback}); + + // Can clawback now + mptAlice.claw(alice, bob, 1); + + // Clear MPTCanClawback + mptAlice.set({.account = alice, .mutableFlags = tfMPTClearCanClawback}); + + // Can not clawback + mptAlice.claw(alice, bob, 1, tecNO_PERMISSION); + } + public: void run() override @@ -2747,39 +3625,39 @@ public: // MPTokenIssuanceCreate testCreateValidation(all - featureSingleAssetVault); - testCreateValidation( - (all | featureSingleAssetVault) - featurePermissionedDomains); - testCreateValidation(all | featureSingleAssetVault); + testCreateValidation(all - featurePermissionedDomains); + testCreateValidation(all); testCreateEnabled(all - featureSingleAssetVault); - testCreateEnabled(all | featureSingleAssetVault); + testCreateEnabled(all); // MPTokenIssuanceDestroy testDestroyValidation(all - featureSingleAssetVault); - testDestroyValidation(all | featureSingleAssetVault); + testDestroyValidation(all); testDestroyEnabled(all - featureSingleAssetVault); - testDestroyEnabled(all | featureSingleAssetVault); + testDestroyEnabled(all); // MPTokenAuthorize testAuthorizeValidation(all - featureSingleAssetVault); - testAuthorizeValidation(all | featureSingleAssetVault); + testAuthorizeValidation(all); testAuthorizeEnabled(all - featureSingleAssetVault); - testAuthorizeEnabled(all | featureSingleAssetVault); + testAuthorizeEnabled(all); // MPTokenIssuanceSet + testSetValidation(all - featureSingleAssetVault - featureDynamicMPT); testSetValidation(all - featureSingleAssetVault); - testSetValidation( - (all | featureSingleAssetVault) - featurePermissionedDomains); - testSetValidation(all | featureSingleAssetVault); + testSetValidation(all - featureDynamicMPT); + testSetValidation(all - featurePermissionedDomains); + testSetValidation(all); testSetEnabled(all - featureSingleAssetVault); - testSetEnabled(all | featureSingleAssetVault); + testSetEnabled(all); // MPT clawback testClawbackValidation(all); testClawback(all); // Test Direct Payment - testPayment(all | featureSingleAssetVault); + testPayment(all); testDepositPreauth(all); testDepositPreauth(all - featureCredentials); @@ -2794,6 +3672,16 @@ public: // Test helpers testHelperFunctions(); + + // Dynamic MPT + testInvalidCreateDynamic(all); + testInvalidSetDynamic(all); + testMutateMPT(all); + testMutateCanLock(all); + testMutateRequireAuth(all); + testMutateCanEscrow(all); + testMutateCanTransfer(all); + testMutateCanClawback(all); } }; diff --git a/src/test/jtx/impl/mpt.cpp b/src/test/jtx/impl/mpt.cpp index 9f7a611feb..f35b1b1ebb 100644 --- a/src/test/jtx/impl/mpt.cpp +++ b/src/test/jtx/impl/mpt.cpp @@ -102,6 +102,8 @@ MPTTester::create(MPTCreate const& arg) jv[sfMaximumAmount] = std::to_string(*arg.maxAmt); if (arg.domainID) jv[sfDomainID] = to_string(*arg.domainID); + if (arg.mutableFlags) + jv[sfMutableFlags] = *arg.mutableFlags; if (submit(arg, jv) != tesSUCCESS) { // Verify issuance doesn't exist @@ -240,19 +242,59 @@ MPTTester::set(MPTSet const& arg) jv[sfDelegate] = arg.delegate->human(); if (arg.domainID) jv[sfDomainID] = to_string(*arg.domainID); - if (submit(arg, jv) == tesSUCCESS && arg.flags.value_or(0)) + if (arg.mutableFlags) + jv[sfMutableFlags] = *arg.mutableFlags; + if (arg.transferFee) + jv[sfTransferFee] = *arg.transferFee; + if (arg.metadata) + jv[sfMPTokenMetadata] = strHex(*arg.metadata); + if (submit(arg, jv) == tesSUCCESS && (arg.flags || arg.mutableFlags)) { auto require = [&](std::optional const& holder, bool unchanged) { auto flags = getFlags(holder); if (!unchanged) { - if (*arg.flags & tfMPTLock) - flags |= lsfMPTLocked; - else if (*arg.flags & tfMPTUnlock) - flags &= ~lsfMPTLocked; - else - Throw("Invalid flags"); + if (arg.flags) + { + if (*arg.flags & tfMPTLock) + flags |= lsfMPTLocked; + else if (*arg.flags & tfMPTUnlock) + flags &= ~lsfMPTLocked; + } + + if (arg.mutableFlags) + { + if (*arg.mutableFlags & tfMPTSetCanLock) + flags |= lsfMPTCanLock; + else if (*arg.mutableFlags & tfMPTClearCanLock) + flags &= ~lsfMPTCanLock; + + if (*arg.mutableFlags & tfMPTSetRequireAuth) + flags |= lsfMPTRequireAuth; + else if (*arg.mutableFlags & tfMPTClearRequireAuth) + flags &= ~lsfMPTRequireAuth; + + if (*arg.mutableFlags & tfMPTSetCanEscrow) + flags |= lsfMPTCanEscrow; + else if (*arg.mutableFlags & tfMPTClearCanEscrow) + flags &= ~lsfMPTCanEscrow; + + if (*arg.mutableFlags & tfMPTSetCanClawback) + flags |= lsfMPTCanClawback; + else if (*arg.mutableFlags & tfMPTClearCanClawback) + flags &= ~lsfMPTCanClawback; + + if (*arg.mutableFlags & tfMPTSetCanTrade) + flags |= lsfMPTCanTrade; + else if (*arg.mutableFlags & tfMPTClearCanTrade) + flags &= ~lsfMPTCanTrade; + + if (*arg.mutableFlags & tfMPTSetCanTransfer) + flags |= lsfMPTCanTransfer; + else if (*arg.mutableFlags & tfMPTClearCanTransfer) + flags &= ~lsfMPTCanTransfer; + } } env_.require(mptflags(*this, flags, holder)); }; @@ -313,6 +355,43 @@ MPTTester::checkFlags( return expectedFlags == getFlags(holder); } +[[nodiscard]] bool +MPTTester::checkMetadata(std::string const& metadata) const +{ + return forObject([&](SLEP const& sle) -> bool { + if (sle->isFieldPresent(sfMPTokenMetadata)) + return strHex(sle->getFieldVL(sfMPTokenMetadata)) == + strHex(metadata); + return false; + }); +} + +[[nodiscard]] bool +MPTTester::isMetadataPresent() const +{ + return forObject([&](SLEP const& sle) -> bool { + return sle->isFieldPresent(sfMPTokenMetadata); + }); +} + +[[nodiscard]] bool +MPTTester::checkTransferFee(std::uint16_t transferFee) const +{ + return forObject([&](SLEP const& sle) -> bool { + if (sle->isFieldPresent(sfTransferFee)) + return sle->getFieldU16(sfTransferFee) == transferFee; + return false; + }); +} + +[[nodiscard]] bool +MPTTester::isTransferFeePresent() const +{ + return forObject([&](SLEP const& sle) -> bool { + return sle->isFieldPresent(sfTransferFee); + }); +} + void MPTTester::pay( Account const& src, diff --git a/src/test/jtx/mpt.h b/src/test/jtx/mpt.h index 4756ca723d..2eacac68ec 100644 --- a/src/test/jtx/mpt.h +++ b/src/test/jtx/mpt.h @@ -106,6 +106,7 @@ struct MPTCreate std::optional holderCount = std::nullopt; bool fund = true; std::optional flags = {0}; + std::optional mutableFlags = std::nullopt; std::optional domainID = std::nullopt; std::optional err = std::nullopt; }; @@ -139,6 +140,9 @@ struct MPTSet std::optional ownerCount = std::nullopt; std::optional holderCount = std::nullopt; std::optional flags = std::nullopt; + std::optional mutableFlags = std::nullopt; + std::optional transferFee = std::nullopt; + std::optional metadata = std::nullopt; std::optional delegate = std::nullopt; std::optional domainID = std::nullopt; std::optional err = std::nullopt; @@ -182,6 +186,18 @@ public: uint32_t const expectedFlags, std::optional const& holder = std::nullopt) const; + [[nodiscard]] bool + checkMetadata(std::string const& metadata) const; + + [[nodiscard]] bool + isMetadataPresent() const; + + [[nodiscard]] bool + checkTransferFee(std::uint16_t transferFee) const; + + [[nodiscard]] bool + isTransferFeePresent() const; + Account const& issuer() const { diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp index da3b57c8fe..6a6e598f42 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.cpp @@ -36,9 +36,17 @@ MPTokenIssuanceCreate::preflight(PreflightContext const& ctx) ctx.rules.enabled(featureSingleAssetVault))) return temDISABLED; + if (ctx.tx.isFieldPresent(sfMutableFlags) && + !ctx.rules.enabled(featureDynamicMPT)) + return temDISABLED; + if (auto const ret = preflight1(ctx); !isTesSuccess(ret)) return ret; + if (auto const mutableFlags = ctx.tx[~sfMutableFlags]; mutableFlags && + (!*mutableFlags || *mutableFlags & tfMPTokenIssuanceCreateMutableMask)) + return temINVALID_FLAG; + if (ctx.tx.getFlags() & tfMPTokenIssuanceCreateMask) return temINVALID_FLAG; @@ -132,6 +140,9 @@ MPTokenIssuanceCreate::create( if (args.domainId) (*mptIssuance)[sfDomainID] = *args.domainId; + if (args.mutableFlags) + (*mptIssuance)[sfMutableFlags] = *args.mutableFlags; + view.insert(mptIssuance); } @@ -158,6 +169,7 @@ MPTokenIssuanceCreate::doApply() .transferFee = tx[~sfTransferFee], .metadata = tx[~sfMPTokenMetadata], .domainId = tx[~sfDomainID], + .mutableFlags = tx[~sfMutableFlags], }); return result ? tesSUCCESS : result.error(); } diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h index ea01908dff..0527b9602f 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceCreate.h @@ -38,6 +38,7 @@ struct MPTCreateArgs std::optional transferFee{}; std::optional const& metadata{}; std::optional domainId{}; + std::optional mutableFlags{}; }; class MPTokenIssuanceCreate : public Transactor diff --git a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp index e05862af37..83b771c705 100644 --- a/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp +++ b/src/xrpld/app/tx/detail/MPTokenIssuanceSet.cpp @@ -26,6 +26,24 @@ namespace ripple { +// Maps set/clear mutable flags in an MPTokenIssuanceSet transaction to the +// corresponding ledger mutable flags that control whether the change is +// allowed. +struct MPTMutabilityFlags +{ + std::uint32_t setFlag; + std::uint32_t clearFlag; + std::uint32_t canMutateFlag; +}; + +static constexpr std::array mptMutabilityFlags = { + {{tfMPTSetCanLock, tfMPTClearCanLock, lsfMPTCanMutateCanLock}, + {tfMPTSetRequireAuth, tfMPTClearRequireAuth, lsfMPTCanMutateRequireAuth}, + {tfMPTSetCanEscrow, tfMPTClearCanEscrow, lsfMPTCanMutateCanEscrow}, + {tfMPTSetCanTrade, tfMPTClearCanTrade, lsfMPTCanMutateCanTrade}, + {tfMPTSetCanTransfer, tfMPTClearCanTransfer, lsfMPTCanMutateCanTransfer}, + {tfMPTSetCanClawback, tfMPTClearCanClawback, lsfMPTCanMutateCanClawback}}}; + NotTEC MPTokenIssuanceSet::preflight(PreflightContext const& ctx) { @@ -37,6 +55,14 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) ctx.rules.enabled(featureSingleAssetVault))) return temDISABLED; + auto const mutableFlags = ctx.tx[~sfMutableFlags]; + auto const metadata = ctx.tx[~sfMPTokenMetadata]; + auto const transferFee = ctx.tx[~sfTransferFee]; + auto const isMutate = mutableFlags || metadata || transferFee; + + if (isMutate && !ctx.rules.enabled(featureDynamicMPT)) + return temDISABLED; + if (ctx.tx.isFieldPresent(sfDomainID) && ctx.tx.isFieldPresent(sfHolder)) return temMALFORMED; @@ -57,13 +83,54 @@ MPTokenIssuanceSet::preflight(PreflightContext const& ctx) if (holderID && accountID == holderID) return temMALFORMED; - if (ctx.rules.enabled(featureSingleAssetVault)) + if (ctx.rules.enabled(featureSingleAssetVault) || + ctx.rules.enabled(featureDynamicMPT)) { // Is this transaction actually changing anything ? - if (txFlags == 0 && !ctx.tx.isFieldPresent(sfDomainID)) + if (txFlags == 0 && !ctx.tx.isFieldPresent(sfDomainID) && !isMutate) return temMALFORMED; } + if (ctx.rules.enabled(featureDynamicMPT)) + { + // Holder field is not allowed when mutating MPTokenIssuance + if (isMutate && holderID) + return temMALFORMED; + + // Can not set flags when mutating MPTokenIssuance + if (isMutate && (txFlags & tfUniversalMask)) + return temMALFORMED; + + if (transferFee && *transferFee > maxTransferFee) + return temBAD_TRANSFER_FEE; + + if (metadata && metadata->length() > maxMPTokenMetadataLength) + return temMALFORMED; + + if (mutableFlags) + { + if (!*mutableFlags || + (*mutableFlags & tfMPTokenIssuanceSetMutableMask)) + return temINVALID_FLAG; + + // Can not set and clear the same flag + if (std::any_of( + mptMutabilityFlags.begin(), + mptMutabilityFlags.end(), + [mutableFlags](auto const& f) { + return (*mutableFlags & f.setFlag) && + (*mutableFlags & f.clearFlag); + })) + return temINVALID_FLAG; + + // Trying to set a non-zero TransferFee and clear MPTCanTransfer + // in the same transaction is not allowed. + if (transferFee.value_or(0) && + (*mutableFlags & tfMPTClearCanTransfer)) + return temMALFORMED; + } + } + return preflight2(ctx); } @@ -116,7 +183,8 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) if (!sleMptIssuance->isFlag(lsfMPTCanLock)) { // For readability two separate `if` rather than `||` of two conditions - if (!ctx.view.rules().enabled(featureSingleAssetVault)) + if (!ctx.view.rules().enabled(featureSingleAssetVault) && + !ctx.view.rules().enabled(featureDynamicMPT)) return tecNO_PERMISSION; else if (ctx.tx.isFlag(tfMPTLock) || ctx.tx.isFlag(tfMPTUnlock)) return tecNO_PERMISSION; @@ -152,6 +220,44 @@ MPTokenIssuanceSet::preclaim(PreclaimContext const& ctx) } } + // sfMutableFlags is soeDEFAULT, defaulting to 0 if not specified on + // the ledger. + auto const currentMutableFlags = + sleMptIssuance->getFieldU32(sfMutableFlags); + + auto isMutableFlag = [&](std::uint32_t mutableFlag) -> bool { + return currentMutableFlags & mutableFlag; + }; + + if (auto const mutableFlags = ctx.tx[~sfMutableFlags]) + { + if (std::any_of( + mptMutabilityFlags.begin(), + mptMutabilityFlags.end(), + [mutableFlags, &isMutableFlag](auto const& f) { + return !isMutableFlag(f.canMutateFlag) && + ((*mutableFlags & (f.setFlag | f.clearFlag))); + })) + return tecNO_PERMISSION; + } + + if (!isMutableFlag(lsfMPTCanMutateMetadata) && + ctx.tx.isFieldPresent(sfMPTokenMetadata)) + return tecNO_PERMISSION; + + if (auto const fee = ctx.tx[~sfTransferFee]) + { + // A non-zero TransferFee is only valid if the lsfMPTCanTransfer flag + // was previously enabled (at issuance or via a prior mutation). Setting + // it by tfMPTSetCanTransfer in the current transaction does not meet + // this requirement. + if (fee > 0u && !sleMptIssuance->isFlag(lsfMPTCanTransfer)) + return tecNO_PERMISSION; + + if (!isMutableFlag(lsfMPTCanMutateTransferFee)) + return tecNO_PERMISSION; + } + return tesSUCCESS; } @@ -180,9 +286,47 @@ MPTokenIssuanceSet::doApply() else if (txFlags & tfMPTUnlock) flagsOut &= ~lsfMPTLocked; + if (auto const mutableFlags = ctx_.tx[~sfMutableFlags].value_or(0)) + { + for (auto const& f : mptMutabilityFlags) + { + if (mutableFlags & f.setFlag) + flagsOut |= f.canMutateFlag; + else if (mutableFlags & f.clearFlag) + flagsOut &= ~f.canMutateFlag; + } + + if (mutableFlags & tfMPTClearCanTransfer) + { + // If the lsfMPTCanTransfer flag is being cleared, then also clear + // the TransferFee field. + sle->makeFieldAbsent(sfTransferFee); + } + } + if (flagsIn != flagsOut) sle->setFieldU32(sfFlags, flagsOut); + if (auto const transferFee = ctx_.tx[~sfTransferFee]) + { + // TransferFee uses soeDEFAULT style: + // - If the field is absent, it is interpreted as 0. + // - If the field is present, it must be non-zero. + // Therefore, when TransferFee is 0, the field should be removed. + if (transferFee == 0) + sle->makeFieldAbsent(sfTransferFee); + else + sle->setFieldU16(sfTransferFee, *transferFee); + } + + if (auto const metadata = ctx_.tx[~sfMPTokenMetadata]) + { + if (metadata->empty()) + sle->makeFieldAbsent(sfMPTokenMetadata); + else + sle->setFieldVL(sfMPTokenMetadata, *metadata); + } + if (domainID) { // This is enforced in preflight. From 17a2606591cf4f45b03b128630895ebef66b42bd Mon Sep 17 00:00:00 2001 From: Vito Tumas <5780819+Tapanito@users.noreply.github.com> Date: Tue, 16 Sep 2025 11:51:55 +0200 Subject: [PATCH 65/66] Bugfix: Adds graceful peer disconnection (#5669) The XRPL establishes connections in three stages: first a TCP connection, then a TLS/SSL handshake to secure the connection, and finally an upgrade to the bespoke XRP Ledger peer-to-peer protocol. During connection termination, xrpld directly closes the TCP connection, bypassing the TLS/SSL shutdown handshake. This makes peer disconnection diagnostics more difficult - abrupt TCP termination appears as if the peer crashed rather than disconnected gracefully. This change refactors the connection lifecycle with the following changes: - Enhanced outgoing connection logic with granular timeouts for each connection stage (TCP, TLS, XRPL handshake) to improve diagnostic capabilities - Updated both PeerImp and ConnectAttempt to use proper asynchronous TLS shutdown procedures for graceful connection termination --- src/xrpld/overlay/detail/ConnectAttempt.cpp | 488 ++++++++++++++------ src/xrpld/overlay/detail/ConnectAttempt.h | 208 ++++++++- src/xrpld/overlay/detail/PeerImp.cpp | 397 +++++++++------- src/xrpld/overlay/detail/PeerImp.h | 208 ++++++++- 4 files changed, 966 insertions(+), 335 deletions(-) diff --git a/src/xrpld/overlay/detail/ConnectAttempt.cpp b/src/xrpld/overlay/detail/ConnectAttempt.cpp index 397ac06ba6..c1bc4bb069 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.cpp +++ b/src/xrpld/overlay/detail/ConnectAttempt.cpp @@ -24,6 +24,8 @@ #include +#include + namespace ripple { ConnectAttempt::ConnectAttempt( @@ -45,6 +47,7 @@ ConnectAttempt::ConnectAttempt( , usage_(usage) , strand_(boost::asio::make_strand(io_context)) , timer_(io_context) + , stepTimer_(io_context) , stream_ptr_(std::make_unique( socket_type(std::forward(io_context)), *context)) @@ -52,14 +55,14 @@ ConnectAttempt::ConnectAttempt( , stream_(*stream_ptr_) , slot_(slot) { - JLOG(journal_.debug()) << "Connect " << remote_endpoint; } ConnectAttempt::~ConnectAttempt() { + // slot_ will be null if we successfully connected + // and transferred ownership to a PeerImp if (slot_ != nullptr) overlay_.peerFinder().on_closed(slot_); - JLOG(journal_.trace()) << "~ConnectAttempt"; } void @@ -68,16 +71,29 @@ ConnectAttempt::stop() if (!strand_.running_in_this_thread()) return boost::asio::post( strand_, std::bind(&ConnectAttempt::stop, shared_from_this())); - if (socket_.is_open()) - { - JLOG(journal_.debug()) << "Stop"; - } - close(); + + if (!socket_.is_open()) + return; + + JLOG(journal_.debug()) << "stop: Stop"; + + shutdown(); } void ConnectAttempt::run() { + if (!strand_.running_in_this_thread()) + return boost::asio::post( + strand_, std::bind(&ConnectAttempt::run, shared_from_this())); + + JLOG(journal_.debug()) << "run: connecting to " << remote_endpoint_; + + ioPending_ = true; + + // Allow up to connectTimeout_ seconds to establish remote peer connection + setTimer(ConnectionStep::TcpConnect); + stream_.next_layer().async_connect( remote_endpoint_, boost::asio::bind_executor( @@ -90,61 +106,177 @@ ConnectAttempt::run() //------------------------------------------------------------------------------ +void +ConnectAttempt::shutdown() +{ + XRPL_ASSERT( + strand_.running_in_this_thread(), + "ripple::ConnectAttempt::shutdown: strand in this thread"); + + if (!socket_.is_open()) + return; + + shutdown_ = true; + boost::beast::get_lowest_layer(stream_).cancel(); + + tryAsyncShutdown(); +} + +void +ConnectAttempt::tryAsyncShutdown() +{ + XRPL_ASSERT( + strand_.running_in_this_thread(), + "ripple::ConnectAttempt::tryAsyncShutdown : strand in this thread"); + + if (!shutdown_ || currentStep_ == ConnectionStep::ShutdownStarted) + return; + + if (ioPending_) + return; + + // gracefully shutdown the SSL socket, performing a shutdown handshake + if (currentStep_ != ConnectionStep::TcpConnect && + currentStep_ != ConnectionStep::TlsHandshake) + { + setTimer(ConnectionStep::ShutdownStarted); + return stream_.async_shutdown(bind_executor( + strand_, + std::bind( + &ConnectAttempt::onShutdown, + shared_from_this(), + std::placeholders::_1))); + } + + close(); +} + +void +ConnectAttempt::onShutdown(error_code ec) +{ + cancelTimer(); + + if (ec) + { + // - eof: the stream was cleanly closed + // - operation_aborted: an expired timer (slow shutdown) + // - stream_truncated: the tcp connection closed (no handshake) it could + // occur if a peer does not perform a graceful disconnect + // - broken_pipe: the peer is gone + // - application data after close notify: benign SSL shutdown condition + bool shouldLog = + (ec != boost::asio::error::eof && + ec != boost::asio::error::operation_aborted && + ec.message().find("application data after close notify") == + std::string::npos); + + if (shouldLog) + { + JLOG(journal_.debug()) << "onShutdown: " << ec.message(); + } + } + + close(); +} + void ConnectAttempt::close() { XRPL_ASSERT( strand_.running_in_this_thread(), "ripple::ConnectAttempt::close : strand in this thread"); - if (socket_.is_open()) - { - try - { - timer_.cancel(); - socket_.close(); - } - catch (boost::system::system_error const&) - { - // ignored - } + if (!socket_.is_open()) + return; - JLOG(journal_.debug()) << "Closed"; - } + cancelTimer(); + + error_code ec; + socket_.close(ec); } void ConnectAttempt::fail(std::string const& reason) { JLOG(journal_.debug()) << reason; - close(); + shutdown(); } void ConnectAttempt::fail(std::string const& name, error_code ec) { JLOG(journal_.debug()) << name << ": " << ec.message(); - close(); + shutdown(); } void -ConnectAttempt::setTimer() +ConnectAttempt::setTimer(ConnectionStep step) { - try + currentStep_ = step; + + // Set global timer (only if not already set) + if (timer_.expiry() == std::chrono::steady_clock::time_point{}) { - timer_.expires_after(std::chrono::seconds(15)); - } - catch (boost::system::system_error const& e) - { - JLOG(journal_.error()) << "setTimer: " << e.code(); - return; + try + { + timer_.expires_after(connectTimeout); + timer_.async_wait(boost::asio::bind_executor( + strand_, + std::bind( + &ConnectAttempt::onTimer, + shared_from_this(), + std::placeholders::_1))); + } + catch (std::exception const& ex) + { + JLOG(journal_.error()) << "setTimer (global): " << ex.what(); + return close(); + } } - timer_.async_wait(boost::asio::bind_executor( - strand_, - std::bind( - &ConnectAttempt::onTimer, - shared_from_this(), - std::placeholders::_1))); + // Set step-specific timer + try + { + std::chrono::seconds stepTimeout; + switch (step) + { + case ConnectionStep::TcpConnect: + stepTimeout = StepTimeouts::tcpConnect; + break; + case ConnectionStep::TlsHandshake: + stepTimeout = StepTimeouts::tlsHandshake; + break; + case ConnectionStep::HttpWrite: + stepTimeout = StepTimeouts::httpWrite; + break; + case ConnectionStep::HttpRead: + stepTimeout = StepTimeouts::httpRead; + break; + case ConnectionStep::ShutdownStarted: + stepTimeout = StepTimeouts::tlsShutdown; + break; + case ConnectionStep::Complete: + case ConnectionStep::Init: + return; // No timer needed for init or complete step + } + + // call to expires_after cancels previous timer + stepTimer_.expires_after(stepTimeout); + stepTimer_.async_wait(boost::asio::bind_executor( + strand_, + std::bind( + &ConnectAttempt::onTimer, + shared_from_this(), + std::placeholders::_1))); + + JLOG(journal_.trace()) << "setTimer: " << stepToString(step) + << " timeout=" << stepTimeout.count() << "s"; + } + catch (std::exception const& ex) + { + JLOG(journal_.error()) + << "setTimer (step " << stepToString(step) << "): " << ex.what(); + return close(); + } } void @@ -153,6 +285,7 @@ ConnectAttempt::cancelTimer() try { timer_.cancel(); + stepTimer_.cancel(); } catch (boost::system::system_error const&) { @@ -165,34 +298,69 @@ ConnectAttempt::onTimer(error_code ec) { if (!socket_.is_open()) return; - if (ec == boost::asio::error::operation_aborted) - return; + if (ec) { + // do not initiate shutdown, timers are frequently cancelled + if (ec == boost::asio::error::operation_aborted) + return; + // This should never happen JLOG(journal_.error()) << "onTimer: " << ec.message(); return close(); } - fail("Timeout"); + + // Determine which timer expired by checking their expiry times + auto const now = std::chrono::steady_clock::now(); + bool globalExpired = (timer_.expiry() <= now); + bool stepExpired = (stepTimer_.expiry() <= now); + + if (globalExpired) + { + JLOG(journal_.debug()) + << "onTimer: Global timeout; step: " << stepToString(currentStep_); + } + else if (stepExpired) + { + JLOG(journal_.debug()) + << "onTimer: Step timeout; step: " << stepToString(currentStep_); + } + else + { + JLOG(journal_.warn()) << "onTimer: Unexpected timer callback"; + } + + close(); } void ConnectAttempt::onConnect(error_code ec) { - cancelTimer(); + ioPending_ = false; - if (ec == boost::asio::error::operation_aborted) - return; - endpoint_type local_endpoint; - if (!ec) - local_endpoint = socket_.local_endpoint(ec); if (ec) + { + if (ec == boost::asio::error::operation_aborted) + return tryAsyncShutdown(); + return fail("onConnect", ec); + } + if (!socket_.is_open()) return; - JLOG(journal_.trace()) << "onConnect"; - setTimer(); + // check if connection has really been established + socket_.local_endpoint(ec); + if (ec) + return fail("onConnect", ec); + + if (shutdown_) + return tryAsyncShutdown(); + + ioPending_ = true; + + setTimer(ConnectionStep::TlsHandshake); + stream_.set_verify_mode(boost::asio::ssl::verify_none); stream_.async_handshake( boost::asio::ssl::stream_base::client, @@ -207,25 +375,30 @@ ConnectAttempt::onConnect(error_code ec) void ConnectAttempt::onHandshake(error_code ec) { - cancelTimer(); - if (!socket_.is_open()) - return; - if (ec == boost::asio::error::operation_aborted) - return; - endpoint_type local_endpoint; - if (!ec) - local_endpoint = socket_.local_endpoint(ec); + ioPending_ = false; + + if (ec) + { + if (ec == boost::asio::error::operation_aborted) + return tryAsyncShutdown(); + + return fail("onHandshake", ec); + } + + auto const local_endpoint = socket_.local_endpoint(ec); if (ec) return fail("onHandshake", ec); - JLOG(journal_.trace()) << "onHandshake"; + setTimer(ConnectionStep::HttpWrite); + + // check if we connected to ourselves if (!overlay_.peerFinder().onConnected( slot_, beast::IPAddressConversion::from_asio(local_endpoint))) - return fail("Duplicate connection"); + return fail("Self connection"); auto const sharedValue = makeSharedValue(*stream_ptr_, journal_); if (!sharedValue) - return close(); // makeSharedValue logs + return shutdown(); // makeSharedValue logs req_ = makeRequest( !overlay_.peerFinder().config().peerPrivate, @@ -242,7 +415,11 @@ ConnectAttempt::onHandshake(error_code ec) remote_endpoint_.address(), app_); - setTimer(); + if (shutdown_) + return tryAsyncShutdown(); + + ioPending_ = true; + boost::beast::http::async_write( stream_, req_, @@ -257,13 +434,23 @@ ConnectAttempt::onHandshake(error_code ec) void ConnectAttempt::onWrite(error_code ec) { - cancelTimer(); - if (!socket_.is_open()) - return; - if (ec == boost::asio::error::operation_aborted) - return; + ioPending_ = false; + if (ec) + { + if (ec == boost::asio::error::operation_aborted) + return tryAsyncShutdown(); + return fail("onWrite", ec); + } + + if (shutdown_) + return tryAsyncShutdown(); + + ioPending_ = true; + + setTimer(ConnectionStep::HttpRead); + boost::beast::http::async_read( stream_, read_buf_, @@ -280,39 +467,27 @@ void ConnectAttempt::onRead(error_code ec) { cancelTimer(); + ioPending_ = false; + currentStep_ = ConnectionStep::Complete; - if (!socket_.is_open()) - return; - if (ec == boost::asio::error::operation_aborted) - return; - if (ec == boost::asio::error::eof) - { - JLOG(journal_.info()) << "EOF"; - setTimer(); - return stream_.async_shutdown(boost::asio::bind_executor( - strand_, - std::bind( - &ConnectAttempt::onShutdown, - shared_from_this(), - std::placeholders::_1))); - } if (ec) - return fail("onRead", ec); - processResponse(); -} - -void -ConnectAttempt::onShutdown(error_code ec) -{ - cancelTimer(); - if (!ec) { - JLOG(journal_.error()) << "onShutdown: expected error condition"; - return close(); + if (ec == boost::asio::error::eof) + { + JLOG(journal_.debug()) << "EOF"; + return shutdown(); + } + + if (ec == boost::asio::error::operation_aborted) + return tryAsyncShutdown(); + + return fail("onRead", ec); } - if (ec != boost::asio::error::eof) - return fail("onShutdown", ec); - close(); + + if (shutdown_) + return tryAsyncShutdown(); + + processResponse(); } //-------------------------------------------------------------------------- @@ -320,48 +495,69 @@ ConnectAttempt::onShutdown(error_code ec) void ConnectAttempt::processResponse() { - if (response_.result() == boost::beast::http::status::service_unavailable) - { - Json::Value json; - Json::Reader r; - std::string s; - s.reserve(boost::asio::buffer_size(response_.body().data())); - for (auto const buffer : response_.body().data()) - s.append( - static_cast(buffer.data()), - boost::asio::buffer_size(buffer)); - auto const success = r.parse(s, json); - if (success) - { - if (json.isObject() && json.isMember("peer-ips")) - { - Json::Value const& ips = json["peer-ips"]; - if (ips.isArray()) - { - std::vector eps; - eps.reserve(ips.size()); - for (auto const& v : ips) - { - if (v.isString()) - { - error_code ec; - auto const ep = parse_endpoint(v.asString(), ec); - if (!ec) - eps.push_back(ep); - } - } - overlay_.peerFinder().onRedirects(remote_endpoint_, eps); - } - } - } - } - if (!OverlayImpl::isPeerUpgrade(response_)) { - JLOG(journal_.info()) - << "Unable to upgrade to peer protocol: " << response_.result() - << " (" << response_.reason() << ")"; - return close(); + // A peer may respond with service_unavailable and a list of alternative + // peers to connect to, a differing status code is unexpected + if (response_.result() != + boost::beast::http::status::service_unavailable) + { + JLOG(journal_.warn()) + << "Unable to upgrade to peer protocol: " << response_.result() + << " (" << response_.reason() << ")"; + return shutdown(); + } + + // Parse response body to determine if this is a redirect or other + // service unavailable + std::string responseBody; + responseBody.reserve(boost::asio::buffer_size(response_.body().data())); + for (auto const buffer : response_.body().data()) + responseBody.append( + static_cast(buffer.data()), + boost::asio::buffer_size(buffer)); + + Json::Value json; + Json::Reader reader; + auto const isValidJson = reader.parse(responseBody, json); + + // Check if this is a redirect response (contains peer-ips field) + auto const isRedirect = + isValidJson && json.isObject() && json.isMember("peer-ips"); + + if (!isRedirect) + { + JLOG(journal_.warn()) + << "processResponse: " << remote_endpoint_ + << " failed to upgrade to peer protocol: " << response_.result() + << " (" << response_.reason() << ")"; + + return shutdown(); + } + + Json::Value const& peerIps = json["peer-ips"]; + if (!peerIps.isArray()) + return fail("processResponse: invalid peer-ips format"); + + // Extract and validate peer endpoints + std::vector redirectEndpoints; + redirectEndpoints.reserve(peerIps.size()); + + for (auto const& ipValue : peerIps) + { + if (!ipValue.isString()) + continue; + + error_code ec; + auto const endpoint = parse_endpoint(ipValue.asString(), ec); + if (!ec) + redirectEndpoints.push_back(endpoint); + } + + // Notify PeerFinder about the redirect redirectEndpoints may be empty + overlay_.peerFinder().onRedirects(remote_endpoint_, redirectEndpoints); + + return fail("processResponse: failed to connect to peer: redirected"); } // Just because our peer selected a particular protocol version doesn't @@ -381,11 +577,11 @@ ConnectAttempt::processResponse() auto const sharedValue = makeSharedValue(*stream_ptr_, journal_); if (!sharedValue) - return close(); // makeSharedValue logs + return shutdown(); // makeSharedValue logs try { - auto publicKey = verifyHandshake( + auto const publicKey = verifyHandshake( response_, *sharedValue, overlay_.setup().networkID, @@ -393,11 +589,10 @@ ConnectAttempt::processResponse() remote_endpoint_.address(), app_); - JLOG(journal_.info()) - << "Public Key: " << toBase58(TokenType::NodePublic, publicKey); - JLOG(journal_.debug()) << "Protocol: " << to_string(*negotiatedProtocol); + JLOG(journal_.info()) + << "Public Key: " << toBase58(TokenType::NodePublic, publicKey); auto const member = app_.cluster().member(publicKey); if (member) @@ -405,10 +600,21 @@ ConnectAttempt::processResponse() JLOG(journal_.info()) << "Cluster name: " << *member; } - auto const result = overlay_.peerFinder().activate( - slot_, publicKey, static_cast(member)); + auto const result = + overlay_.peerFinder().activate(slot_, publicKey, !member->empty()); if (result != PeerFinder::Result::success) - return fail("Outbound " + std::string(to_string(result))); + { + std::stringstream ss; + ss << "Outbound Connect Attempt " << remote_endpoint_ << " " + << to_string(result); + return fail(ss.str()); + } + + if (!socket_.is_open()) + return; + + if (shutdown_) + return tryAsyncShutdown(); auto const peer = std::make_shared( app_, diff --git a/src/xrpld/overlay/detail/ConnectAttempt.h b/src/xrpld/overlay/detail/ConnectAttempt.h index febbe88f45..38b9482d9d 100644 --- a/src/xrpld/overlay/detail/ConnectAttempt.h +++ b/src/xrpld/overlay/detail/ConnectAttempt.h @@ -22,90 +22,258 @@ #include +#include + namespace ripple { -/** Manages an outbound connection attempt. */ +/** + * @class ConnectAttempt + * @brief Manages outbound peer connection attempts with comprehensive timeout + * handling + * + * The ConnectAttempt class handles the complete lifecycle of establishing an + * outbound connection to a peer in the XRPL network. It implements a + * sophisticated dual-timer system that provides both global timeout protection + * and per-step timeout diagnostics. + * + * The connection establishment follows these steps: + * 1. **TCP Connect**: Establish basic network connection + * 2. **TLS Handshake**: Negotiate SSL/TLS encryption + * 3. **HTTP Write**: Send peer handshake request + * 4. **HTTP Read**: Receive and validate peer response + * 5. **Complete**: Connection successfully established + * + * Uses a hybrid timeout approach: + * - **Global Timer**: Hard limit (20s) for entire connection process + * - **Step Timers**: Individual timeouts for each connection phase + * + * - All errors result in connection termination + * + * All operations are serialized using boost::asio::strand to ensure thread + * safety. The class is designed to be used exclusively within the ASIO event + * loop. + * + * @note This class should not be used directly. It is managed by OverlayImpl + * as part of the peer discovery and connection management system. + * + */ class ConnectAttempt : public OverlayImpl::Child, public std::enable_shared_from_this { private: using error_code = boost::system::error_code; - using endpoint_type = boost::asio::ip::tcp::endpoint; - using request_type = boost::beast::http::request; - using response_type = boost::beast::http::response; - using socket_type = boost::asio::ip::tcp::socket; using middle_type = boost::beast::tcp_stream; using stream_type = boost::beast::ssl_stream; using shared_context = std::shared_ptr; + /** + * @enum ConnectionStep + * @brief Represents the current phase of the connection establishment + * process + * + * Used for tracking progress and providing detailed timeout diagnostics. + * Each step has its own timeout value defined in StepTimeouts. + */ + enum class ConnectionStep { + Init, // Initial state, nothing started + TcpConnect, // Establishing TCP connection to remote peer + TlsHandshake, // Performing SSL/TLS handshake + HttpWrite, // Sending HTTP upgrade request + HttpRead, // Reading HTTP upgrade response + Complete, // Connection successfully established + ShutdownStarted // Connection shutdown has started + }; + + // A timeout for connection process, greater than all step timeouts + static constexpr std::chrono::seconds connectTimeout{25}; + + /** + * @struct StepTimeouts + * @brief Defines timeout values for each connection step + * + * These timeouts are designed to detect slow individual phases while + * allowing the global timeout to enforce the overall time limit. + */ + struct StepTimeouts + { + // TCP connection timeout + static constexpr std::chrono::seconds tcpConnect{8}; + // SSL handshake timeout + static constexpr std::chrono::seconds tlsHandshake{8}; + // HTTP write timeout + static constexpr std::chrono::seconds httpWrite{3}; + // HTTP read timeout + static constexpr std::chrono::seconds httpRead{3}; + // SSL shutdown timeout + static constexpr std::chrono::seconds tlsShutdown{2}; + }; + + // Core application and networking components Application& app_; - std::uint32_t const id_; + Peer::id_t const id_; beast::WrappedSink sink_; beast::Journal const journal_; endpoint_type remote_endpoint_; Resource::Consumer usage_; + boost::asio::strand strand_; boost::asio::basic_waitable_timer timer_; - std::unique_ptr stream_ptr_; + boost::asio::basic_waitable_timer stepTimer_; + + std::unique_ptr stream_ptr_; // SSL stream (owned) socket_type& socket_; stream_type& stream_; boost::beast::multi_buffer read_buf_; + response_type response_; std::shared_ptr slot_; request_type req_; + bool shutdown_ = false; // Shutdown has been initiated + bool ioPending_ = false; // Async I/O operation in progress + ConnectionStep currentStep_ = ConnectionStep::Init; + public: + /** + * @brief Construct a new ConnectAttempt object + * + * @param app Application context providing configuration and services + * @param io_context ASIO I/O context for async operations + * @param remote_endpoint Target peer endpoint to connect to + * @param usage Resource usage tracker for rate limiting + * @param context Shared SSL context for encryption + * @param id Unique peer identifier for this connection attempt + * @param slot PeerFinder slot representing this connection + * @param journal Logging interface for diagnostics + * @param overlay Parent overlay manager + * + * @note The constructor only initializes the object. Call run() to begin + * the actual connection attempt. + */ ConnectAttempt( Application& app, boost::asio::io_context& io_context, endpoint_type const& remote_endpoint, Resource::Consumer usage, shared_context const& context, - std::uint32_t id, + Peer::id_t id, std::shared_ptr const& slot, beast::Journal journal, OverlayImpl& overlay); ~ConnectAttempt(); + /** + * @brief Stop the connection attempt + * + * This method is thread-safe and can be called from any thread. + */ void stop() override; + /** + * @brief Begin the connection attempt + * + * This method is thread-safe and posts to the strand if needed. + */ void run(); private: + /** + * @brief Set timers for the specified connection step + * + * @param step The connection step to set timers for + * + * Sets both the step-specific timer and the global timer (if not already + * set). + */ void - close(); - void - fail(std::string const& reason); - void - fail(std::string const& name, error_code ec); - void - setTimer(); + setTimer(ConnectionStep step); + + /** + * @brief Cancel both global and step timers + * + * Used during cleanup and when connection completes successfully. + * Exceptions from timer cancellation are safely ignored. + */ void cancelTimer(); + + /** + * @brief Handle timer expiration events + * + * @param ec Error code from timer operation + * + * Determines which timer expired (global vs step) and logs appropriate + * diagnostic information before terminating the connection. + */ void onTimer(error_code ec); + + // Connection phase handlers void - onConnect(error_code ec); + onConnect(error_code ec); // TCP connection completion handler void - onHandshake(error_code ec); + onHandshake(error_code ec); // TLS handshake completion handler void - onWrite(error_code ec); + onWrite(error_code ec); // HTTP write completion handler void - onRead(error_code ec); + onRead(error_code ec); // HTTP read completion handler + + // Error and cleanup handlers void - onShutdown(error_code ec); + fail(std::string const& reason); // Fail with custom reason + void + fail(std::string const& name, error_code ec); // Fail with system error + void + shutdown(); // Initiate graceful shutdown + void + tryAsyncShutdown(); // Attempt async SSL shutdown + void + onShutdown(error_code ec); // SSL shutdown completion handler + void + close(); // Force close socket + + /** + * @brief Process the HTTP upgrade response from peer + * + * Validates the peer's response, extracts protocol information, + * verifies handshake, and either creates a PeerImp or handles + * redirect responses. + */ void processResponse(); + static std::string + stepToString(ConnectionStep step) + { + switch (step) + { + case ConnectionStep::Init: + return "Init"; + case ConnectionStep::TcpConnect: + return "TcpConnect"; + case ConnectionStep::TlsHandshake: + return "TlsHandshake"; + case ConnectionStep::HttpWrite: + return "HttpWrite"; + case ConnectionStep::HttpRead: + return "HttpRead"; + case ConnectionStep::Complete: + return "Complete"; + case ConnectionStep::ShutdownStarted: + return "ShutdownStarted"; + } + return "Unknown"; + }; + template static boost::asio::ip::tcp::endpoint parse_endpoint(std::string const& s, boost::system::error_code& ec) diff --git a/src/xrpld/overlay/detail/PeerImp.cpp b/src/xrpld/overlay/detail/PeerImp.cpp index 2cd9432eb8..93371f42ab 100644 --- a/src/xrpld/overlay/detail/PeerImp.cpp +++ b/src/xrpld/overlay/detail/PeerImp.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -59,6 +60,10 @@ std::chrono::milliseconds constexpr peerHighLatency{300}; /** How often we PING the peer to check for latency and sendq probe */ std::chrono::seconds constexpr peerTimerInterval{60}; + +/** The timeout for a shutdown timer */ +std::chrono::seconds constexpr shutdownTimerInterval{5}; + } // namespace // TODO: Remove this exclusion once unit tests are added after the hotfix @@ -215,23 +220,17 @@ PeerImp::stop() { if (!strand_.running_in_this_thread()) return post(strand_, std::bind(&PeerImp::stop, shared_from_this())); - if (socket_.is_open()) - { - // The rationale for using different severity levels is that - // outbound connections are under our control and may be logged - // at a higher level, but inbound connections are more numerous and - // uncontrolled so to prevent log flooding the severity is reduced. - // - if (inbound_) - { - JLOG(journal_.debug()) << "Stop"; - } - else - { - JLOG(journal_.info()) << "Stop"; - } - } - close(); + + if (!socket_.is_open()) + return; + + // The rationale for using different severity levels is that + // outbound connections are under our control and may be logged + // at a higher level, but inbound connections are more numerous and + // uncontrolled so to prevent log flooding the severity is reduced. + JLOG(journal_.debug()) << "stop: Stop"; + + shutdown(); } //------------------------------------------------------------------------------ @@ -241,11 +240,14 @@ PeerImp::send(std::shared_ptr const& m) { if (!strand_.running_in_this_thread()) return post(strand_, std::bind(&PeerImp::send, shared_from_this(), m)); - if (gracefulClose_) - return; - if (detaching_) + + if (!socket_.is_open()) return; + // we are in progress of closing the connection + if (shutdown_) + return tryAsyncShutdown(); + auto validator = m->getValidatorKey(); if (validator && !squelch_.expireSquelch(*validator)) { @@ -287,6 +289,7 @@ PeerImp::send(std::shared_ptr const& m) if (sendq_size != 0) return; + writePending_ = true; boost::asio::async_write( stream_, boost::asio::buffer( @@ -573,34 +576,21 @@ PeerImp::hasRange(std::uint32_t uMin, std::uint32_t uMax) //------------------------------------------------------------------------------ void -PeerImp::close() +PeerImp::fail(std::string const& name, error_code ec) { XRPL_ASSERT( strand_.running_in_this_thread(), - "ripple::PeerImp::close : strand in this thread"); - if (socket_.is_open()) - { - detaching_ = true; // DEPRECATED - try - { - timer_.cancel(); - socket_.close(); - } - catch (boost::system::system_error const&) - { - // ignored - } + "ripple::PeerImp::fail : strand in this thread"); - overlay_.incPeerDisconnect(); - if (inbound_) - { - JLOG(journal_.debug()) << "Closed"; - } - else - { - JLOG(journal_.info()) << "Closed"; - } - } + if (!socket_.is_open()) + return; + + JLOG(journal_.warn()) << name << " from " + << toBase58(TokenType::NodePublic, publicKey_) + << " at " << remote_address_.to_string() << ": " + << ec.message(); + + shutdown(); } void @@ -613,45 +603,39 @@ PeerImp::fail(std::string const& reason) (void(Peer::*)(std::string const&)) & PeerImp::fail, shared_from_this(), reason)); - if (journal_.active(beast::severities::kWarning) && socket_.is_open()) + + if (!socket_.is_open()) + return; + + // Call to name() locks, log only if the message will be outputed + if (journal_.active(beast::severities::kWarning)) { std::string const n = name(); JLOG(journal_.warn()) << (n.empty() ? remote_address_.to_string() : n) << " failed: " << reason; } - close(); + + shutdown(); } void -PeerImp::fail(std::string const& name, error_code ec) +PeerImp::tryAsyncShutdown() { XRPL_ASSERT( strand_.running_in_this_thread(), - "ripple::PeerImp::fail : strand in this thread"); - if (socket_.is_open()) - { - JLOG(journal_.warn()) - << name << " from " << toBase58(TokenType::NodePublic, publicKey_) - << " at " << remote_address_.to_string() << ": " << ec.message(); - } - close(); -} + "ripple::PeerImp::tryAsyncShutdown : strand in this thread"); -void -PeerImp::gracefulClose() -{ - XRPL_ASSERT( - strand_.running_in_this_thread(), - "ripple::PeerImp::gracefulClose : strand in this thread"); - XRPL_ASSERT( - socket_.is_open(), "ripple::PeerImp::gracefulClose : socket is open"); - XRPL_ASSERT( - !gracefulClose_, - "ripple::PeerImp::gracefulClose : socket is not closing"); - gracefulClose_ = true; - if (send_queue_.size() > 0) + if (!shutdown_ || shutdownStarted_) return; - setTimer(); + + if (readPending_ || writePending_) + return; + + shutdownStarted_ = true; + + setTimer(shutdownTimerInterval); + + // gracefully shutdown the SSL socket, performing a shutdown handshake stream_.async_shutdown(bind_executor( strand_, std::bind( @@ -659,69 +643,125 @@ PeerImp::gracefulClose() } void -PeerImp::setTimer() +PeerImp::shutdown() +{ + XRPL_ASSERT( + strand_.running_in_this_thread(), + "ripple::PeerImp::shutdown: strand in this thread"); + + if (!socket_.is_open() || shutdown_) + return; + + shutdown_ = true; + + boost::beast::get_lowest_layer(stream_).cancel(); + + tryAsyncShutdown(); +} + +void +PeerImp::onShutdown(error_code ec) +{ + cancelTimer(); + if (ec) + { + // - eof: the stream was cleanly closed + // - operation_aborted: an expired timer (slow shutdown) + // - stream_truncated: the tcp connection closed (no handshake) it could + // occur if a peer does not perform a graceful disconnect + // - broken_pipe: the peer is gone + bool shouldLog = + (ec != boost::asio::error::eof && + ec != boost::asio::error::operation_aborted && + ec.message().find("application data after close notify") == + std::string::npos); + + if (shouldLog) + { + JLOG(journal_.debug()) << "onShutdown: " << ec.message(); + } + } + + close(); +} + +void +PeerImp::close() +{ + XRPL_ASSERT( + strand_.running_in_this_thread(), + "ripple::PeerImp::close : strand in this thread"); + + if (!socket_.is_open()) + return; + + cancelTimer(); + + error_code ec; + socket_.close(ec); + + overlay_.incPeerDisconnect(); + + // The rationale for using different severity levels is that + // outbound connections are under our control and may be logged + // at a higher level, but inbound connections are more numerous and + // uncontrolled so to prevent log flooding the severity is reduced. + JLOG((inbound_ ? journal_.debug() : journal_.info())) << "close: Closed"; +} + +//------------------------------------------------------------------------------ + +void +PeerImp::setTimer(std::chrono::seconds interval) { try { - timer_.expires_after(peerTimerInterval); + timer_.expires_after(interval); } - catch (boost::system::system_error const& e) + catch (std::exception const& ex) { - JLOG(journal_.error()) << "setTimer: " << e.code(); - return; + JLOG(journal_.error()) << "setTimer: " << ex.what(); + return shutdown(); } + timer_.async_wait(bind_executor( strand_, std::bind( &PeerImp::onTimer, shared_from_this(), std::placeholders::_1))); } -// convenience for ignoring the error code -void -PeerImp::cancelTimer() -{ - try - { - timer_.cancel(); - } - catch (boost::system::system_error const&) - { - // ignored - } -} - -//------------------------------------------------------------------------------ - -std::string -PeerImp::makePrefix(id_t id) -{ - std::stringstream ss; - ss << "[" << std::setfill('0') << std::setw(3) << id << "] "; - return ss.str(); -} - void PeerImp::onTimer(error_code const& ec) { - if (!socket_.is_open()) - return; + XRPL_ASSERT( + strand_.running_in_this_thread(), + "ripple::PeerImp::onTimer : strand in this thread"); - if (ec == boost::asio::error::operation_aborted) + if (!socket_.is_open()) return; if (ec) { + // do not initiate shutdown, timers are frequently cancelled + if (ec == boost::asio::error::operation_aborted) + return; + // This should never happen JLOG(journal_.error()) << "onTimer: " << ec.message(); return close(); } - if (large_sendq_++ >= Tuning::sendqIntervals) + // the timer expired before the shutdown completed + // force close the connection + if (shutdown_) { - fail("Large send queue"); - return; + JLOG(journal_.debug()) << "onTimer: shutdown timer expired"; + return close(); } + if (large_sendq_++ >= Tuning::sendqIntervals) + return fail("Large send queue"); + if (auto const t = tracking_.load(); !inbound_ && t != Tracking::converged) { clock_type::duration duration; @@ -737,17 +777,13 @@ PeerImp::onTimer(error_code const& ec) (duration > app_.config().MAX_UNKNOWN_TIME))) { overlay_.peerFinder().on_failure(slot_); - fail("Not useful"); - return; + return fail("Not useful"); } } // Already waiting for PONG if (lastPingSeq_) - { - fail("Ping Timeout"); - return; - } + return fail("Ping Timeout"); lastPingTime_ = clock_type::now(); lastPingSeq_ = rand_int(); @@ -758,22 +794,28 @@ PeerImp::onTimer(error_code const& ec) send(std::make_shared(message, protocol::mtPING)); - setTimer(); + setTimer(peerTimerInterval); } void -PeerImp::onShutdown(error_code ec) +PeerImp::cancelTimer() noexcept { - cancelTimer(); - // If we don't get eof then something went wrong - if (!ec) + try { - JLOG(journal_.error()) << "onShutdown: expected error condition"; - return close(); + timer_.cancel(); } - if (ec != boost::asio::error::eof) - return fail("onShutdown", ec); - close(); + catch (std::exception const& ex) + { + JLOG(journal_.error()) << "cancelTimer: " << ex.what(); + } +} + +std::string +PeerImp::makePrefix(id_t id) +{ + std::stringstream ss; + ss << "[" << std::setfill('0') << std::setw(3) << id << "] "; + return ss.str(); } //------------------------------------------------------------------------------ @@ -786,6 +828,10 @@ PeerImp::doAccept() JLOG(journal_.debug()) << "doAccept: " << remote_address_; + // a shutdown was initiated before the handshake, there is nothing to do + if (shutdown_) + return tryAsyncShutdown(); + auto const sharedValue = makeSharedValue(*stream_ptr_, journal_); // This shouldn't fail since we already computed @@ -793,7 +839,7 @@ PeerImp::doAccept() if (!sharedValue) return fail("makeSharedValue: Unexpected failure"); - JLOG(journal_.info()) << "Protocol: " << to_string(protocol_); + JLOG(journal_.debug()) << "Protocol: " << to_string(protocol_); JLOG(journal_.info()) << "Public Key: " << toBase58(TokenType::NodePublic, publicKey_); @@ -836,7 +882,7 @@ PeerImp::doAccept() if (!socket_.is_open()) return; if (ec == boost::asio::error::operation_aborted) - return; + return tryAsyncShutdown(); if (ec) return fail("onWriteResponse", ec); if (write_buffer->size() == bytes_transferred) @@ -865,6 +911,10 @@ PeerImp::domain() const void PeerImp::doProtocolStart() { + // a shutdown was initiated before the handshare, there is nothing to do + if (shutdown_) + return tryAsyncShutdown(); + onReadMessage(error_code(), 0); // Send all the validator lists that have been loaded @@ -896,30 +946,45 @@ PeerImp::doProtocolStart() if (auto m = overlay_.getManifestsMessage()) send(m); - setTimer(); + setTimer(peerTimerInterval); } // Called repeatedly with protocol message data void PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) { + XRPL_ASSERT( + strand_.running_in_this_thread(), + "ripple::PeerImp::onReadMessage : strand in this thread"); + + readPending_ = false; + if (!socket_.is_open()) return; - if (ec == boost::asio::error::operation_aborted) - return; - if (ec == boost::asio::error::eof) - { - JLOG(journal_.info()) << "EOF"; - return gracefulClose(); - } + if (ec) + { + if (ec == boost::asio::error::eof) + { + JLOG(journal_.debug()) << "EOF"; + return shutdown(); + } + + if (ec == boost::asio::error::operation_aborted) + return tryAsyncShutdown(); + return fail("onReadMessage", ec); + } + // we started shutdown, no reason to process further data + if (shutdown_) + return tryAsyncShutdown(); + if (auto stream = journal_.trace()) { - if (bytes_transferred > 0) - stream << "onReadMessage: " << bytes_transferred << " bytes"; - else - stream << "onReadMessage"; + stream << "onReadMessage: " + << (bytes_transferred > 0 + ? to_string(bytes_transferred) + " bytes" + : ""); } metrics_.recv.add_message(bytes_transferred); @@ -941,17 +1006,29 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) 350ms, journal_); - if (ec) - return fail("onReadMessage", ec); if (!socket_.is_open()) return; - if (gracefulClose_) - return; + + // the error_code is produced by invokeProtocolMessage + // it could be due to a bad message + if (ec) + return fail("onReadMessage", ec); + if (bytes_consumed == 0) break; + read_buffer_.consume(bytes_consumed); } + // check if a shutdown was initiated while processing messages + if (shutdown_) + return tryAsyncShutdown(); + + readPending_ = true; + + XRPL_ASSERT( + !shutdownStarted_, "ripple::PeerImp::onReadMessage : shutdown started"); + // Timeout on writes only stream_.async_read_some( read_buffer_.prepare(std::max(Tuning::readBufferBytes, hint)), @@ -967,18 +1044,29 @@ PeerImp::onReadMessage(error_code ec, std::size_t bytes_transferred) void PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) { + XRPL_ASSERT( + strand_.running_in_this_thread(), + "ripple::PeerImp::onWriteMessage : strand in this thread"); + + writePending_ = false; + if (!socket_.is_open()) return; - if (ec == boost::asio::error::operation_aborted) - return; + if (ec) + { + if (ec == boost::asio::error::operation_aborted) + return tryAsyncShutdown(); + return fail("onWriteMessage", ec); + } + if (auto stream = journal_.trace()) { - if (bytes_transferred > 0) - stream << "onWriteMessage: " << bytes_transferred << " bytes"; - else - stream << "onWriteMessage"; + stream << "onWriteMessage: " + << (bytes_transferred > 0 + ? to_string(bytes_transferred) + " bytes" + : ""); } metrics_.sent.add_message(bytes_transferred); @@ -987,8 +1075,17 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) !send_queue_.empty(), "ripple::PeerImp::onWriteMessage : non-empty send buffer"); send_queue_.pop(); + + if (shutdown_) + return tryAsyncShutdown(); + if (!send_queue_.empty()) { + writePending_ = true; + XRPL_ASSERT( + !shutdownStarted_, + "ripple::PeerImp::onWriteMessage : shutdown started"); + // Timeout on writes only return boost::asio::async_write( stream_, @@ -1002,16 +1099,6 @@ PeerImp::onWriteMessage(error_code ec, std::size_t bytes_transferred) std::placeholders::_1, std::placeholders::_2))); } - - if (gracefulClose_) - { - return stream_.async_shutdown(bind_executor( - strand_, - std::bind( - &PeerImp::onShutdown, - shared_from_this(), - std::placeholders::_1))); - } } //------------------------------------------------------------------------------ diff --git a/src/xrpld/overlay/detail/PeerImp.h b/src/xrpld/overlay/detail/PeerImp.h index 3d9a0c0b1e..c2221c136d 100644 --- a/src/xrpld/overlay/detail/PeerImp.h +++ b/src/xrpld/overlay/detail/PeerImp.h @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -49,6 +50,68 @@ namespace ripple { struct ValidatorBlobInfo; class SHAMap; +/** + * @class PeerImp + * @brief This class manages established peer-to-peer connections, handles + message exchange, monitors connection health, and graceful shutdown. + * + + * The PeerImp shutdown mechanism is a multi-stage process + * designed to ensure graceful connection termination while handling ongoing + * I/O operations safely. The shutdown can be initiated from multiple points + * and follows a deterministic state machine. + * + * The shutdown process can be triggered from several entry points: + * - **External requests**: `stop()` method called by overlay management + * - **Error conditions**: `fail(error_code)` or `fail(string)` on protocol + * violations + * - **Timer expiration**: Various timeout scenarios (ping timeout, large send + * queue) + * - **Connection health**: Peer tracking divergence or unknown state timeouts + * + * The shutdown follows this progression: + * + * Normal Operation → shutdown() → tryAsyncShutdown() → onShutdown() → close() + * ↓ ↓ ↓ ↓ + * Set shutdown_ SSL graceful Timer cancel Socket close + * Cancel timer shutdown start & cleanup & metrics + * 5s safety timer Set shutdownStarted_ update + * + * Two primary flags coordinate the shutdown process: + * - `shutdown_`: Set when shutdown is requested + * - `shutdownStarted_`: Set when SSL shutdown begins + * + * The shutdown mechanism carefully coordinates with ongoing read/write + * operations: + * + * **Read Operations (`onReadMessage`)**: + * - Checks `shutdown_` flag after processing each message batch + * - If shutdown initiated during processing, calls `tryAsyncShutdown()` + * + * **Write Operations (`onWriteMessage`)**: + * - Checks `shutdown_` flag before queuing new writes + * - Calls `tryAsyncShutdown()` when shutdown flag detected + * + * Multiple timers require coordination during shutdown: + * 1. **Peer Timer**: Regular ping/pong timer cancelled immediately in + * `shutdown()` + * 2. **Shutdown Timer**: 5-second safety timer ensures shutdown completion + * 3. **Operation Cancellation**: All pending async operations are cancelled + * + * The shutdown implements fallback mechanisms: + * - **Graceful Path**: SSL shutdown → Socket close → Cleanup + * - **Forced Path**: If SSL shutdown fails or times out, proceeds to socket + * close + * - **Safety Timer**: 5-second timeout prevents hanging shutdowns + * + * All shutdown operations are serialized through the boost::asio::strand to + * ensure thread safety. The strand guarantees that shutdown state changes + * and I/O operation callbacks are executed sequentially. + * + * @note This class requires careful coordination between async operations, + * timer management, and shutdown procedures to ensure no resource leaks + * or hanging connections in high-throughput networking scenarios. + */ class PeerImp : public Peer, public std::enable_shared_from_this, public OverlayImpl::Child @@ -79,6 +142,8 @@ private: socket_type& socket_; stream_type& stream_; boost::asio::strand strand_; + + // Multi-purpose timer for peer activity monitoring and shutdown safety waitable_timer timer_; // Updated at each stage of the connection process to reflect @@ -95,7 +160,6 @@ private: std::atomic tracking_; clock_type::time_point trackingTime_; - bool detaching_ = false; // Node public key of peer. PublicKey const publicKey_; std::string name_; @@ -175,7 +239,19 @@ private: http_response_type response_; boost::beast::http::fields const& headers_; std::queue> send_queue_; - bool gracefulClose_ = false; + + // Primary shutdown flag set when shutdown is requested + bool shutdown_ = false; + + // SSL shutdown coordination flag + bool shutdownStarted_ = false; + + // Indicates a read operation is currently pending + bool readPending_ = false; + + // Indicates a write operation is currently pending + bool writePending_ = false; + int large_sendq_ = 0; std::unique_ptr load_event_; // The highest sequence of each PublisherList that has @@ -425,9 +501,6 @@ public: bool isHighLatency() const override; - void - fail(std::string const& reason); - bool compressionEnabled() const override { @@ -441,32 +514,129 @@ public: } private: - void - close(); - + /** + * @brief Handles a failure associated with a specific error code. + * + * This function is called when an operation fails with an error code. It + * logs the warning message and gracefully shutdowns the connection. + * + * The function will do nothing if the connection is already closed or if a + * shutdown is already in progress. + * + * @param name The name of the operation that failed (e.g., "read", + * "write"). + * @param ec The error code associated with the failure. + * @note This function must be called from within the object's strand. + */ void fail(std::string const& name, error_code ec); + /** + * @brief Handles a failure described by a reason string. + * + * This overload is used for logical errors or protocol violations not + * associated with a specific error code. It logs a warning with the + * given reason, then initiates a graceful shutdown. + * + * The function will do nothing if the connection is already closed or if a + * shutdown is already in progress. + * + * @param reason A descriptive string explaining the reason for the failure. + * @note This function must be called from within the object's strand. + */ void - gracefulClose(); + fail(std::string const& reason); + /** @brief Initiates the peer disconnection sequence. + * + * This is the primary entry point to start closing a peer connection. It + * marks the peer for shutdown and cancels any outstanding asynchronous + * operations. This cancellation allows the graceful shutdown to proceed + * once the handlers for the cancelled operations have completed. + * + * @note This method must be called on the peer's strand. + */ void - setTimer(); + shutdown(); + /** @brief Attempts to perform a graceful SSL shutdown if conditions are + * met. + * + * This helper function checks if the peer is in a state where a graceful + * SSL shutdown can be performed (i.e., shutdown has been requested and no + * I/O operations are currently in progress). + * + * @note This method must be called on the peer's strand. + */ void - cancelTimer(); + tryAsyncShutdown(); + + /** + * @brief Handles the completion of the asynchronous SSL shutdown. + * + * This function is the callback for the `async_shutdown` operation started + * in `shutdown()`. Its first action is to cancel the timer. It + * then inspects the error code to determine the outcome. + * + * Regardless of the result, this function proceeds to call `close()` to + * ensure the underlying socket is fully closed. + * + * @param ec The error code resulting from the `async_shutdown` operation. + */ + void + onShutdown(error_code ec); + + /** + * @brief Forcibly closes the underlying socket connection. + * + * This function provides the final, non-graceful shutdown of the peer + * connection. It ensures any pending timers are cancelled and then + * immediately closes the TCP socket, bypassing the SSL shutdown handshake. + * + * After closing, it notifies the overlay manager of the disconnection. + * + * @note This function must be called from within the object's strand. + */ + void + close(); + + /** + * @brief Sets and starts the peer timer. + * + * This function starts timer, which is used to detect inactivity + * and prevent stalled connections. It sets the timer to expire after the + * predefined `peerTimerInterval`. + * + * @note This function will terminate the connection in case of any errors. + */ + void + setTimer(std::chrono::seconds interval); + + /** + * @brief Handles the expiration of the peer activity timer. + * + * This callback is invoked when the timer set by `setTimer` expires. It + * watches the peer connection, checking for various timeout and health + * conditions. + * + * @param ec The error code associated with the timer's expiration. + * `operation_aborted` is expected if the timer was cancelled. + */ + void + onTimer(error_code const& ec); + + /** + * @brief Cancels any pending wait on the peer activity timer. + * + * This function is called to stop the timer. It gracefully manages any + * errors that might occur during the cancellation process. + */ + void + cancelTimer() noexcept; static std::string makePrefix(id_t id); - // Called when the timer wait completes - void - onTimer(boost::system::error_code const& ec); - - // Called when SSL shutdown completes - void - onShutdown(error_code ec); - void doAccept(); From 9494fc9668707b767ed0897f20fe53a33b30d726 Mon Sep 17 00:00:00 2001 From: Jingchen Date: Wed, 17 Sep 2025 14:29:15 +0100 Subject: [PATCH 66/66] chore: Use self hosted windows runners (#5780) This changes switches from the GitHub-managed Windows runners to self-hosted runners to significantly reduce build time. --- .github/scripts/strategy-matrix/windows.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/strategy-matrix/windows.json b/.github/scripts/strategy-matrix/windows.json index 5e6e536750..08b41e3f89 100644 --- a/.github/scripts/strategy-matrix/windows.json +++ b/.github/scripts/strategy-matrix/windows.json @@ -2,7 +2,7 @@ "architecture": [ { "platform": "windows/amd64", - "runner": ["windows-latest"] + "runner": ["self-hosted", "Windows", "devbox"] } ], "os": [