From 19a5bfd9ba467c701360a0ae3438c30aa3630104 Mon Sep 17 00:00:00 2001 From: Maxnflaxl Date: Sun, 12 Apr 2026 14:07:53 +0200 Subject: [PATCH 1/5] #1969: signing/verifying a message --- .gitignore | 60 +++++++--------- core/version.h | 12 ++++ utility/cli/options.cpp | 6 ++ utility/cli/options.h | 4 ++ wallet/api/v7_0/v7_0_api_defs.h | 19 ++++- wallet/api/v7_0/v7_0_api_handle.cpp | 45 ++++++++++-- wallet/api/v7_0/v7_0_api_parse.cpp | 46 ++++++++++-- wallet/cli/cli.cpp | 105 ++++++++++++++++++++++++++++ 8 files changed, 253 insertions(+), 44 deletions(-) create mode 100644 core/version.h diff --git a/.gitignore b/.gitignore index b955fe58d6..4dc53ab9e2 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ cmake_install.cmake install_manifest.txt compile_commands.json CTestTestfile.cmake +.cache/ .vs .vscode/* @@ -58,47 +59,36 @@ release/ cmake-build-*/ docs -wallet/unittests/wallet_test beam/beam -beam/unittests/node_test bin/address_test bin/asyncevent_test bin/reactor_test bin/tcpserver_test bin/timer_test -chain/unittests/chain_test -miner/unittests/equihash_test -miner/unittests/miner_test -pool/unittests/pool_test -utility/unittest/serialize_test -utility/unittest/serialization_adapters_test bin/channel_test -utility/unittest/timer_test -keychain/unittests/keychain_test -p2p/unittest/connectivity_stub_test -utility/unittest/address_test -utility/unittest/asyncevent_test -utility/unittest/channel_test -utility/unittest/logger_test -utility/unittest/reactor_test -utility/unittest/shared_data_test -utility/unittest/tcpclient_test -utility/unittest/tcpserver_test -wallet/unittests/keychain_test -wallet/unittests/wallet.dat -wallet/unittests/wallet_network_test -wallet/unittests/private_key_test -core/unittest/storage_test -core/unittest/ecc_test -.vs/ CMakeSettings.json -/utility/unittest/config_test -/p2p/unittest/dialog_test -/p2p/unittest/msg_serializer_test -/p2p/unittest/twopeers_test -/utility/unittest/bridge_test -/utility/unittest/config_test beam/wallet.dat + +# In-tree CMake executables (Unix/macOS; out-of-source builds use ignored build/) +beam/beam-node +bvm/ethash_service/ethash-service +bvm/sid_generator/generate-sid +explorer/explorer-node +node/functionaltests/*_test +node/utils/laser_beam_demo +node/utils/node_net_sim +node/utils/pipe_link +pow/miner_client +wallet/api/wallet-api +wallet/broadcaster/broadcaster +wallet/cli/beam-wallet +wasmclient/wasm-client +3rdparty/libbitcoin/examples/atomic_swap +3rdparty/libbitcoin/examples/electrum_get_balance +3rdparty/libbitcoin/examples/ethereum_example +3rdparty/libbitcoin/examples/get_info +3rdparty/libbitcoin/examples/sign_tx + 3rdparty/opencl-miner/equihash_150_5.dat ui/beam.rc ui/beam.png @@ -110,6 +100,8 @@ ui/*.qm ui/translations.qrc beam_version.gen keykeeper/wasm-key-keeper.* +*/unittest/* +*/unittests/* -out/ -wmake.sh +out/ +wmake.sh diff --git a/core/version.h b/core/version.h new file mode 100644 index 0000000000..b2152f025d --- /dev/null +++ b/core/version.h @@ -0,0 +1,12 @@ +#include + +inline constexpr unsigned VERSION_MAJOR = 7; +inline constexpr unsigned VERSION_MINOR = 5; +inline constexpr unsigned VERSION_REVISION = 14456; +inline const std::string GIT_COMMIT_HASH = "16f9d5870a70026e8cb2e9b94f7bf5470224a4d4"; +inline const std::string PROJECT_VERSION = "7.5.14456"; +inline const std::string BRANCH_NAME = "boost-1.90"; + +#ifndef BEAM_LIB_VERSION +#define BEAM_LIB_VERSION "7.5.14456" +#endif diff --git a/utility/cli/options.cpp b/utility/cli/options.cpp index 1ec70ae28d..f92fee7939 100644 --- a/utility/cli/options.cpp +++ b/utility/cli/options.cpp @@ -159,6 +159,10 @@ namespace beam const char* PAYMENT_PROOF_EXPORT = "payment_proof_export"; const char* PAYMENT_PROOF_VERIFY = "payment_proof_verify"; const char* PAYMENT_PROOF_DATA = "payment_proof"; + const char* SIGN_MESSAGE = "sign_message"; + const char* VERIFY_MESSAGE = "verify_message"; + const char* MSG_TO_SIGN = "message"; + const char* SIGNATURE = "signature"; const char* TX_ID = "tx_id"; const char* SEED_PHRASE = "seed_phrase"; const char* IGNORE_DICTIONARY = "ignore_dictionary"; @@ -475,6 +479,8 @@ namespace beam (cli::KEY_SUBKEY, po::value>(), "miner key index (use with export_miner_key)") (cli::WALLET_ADDR, po::value()->default_value("*"), "wallet address") (cli::PAYMENT_PROOF_DATA, po::value(), "payment proof data to verify") + (cli::MSG_TO_SIGN, po::value()->default_value(""), "message to sign or verify") + (cli::SIGNATURE, po::value(), "hex-encoded signature (for verify_message)") (cli::HID_INSTALL_FILE, po::value(), "App image file to install on HID device. If not specified - integrated image will be used") (cli::UTXO, po::value>()->multitoken(), "set IDs of specific UTXO to send") (cli::IMPORT_EXPORT_PATH, po::value()->default_value("export.dat"), "path to import or export wallet data (should be used with import_data|export_data)") diff --git a/utility/cli/options.h b/utility/cli/options.h index 963e393d0d..2ca3f49c59 100644 --- a/utility/cli/options.h +++ b/utility/cli/options.h @@ -144,6 +144,10 @@ namespace beam extern const char* VERSION_FULL; extern const char* GIT_COMMIT_HASH; extern const char* WALLET_ADDR; + extern const char* SIGN_MESSAGE; + extern const char* VERIFY_MESSAGE; + extern const char* MSG_TO_SIGN; + extern const char* SIGNATURE; extern const char* CHANGE_ADDRESS_EXPIRATION; extern const char* WALLET_ADDRESS_LIST; extern const char* WALLET_ADDRESS_VERIFY; diff --git a/wallet/api/v7_0/v7_0_api_defs.h b/wallet/api/v7_0/v7_0_api_defs.h index eaf5ec5af2..cb1c7d551b 100644 --- a/wallet/api/v7_0/v7_0_api_defs.h +++ b/wallet/api/v7_0/v7_0_api_defs.h @@ -16,7 +16,9 @@ #include #include #include +#include #include "core/ecc_native.h" +#include "wallet/core/common.h" namespace beam::wallet { @@ -28,7 +30,8 @@ namespace beam::wallet macro(IPFSUnpin, "ipfs_unpin", API_WRITE_ACCESS, API_ASYNC, APPS_ALLOWED) \ macro(IPFSGc, "ipfs_gc", API_WRITE_ACCESS, API_ASYNC, APPS_ALLOWED) \ macro(SignMessage, "sign_message", API_READ_ACCESS, API_SYNC, APPS_ALLOWED) \ - macro(VerifySignature, "verify_signature", API_READ_ACCESS, API_SYNC, APPS_ALLOWED) + macro(VerifySignature, "verify_signature", API_READ_ACCESS, API_SYNC, APPS_ALLOWED) \ + macro(VerifyMessage, "verify_message", API_READ_ACCESS, API_SYNC, APPS_ALLOWED) // TODO:IPFS add ipfs_caps/ev_ipfs_state methods that returns all available capabilities and ipfs state struct IPFSAdd @@ -96,7 +99,8 @@ namespace beam::wallet struct SignMessage { - std::vector keyMaterial; + boost::optional> keyMaterial; // deprecated, kept for backward compat + boost::optional address; // preferred: own wallet address std::string message; struct Response { @@ -114,4 +118,15 @@ namespace beam::wallet bool result; }; }; + + struct VerifyMessage + { + WalletID address = Zero; + std::string message; + std::vector signature; + struct Response + { + bool isValid; + }; + }; } diff --git a/wallet/api/v7_0/v7_0_api_handle.cpp b/wallet/api/v7_0/v7_0_api_handle.cpp index 3d2597219e..52aaf6e6a9 100644 --- a/wallet/api/v7_0/v7_0_api_handle.cpp +++ b/wallet/api/v7_0/v7_0_api_handle.cpp @@ -230,11 +230,35 @@ namespace beam::wallet { SignMessage::Response resp; ECC::Hash::Value hv; - MyProcessor::DeriveKeyPreimage(hv, Blob(req.keyMaterial)); - auto db = getWalletDB(); - auto pKdf = db->get_MasterKdf(); ECC::Scalar::Native sk; - pKdf->DeriveKey(sk, hv); + + auto db = getWalletDB(); + + if (req.keyMaterial) + { + // Legacy path: derive key from raw key material + MyProcessor::DeriveKeyPreimage(hv, Blob(*req.keyMaterial)); + auto pKdf = db->get_MasterKdf(); + pKdf->DeriveKey(sk, hv); + } + else + { + // Address-based path + WalletAddress addr; + if (req.address) + { + auto found = db->getAddressByToken(*req.address); + if (!found || !found->isOwn()) + throw jsonrpc_exception(ApiError::InvalidParamsJsonRpc, "Address not found or not owned by this wallet"); + addr = *found; + } + else + { + db->getDefaultAddressAlways(addr); + } + PeerID pid; + db->get_SbbsPeerID(sk, pid, addr.m_OwnID); + } GetMessageHash(hv, req.message); @@ -261,4 +285,17 @@ namespace beam::wallet resp.result = sig.IsValid(hv, req.publicKey); doResponse(id, resp); } + + void V70Api::onHandleVerifyMessage(const JsonRpcId& id, VerifyMessage&& req) + { + ECC::Hash::Value hv; + GetMessageHash(hv, req.message); + + Deserializer d; + ECC::Signature sig; + d.reset(req.signature); + d & sig; + + doResponse(id, VerifyMessage::Response{req.address.m_Pk.CheckSignature(hv, sig)}); + } } diff --git a/wallet/api/v7_0/v7_0_api_parse.cpp b/wallet/api/v7_0/v7_0_api_parse.cpp index 3390c46a8f..3699882022 100644 --- a/wallet/api/v7_0/v7_0_api_parse.cpp +++ b/wallet/api/v7_0/v7_0_api_parse.cpp @@ -207,9 +207,20 @@ namespace beam::wallet std::pair V70Api::onParseSignMessage(const JsonRpcId& id, const nlohmann::json& params) { SignMessage message; - message.message = getMandatoryParam(params, "message"); - auto km = getMandatoryParam(params, "key_material"); - message.keyMaterial = from_hex(km); + message.message = getOptionalParam(params, "message").get_value_or(""); + + auto km = getOptionalParam(params, "key_material"); + auto addr = getOptionalParam(params, "address"); + + if (km && addr) + throw jsonrpc_exception(ApiError::InvalidParamsJsonRpc, "Provide either 'address' or 'key_material', not both"); + + if (km) + message.keyMaterial = from_hex(*km); + else if (addr) + message.address = *addr; + // else: neither provided – default address will be used in the handler + return std::make_pair(message, MethodInfo()); } @@ -233,7 +244,7 @@ namespace beam::wallet message.message = getMandatoryParam(params, "message"); message.publicKey = getMandatoryParam(params, "public_key"); message.signature = getMandatoryParam(params, "signature"); - + return std::make_pair(message, MethodInfo()); } @@ -246,4 +257,31 @@ namespace beam::wallet {"result", res.result } }; } + + std::pair V70Api::onParseVerifyMessage(const JsonRpcId& id, const nlohmann::json& params) + { + VerifyMessage message; + auto addrStr = getMandatoryParam(params, "address"); + if (!message.address.FromHex(addrStr)) + throw jsonrpc_exception(ApiError::InvalidParamsJsonRpc, "Invalid 'address'"); + + message.message = getOptionalParam(params, "message").get_value_or(""); + message.signature = getMandatoryParam(params, "signature"); + + return std::make_pair(message, MethodInfo()); + } + + void V70Api::getResponse(const JsonRpcId& id, const VerifyMessage::Response& res, json& msg) + { + msg = json + { + {JsonRpcHeader, JsonRpcVersion}, + {"id", id}, + {"result", + { + {"is_valid", res.isValid} + } + } + }; + } } diff --git a/wallet/cli/cli.cpp b/wallet/cli/cli.cpp index ecc2d5a538..e6500cc72a 100644 --- a/wallet/cli/cli.cpp +++ b/wallet/cli/cli.cpp @@ -3326,6 +3326,109 @@ namespace return 1; } + // Hash a message for signing/verification: SHA256("beam.signed.message" + len + message) + void GetSignMessageHash(ECC::Hash::Value& hv, const std::string& message) + { + std::stringstream ss; + ss << "beam.signed.message" << message.size() << message; + ECC::Hash::Processor() << ss.str() >> hv; + } + + int SignMessage(const po::variables_map& vm) + { + auto walletDB = OpenDataBase(vm); + + // Resolve address + WalletAddress addr; + std::string addressToken = vm[cli::WALLET_ADDR].as(); + if (addressToken == "*") + { + walletDB->getDefaultAddressAlways(addr); + } + else + { + auto found = walletDB->getAddressByToken(addressToken); + if (!found) + { + BEAM_LOG_ERROR() << "Address not found: " << addressToken; + return -1; + } + addr = *found; + } + + if (!addr.isOwn()) + { + BEAM_LOG_ERROR() << "Address is not owned by this wallet"; + return -1; + } + + // Derive the signing key + ECC::Scalar::Native sk; + PeerID pid; + walletDB->get_SbbsPeerID(sk, pid, addr.m_OwnID); + + // Hash the message + std::string message = vm[cli::MSG_TO_SIGN].as(); + ECC::Hash::Value hv; + GetSignMessageHash(hv, message); + + // Sign + ECC::Signature sig; + sig.Sign(hv, sk); + + Serializer s; + s & sig; + auto sigHex = to_hex(s.buffer().first, s.buffer().second); + + cout << "Signature: " << sigHex << std::endl; + cout << "Address: " << std::to_string(addr.m_BbsAddr) << std::endl; + return 0; + } + + int VerifyMessage(const po::variables_map& vm) + { + std::string addressStr = vm[cli::WALLET_ADDR].as(); + if (addressStr == "*") + { + BEAM_LOG_ERROR() << "Please specify --address for verify_message"; + return -1; + } + + WalletID wid; + if (!wid.FromHex(addressStr)) + { + BEAM_LOG_ERROR() << "Invalid address: " << addressStr; + return -1; + } + + if (vm.count(cli::SIGNATURE) == 0) + { + BEAM_LOG_ERROR() << "Please specify --signature"; + return -1; + } + + std::string message = vm[cli::MSG_TO_SIGN].as(); + ECC::Hash::Value hv; + GetSignMessageHash(hv, message); + + ByteBuffer sigBuf = from_hex(vm[cli::SIGNATURE].as()); + Deserializer d; + ECC::Signature sig; + d.reset(sigBuf); + d & sig; + + if (wid.m_Pk.CheckSignature(hv, sig)) + { + cout << "Good signature from " << addressStr << std::endl; + return 0; + } + else + { + cout << "Invalid signature from " << addressStr << std::endl; + return -1; + } + } + } // namespace io::Reactor::Ptr reactor; @@ -3363,6 +3466,8 @@ int main(int argc, char* argv[]) {cli::TX_DETAILS, TxDetails, "print details of the transaction with given ID"}, {cli::PAYMENT_PROOF_EXPORT, ExportPaymentProof, "export payment proof by transaction ID"}, {cli::PAYMENT_PROOF_VERIFY, VerifyPaymentProof, "verify payment proof"}, + {cli::SIGN_MESSAGE, SignMessage, "sign a message with a wallet address"}, + {cli::VERIFY_MESSAGE, VerifyMessage, "verify a message signature for a wallet address"}, {cli::GENERATE_PHRASE, GeneratePhrase, "generate new seed phrase"}, {cli::WALLET_ADDRESS_LIST, ShowAddressList, "print addresses"}, {cli::WALLET_ADDRESS_VERIFY, VerifyAddress, "verify your Endpoint on the attached HW wallet"}, From cbeb361cc432b456dd62c6c27b5969f4a7e7e84b Mon Sep 17 00:00:00 2001 From: Maxnflaxl Date: Wed, 15 Apr 2026 12:28:29 +0200 Subject: [PATCH 2/5] fix rate typo in explorer parser --- bvm/Shaders/Explorer/Parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bvm/Shaders/Explorer/Parser.cpp b/bvm/Shaders/Explorer/Parser.cpp index 69b0bb34b7..b633a87154 100644 --- a/bvm/Shaders/Explorer/Parser.cpp +++ b/bvm/Shaders/Explorer/Parser.cpp @@ -3118,7 +3118,7 @@ void ParserContext::OnState_DEX(uint32_t /* iVer */) DocAddTableHeader("Amount2"); DocAddTableHeader("Amount-LP-Token"); DocAddTableHeader("Rate 1:2"); - DocAddTableHeader("Rate 2:2"); + DocAddTableHeader("Rate 2:1"); } Env::Key_T k0, k1; From 329bc47267b7d5cf91be95ace69d91ae4ecd6cca Mon Sep 17 00:00:00 2001 From: Maxnflaxl Date: Sat, 16 May 2026 02:36:03 +0200 Subject: [PATCH 3/5] rebuild explorer parser wasm --- bvm/Shaders/Explorer/Parser.wasm | Bin 78411 -> 81339 bytes bvm/Shaders/make_all.sh | 113 +++++++++++++++++++++++++++++++ bvm/Shaders/make_shader.sh | 32 +++++++++ 3 files changed, 145 insertions(+) mode change 100644 => 100755 bvm/Shaders/Explorer/Parser.wasm create mode 100755 bvm/Shaders/make_all.sh create mode 100755 bvm/Shaders/make_shader.sh diff --git a/bvm/Shaders/Explorer/Parser.wasm b/bvm/Shaders/Explorer/Parser.wasm old mode 100644 new mode 100755 index 4cfd23076c36b88f81e76f436f4f5f4575377dcf..63a66fccf5ba3a368cf7665a9d48cf59da9527cb GIT binary patch literal 81339 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 literal 78411 zcmeEv3!Ifzx%Ye9mzh1lR#A!Z^6stDOi3aN=%~m$qC$$0AX%pl4$Kzj!pt}q#1jLf zB2sE$nU~U}vak}<)Q)K>*+n#uVOqyJWt3K0+M#nirIq>p|Ib?QZNGaDGr;$&@B1D7 zG5dYjeLc^!p4)oXdX1L8m8M}B=1t~XF0j_EGcGXk&oJ=3*0{i4#}xiG*D)7=9lY>2 z&0l#k@Mbc}FxR5&TB)&C)y7|I?OLRx05efRewdWP8w;Z+zg@nt1hd#PJ6mg5*V_~9 zQrD{5+1<;$*49O()%}LSgq%t^v$uPo$1s^P1}Ud?cK4N3niVN^&cMp)#~XH}q2|u+ zrG^tPa@^Ed(_U|H%Ozz^t2AjkJ+HKK*~%Ux#YW_%=aTTHuhbvDwDnCf(kxc9xTUvm z$?07KD~$}_$H@D~@nFD+cr zGIiQ?qej|h^1FN6`b%nFb$maw)W5i;b3i9$nG`luD-C;{o!!ffS~lU#Y3b}YWOlX7 zOZ`jcl-icB=r?jQR3>BfC%v+?vb*;Z%gD|v^{?n|T{6Y^km)}kYg`pP zPc^PKYw!N8y}|skX^~uy7{Y&s=XMnBBjwRG@|Q}|&_-r_gn#oY$M)XtO?IvNCd061 z@qgEBu3a?7aLt0_nFY(9)~i#r7qO|NS&T4*WfeUO84Z8fa?WgISAa@*Am zaxEAh8Zj4QXc(Ml=5J(?aXCp|cOH|Qkum5QeopmStInE@{N-#zb(Z_RZ93~T=yn+a z>;xv(0<@2r%(PbUr`g>&)9S>NIn%m;Ka$Xj@eW=0yPw+At@9NS=Hur(Mti}rW(g#0 zZ>Q0Yv}cX>{3m6Gj+aG3gLOeM<<)i-(_U?l4<0Ym?xwsKjIL-}nrSa)VreO+ZF(|Z z&|%`{YtpfjHWRn4KP8q3^rXC@t-o|)sivep^6$sQQfrXvVvwC3dXyM?9mk*TTbRc41jjkYnvCIoGHy1;e3ivgB8ARd4Lp|uyC$%f%gIfGPHm0C5CIEqL} zYr89IDir4eg4$*H6!pcF<6yCv>+Ix$QAlsFjG}Y;7*d{-znSeWMj^{0Zt5)Nx~Z*hdaIjRTdZk*6aS67e6U!%rI?y-)uB{vvwNjm19};3MuS87 z_o7O=kmX!VWU2~oW=*V$l)Svzcoz#}$@t~bWxF(t)U`+jJlw4GgPu}bF#vaiYfiJW z#oG05W@t;%8Dn_suy`>IjM~w?Vip{3E*$PmD=Wj{Slt{)yQWw(6R56HqZS4cA8t@( zBMPZ$s5%&muBB(-a0Y(gkr`)H6B%^p)~sP~4rk?XR?c*ch`_b-w-6C^7MTww(m&mr zz!gZGSf1;qI*QJ!=~iB)8O5v=A?cw=wg`-^axJ9w0!%8=DT24Ha?>g?i^OcC5Eva& zY;Qt){!{E2Lja-YU8kSFbw$HXt&+ARJ&U$dt5C&Z2ZSM4g%m=slIW;J*7M6ju3(PM z>t3EC5s9il|UEYcpGubha zUN1lcz|`oSc0CLIn{UtUupJ6p@(*cB=*!*;A;(2Wv||OP*SppY`#x&={2&Y~vCnPG;}$&o>Vw@kMnnmL8!^k#y}E6j}zSD#%_scdSI zT6$_2B_Ll)B;w`0IT_81gqfuaja$qt6&>~Rn5rL zZnjJUDE!ogGZz`KrU>+anGBCrU8`*B`4C`w>e+~BNYX~w3g(`7vlS(Vk5_O?g;#%i zN@7%>p5on_V#B$ChgY3jP^D~Q6q9jstv0c!#2`lN;) z;HXM`OQ;fSCBw{L!Lmwms8A!KMC%$>RGrLU$=bP3WQ3$FVH8$G(Xf2YT!CkXfx$K# zGYmuVl19TQ+RetfvYO24Mqcq)3s2xJMi$DsQAk;{>{$esXU;)Ei_0!gIpf(_027dq zZO?z64f|Cj|wF`gQXLBf(A z9dD3x!OxjgBAcy^uDu3g+dhAci796w)C|>5TD6k`(W@40%u0j6Lkk@v0Rd-R4ut>& z=w5MAXc7K;D%Px}e!>KIV==vUO_A&wCGiFbtX%_kt(7?2twA=+twEOEY;HiM!#6gY zm#@d0v9Oq_FWAthopx`V;hpMc>W2qZhYu(~%2mh|BnTo3X#yQkB#@Brc(djcbA_}( zHi9x_Z8O@pZp5SnvRPu-Q8=U~4j8A)k(?IfShE1vOb2ua(G6U~8$aEsBfSXi+EfI* z@w5#k14vH0COWc3_Xk*R{r>iy+n@i?y2WG697eQRzhdmDC58YsLsAm0MUoz+4(O&1 z3K>fO6roEW;jCW$ZfM~m$ z?eMI2jSdXlAen=VRFV|oWYTE1!GPYX3P3CM4j%y6Qymu-Q>P;zby64>e>$YLS~n21 zv_7CVC{0-zmifqumBNw0bG+z3LFeGnr;9C-rLbQb+|;vlhSP>`4^Rw zrL-s(W$jN!`w9@`D_CxV0SBBxVIa}L6wWr0`VL9O(7h=F^ZbVhpNW1!U;};u%m{N< z9VoWBu1_%G8erR4K2E^~ZGu+Yfz}L(G8=V#WKqN>*MiCbh@iIuSQo6BcEy@iD+H^e z0Vx6Eb7_kz`wwQ!X!;;but-%yN-YCu(nL+#n*uWMr-`v()G`dr-|i^XNF@#}EmNvA zAF)U=TOhj#nwnpz<>*{avo@R|#t+gUEnuwzkm2~VV3~js02MliG6is_1dJdMXz_+* z72wOQ!KgVijU9{RmlpV;W6rJGe5b6T(i7vkv0~4Z?FtK@Fzq`{l|O1;OI2I~&57 z5VQ1p2EK)PjNhAO;fG}xKb3(Kw!Ealc2#Wcu}%(E94{?&B#z8cu=%% zC|bY8v1t?8zQ7zZ0}m<24kxCGKfnPDFU-aSsfp016A=7mvxphYvcXzG8p2tFf8dZgU!qVycd zdg&0d9(^iV57%j0WIc;xL|WEE7iyOlAQa#-=L>UD{d~=-T=)*-(w+viNP9{isSlib zN1g@!NoXbyP_EWEpIv)IH1X+=0ly(p_L!F9+ zgRO%hvt4NjxgS{Ad2nDdE4c^_z{3KqFPgxC01_!3Ma$ctN{W@gov6@;h_|v`y#p)0 zM!1&Docsz1GNC?$dkl%5jt6*5o7dqTMnP%~$=ZmDgy;1a9 zSalXLPPkw>g7V8$Rn@kys>006=5l|qY#Tb4a{N&tHN8^_K6IA9h9>Pq-&;rVhTRuH z7RHuq&Id=fXxZ2@Yng?kjX&|b`re^pN?@K9lP1-?{<}B-C<~J%MT-WWfQ@kT=rl}6 zTQ#)IH-FzRM$|a8Y*M~u1N(6yxE2i5Z8U1q^`cOKTro7jQU-?wKNn*Jb}>=>H~~oO zyaNEzIwHnsei&48m!(YOkTy^jnfVVB0v2!t$cTWaU7~!WNcBw=1#W51hZ@Bd<&BhP zKYqr_w=l>_OXb*c<}=6V63w zT^1R?MTf=pL@ca1%kwQYK!!e5XY2ahPswMfZecyv2%E-(<3EsbH@{#s*EWN=>XBoe z0f)DG^#~>92uf1GN>L)*W+`KYzI&yF(#0gv5dQ!#e}d$KW} z{KeoN3OJce2kF*W%P@_c_o>@}Gw*Yu6AeSviFbsFdh`(M-n~rq+&q4Uu#@b40d6WZ zz-E+miFTU*B1=*7V!=%|4ur<(eeGHn1*zcgW&A6^;Q$TqnTm~nsY>Ht8eQYBR&4yf zDvjSU(#APoj{Uk1VlE+ITqD1=Y78Gxdu1|f85JiJ= zfLj#+Zi$HGpkJ-n;SVc!nE&$V8ov(4X&fSVmCg6wM2Wl0N<5P&@s+X?uO>>|Q&!@- zt77BOQ2p<4l0JQ6rGQTJ_e%W`NcUE(KLo8*!+SUNpQ%{?h7s2vy1LT%@2Ff~&+hRs zSMvbQ?7p&@ZLZkzgJIq1%tqLuonTne*2HA=-Xzk! zY#t0j{ASqp^?o5~R+u*BB(qjw391(gDm~5@F>2vNxxHP1`Pc#T*&G|FnztHgxc0Us z%2@gGGCLDx>_nNNO;Mn*FC+h;043~ebE1qHlo5OiL2ZkbQds}Gv_zE@&3=O~0tMPA zfhuI>K4Y5NBmn1LOGO|M$EFg;B(l6WSyXI^QJ}z7B09p}y*nv$bzs=%h3$(t^;D49 z(~^rp0G}#0%kT>M^?X+XSN%nNfol0n5y6rB+z7{b?7kmkUHiy~`3)5>r+7^kkfdLR zDi%`Av5rAMKd?b)BCG&N($KVGu=}r%114fCstrIs!shcXy*`9*BJi%_o}nl*0YpD6 z^#ub#+Vx>tE_b%CH+?-6LYzymk{%+qWTA-GWJK2y7$NN|Kl&|f?Auiu+ctb--;6f) z`KtIA$coaa;5sc1S4B%dL$H-E6eCpX0?PT<*$=HJK?|yb`H};)qV_!*G^erEOG%UcKaGsb)%CjcV0LjqblwzLu9z(>A zCQ`;?f^F=8N612Y$a|53cSLM@4ce(4qvhK}wYn-FkY>GxitCJs!47ScF$X^l?L>{^ zxncKzLe^ZBCC%i&%lUynv6^@hF~IzzD$yXZ&?MY0KuA+dy^{k}sgE(W3U#Y;nz@Z^ zQt+O|IS0L%Uo%Bmo8C<(0#z{+C}A{M)D)~>qa=&d~G8J3PoMtBZva~ z5VfjP=FsoWlS0x&kNGE9S#~$POJkWN(SOsK0o}63Fm(IV5z?)cQ*^5)nU^`>d0P#` zXf-)1h>UTEmMWE!82=)1ua5>wc)BI6Eq8|u2X@uV`>#aYI!wS z_PXn9r5Q}Oe6fdJf3~b^U%p}9J8RmN+{k^AGFcl(H{QUZNts+Qw4gE z4U7c{`*c_{P)fd1A?=|sO);`M#AWgcA+t0*Vr&chMhc;7spfJ&s*OrNM&p~l^ivEH zqqMwSpslH~`hVe;s>4B$RBdgonMr3IUxzzt?wFcj2sIkKKM4mK9hCPgNgtI)?7hr7 z=)ou`5~yQDHnm6e3_rJ?d{+1Thke&G7P;XaMR98sun=CrAn<2yeKC~$E`>gKCjTab z)P`3b;S-y0hGYv!U}KCkkI{$^i< zrwOZid-Y;?Mpg0ZxJk%$>*Q}GN!P*mD+74P+%f530BJpdtM}aiMutzsV#QXYw}~U* z3OPzmghQWHu)P~(WKr@+1_avCfWSKLOKDn2sfj{KOX-PzYS@X&=C(md!?q3}jb{yM zWQCC4k{DUil(W3@iIV%u@TSfkgAHU&2#y|J#JGzslDkLH7n}^`K)sLE(1}qbINzoz zfm?nA5cC1CbUgxJ7(7@RK&%g$sE;&_1|8t=(pzIp`_S`!ymD`v@JEG?@5@|KMH7%O zGPzfURja4zGy9(m+c6+!V0n*-4k9vOm*LBR&sjQVJ+Lg`=C>4U#d`g*cxp1f;$?Z6 zr1xekH4y#};Zh5p?s2ciE~MbwVl)I^QgToXd)e6IETmkMo*@LKwtKaT14+3e;z0p< zCGEhMYCRMC;p85@DijdA-B3?tpA-#+kwFL?LeJd4L%TWcMzWH!%r*1hCxgjhq*%V# zG$=EFQD6rPXqkhqeJ4w9H(+`}z~rl)GUkMnYcIf*8wN}&`~Y3n>^m?$wL38VV9&tx zM*)))Qj@b!sR@zAlxG#GDdSU9YTtqBhr0vQkE(#_v%@dCSN{K^rl*GmQ~sGMK-$fc z+bH{kV?$aRyH9CpzhP);KcAMy?K@if@vuMwXZp#Wf$0VT)BiUN{6BUFrl0N^m~Is? z)r8bkvrnlBF6gS9sm`aS+I>e&KieIco*OZk5CLwAJ@<#KRz?p&s?HbZt&HJZ%)2^h{aaqta(o3%{Px5=uGqcRZTSHBq|= zURImCB|l8mO&V0O6IR||BmR}&s}VKYdA$c?;>4~R)ElF2_@lH{cjLG*>5z<>ioPCc zdZE7kWhV$%QnaH&m15MBGScD^6Lfp<_of1br=rG|YvkV`0s>3zJ8<_WO|@^rto9)u zAs4g>L&@;eLANN1l1JY(LCKk08e}I{GRVGLxgYOorqe9L&|vMuJj;KA!^YcqL_UI? z4Da}M`UfTIG!X36cS<)y5-6`^U6 z8yFf$O7>DmG{RT*s4t;&#o1I}>cl63Yn^1ib}2E%rycV2?4=cOc2~7Tuh@>@Y|}s! ztjJeGy}pQ$Yj7S3B$lj6D8qKVNqNM?3}J~&n2G64nr>Zyyl+Yfz26cNc7o5F9^1i> zkw;{S-sEVoF7PH{iw$A_mVtWQZBwWcWFy)9^QG7hs{qp^3$ddz$?sJfa0wU;!W$Yg z^I)_?zF$)MQhB~LPja4Y+(ic(gBq=A!EUbW8AOta4A;~N(pIPX&s47t`0Z9DW zg@FnS3v*smIYXD~9EE#cXW}9&xEU8NrLu1EM$$DVO?MsWS>pUmUFJ0CvF(y(&rNa z#Ah7-$hcO)0I;y#5}b$wq(Tp88nBh!Qv!~KQ4wXanB<%y4vuLk(z6a9sRNXB@^H7rl~#J0{i=MCfd6Z_hFT*OlFhuH3!6@av^H%_WbiC2Z$ObH`>qIMY;J4k{tE1 zjSOL6=U696LZk2PXnsq z5&hu-hbb{@zem_IIKgDCVN7l_k4|Tm9fNC#YSt8RQ5Kk8tz4v#b5lwUh#IMw0W=>F znaE`qvd+yBl=z8*GUtP` zt}q6e%HUXx6cczC$Kr+su&;40a>2I7q3O?9odq^;_9J;raX&l)GsXSe3kSISxu`JS z-LG(jSe<%4h(*F6w_(3lT% zyy6XqE8C?47;4TP@4kgi9PA#9+VzEr?pr`46AD>D{{iT=wgbCRIijynui6%%<%mBE zaTY+pk3`0ExE54YXY(SZU&E*~f#!xscCk#W)m+~V28}=fT zLPwAv4Z%?KIH5#fFfX#0To=-4yO83DA6+wpPmD+N$0&sghr6Od%)tO&GUz5l!8w$Q zNH^>Neh=?SkKZ`sUlg2aEjCsco0h9{$kQ8Ydx(^I^Wa= zo2h2f<=jJ#G0rGX6#JomE04oSqZ7r70QAAcExDfnd))1KvBiqO>26vPxW@quh|C=D z09m>ICOIUUy2p-c_BRoMP#{|rc>;b)rOnbutwROdrj5c70=CJ*1Uef9M^7syI~^$5 zkaF$rM7Lfk*|~(0ou-nF+a0PX*}hi;&8zjSr+rxS3&z@_>(&ev50eXb1ptSM8KnVW zX#j8n9P#J?;HtLv4hSi2X$TNp_b}XOH3(A$QWWC`VuCw51PTzP!xVt_JA46AvHcd% zLD;MwyAzX<&M8+Y+i+5i5y9hoG z((gYy`egK62eqBhwmFrEwWtZ1sXfe%-w4bFp~$>y0j^4}11V5eKZ!vtWyZTvCJk0s z@QsWEXmSK^N<2JoV5L%&Jnx$vO6xO`Fx}clORr(N^)l|v#1n;;TGzsUP48uhqqyR~ z%|S(|QuKMg?05})blh5WE2`ee8Ge$#`;-=8)0+0ciATt|IgkhwR`rvuj3g!Ize_Ha z1jYaFQo%a-hgvG)UCt`A5=s$_fJ5C32sW2fOGycLw@yqv-=f22q|<(+P+aaeX1I;5 z!o1554i4iSHUsaf8S}30W9P7%1U9jE=OGRXDt~swO`OZ!ma^>d?9^T1E}FM9;mZE| z+An)%iTp0~Tq-bx8arG*LsZo1`#Ax>J1gw!PbkyZRWW*gV2MLj3_L#*DmZ@7pmO{m zGw}Nu5zosV?0*00*6pAeJpj|YgS$<5yB(}M&VlcUxL$5YMG&-RXA#C3V_r#J)r#x4 zBAHFv^LA8M)mu_fu~i4~_*U0dA?^{^sz(yt7EQ8FC#q#AOjO$l0tD^~wt@Oh%ijTt zMBNx((ZxT1GtUJ|cvHCrdqysH^x(0;wmP6`!Eo_`S->kcj-V#%?I+~=;VbCWI1`io zgK@@1xsHmRT69w4hDbSF#CBaX|8q(y@)C52runBXzf(jPHUVKENwU6Z+-EnDUhUlP z6u%L4;9?7$M1z;%8HmM@?yM5`6UPziu^fVr2L)(8$~|E z<2zwTNukiER3Sg?SnpQBe8~N?K8veJmD2(2y!$?n zet{FkfW2?I@6-)Q-j)cHG6nR$1jgrAP`f~`XQP2cV zMd6AdUcZLx7R8EJUn$^?Zuu_|*~Ans?0SsP+6j+(fe-#Ui-(GyV%OZ_(qGi@{#_;D zUMgiEdcR5xZeKCp|B{vkz-n3*7H!g*^IBL7sIJ3hc~MSE;Wuh;v|`;yfzNbJdu{5vX}Yl1XgeTPCrcOk$Ls-hod} zWHBDl$)0hhJF@8Xm9*h)5v&N~7vP)Wu0J@P0Go^l`&y9n85j#w`WT_-x^npmCam}_G>(Lv{8Inhzq z#d2c99uv!ojeBf7D?0FT@vP{`^YN_c(D#dHMaRB>JS#f*1L9e+(T|Vk#fE=iSzdJf z2bJYTCvb3CUUUW%%JQO9cuQGcbPf~C@?w*yFUyS0;*j#p=rj&3&y3FFuxKXl#8m9b zJ6xwL_L6%n6Fp(M2|BW+WD3_8V^Hi0F(|m`IcsDxW;E{PejL||9^8v=*8heVF<`mg zg)aaFvf)oEvBAK>ZN)#o6AA~{Q0JW$HxACs?dL~Dh9~B(xCL!1!5TG4M2LZ-TJ#6@ z;0HUE7(w6%mh_7-NtI)HC$#5xk&9E+!Cm-s@(33lGT_lc2{?M83$YA-5u8#4du2Kl zFQls%Ryu*Vk~YA-g&%$|;{;ZM1l`Q0@CY%x20yrObG4t#4b&s?f?EgjpJuU%-Um4& zBq2-9ixCo%=xquo8-#yie`lIrMRbdvtDKLjv| z3L+6m{;|I;Rp#SURzB>RzAe0pcaV?UFbmGT>-czNMksaWMQz<6equs)t zkIv*sBUH+=vwja(={CSsavnh#gyn4t42fM?#FN>$C)B@oVO+47p~yhH<5D}Xq_)nvR!a$t4{o%@ytNdvgwG!DOajGYeDQy)TT0Nyu^BYo4fw4e$4Hi+o-a-}Lv z1=s86oxDFDI~M~;c_()9T2@CnFVGGzT#jvx8|q+?r|@buTCK{e@)Wl~ix()u)oPk7 zQp6g3Phc1|(Eefc%28;ePlc2)idsM2qgpSP*HQujNa6MD%?NA2Dz{n(i@56DO)sv; zezxV^A_qHhpu4?LLz=P`G0bOB(r_BG*0lCSLw0LGZL0&0gqI!)5xIR8REI&~BH{@5 zd4iM*?z5f0k`FQ!7Q201z=G zO&DqshoV{NNa!GuL=6-OCx+Qz!H;WKDnjn@Y*cyJVF&yM z;{}JI@=!ZW04vmp9IkMp7Lm?mYtXnb$<4U*LLP-V$6`<+obG@;eVIe?DyR!}HK>^X zC9@GFs+xN=j0pw1@vnQ>h8WCBYJ;F)JDKI}*yt!{lR(d7HjyE@7G?W?vNN)=udzis zLxyO@X)Oz=K8fTCAJX@v$0&>1jSmU;oEyiX;`c{hF}TLzpdf%0bM`Nqc)H4*ar|uK|*@H$oS^p-5lYGq+(WKgS{c`2828!gj zKolu8LmKVr= z@(EfwwUSPBcjZ<%T9VEEqZn;OKS`=Z4_EJpbUliGe6>XhH}aAlw$Do*J1E6Vqikp} z!RJ>u=H!rqLGQ!iT#C=_mJi4KBon=l@UIfsP;Gddxyk@@ zl$DC%Y=f~TGglx8tRl>Nn}zR3pa{mQ3PvUHGn^`Pk{$e2C4df*z&k>dNL`I1n9{?* z=cG{K37a;j*rncP>}AWUDW>v&Q0t+=x*hQ{3V)6EW($|~%lG2MnSYnMSp%eL$>+di zq(#K!@{4l1gqzV{U>Tifkrol@@{+nzfjxI z`Wi>{EIt$>#zp?WnS&H;mqGVQ`ClWE@jaV@9_l@;o6EO*dVJnvOp1B<_mw>&YKEc@ zwZ<#xti$@bM^Q`hD+0E9$XK#3rLT;()((MKq?T`wkS~$7@-tajn2aeG>(WMKGyWmQ zIpsZN_+jGEU@UKF>`CU0K#)A_@)upXtiaMs$Pc7>2y&N0cd}IZCTH%{+axr~GX5z$ ztUFouIAwP;*XmQSm2X6>8Xna;znQUTn)%+ylkVY3JIw0lv^r{s#~ahA43hxU1xYrT zpRkHK#`GfQy1$r0ATR#OT=PgOpLGtkB-WA<-G(2NnDMvca{LUVv`I+uTZ~^`r-y|E zU0gORZQnFk{6zB}d~r_vUEA(zGRU_Q>C6#dz`s2$WYI zl>gf-c`R5mdc$Xt6h|HljtsTB&eakfKPwa~z5v#xMvUdNxnK`ngHAAlh|LCLULmE{ zubV;$yMA2-7QAH;G*?;5~~(Ujr94zJF~%&u9)#}diTQ<4@QlX zT--zD6|Uy94E;kodkS5h;FBs$t`p_`|3(iw~bO^G5X}&zb3$#42P9TH^Iim4JQ`NQoN)Rn@FQ`fr zJ#{sz_!N-L*>cBTT7u6^g=x5G4IT)UuPz-%^Kxj)pP;3mh&U>IGyaQmmjoI^YY}?puqGP?g#K?c7~+rX>OeF_WkuF?u$es5Er{CiwlbH9 z3h&F*H_YX{dlMH{>{coTTQ)dXrMGP0Uh(f%d}z2)!>Wpm+S=n$Kfz822LBMY@240O zco;Awf7j(XmiC@eb#NKR0<9n?p;{Lk-Hg+Tl`BI3{z!+^lTjbxGe`Q=eD?Ue;gDX80riMI1?q^dO0mlnsDM!P6}Gfi7H*_7P;EB4#_Ks3@yLzNO>dINc%PH} z=)jUl#}@|6kRAwfs5Hsgi><@=894QEx;Gi<@W5^=`!W!bFj@wJQTj4)`)(@xq6n3h zp3XbieNpru!&mkNK<*C*r}#NiM%D*`%bUWjKY%vE_J`;R0cj;z1laziwy=HXVwu8G zKLk9Kv;7f~j#Q76jusKWkV!@HyRSsVtL3b;0t-m`#$Ml}T7TQ4TE7Vqs-cl&QppkS z+M`;p?NP0vPY0`W6eHZUN40J$uN4!0a7!zZHpGWb6$o}`1-XcFp2)?TP%fez(;)dG zi7fbbt^{Ph-4E9l$Kl(jTZ~RSWx6$o-X#Bj7ji&`;rk9Z%f13mE=Mg!h`b)}?PntH z4o`EuQ}Cpyu;Aa$CH^$znqRT7iIqJbEUz$f6p!zgZpf=iK{7+lS{f@Qc(#^&*SZTT=T9yswom2<1MBj z!qouqK&<1Mba&$x+6J)C@`#Ou^)DZ3PHjF6di^gX5{O=ftS+Ch_ zq1iirG@t-B!V8IN7{Rt-RbcsmvZ3$bs~G~C6aWJ(Hyedo*Or@Sh@lQ{`2lx0`^~r^ z{0u@>+`_TqYwE%F*wDK5g<8)m)Nr0Cq)H%ZRsrEO830eV5A-HoM$G^}bPU%j#N%g* zKLeE8{UkUpj7)HjduGTX@Ds(k8afIrzzr&b^SbsTg3b`h%%Z(64nNgkh8Q#&0uh54 z5G$6!U^$Nr@<|QY_{cB_2}#2vy&Q$RM|wG#R6;ti0{p_6?=w8jrho%zpTe@DDOybz zM_1zdKz>(@*e0N zqOV;l>qH%6OX@26WRKQWj7A(Kfadd~M9riru0P2mwpe@*V?rg9?uAf!jQTz~SRGl; zM+d`JO7|#-%~AhnV$@0XCH5g9@2{-EXfJ)&wE}~04&(bhN$c_|aJdTRFc>JV={y#D zMdZW^r9h}{8Wy5pKEr;zEiyi!1EH0cpZN7iVo>tYFflj6kDrwV&eNr0dz)mimqsX{ zDjv0Q8bZLc@|U8fL1;i@mgijVRy!K6$^+rnkusyW(R#ZH>c?3H;56Z@{DKK!sa&Nj zY+yIku(0yyVARPSz$D%3Ffm`pZnVLZy`PU@y0J6H97uz8wy2EWtBKmX?H1F3E7yBN zYF@@^aMfcEjdP655<#w429rbyu;*(a`w$YsRFRWR!*$-;3Ifb^+lKc^DY>2{B^3p6 zkRVKCmv;q67(O0%J!J!G^x2FW*w`AQMr|%-{N1G=!uymZ!o>ZPjjqz_UL?>4!-+S_ zJQ({LX9f5qR61zVk1Tyq#x7J7>tws6qb01B>RXg6}vzF5H-4Nubt)uLrCK0i)uW5LEYeJw=ZT@j@mjHp%qw;6b}2iYPe>U|1uG-;E2Nf0YJDN9w-c zvf_81AE-7$O!ak{D%5v^gr6Wt(%|;pRYzT8nvN*YD9&AKg;yZ#LSR8fN$?`fO>a9v zo|FVHWr=#p#oiTg`e%+bL0FO{==RyY=QJ5F>4v?cYNFCj51VA8pjaJ8EX z@dxVcn7R=FuQC|?7}%46_8u7v+zfE;sL%x+6U?9oe(Q!|3wj!GR0I(W2(C~glW}Wd zZ0G*XG}HbuHjKJpoDdPfoA8`iw2(+!2N6Sxx#_bN z(Qox4R?On%Dt-{d6_|VlUgKTMbOwg$Q~FfAEf4z;eyD+pWC7%W&?vi z$+vqWU8`^RhF$ynEnJE|9rcDRE3#e)7)qp<@Dncp$5ei`<3``xj z$h*cg%UDyiwkWOvMuBQfbk?xQ_+omiGJf_y*S~-{1wE zr{0j5^Dq0h=|p$%^mT+DP=0TfU7)EoNp~E|M}tB-7oEI2@3kX&uROc$`ufc}I}oh7;#H#(d0dh2GW5B{##UE%%FAnQ8Cc8n+-2s z7qoH`+8J)rR5P_R+@$dmb%w)+g|tH(j9Uo(>n8o{xN%gR;VvY{U?I~?D9V595cfH& zVER2ZDd=#IJ^)9t26KKB|Bbu6s18&x0Y@s*07?x(L;w}xe8qu^8eI|{@w^4gtwC0* z+1!9ihi_~)FJF&0V`0(3{oV`((7j{KI8@=(!)FFK;g(&H9HTI1(KU)6kf=2)GH#@S zPPaa%qnICUfgXMjRX7{_xR*z+M1M#Soq1ub^tz&^<R&W6%xPs63 zaP=chLG-hS_s}$e?>&@qEbMPvI8N^))+pKwKs}fPxSse`0RSu&^*=ZYZW;qL2yFZ$ zfSuOB#yVPqL)DHrZ^bi=;z}VY2X+R)j^GO#N;-f%zqoq9avdEw~x&TLuRK3_K5q z!yy>Tb{J$}Xp2=;d9)AKDOF~nN{&6TdwB6NOgO3uN&F-rnbMF%OC(!fyXxZOBh)EG zwF3hv?_J0?6#x$`1Hem_15cA}8o)!_AX^YC$<~?$(BUj1QONBhKnDj=NC;dF;6i{T zI%hI4i?0>C8622r3P{=>xjY050MEba- z6UF|}ps+tMWZT80k->k9>T%YJZh8cjhfL44bDyKDE4xLwKimgKr_#l=@VtBiA9S)peMA1CF0isR>21Ae6GmW6z6m!N6)}GrLC+kCd($v zG>)l`E!UN|jCS2RspX3;7vcaMI2&tfjWB0Gn_0ZN*y#|lwxxY5JKcbAe(9~}@-@QA zgbrpHl^aSqDY>&F`XO#xsSbiWn0K&vunqxvy@y*& zkpyY>!IJ7yAKGB9OUDFAF0~Z|iOt|*b}eL!k~)LMTIgdah0}XiLc+Kk2W8B85Dgf# zY?(pf31AGQh%Tn8P|W2oZ#Le=!o0~{mq(Y_Lk0bNYh|YmJ9A0}Goh1zf-0U!3l6Qn zh|A?b_-f#3&*d6-RuxfUyh=5S07yUU+dzY}O2|;sda1eTM5q$|y~VVzuw^hyq9DDs zShL=>wiauLwiGRJt#!C5J0qr`oPfmUFmfSJ13DTk*35*D1sQ}$RtRAb>|`d!lU1-$ z49*9kTMJPgRKXEQ4J=g$pw+cBoUpTvZy=F#vI~U;HUT`aS9OYV4STZ)J@64Mc?CU* z;m56%?RnhEFhrD62X>u)r7nZntpZ4}X!%|TlPI+cm7RVh(l|vTIM*tWsw9br766g9 zo7c^-KzKHEE=6QjZPO|?#UrBX8G9~VAK}e%Kd?-5t@rB@MFnDmG9DE8%805#vni`L zvLM7*UsQiDzik+xkCj^zQ$QYJ->oo(S{E12dJh6 zP6C_tPh)bp+V1Y#4`e-uZU#WdW~dfi^XHF&a)Vbm55@Ep=px;0y0%r&vpbJR83Gh8(C7xg8z4|~3xd~!2MWK~&GaBr2={_^Pqv5}b8MSCe; ze#i|G=pi3u^AnNu6YN2zDOgi-=N|f#V*K7%{uXu+`D7rnU@!ztlah7@e#J-ADHE21 zCDsFGZ=osNu5Hq|fTM!Eq|sQ9bAv)>SWPKpW4|BS&61ZNggP&ku9ix!c|PBfV$$g3 z{T@)IMP1PDGVudA5$={I*x4}+kV6h@@qa12j))FylwbfsCl!G7C@#c!-4{>%d9y;(DAYXnd z?w1_0T@DQJAg%@Ve-tcWfPhC4gEhfW03pQPq9-vlNP9zM99cRtX@NbQbdXIxSBT zvH%~Jokjl@oxEi}ABH_5lg>i4h!TLVYn6LlQZN!hNA$X&ZGgd|*M$r#0U^pty0PGk zv6LGNrNxa!;<^sN1Dqkl7b7xxP#NRk1V|^%+xb6o6}rL%)|;M5qsz+u(XsVu7A_#Q zLSz96c+f01C}b_jB^W79v5MzM&<{qB&es%dPKERsTM`iXIL9S2P%jBZQpq->BqiDd zS3#mN&{?QGL{@=FR=BRXRAu}alUfD#AOKJ?N|dWWi!fb69NL%=++-av@wp(lu>+5Q zYi@B<^$G?Pr2{Jo8}SZA^+bs=2c|?!B;w4GA#vsou}kN>UeQ@m1@IMIGV)+m*x{-$ zMGatrUKNC?zL-c>g|9FG@+MXVvx8M(`yvh-9t(>Zxa-JR^is&kH5epF0Y1o)-Z1Sz zO)1JF0_Z?>$zv!4@}DIC#v|4VrsNFb@nwXQOEN=kkQkQB-K{r>$fQtNJ!G}j*ksC-jGnS~Bv`u5*1IpcL!NmN(`0+yVj%WgiJ}P2qn<3%Wx58$7uUCO-W@Zv~YK{K4UN zb(G!vQ3CeKPEGiR5of)gqf`?c(RQ@yjW0W{I9iJ`zn2ze9v0|-I%DITnsTs&1fBGFYc{F;zc?nE+SO-(2?b0n)k{{!cjP~ zMoT;UV(;w^(9Sw4;08vl&RIrBJVL&xj#`eQqT&k$6O6++8XgcsolC(_^yC_}5;=|u zFBKLhyi}uf*SLGRxLi=dcTH-%+|OZr3q+0%do^@~&X+1j;h>Hgjqo=AUJ>5fW0`9Q zHG>ZejsQ<}1Jvbs6<(Py*xtuKL*^GnV$$&%4^I3%YlsUgde$%ZWY)ITnPmWuCKBv# zZfBq$27WRs91rqCyV-1n)idlG}HQ1TJ$H;4*^-tp=6U6wb3S!d-F$!rAi@6yEu^M$lQG7;-MdB8zm`$caEsRNtqfn6m2w32l zxV1_9(^`XdSRsDGEVn;RZa+e+*Fg^^6mZ~#b4XA;N^abL1yXhT`$EZbt`~Iy%_UH&YDjr?2Vp98zpIwJVE5prSA>tD490{+? z4C_cet|$#AMP{t63N@0(hy|!eED?PQlHt-iv|Obq%wuG@Bvq}4+#7bvLKKxL|17NH zMg!h&6+|*lIVpsJfm^@<35<}ps8Bz}7GPaB8V$lJQC9~aV*cMSXwaflBDjn+*1_THLF-=>)~N}c@pz0}E%N0(Qa+GwFq=GxoJ)z;k@ zE#B{;{DSx)2DMw_qc@Z{kzAAp_*Hyejuza%*jsS_g5X|(scT7ylLo0HMNCE63h9SI zJSkWDgV5y%C!yvrCJ4mKARvfZMD;)*V<+SbT(lE4Gf8%&u)jYKQ%bSlHVoP4(tfElEUj z`C)cM1o8=oUEmSA^<=(=Aujw zoB_8h;4lq&B{>_IN4O7!s|q0HWs6SqFc>P_WOlq$QG<*XH9+xMVby2>te59>kd!Z` z=JR|9V@VljNvj5&$0*iBHGfCNq4}G=MX2;X>(sZ_BZ0;Zcq4tp68DBsY`ll2O-*FO zM6=G|)_P%`Ic3`T8{@pQUM`h56{lrf875?r9H8_!AG2anavpW?C{H))D15MKfhYVx zf20m?aYNF>!6?Y3+$(m5_h~69NolF!!ui8#jD`Rl0)S8kEYv8!DAuwS5 z&4)q_BxCCc6U>Cs6L_;x7V{T%ff@@YA*feo687KKOiVU`YbzAmDNY6L+TfGv`BU@b z@32=JShOlIfCU2tIXjj004GnALDcAc(D(9f4YsYPdO88$mk=lf%SP zQZuz;GZnPkM7#L>5kyjl`J1YYL#*fi(=t;LhG{E4CKyCOlv5t>=};|VU=z$tEDG zC?XMtI}^cG7+c5~Oe8w7l8n}bW)T#EvW651Uc8p0#CRomF|C6a(E)=O^C}&OwHfJ& zUQ`ECn7twq3xQq* z2fZljxsKowjYpvMarH{Xk+!Z^uSV{hA6t_m|^dL%F#KT4SCc9Squ z4UG_HY$XOW=pj*<`77>Y3M!)n;d3gwJ92|I)T6T1UM;#CB5-uk-6T2z^vI*TRTYA& zdUSUl9?VE|w+|3(m-s9QQZO3boyRc=w`z2^W;fYl9oM)?#)Gd9#MsMN=0I0et6#@` zk5%uq&&DtxI?vw4fsfNmusjZY99GXiKpZ&Xtoi5-q&?87@X@j$@PLm(7?EXaMy>fM zl#PI;2YfVIGT@_OImJgMP8>o9x4j{7oZ=_jo{SxT*m4+yWUgaaBm_7PY9a(5X6Oc< zw}AV6y&MQBrVx~$mf$!jCft%0$;cxa%qO!&{C@dq1+F!rXB;Teyq4!ce6f!RB}UQ{ zO4L^dDf4k^#70Ct#$q=MRnt|;R?4`oG>kKMxL{Xj!MUwkqvjA;;N{I}9?T;UGJ_E{ zoKg*^q+tp8mjwl`oW|+eG5|tIhXBN->bnCVuv!&>)G~JmKms`dK(dA50H_;I0+6jG z0T^j+Hvp{MFaXGg0|07{R2hJ9ErkHg?jC?x0aXD=k+?ema>xk))>*>?P&b?eAX`fU zFw)#^09d(U0FVs_0EAVdWn2Xcg!UBz5US|zC=i^fDgf2ugB~=@66TN-0HnP)90lrz zlK^CENdQKg+YJCKHw*x>;Q)a8#FJ3qPwjMQwlTaHLpSBX-BxaxZBQ}}%4B-I){?*s zaTE}d!qA<`3=2GV2>y=nxUFs`dS)vvXRxRp%HcXk_#lLn@G2){am)`{T&xx?K9!}a zVRX8ph6HZUY!5~-Oe^4AdBpEQ706Pb#N8wyOSFcWscGWq9_N^eOfR*Y>5(og%nhnR z)wu;#$|lA&GO~%`U^;shU#f#y)x~GUl|nuECQ9Ah4jT-bhB+c_Ia^>B`@{Z}sS=lA zmg0?P&lG0GJxa{t>=`f{$}WW?FdrYYBEtklFe$3eUg$yBDgyv%JPv^H1X?Bf2wMRR z;5i{?>EERn&OuudyyEPaz96`k4tViXLC{Q7?}`oC-RA9ju2XXBz0a$Hx11MmUrbEV^G(J&b~ zj)t8E71>T!6LUw)E!i;l;fTkr88DF~K%A#n7=$5yMmc>1e{&knHR1QXTLjDwKfyHz zBRX|L24RS3SO;sVdUVG{if$;G4LR9$&P1qnhU;q^R=5aDU z5yud##3kK5)p6+nmo)$%@Rt%HOe+#cGcp7zDr)>X$gfEjQvC`c)v{mTJS;}S*XzU! z&jjtE&e1M}m9eo{D<0~cFU)G)I+(l6fm(p_@Wt2qF<0WD9#darHyo>`)ocgvK;K69 z{Fr+2sN;TR5Tk_BR5;Q>SH3#ZfrIJ1O#*{vygi6(xGPDJ>EHi?D{640WB6N0YAfAB zqJ!w^W|d$naAN>i$SXsKGfr?|(m1?;YcGI7Tu{_t@C=g)r!EjHyG?*JJ^@BZr?42t zvqUT)KyBtDAV85=?C_!l1Vqm=Q3a0&1fa7H^aw?X^aULqG>8)!vkGREG{8H2iqk9v zqmA?Ar%QB(+e>`nOOg6Y$vtPU+|%m;@S1@qX=BVltW>|J3T8-Z%` z3^+%YFp>&5Hv*i4p#bKJp~#&-!cf2*${C8>a$*EH2SWkQjq+nWI%I-({$QnmhrqPa zcm5#4Aadsqb`gA>3&x>P6ZyE{#LnibZrx8pdlk-kBa8n*kPmyFq^zW(0s zON<*HeeT!)-PLg5mv(-%w&W_4_{_x{oU9zkAL^XWA3}*pX=WKuA!CB7dFjqHpaa%`Iud2ocf`Q3-y=1(DL*R zzxaxK^{qLTA@{u3=&0C)wI^$LA-S64;(3ID2(<&x)8 zla4;5>6Uvhzi4gE&I9Uq9P!{Onb*Ga?*I7X1FQdX>)=E8PVN6>-+62Hzv}u)8y`C0 zxLeF~PWslg_buD?R_Cm*eeLzD-gWcPmUnJ^^g|!*f8fyk2TmE>_|y~APHZ{-taqOK z)sI~FrNbA019iKh^*i|1na>C@#pY^S? zmj9$T^{uAgU;WJAzW>CXcRqT}XOEe^;ij`cdHvv9XZ-9>-+bTf8~-rB`7e*n_{*bz zJLkl=onLoX%k7Wc+Wm>wKU+WXyA4Mgk^jbj;E%nJOe&ms%QLs$u+03QWncby)8liG zI{YoSx1Mv$>|<7+y5_XO=TE)(p6)BxZh2qoe;!)-(dS;i{ySsl-t%vdrau3X554yI zMRV)DEr-wg;=|8$4}9y|L)X1`qxF?rCY|xI+?Bui*LhFhv*CuWnQz{5@@JoJ`djA2 z6JCDVIN_!3-+SNRK6U-)HoWVPuhb7NSTm{i2krY!{qhISuX*R(1IC}wxZvC|FEriL zZ{PCXYrVVQ_HgQfu4{J9`u#&6X`b-h2M@UBjW5r8DgDCRezxgPiynI7wRzKK8s|Uv zIrGgOUmJhSmh(4#=$7aI{Q3RX|E2qz&z=6>51)P2RO8R5yz%my4PX9b@yRu>zVhPs zLymO+yY2TEJ?oucD(0?gc<{o1x&F$o6V6|F{fiIuzOVV1#shMH&Ru?X>+L7rarCFR zeP-oJ-JyWT%;=Cq&OYwf(C_4tl$;}6UJ_LK*1bN*@ezs;F& zM6T03>FoP%JL`%^o~XO$swb~Ly!P22KlSltZ@IAjPapjEH&%RU?PJd@J+S+G_G90; z_7Pu`uay*@SJg~9g@Ex7%$9WQ=i_Sm0!J6^u~nHTSyc;HWdf9Lxr{_cv? zn-6{9!~1>XqEoCXKb-SS=hJ_ha@y9DX5aL+^PYO&DZjgC@;!~%d`sDG> zfwNb=KIx1#(;uGqqr)G}{QTz~H+*GX&rgnjV(^LY+|e}e>7L`K)g5@-*{?tR)q^+x z@!V-|4&L(4tN!`Uma{(mG9Kl{ow4D?$f$^!8_FNl6ftC{iWWnQh$)L2wa8l zXSQ^9mU=H)G9`FAHh7w9bYI+6>NN&>miM-_wk+)|84LT#k`^voQEDCNEV(MDWGrau z>+4_9+dZ&+MWpmpqqMrGvu#;he^9Wu)YI9rtmL+|u59Zv+C2H`=`i|A{Vr16mY$y8 z?p3AUKBH@3rQro{=ag25jm%Me^1NVJ$AL+9wp?P&i?tw)_Zj^%@VQ;BrPan+rAv&3 zrT+f5uH}8k{4Q5DQED|7^p;k+?6u>2O8H-yw+ge-tyLY-@))LaI4V&9Hqd9B*1d9NTc5PPxSQR(3%V~Z^}0u~ z02(~Eba6{>tGl4o)rw62^{B8(0)C+HydLygGQ8GSRUzz1=QMYBwJuqoYvmSyeLL+0xbt zxGfy$>%o}u#73CV+0wTn?5NLpw?bBP%d(En?&WSzskg1W)tJ|^+P&C^Y^#QMkRX_w zd|H2}ae9|vPbCDXP^qoP*)4tjVKb-qbT3;Gyt=)ml`U;ugr>@99q4V5>8e-1F}JgA zC2*}M5$k2LESxhzkDv)Gj_zfEc&l+nOE2hiIVh%8m393FjF#U1B>>xk-nM0en$uUW zXj_UVdpd-G`r7`bg!y*0EH8Pz{Xm4#)v^+6sMN1&G!I;2tZ3;Y8Xy&;ntN^uv<|d#1)W=JEv@X4WWCbP>|W(A>h36Y8RrZD%e@#O z7O6WQs0U?t0{0y{>9o%7zLI{P*WKE7;U&fy16{3suD5I%sRv}dw5_wP{}RxDcPH3_ z(DON^i(OCYK>nagE|i{CC9WK*MjTTK3_%qF4BWDo^mlcV%~L$#@S2bWXQ%Mycd3?d}FD zRZqrTlne*dgQ*$KrIwZFtG^&cS=}K;qHG7LNex%cnLttR3rf9{dq5H;_w+6-M_@*? zodq36b8lPga%8OT>F(|K-{!8Aw`JYk9c_X`NnJm2tMP5kVlBBo_0$Qa^+y;E6TGsUz)J{+Wt3+le`H6&PoCUo0?zu$u$r z-;hVX zhCEEg0~-+Poh*b5n3NPvMiSNIIXx?kr9$i>-2y$rF}X@Xrb=`MOO$eSw$F{vrkGhl zLRa|_zU}i|pL0jD{E=ghs9$Pz!6e!`brT>jJy?>7!mLYRC`<{AIjdvT8V%n$l`z8{ zz{=}W3CPi}RKs)8ihr@#u;gqYulbBcOJ*;a zzfe6bT;we>z#}>*`+8yYC88Y0l6?(P5<@jH_Ij*p*3X@4DE2zla90`UlzLY5wm}S` zQ#F-lh&5yO>E|%l=q;7<`8iOz6wg?=qGjr|>HF0%<=q7S{&jo=-3f0#Aj>ya8ec2* zJ?b|HI&)e&`wyyJUg}?>`1ZkL`0SAw=XPD#J>f(J5XC(fwk_`}wKhP>>~)uQclGtZ z?P#pNK1qer2&u+DM^iv{HooO;yQ>=1Xq-4ZTDG}u`R@Bj_S;zBwW_O@ z)<(6csp-7AUHvB?KQObY336g~+bRgYwpJt@?H)TYv!Pn)Nk><12zAulCp8{2FBk;) zyxZK?e;zndUsF>n_`f`WEh4q=T~a&olB%!cS&hJQ|X?@AaV z|5Dl~6FF>{p34Cj>3Q04Ae=TZa|9qfe5OWxTdN|mDu@SsE0J2G>y_j0jLz%74GLr1 zg>B3J(WiFg$%<;RyqCoZwjwSflnYvEJZe_OQe`wNN@c*s;+$gA2_tFYv~EzyYOICR zS~{0e%$6D&-+E5Pn$55{(0|)1AM#;;0h5l*B1mg&cz?jcf<#yR8&NJuf5*wkP9YCG zx23JG6mr3!5}5InfzHk{me_dE0!YMCZ!i^kZD@Sk;brObyP$JJb6O(mMnmJJpSXR>ogQrB!f`lcDPL_N) zd+`{sCMv8mkJc_kF$v-7g z3+%tyXtZW9vJUc@f_s?EBW%kQgEYD;Qpo)>t|z zL5EAEbzfLDMElZ=*C&|^&xvMIgPUSm>Y=c#4OLFA<`i-R@cCnUADm1mbVfVIfdZFE7)7OC1dZU~!D{HCdi zPKQOSrHmTi+Zww@uXKg&N=yDCC`*nJepy}_v6tmeYV6*y*N6(+963KMuQRE!XZ>Ce zMC))(O=>qb29`K=1te2eWm4lshlfL3(9)*upL3VN?V&35E(!~%`GO5_W3{PiZeMe$ zv-{!*HL!)CuZqqo-D=;oWUC}0?puoQtmTplLZGpKlO-a2PWQ_0<)yCffj(~f0RY@3 zka&=ju_(bl;AR3VJ-NSkpma1M5cs!@wEh02)D9oAZ+Rgy!GAfN}huu>^kA|7ppL{Qb70+g2WEG?x#U%XEIQD(+#$2;~K zCmZ$9P3w7jJUcV~vfRYPy^MxM$j1HCY?v$pN4Er5th7-tQ`DC0k|Fk&F8y>dr4u zpr0SLKHZ`ATr%gJ@&;0+UtARivhS(;alWVV!(GG>@d>wowPw9MY;9JQe~wX6LZvrl z4ag>$(U(=fFdxG(de#1IAX(>_B59P~>WPiPI|?1VaB;}6WG#Hl*Y`VDkq zt6V)9Nk?ztex43w$FPX8_Y*p4Q`hMoT#FIWV36QVQI6&fW;gmhv_E!Ih3s4sB~Czptt^z5a!xzW|0QAas4*smBsLPjrB#&M1eM!$qIx|uRg za%AvuA(YX{lyRDhy##uPVy`h260zrn!dXf*ZfEU?dtopG;LwjLMbE(MpHqsS!OZ9v z+>`C1fo^cGMUUh3?fSJV2xhMLzJ_M;$-v@|e#5Z=>&TTsGm0(pQrYJ&IqY-Y>MA9N zlHCeZt~58dRnz|m{!UyojGJ4QW`^sHhGDzz&88!?|F=cybLtWTgXz7uF@($bNZ)u^ Vc|7-U1m_<;#joUC=yvAu=O0{(ZNUHl diff --git a/bvm/Shaders/make_all.sh b/bvm/Shaders/make_all.sh new file mode 100755 index 0000000000..9e9e025767 --- /dev/null +++ b/bvm/Shaders/make_all.sh @@ -0,0 +1,113 @@ +#!/bin/sh +# Build every shader. Optional --export [out_dir] also collects per-contract +# parser modules into a single folder ready for an explorer's +# --contract_rich_parser_folder. Default out_dir: Explorer/modules/ +# +# Usage: +# make_all.sh # build only +# make_all.sh --export # build, export to Explorer/modules/ +# make_all.sh --export # build, export to +set -e +cd "$(dirname "$0")" + +EXPORT_FLAGS="" +if [ "$1" = "--export" ]; then + OUT_DIR="${2:-Explorer/modules}" + EXPORT_FLAGS="--export $OUT_DIR" + mkdir -p "$OUT_DIR" + find "$OUT_DIR" -name '*.wasm' -delete +fi + +build() { + ./make_shader.sh $EXPORT_FLAGS "$1" +} + +build amm/app +build amm/contract +build amm/parser +build asset_man/app +build asset_man/contract +build bans/app +build bans/contract +build bans/parser +build blackhole/app +build blackhole/contract +build blackhole/parser +build aphorize/contract +build dao-accumulator/app +build dao-accumulator/contract +build dao-accumulator/parser +build dao-core/app +build dao-core/app-admin +build dao-core/contract +build dao-core/parser +build dao-core-masternet/app +build dao-core-masternet/app-admin +build dao-core-masternet/contract +build dao-core-testnet/app +build dao-core-testnet/app-admin +build dao-core-testnet/contract +build dao-core2/app +build dao-core2/contract +build dao-core2/parser +build dao-vote/app +build dao-vote/contract +build dao-vote/parser +build dao-vault/app +build dao-vault/contract +build dao-vault/parser +build dummy/app +build dummy/contract +build faucet/app +build faucet/contract +build faucet/parser +build faucet2/app +build faucet2/contract +build faucet2/parser +build fuddle/contract +build gallery/app +build gallery/app-admin +build gallery/contract +build gallery/parser +build minter/app +build minter/contract +build minter/parser +build mirrorcoin/app +build mirrorcoin/contract +build nephrite/app +build nephrite/contract +build nephrite/parser +build oracle/contract +build oracle2/app +build oracle2/contract +build oracle2/parser +build perpetual/app +build perpetual/contract +build pipe/contract +build playground/app +build playground/contract +build profit_pool/app +build profit_pool/contract +build roulette/app +build roulette/contract +build sidechain/contract +build sidechain_pos/parser +build pbft/parser +build StableCoin/contract +build upgradable/contract +build upgradable2/contract +build upgradable2/Test/test_app +build upgradable2/Test/test_v0 +build upgradable2/Test/test_v1 +build upgradable3/Test/test_app +build upgradable3/Test/test_v0 +build upgradable3/Test/test_v1 +build upgradable3/Test/test_v0_migrate +build vault/app +build vault/contract +build vault/parser +build vault_anon/app +build vault_anon/contract +build vault_anon/parser +build voting/app +build voting/contract diff --git a/bvm/Shaders/make_shader.sh b/bvm/Shaders/make_shader.sh new file mode 100755 index 0000000000..972aacfc1b --- /dev/null +++ b/bvm/Shaders/make_shader.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# Build a wasm shader. Optional --export additionally copies +# parser modules (basename "parser") into for bundling into +# an explorer --contract_rich_parser_folder. No-op for non-parser targets. +# +# Usage: +# make_shader.sh # build only +# make_shader.sh --export # build, then export if parser + +CLANG="${CLANG:-/opt/homebrew/opt/llvm/bin/clang}" + +EXPORT_DIR="" +if [ "$1" = "--export" ]; then + EXPORT_DIR="$2" + shift 2 +fi + +NAME="$1" + +"$CLANG" -O3 --target=wasm32 -std=c++17 -fno-rtti \ + -Wl,--export-dynamic,--no-entry,--allow-undefined \ + -nostdlib "$NAME.cpp" --output "$NAME.wasm" + +if [ -n "$EXPORT_DIR" ]; then + case "$NAME" in + */parser) + mkdir -p "$EXPORT_DIR" + BASENAME=$(dirname "$NAME") + cp "$NAME.wasm" "$EXPORT_DIR/$BASENAME.parser.wasm" + ;; + esac +fi From 6866e25bf3681770fb137a5d722ded5c63137cf3 Mon Sep 17 00:00:00 2001 From: Maxnflaxl Date: Fri, 19 Jun 2026 14:43:26 +0200 Subject: [PATCH 4/5] remove stray core/version.h and shader make scripts core/version.h is generated into the build dir by CMake (core/version.h.in); the committed copy is an in-source build artifact (it hardcodes a stale version) and no translation unit references it. make_all.sh / make_shader.sh are shader build helpers that don't belong in this PR. --- bvm/Shaders/make_all.sh | 113 ------------------------------------- bvm/Shaders/make_shader.sh | 32 ----------- core/version.h | 12 ---- 3 files changed, 157 deletions(-) delete mode 100755 bvm/Shaders/make_all.sh delete mode 100755 bvm/Shaders/make_shader.sh delete mode 100644 core/version.h diff --git a/bvm/Shaders/make_all.sh b/bvm/Shaders/make_all.sh deleted file mode 100755 index 9e9e025767..0000000000 --- a/bvm/Shaders/make_all.sh +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/sh -# Build every shader. Optional --export [out_dir] also collects per-contract -# parser modules into a single folder ready for an explorer's -# --contract_rich_parser_folder. Default out_dir: Explorer/modules/ -# -# Usage: -# make_all.sh # build only -# make_all.sh --export # build, export to Explorer/modules/ -# make_all.sh --export # build, export to -set -e -cd "$(dirname "$0")" - -EXPORT_FLAGS="" -if [ "$1" = "--export" ]; then - OUT_DIR="${2:-Explorer/modules}" - EXPORT_FLAGS="--export $OUT_DIR" - mkdir -p "$OUT_DIR" - find "$OUT_DIR" -name '*.wasm' -delete -fi - -build() { - ./make_shader.sh $EXPORT_FLAGS "$1" -} - -build amm/app -build amm/contract -build amm/parser -build asset_man/app -build asset_man/contract -build bans/app -build bans/contract -build bans/parser -build blackhole/app -build blackhole/contract -build blackhole/parser -build aphorize/contract -build dao-accumulator/app -build dao-accumulator/contract -build dao-accumulator/parser -build dao-core/app -build dao-core/app-admin -build dao-core/contract -build dao-core/parser -build dao-core-masternet/app -build dao-core-masternet/app-admin -build dao-core-masternet/contract -build dao-core-testnet/app -build dao-core-testnet/app-admin -build dao-core-testnet/contract -build dao-core2/app -build dao-core2/contract -build dao-core2/parser -build dao-vote/app -build dao-vote/contract -build dao-vote/parser -build dao-vault/app -build dao-vault/contract -build dao-vault/parser -build dummy/app -build dummy/contract -build faucet/app -build faucet/contract -build faucet/parser -build faucet2/app -build faucet2/contract -build faucet2/parser -build fuddle/contract -build gallery/app -build gallery/app-admin -build gallery/contract -build gallery/parser -build minter/app -build minter/contract -build minter/parser -build mirrorcoin/app -build mirrorcoin/contract -build nephrite/app -build nephrite/contract -build nephrite/parser -build oracle/contract -build oracle2/app -build oracle2/contract -build oracle2/parser -build perpetual/app -build perpetual/contract -build pipe/contract -build playground/app -build playground/contract -build profit_pool/app -build profit_pool/contract -build roulette/app -build roulette/contract -build sidechain/contract -build sidechain_pos/parser -build pbft/parser -build StableCoin/contract -build upgradable/contract -build upgradable2/contract -build upgradable2/Test/test_app -build upgradable2/Test/test_v0 -build upgradable2/Test/test_v1 -build upgradable3/Test/test_app -build upgradable3/Test/test_v0 -build upgradable3/Test/test_v1 -build upgradable3/Test/test_v0_migrate -build vault/app -build vault/contract -build vault/parser -build vault_anon/app -build vault_anon/contract -build vault_anon/parser -build voting/app -build voting/contract diff --git a/bvm/Shaders/make_shader.sh b/bvm/Shaders/make_shader.sh deleted file mode 100755 index 972aacfc1b..0000000000 --- a/bvm/Shaders/make_shader.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/sh -# Build a wasm shader. Optional --export additionally copies -# parser modules (basename "parser") into for bundling into -# an explorer --contract_rich_parser_folder. No-op for non-parser targets. -# -# Usage: -# make_shader.sh # build only -# make_shader.sh --export # build, then export if parser - -CLANG="${CLANG:-/opt/homebrew/opt/llvm/bin/clang}" - -EXPORT_DIR="" -if [ "$1" = "--export" ]; then - EXPORT_DIR="$2" - shift 2 -fi - -NAME="$1" - -"$CLANG" -O3 --target=wasm32 -std=c++17 -fno-rtti \ - -Wl,--export-dynamic,--no-entry,--allow-undefined \ - -nostdlib "$NAME.cpp" --output "$NAME.wasm" - -if [ -n "$EXPORT_DIR" ]; then - case "$NAME" in - */parser) - mkdir -p "$EXPORT_DIR" - BASENAME=$(dirname "$NAME") - cp "$NAME.wasm" "$EXPORT_DIR/$BASENAME.parser.wasm" - ;; - esac -fi diff --git a/core/version.h b/core/version.h deleted file mode 100644 index b2152f025d..0000000000 --- a/core/version.h +++ /dev/null @@ -1,12 +0,0 @@ -#include - -inline constexpr unsigned VERSION_MAJOR = 7; -inline constexpr unsigned VERSION_MINOR = 5; -inline constexpr unsigned VERSION_REVISION = 14456; -inline const std::string GIT_COMMIT_HASH = "16f9d5870a70026e8cb2e9b94f7bf5470224a4d4"; -inline const std::string PROJECT_VERSION = "7.5.14456"; -inline const std::string BRANCH_NAME = "boost-1.90"; - -#ifndef BEAM_LIB_VERSION -#define BEAM_LIB_VERSION "7.5.14456" -#endif From da0e926d49bb51c4b5d63c2b25de05bf8b2e1739 Mon Sep 17 00:00:00 2001 From: Maxnflaxl Date: Fri, 19 Jun 2026 15:37:16 +0200 Subject: [PATCH 5/5] sign_message: bind the signer's pubkey into the signed digest The address-based sign_message / verify_message digest is now H("beam.signed.message" | pubkey | len | message) instead of H("beam.signed.message" | len | message). Folding the WalletID public key into the hash makes the Schnorr challenge key-dependent (as in BIP-340 and Monero), binding each signature to exactly one address and preventing related-key signature malleability; the fixed-width framing also removes the ambiguous decimal length. The legacy key_material path and verify_signature keep the previous key-independent digest for backward compatibility. --- wallet/api/v7_0/v7_0_api_handle.cpp | 16 +++++++++++++--- wallet/cli/cli.cpp | 16 +++++++++------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/wallet/api/v7_0/v7_0_api_handle.cpp b/wallet/api/v7_0/v7_0_api_handle.cpp index 52aaf6e6a9..4c73e7723e 100644 --- a/wallet/api/v7_0/v7_0_api_handle.cpp +++ b/wallet/api/v7_0/v7_0_api_handle.cpp @@ -224,6 +224,16 @@ namespace beam::wallet << ss.str() >> hv; } + + void GetMessageHash(ECC::Hash::Value& hv, const PeerID& pk, const std::string& message) + { + ECC::Hash::Processor() + << "beam.signed.message" + << pk + << (uint64_t) message.size() + << Blob(message.data(), (uint32_t) message.size()) + >> hv; + } } void V70Api::onHandleSignMessage(const JsonRpcId& id, SignMessage&& req) @@ -240,6 +250,7 @@ namespace beam::wallet MyProcessor::DeriveKeyPreimage(hv, Blob(*req.keyMaterial)); auto pKdf = db->get_MasterKdf(); pKdf->DeriveKey(sk, hv); + GetMessageHash(hv, req.message); } else { @@ -258,10 +269,9 @@ namespace beam::wallet } PeerID pid; db->get_SbbsPeerID(sk, pid, addr.m_OwnID); + GetMessageHash(hv, pid, req.message); } - GetMessageHash(hv, req.message); - ECC::Signature sig; sig.Sign(hv, sk); @@ -289,7 +299,7 @@ namespace beam::wallet void V70Api::onHandleVerifyMessage(const JsonRpcId& id, VerifyMessage&& req) { ECC::Hash::Value hv; - GetMessageHash(hv, req.message); + GetMessageHash(hv, req.address.m_Pk, req.message); Deserializer d; ECC::Signature sig; diff --git a/wallet/cli/cli.cpp b/wallet/cli/cli.cpp index e6500cc72a..b1401ce680 100644 --- a/wallet/cli/cli.cpp +++ b/wallet/cli/cli.cpp @@ -3326,12 +3326,14 @@ namespace return 1; } - // Hash a message for signing/verification: SHA256("beam.signed.message" + len + message) - void GetSignMessageHash(ECC::Hash::Value& hv, const std::string& message) + void GetSignMessageHash(ECC::Hash::Value& hv, const PeerID& pk, const std::string& message) { - std::stringstream ss; - ss << "beam.signed.message" << message.size() << message; - ECC::Hash::Processor() << ss.str() >> hv; + ECC::Hash::Processor() + << "beam.signed.message" + << pk + << (uint64_t) message.size() + << Blob(message.data(), (uint32_t) message.size()) + >> hv; } int SignMessage(const po::variables_map& vm) @@ -3370,7 +3372,7 @@ namespace // Hash the message std::string message = vm[cli::MSG_TO_SIGN].as(); ECC::Hash::Value hv; - GetSignMessageHash(hv, message); + GetSignMessageHash(hv, pid, message); // Sign ECC::Signature sig; @@ -3409,7 +3411,7 @@ namespace std::string message = vm[cli::MSG_TO_SIGN].as(); ECC::Hash::Value hv; - GetSignMessageHash(hv, message); + GetSignMessageHash(hv, wid.m_Pk, message); ByteBuffer sigBuf = from_hex(vm[cli::SIGNATURE].as()); Deserializer d;