From 7a74dcb76c3d55510b643b436706933029a37578 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Wed, 17 Apr 2024 09:56:06 +0200 Subject: [PATCH 1/3] feat(FIO): Add support for "remaddress" action type (#3795) * [FIO]: Add support for "remaddress" action * [FIO]: Add `SigningOutput::action_name` parameter --- src/FIO/Action.cpp | 2 +- src/FIO/Action.h | 9 +- src/FIO/Signer.cpp | 4 +- src/FIO/TransactionBuilder.cpp | 90 ++++++++++++++----- src/FIO/TransactionBuilder.h | 30 ++++++- src/proto/FIO.proto | 17 ++++ tests/chains/FIO/SignerTests.cpp | 1 + tests/chains/FIO/TWFIOTests.cpp | 32 +++++++ tests/chains/FIO/TransactionBuilderTests.cpp | 6 +- tests/chains/FIO/TransactionCompilerTests.cpp | 3 +- 10 files changed, 165 insertions(+), 29 deletions(-) diff --git a/src/FIO/Action.cpp b/src/FIO/Action.cpp index 15b0b21906b..5be60a17866 100644 --- a/src/FIO/Action.cpp +++ b/src/FIO/Action.cpp @@ -41,7 +41,7 @@ void Action::serialize(Data& out) const { append(out, 0); // 00 } -void AddPubAddressData::serialize(Data& out) const { +void PubAddressActionData::serialize(Data& out) const { encodeString(fioAddress, out); addresses.serialize(out); encode64LE(fee, out); diff --git a/src/FIO/Action.h b/src/FIO/Action.h index 7e9fd4e56ea..1ebdc3eff91 100644 --- a/src/FIO/Action.h +++ b/src/FIO/Action.h @@ -81,8 +81,11 @@ class Action { void serialize(Data& out) const; }; -/// AddPubAddress action data part. -class AddPubAddressData { +/// A public address action data part. +/// Can be used for `addaddress`, `remaddress` actions. +/// https://dev.fio.net/reference/add_pub_address +/// https://dev.fio.net/reference/remove_pub_address +class PubAddressActionData { public: std::string fioAddress; PublicAddresses addresses; @@ -90,7 +93,7 @@ class AddPubAddressData { std::string tpid; std::string actor; - AddPubAddressData(const std::string& fioAddress, const std::vector& addresses, + PubAddressActionData(const std::string& fioAddress, const std::vector& addresses, uint64_t fee, const std::string& tpid, const std::string& actor) : fioAddress(fioAddress), addresses(addresses), fee(fee), tpid(tpid), actor(actor) {} diff --git a/src/FIO/Signer.cpp b/src/FIO/Signer.cpp index d4e5361c318..440e0c5535a 100644 --- a/src/FIO/Signer.cpp +++ b/src/FIO/Signer.cpp @@ -23,9 +23,11 @@ using namespace std; Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept { FIO::Proto::SigningOutput output; - try { + try { + const string actionName = TransactionBuilder::actionName(input); const string json = TransactionBuilder::sign(input); output.set_json(json); + output.set_action_name(actionName); } catch(const std::exception& e) { output.set_error(Common::Proto::Error_internal); } diff --git a/src/FIO/TransactionBuilder.cpp b/src/FIO/TransactionBuilder.cpp index e9881d1eb71..bf5ca8a8302 100644 --- a/src/FIO/TransactionBuilder.cpp +++ b/src/FIO/TransactionBuilder.cpp @@ -21,6 +21,12 @@ using namespace TW; using namespace std; using json = nlohmann::json; +static constexpr auto gRegisterFioAddress = "regaddress"; +static constexpr auto gAddPubAddress = "addaddress"; +static constexpr auto gRemoveAddress = "remaddress"; +static constexpr auto gTransferFIOPubkey = "trnsfiopubky"; +static constexpr auto gRenewFIOAddress = "renewaddress"; +static constexpr auto gNewFundsRequest = "newfundsreq"; /// Internal helper ChainParams getChainParams(const Proto::SigningInput& input) { @@ -38,6 +44,25 @@ bool TransactionBuilder::expirySetDefaultIfNeeded(uint32_t& expiryTime) { return true; } +string TransactionBuilder::actionName(const Proto::SigningInput& input) { + switch (input.action().message_oneof_case()) { + case Proto::Action::MessageOneofCase::kRegisterFioAddressMessage: + return gRegisterFioAddress; + case Proto::Action::MessageOneofCase::kAddPubAddressMessage: + return gAddPubAddress; + case Proto::Action::MessageOneofCase::kTransferMessage: + return gTransferFIOPubkey; + case Proto::Action::MessageOneofCase::kRenewFioAddressMessage: + return gRenewFIOAddress; + case Proto::Action::MessageOneofCase::kNewFundsRequestMessage: + return gNewFundsRequest; + case Proto::Action::MessageOneofCase::kRemovePubAddressMessage: + return gRemoveAddress; + default: + return {}; + } +} + string TransactionBuilder::sign(Proto::SigningInput in) { PrivateKey privateKey(in.private_key()); PublicKey publicKey = privateKey.getPublicKey(TWPublicKeyTypeSECP256k1); @@ -76,6 +101,16 @@ string TransactionBuilder::sign(Proto::SigningInput in) { action.payer_fio_name(), action.payer_fio_address(), action.payee_fio_name(), content.payee_public_address(), content.amount(), content.coin_symbol(), content.memo(), content.hash(), content.offline_url(), getChainParams(in), action.fee(), in.tpid(), in.expiry(), Data()); + } else if (in.action().has_remove_pub_address_message()) { + const auto action = in.action().remove_pub_address_message(); + // process addresses + std::vector> addresses; + for (int i = 0; i < action.public_addresses_size(); ++i) { + addresses.emplace_back(std::make_pair(action.public_addresses(i).coin_symbol(), action.public_addresses(i).address())); + } + json = TransactionBuilder::createRemovePubAddress(owner, privateKey, + action.fio_address(), addresses, + getChainParams(in), action.fee(), in.tpid(), in.expiry()); } return json; } @@ -96,7 +131,19 @@ string TransactionBuilder::createAddPubAddress(const Address& address, const Pri const vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) { - Transaction transaction = TransactionBuilder::buildUnsignedAddPubAddress(address, fioName, pubAddresses, chainParams, fee, walletTpId, expiryTime); + Transaction transaction = TransactionBuilder::buildUnsignedPubAddressAction(gAddPubAddress, address, fioName, pubAddresses, chainParams, fee, walletTpId, expiryTime); + + Data serTx; + transaction.serialize(serTx); + + return signAndBuildTx(chainParams.chainId, serTx, privateKey); +} + +string TransactionBuilder::createRemovePubAddress(const Address& address, const PrivateKey& privateKey, const string& fioName, + const vector>& pubAddresses, + const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) { + + Transaction transaction = TransactionBuilder::buildUnsignedPubAddressAction(gRemoveAddress, address, fioName, pubAddresses, chainParams, fee, walletTpId, expiryTime); Data serTx; transaction.serialize(serTx); @@ -134,8 +181,6 @@ string TransactionBuilder::createNewFundsRequest(const Address& address, const P const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime, const Data& iv) { - const auto* const apiName = "newfundsreq"; - // use coinSymbol for chainCode as well NewFundsContent newFundsContent { payeePublicAddress, amount, coinSymbol, coinSymbol, memo, hash, offlineUrl }; // serialize and encrypt @@ -154,7 +199,7 @@ string TransactionBuilder::createNewFundsRequest(const Address& address, const P Action action; action.account = ContractPayRequest; - action.name = apiName; + action.name = gNewFundsRequest; action.actionDataSer = serData; action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); @@ -207,7 +252,7 @@ Data TransactionBuilder::buildUnsignedTxBytes(const Proto::SigningInput& in) { for (int i = 0; i < action.public_addresses_size(); ++i) { addresses.emplace_back(std::make_pair(action.public_addresses(i).coin_symbol(), action.public_addresses(i).address())); } - transaction = TransactionBuilder::buildUnsignedAddPubAddress(owner, action.fio_address(), addresses, + transaction = TransactionBuilder::buildUnsignedPubAddressAction(gAddPubAddress, owner, action.fio_address(), addresses, getChainParams(in), action.fee(), in.tpid(), in.expiry()); } else if (in.action().has_transfer_message()) { const auto action = in.action().transfer_message(); @@ -217,7 +262,16 @@ Data TransactionBuilder::buildUnsignedTxBytes(const Proto::SigningInput& in) { const auto action = in.action().renew_fio_address_message(); transaction = TransactionBuilder::buildUnsignedRenewFioAddress(owner, action.fio_address(), getChainParams(in), action.fee(), in.tpid(), in.expiry()); - } + } else if (in.action().has_remove_pub_address_message()) { + const auto action = in.action().remove_pub_address_message(); + // process addresses + std::vector> addresses; + for (int i = 0; i < action.public_addresses_size(); ++i) { + addresses.emplace_back(std::make_pair(action.public_addresses(i).coin_symbol(), action.public_addresses(i).address())); + } + transaction = TransactionBuilder::buildUnsignedPubAddressAction(gRemoveAddress, owner, action.fio_address(), addresses, + getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } Data serTx; transaction.serialize(serTx); @@ -239,12 +293,11 @@ Proto::SigningOutput TransactionBuilder::buildSigningOutput(const Proto::Signing }; output.set_json(tx.dump()); + output.set_action_name(actionName(input)); return output; } Transaction TransactionBuilder::buildUnsignedRegisterFioAddress(const Address& address, const std::string& fioName, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime){ - const auto* const apiName = "regaddress"; - string actor = Actor::actor(address); RegisterFioAddressData raData(fioName, address.string(), fee, walletTpId, actor); Data serData; @@ -252,7 +305,7 @@ Transaction TransactionBuilder::buildUnsignedRegisterFioAddress(const Address& a Action action; action.account = ContractAddress; - action.name = apiName; + action.name = gRegisterFioAddress; action.actionDataSer = serData; action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); @@ -263,8 +316,9 @@ Transaction TransactionBuilder::buildUnsignedRegisterFioAddress(const Address& a return tx; } -Transaction TransactionBuilder::buildUnsignedAddPubAddress(const Address& address, const std::string& fioName, const std::vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { - const auto* const apiName = "addaddress"; +Transaction TransactionBuilder::buildUnsignedPubAddressAction(const std::string& apiName, const Address& address, + const std::string& fioName, const std::vector>& pubAddresses, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { string actor = Actor::actor(address); // convert addresses to add chainCode -- set it equal to coinSymbol @@ -272,10 +326,10 @@ Transaction TransactionBuilder::buildUnsignedAddPubAddress(const Address& addres for (const auto& a: pubAddresses) { pubAddresses2.push_back(PublicAddress{a.first, a.first, a.second}); } - AddPubAddressData aaData(fioName, pubAddresses2, fee, walletTpId, actor); + PubAddressActionData actionData(fioName, pubAddresses2, fee, walletTpId, actor); Data serData; - aaData.serialize(serData); - + actionData.serialize(serData); + Action action; action.account = ContractAddress; action.name = apiName; @@ -290,8 +344,6 @@ Transaction TransactionBuilder::buildUnsignedAddPubAddress(const Address& addres } Transaction TransactionBuilder::buildUnsignedTransfer(const Address& address, const std::string& payeePublicKey, uint64_t amount, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { - const auto* const apiName = "trnsfiopubky"; - string actor = Actor::actor(address); TransferData ttData(payeePublicKey, amount, fee, walletTpId, actor); Data serData; @@ -299,7 +351,7 @@ Transaction TransactionBuilder::buildUnsignedTransfer(const Address& address, co Action action; action.account = ContractToken; - action.name = apiName; + action.name = gTransferFIOPubkey; action.actionDataSer = serData; action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); @@ -311,8 +363,6 @@ Transaction TransactionBuilder::buildUnsignedTransfer(const Address& address, co } Transaction TransactionBuilder::buildUnsignedRenewFioAddress(const Address& address, const std::string& fioName, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { - const auto* const apiName = "renewaddress"; - string actor = Actor::actor(address); RenewFioAddressData raData(fioName, fee, walletTpId, actor); Data serData; @@ -320,7 +370,7 @@ Transaction TransactionBuilder::buildUnsignedRenewFioAddress(const Address& addr Action action; action.account = ContractAddress; - action.name = apiName; + action.name = gRenewFIOAddress; action.actionDataSer = serData; action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); diff --git a/src/FIO/TransactionBuilder.h b/src/FIO/TransactionBuilder.h index 8bd7061db72..735bf38d834 100644 --- a/src/FIO/TransactionBuilder.h +++ b/src/FIO/TransactionBuilder.h @@ -38,6 +38,9 @@ class TransactionBuilder { /// Generic transaction signer: Build a signed transaction, in Json, from the specific SigningInput messages. static std::string sign(Proto::SigningInput in); + /// Returns an action name according to the given signing input. + static std::string actionName(const Proto::SigningInput& input); + /// Create a signed RegisterFioAddress transaction, returned as json string (double quote delimited), suitable for register_fio_address RPC call /// @address The owners' FIO address. Ex.: "FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf" /// @privateKey The private key matching the address, needed for signing. @@ -64,6 +67,20 @@ class TransactionBuilder { const std::vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Create a signed `remaddress` transaction, returned as json string (double quote delimited), suitable for remove_pub_address RPC call + /// @address The owners' FIO address + /// @privateKey The private key matching the address, needed for signing. + /// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust" + /// @addressess List of public addresses to be registered, ex. {{"BTC", "bc1qv...7v"},{"BNB", "bnb1ts3...9s"}} + /// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls. + /// @fee Max fee to spend, can be obtained using get_fee API. + /// @walletTpId The FIO name of the originating wallet (project-wide constant) + /// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry + /// Note: fee is usually 0 for remove_pub_address. + static std::string createRemovePubAddress(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + const std::vector>& pubAddresses, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Create a signed TransferTokens transaction, returned as json string (double quote delimited), suitable for transfer_tokens_pub_key RPC call /// @address The owners' FIO address /// @privateKey The private key matching the address, needed for signing. @@ -131,8 +148,17 @@ class TransactionBuilder { static Transaction buildUnsignedRegisterFioAddress(const Address& address, const std::string& fioName, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); - static Transaction buildUnsignedAddPubAddress(const Address& address, const std::string& fioName, - const std::vector>& pubAddresses, + /// Builds an unsigned transaction to perform an action over public addresses, e.g. adding or removing public addresses. + /// @apiName The action API name, ex. "addaddress", "remaddress". + /// @address The owners' FIO address. + /// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust" + /// @pubAddresses List of public addresses to be registered, ex. {{"BTC", "bc1qv...7v"},{"BNB", "bnb1ts3...9s"}} + /// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls. + /// @fee Max fee to spend, can be obtained using get_fee API. + /// @walletTpId The FIO name of the originating wallet (project-wide constant) + /// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry + static Transaction buildUnsignedPubAddressAction(const std::string& apiName, const Address& address, + const std::string& fioName, const std::vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); static Transaction buildUnsignedTransfer(const Address& address, const std::string& payeePublicKey, uint64_t amount, diff --git a/src/proto/FIO.proto b/src/proto/FIO.proto index 9af884ddf9b..9fdb61b4c70 100644 --- a/src/proto/FIO.proto +++ b/src/proto/FIO.proto @@ -64,6 +64,19 @@ message Action { uint64 fee = 3; } + // Action for removing public chain addresses from a FIO name; remove_pub_address + // Note: actor is not needed, computed from private key + message RemovePubAddress { + // The FIO name already registered to the owner. Ex.: "alice@trust" + string fio_address = 1; + + // List of public addresses to be unregistered, ex. {{"BTC", "bc1qv...7v"},{"BNB", "bnb1ts3...9s"}} + repeated PublicAddress public_addresses = 2; + + // Max fee to spend, can be obtained using get_fee API. + uint64 fee = 3; + } + // Action for transferring FIO coins; transfer_tokens_pub_key // Note: actor is not needed, computed from private key message Transfer { @@ -116,6 +129,7 @@ message Action { Transfer transfer_message = 3; RenewFioAddress renew_fio_address_message = 4; NewFundsRequest new_funds_request_message = 5; + RemovePubAddress remove_pub_address_message = 6; } } @@ -162,4 +176,7 @@ message SigningOutput { // error code description string error_message = 3; + + // Performed action name, ex. "addaddress", "remaddress", "trnsfiopubky" etc. + string action_name = 4; } diff --git a/tests/chains/FIO/SignerTests.cpp b/tests/chains/FIO/SignerTests.cpp index db663c9cf0a..a3104e15afc 100644 --- a/tests/chains/FIO/SignerTests.cpp +++ b/tests/chains/FIO/SignerTests.cpp @@ -103,6 +103,7 @@ TEST(FIOSigner, compile) { Proto::SigningOutput result = Signer::compile(input, signature); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"b0ae295e50c3400a6dee00000000010000980ad20ca85be0e1d195ba85e7cd01102b2f46fca756b200000000a8ed32325d3546494f37754d5a6f6565693548745841443234433479436b70575762663234626a597472524e6a57646d474358485a63637775694500ca9a3b0000000080b2e60e00000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K9VRCnvaTYN7vgcoVKVXgyJTdKUGV8hLXgFLoEbvqAcFxy7DXQ1rSnAfEuabi4ATkgmvnpaSBdVFN7TBtM1wrbZYqeJQw9"]})" , result.json()); + EXPECT_EQ(result.action_name(), "trnsfiopubky"); } } // namespace TW::FIO::tests diff --git a/tests/chains/FIO/TWFIOTests.cpp b/tests/chains/FIO/TWFIOTests.cpp index 77b6acdffaf..6cf1fd3c7d7 100644 --- a/tests/chains/FIO/TWFIOTests.cpp +++ b/tests/chains/FIO/TWFIOTests.cpp @@ -37,6 +37,7 @@ TEST(TWFIO, Address) { } const Data gChainId = parse_hex("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd77"); +const Data gChainIdMainnet = parse_hex("21dcae42c0182200e93f954a074011f9048a7624c6fe81d3c9541a614a88bd1c"); // 5KEDWtAUJcFX6Vz38WXsAQAv2geNqT7UaZC8gYu9kTuryr3qkri FIO6m1fMdTpRkRBnedvYshXCxLFiC5suRU8KDfx8xxtXp2hntxpnf const PrivateKey privKeyBA = PrivateKey(parse_hex("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035")); const PublicKey pubKey6M = privKeyBA.getPublicKey(TWPublicKeyTypeSECP256k1); @@ -58,6 +59,7 @@ TEST(TWFIO, RegisterFioAddress) { ANY_SIGN(input, TWCoinTypeFIO); EXPECT_EQ(Common::Proto::OK, output.error()); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"3f99295ec99b904215ff0000000001003056372503a85b0000c6eaa66498ba01102b2f46fca756b200000000a8ed3232650f6164616d4066696f746573746e65743546494f366d31664d645470526b52426e6564765973685843784c4669433573755255384b44667838787874587032686e7478706e6600f2052a01000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K19ugLriG3ApYgjJCRDsy21p9xgsjbDtqBuZrmAEix9XYzndR1kNbJ6fXCngMJMAhxUHfwHAsPnh58otXiJZkazaM1EkS5"]})", output.json()); + EXPECT_EQ(output.action_name(), "regaddress"); } TEST(TWFIO, AddPubAddress) { @@ -85,6 +87,33 @@ TEST(TWFIO, AddPubAddress) { ANY_SIGN(input, TWCoinTypeFIO); EXPECT_EQ(Common::Proto::OK, output.error()); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"15c2285e2d2d23622eff0000000001003056372503a85b0000c6eaa664523201102b2f46fca756b200000000a8ed3232c9010f6164616d4066696f746573746e65740303425443034254432a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c787770373064377603455448034554482a30786365356342366339324461333762624261393142643430443443394434443732344133613846353103424e4203424e422a626e6231747333646735346170776c76723968757076326e306a366534367135347a6e6e75736a6b39730000000000000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K3zimaMKU8cBhVRPw46KM2u7uQWaAKXrnoeYZ7MEbp6sVJcDQmQR2RtdavpUPwkAnYUkd8NqLun8H48tcxZBcTUgkiPGVJ"]})", output.json()); + EXPECT_EQ(output.action_name(), "addaddress"); +} + +TEST(TWFIO, RemovePubAddress) { + auto privateKey = parse_hex("93083dc4d9e8f613a57e3a862a1fa5d665c5e90141a8428990c945d1c2b56491"); + + Proto::SigningInput input; + input.set_expiry(1713269931); + input.mutable_chain_params()->set_chain_id(string(gChainIdMainnet.begin(), gChainIdMainnet.end())); + input.mutable_chain_params()->set_head_block_number(256054093); + input.mutable_chain_params()->set_ref_block_prefix(2438027034); + input.set_private_key(string(privateKey.begin(), privateKey.end())); + input.set_tpid("trust@fiomembers"); + auto action = input.mutable_action()->mutable_remove_pub_address_message(); + action->set_fio_address("sergeitrust@wallet"); + action->add_public_addresses(); + action->mutable_public_addresses(0)->set_coin_symbol("BTC"); + action->mutable_public_addresses(0)->set_address("bc1q68caps3gqt2c9qxtnkhmzf3whxenrs9cav4wlm"); + action->set_fee(4878336459); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeFIO); + EXPECT_EQ(Common::Proto::OK, output.error()); + std::cout << output.json() << std::endl; + // Successfully broadcasted: https://fio.bloks.io/transaction/0bb6da24a3ea9e3ee57906de1cfa8bad18709acd64bf30908713dd61c54cfaea + EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"ab6c1e664d131a5751910000000001003056372503a85b0000c6eaa664a4ba01b038b9d6c13372f700000000a8ed3232681273657267656974727573744077616c6c65740103425443034254432a62633171363863617073336771743263397178746e6b686d7a6633776878656e72733963617634776c6dcb81c52201000000b038b9d6c13372f71074727573744066696f6d656d6265727300","signatures":["SIG_K1_K3cKHXCFYYB9aLFc9qk2idmWgEA4Q9192fECc3cF7MYHXkw9kZamdeHv3qbVoifG9oS8h6nVAJwJvj5YcnhHmnd3u89ND7"]})", output.json()); + EXPECT_EQ(output.action_name(), "remaddress"); } TEST(TWFIO, Transfer) { @@ -103,6 +132,7 @@ TEST(TWFIO, Transfer) { ANY_SIGN(input, TWCoinTypeFIO); EXPECT_EQ(Common::Proto::OK, output.error()); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"b0ae295e50c3400a6dee00000000010000980ad20ca85be0e1d195ba85e7cd01102b2f46fca756b200000000a8ed32325d3546494f37754d5a6f6565693548745841443234433479436b70575762663234626a597472524e6a57646d474358485a63637775694500ca9a3b0000000080b2e60e00000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K9VRCnvaTYN7vgcoVKVXgyJTdKUGV8hLXgFLoEbvqAcFxy7DXQ1rSnAfEuabi4ATkgmvnpaSBdVFN7TBtM1wrbZYqeJQw9"]})", output.json()); + EXPECT_EQ(output.action_name(), "trnsfiopubky"); } TEST(TWFIO, RenewFioAddress) { @@ -121,6 +151,7 @@ TEST(TWFIO, RenewFioAddress) { ANY_SIGN(input, TWCoinTypeFIO); EXPECT_EQ(Common::Proto::OK, output.error()); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff0000000001003056372503a85b80b1ba2919aea6ba01102b2f46fca756b200000000a8ed32322f0f6e69636b4066696f746573746e6574005ed0b200000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_Jxz7oCJ7Z4ECsxqb2utqBcyP3zPQCeQCBws9wWQjyptUKoWVk2AyCVEqtdMHJwqtLniio5Z7npMnaZB8E4pa2G75P9uGkb"]})", output.json()); + EXPECT_EQ(output.action_name(), "renewaddress"); } TEST(TWFIO, NewFundsRequest) { @@ -149,6 +180,7 @@ TEST(TWFIO, NewFundsRequest) { EXPECT_EQ( R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff000000000100403ed4aa0ba85b00acba384dbdb89a01102b2f46fca756b200000000a8ed32328802106d6172696f4066696f746573746)", output.json().substr(0, 195)); + EXPECT_EQ(output.action_name(), "newfundsreq"); } } // namespace TW::FIO::TWFIOTests diff --git a/tests/chains/FIO/TransactionBuilderTests.cpp b/tests/chains/FIO/TransactionBuilderTests.cpp index 097d587b341..16f16f92b06 100644 --- a/tests/chains/FIO/TransactionBuilderTests.cpp +++ b/tests/chains/FIO/TransactionBuilderTests.cpp @@ -145,7 +145,7 @@ TEST(FIOTransaction, ActionRegisterFioAddressInternal) { } TEST(FIOTransaction, ActionAddPubAddressInternal) { - AddPubAddressData aadata("adam@fiotestnet", {{"BTC", "BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}, {"ETH", "ETH", "0xce5cB6c92Da37bbBa91Bd40D4C9D4D724A3a8F51"}, {"BNB", "BNB", "bnb1ts3dg54apwlvr9hupv2n0j6e46q54znnusjk9s"}}, + PubAddressActionData aadata("adam@fiotestnet", {{"BTC", "BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}, {"ETH", "ETH", "0xce5cB6c92Da37bbBa91Bd40D4C9D4D724A3a8F51"}, {"BNB", "BNB", "bnb1ts3dg54apwlvr9hupv2n0j6e46q54znnusjk9s"}}, 0, "rewards@wallet", "qdfejz2a5wpl"); Data ser1; aadata.serialize(ser1); @@ -468,6 +468,7 @@ TEST(FIOTransactionBuilder, buildSigningOutput) { EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"3f99295ec99b904215ff0000000001003056372503a85b0000c6eaa66498ba01102b2f46fca756b200000000a8ed3232650f6164616d4066696f746573746e65743546494f366d31664d645470526b52426e6564765973685843784c4669433573755255384b44667838787874587032686e7478706e6600f2052a01000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K19ugLriG3ApYgjJCRDsy21p9xgsjbDtqBuZrmAEix9XYzndR1kNbJ6fXCngMJMAhxUHfwHAsPnh58otXiJZkazaM1EkS5"]})" , result.json()); + EXPECT_EQ(result.action_name(), "regaddress"); } { @@ -506,6 +507,7 @@ TEST(FIOTransactionBuilder, buildSigningOutput) { Proto::SigningOutput result = TransactionBuilder::buildSigningOutput(input, signature); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"15c2285e2d2d23622eff0000000001003056372503a85b0000c6eaa664523201102b2f46fca756b200000000a8ed3232c9010f6164616d4066696f746573746e65740303425443034254432a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c787770373064377603455448034554482a30786365356342366339324461333762624261393142643430443443394434443732344133613846353103424e4203424e422a626e6231747333646735346170776c76723968757076326e306a366534367135347a6e6e75736a6b39730000000000000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K3zimaMKU8cBhVRPw46KM2u7uQWaAKXrnoeYZ7MEbp6sVJcDQmQR2RtdavpUPwkAnYUkd8NqLun8H48tcxZBcTUgkiPGVJ"]})" , result.json()); + EXPECT_EQ(result.action_name(), "addaddress"); } { // Test transfer_message @@ -530,6 +532,7 @@ TEST(FIOTransactionBuilder, buildSigningOutput) { Proto::SigningOutput result = TransactionBuilder::buildSigningOutput(input, signature); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"b0ae295e50c3400a6dee00000000010000980ad20ca85be0e1d195ba85e7cd01102b2f46fca756b200000000a8ed32325d3546494f37754d5a6f6565693548745841443234433479436b70575762663234626a597472524e6a57646d474358485a63637775694500ca9a3b0000000080b2e60e00000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K9VRCnvaTYN7vgcoVKVXgyJTdKUGV8hLXgFLoEbvqAcFxy7DXQ1rSnAfEuabi4ATkgmvnpaSBdVFN7TBtM1wrbZYqeJQw9"]})" , result.json()); + EXPECT_EQ(result.action_name(), "trnsfiopubky"); } { // Test renew_fio_address_message @@ -553,6 +556,7 @@ TEST(FIOTransactionBuilder, buildSigningOutput) { Proto::SigningOutput result = TransactionBuilder::buildSigningOutput(input, signature); EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"289b295ec99b904215ff0000000001003056372503a85b80b1ba2919aea6ba01102b2f46fca756b200000000a8ed32322f0f6e69636b4066696f746573746e6574005ed0b200000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_Jxz7oCJ7Z4ECsxqb2utqBcyP3zPQCeQCBws9wWQjyptUKoWVk2AyCVEqtdMHJwqtLniio5Z7npMnaZB8E4pa2G75P9uGkb"]})" , result.json()); + EXPECT_EQ(result.action_name(), "renewaddress"); } } diff --git a/tests/chains/FIO/TransactionCompilerTests.cpp b/tests/chains/FIO/TransactionCompilerTests.cpp index cfdba2b1fee..f3eaecbc159 100644 --- a/tests/chains/FIO/TransactionCompilerTests.cpp +++ b/tests/chains/FIO/TransactionCompilerTests.cpp @@ -85,7 +85,8 @@ TEST(FIOCompiler, CompileWithSignatures) { TW::FIO::Proto::SigningOutput output; ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); - EXPECT_EQ(ExpectedTx, output.json()); + EXPECT_EQ(ExpectedTx, output.json()); + EXPECT_EQ(output.action_name(), "trnsfiopubky"); } { // Double check: check if simple signature process gives the same result. Note that private // keys were not used anywhere up to this point. From 51917c0a8d0a646680e4910024bf694d4e9de05a Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Thu, 18 Apr 2024 21:57:15 +0200 Subject: [PATCH 2/3] feat(fio): Add support for `addbundles` and `remalladdr` actions (#3802) --- src/FIO/Action.cpp | 15 ++++++ src/FIO/Action.h | 31 +++++++++++- src/FIO/TransactionBuilder.cpp | 86 +++++++++++++++++++++++++++++++++ src/FIO/TransactionBuilder.h | 30 ++++++++++++ src/proto/FIO.proto | 24 +++++++++ tests/chains/FIO/TWFIOTests.cpp | 45 +++++++++++++++++ 6 files changed, 230 insertions(+), 1 deletion(-) diff --git a/src/FIO/Action.cpp b/src/FIO/Action.cpp index 5be60a17866..34e1a8455cd 100644 --- a/src/FIO/Action.cpp +++ b/src/FIO/Action.cpp @@ -49,6 +49,13 @@ void PubAddressActionData::serialize(Data& out) const { encodeString(tpid, out); } +void RemoveAllPubAddressActionData::serialize(Data& out) const { + encodeString(fioAddress, out); + encode64LE(fee, out); + EOS::Name(actor).serialize(out); + encodeString(tpid, out); +} + void RegisterFioAddressData::serialize(Data& out) const { encodeString(fioAddress, out); encodeString(ownerPublicKey, out); @@ -81,4 +88,12 @@ void NewFundsRequestData::serialize(Data& out) const { encodeString(tpid, out); } +void AddBundledTransactionsActionData::serialize(Data& out) const { + encodeString(fioAddress, out); + encode64LE(bundledSets, out); + encode64LE(fee, out); + encodeString(tpid, out); + EOS::Name(actor).serialize(out); +} + } // namespace TW::FIO diff --git a/src/FIO/Action.h b/src/FIO/Action.h index 1ebdc3eff91..a5c325d3341 100644 --- a/src/FIO/Action.h +++ b/src/FIO/Action.h @@ -82,7 +82,7 @@ class Action { }; /// A public address action data part. -/// Can be used for `addaddress`, `remaddress` actions. +/// Can be used for `addaddress`, `remaddress`, `remalladdr` (addresses must be empty) actions. /// https://dev.fio.net/reference/add_pub_address /// https://dev.fio.net/reference/remove_pub_address class PubAddressActionData { @@ -100,6 +100,20 @@ class PubAddressActionData { void serialize(Data& out) const; }; +/// RemoveAllPubAddress action data part. +/// https://dev.fio.net/reference/remove_all_pub_address +class RemoveAllPubAddressActionData { +public: + std::string fioAddress; + uint64_t fee; + std::string tpid; + std::string actor; + + RemoveAllPubAddressActionData(const std::string& fioAddress, uint64_t fee, const std::string& tpid, const std::string& actor) : + fioAddress(fioAddress), fee(fee), tpid(tpid), actor(actor) {} + void serialize(Data& out) const; +}; + /// RegisterFioAddress action data part. class RegisterFioAddressData { public: @@ -158,4 +172,19 @@ class NewFundsRequestData { void serialize(Data& out) const; }; +/// AddBundledTransactions action data part. +/// https://dev.fio.net/reference/add_bundled_transactions +class AddBundledTransactionsActionData { +public: + std::string fioAddress; + uint64_t bundledSets; + uint64_t fee; + std::string tpid; + std::string actor; + + AddBundledTransactionsActionData(const std::string& fioAddress, uint64_t bundledSets, uint64_t fee, const std::string& tpid, const std::string& actor) : + fioAddress(fioAddress), bundledSets(bundledSets), fee(fee), tpid(tpid), actor(actor) {} + void serialize(Data& out) const; +}; + } // namespace TW::FIO diff --git a/src/FIO/TransactionBuilder.cpp b/src/FIO/TransactionBuilder.cpp index bf5ca8a8302..4a890d81705 100644 --- a/src/FIO/TransactionBuilder.cpp +++ b/src/FIO/TransactionBuilder.cpp @@ -24,9 +24,11 @@ using json = nlohmann::json; static constexpr auto gRegisterFioAddress = "regaddress"; static constexpr auto gAddPubAddress = "addaddress"; static constexpr auto gRemoveAddress = "remaddress"; +static constexpr auto gRemoveAllPubAddresses = "remalladdr"; static constexpr auto gTransferFIOPubkey = "trnsfiopubky"; static constexpr auto gRenewFIOAddress = "renewaddress"; static constexpr auto gNewFundsRequest = "newfundsreq"; +static constexpr auto gAddBundledTransactions = "addbundles"; /// Internal helper ChainParams getChainParams(const Proto::SigningInput& input) { @@ -58,6 +60,10 @@ string TransactionBuilder::actionName(const Proto::SigningInput& input) { return gNewFundsRequest; case Proto::Action::MessageOneofCase::kRemovePubAddressMessage: return gRemoveAddress; + case Proto::Action::MessageOneofCase::kRemoveAllPubAddressesMessage: + return gRemoveAllPubAddresses; + case Proto::Action::MessageOneofCase::kAddBundledTransactionsMessage: + return gAddBundledTransactions; default: return {}; } @@ -111,6 +117,14 @@ string TransactionBuilder::sign(Proto::SigningInput in) { json = TransactionBuilder::createRemovePubAddress(owner, privateKey, action.fio_address(), addresses, getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } else if (in.action().has_remove_all_pub_addresses_message()) { + const auto action = in.action().remove_all_pub_addresses_message(); + json = TransactionBuilder::createRemoveAllPubAddresses(owner, privateKey, + action.fio_address(), getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } else if (in.action().has_add_bundled_transactions_message()) { + const auto action = in.action().add_bundled_transactions_message(); + json = TransactionBuilder::createAddBundledTransactions(owner, privateKey, action.fio_address(), + action.bundle_sets(), getChainParams(in), action.fee(), in.tpid(), in.expiry()); } return json; } @@ -151,6 +165,28 @@ string TransactionBuilder::createRemovePubAddress(const Address& address, const return signAndBuildTx(chainParams.chainId, serTx, privateKey); } +std::string TransactionBuilder::createRemoveAllPubAddresses(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { + + Transaction transaction = TransactionBuilder::buildUnsignedRemoveAllAddressesAction(address, fioName, chainParams, fee, walletTpId, expiryTime); + + Data serTx; + transaction.serialize(serTx); + + return signAndBuildTx(chainParams.chainId, serTx, privateKey); +} + +std::string TransactionBuilder::createAddBundledTransactions(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { + + Transaction transaction = TransactionBuilder::buildUnsignedAddBundledTransactions(address, fioName, bundleSets, chainParams, fee, walletTpId, expiryTime); + + Data serTx; + transaction.serialize(serTx); + + return signAndBuildTx(chainParams.chainId, serTx, privateKey); +} + string TransactionBuilder::createTransfer(const Address& address, const PrivateKey& privateKey, const string& payeePublicKey, uint64_t amount, const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) { @@ -271,6 +307,14 @@ Data TransactionBuilder::buildUnsignedTxBytes(const Proto::SigningInput& in) { } transaction = TransactionBuilder::buildUnsignedPubAddressAction(gRemoveAddress, owner, action.fio_address(), addresses, getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } else if (in.action().has_remove_all_pub_addresses_message()) { + const auto action = in.action().remove_all_pub_addresses_message(); + transaction = TransactionBuilder::buildUnsignedRemoveAllAddressesAction(owner, action.fio_address(), + getChainParams(in), action.fee(), in.tpid(), in.expiry()); + } else if (in.action().has_add_bundled_transactions_message()) { + const auto action = in.action().add_bundled_transactions_message(); + transaction = TransactionBuilder::buildUnsignedAddBundledTransactions(owner, action.fio_address(), action.bundle_sets(), + getChainParams(in), action.fee(), in.tpid(), in.expiry()); } Data serTx; @@ -381,4 +425,46 @@ Transaction TransactionBuilder::buildUnsignedRenewFioAddress(const Address& addr return tx; } +Transaction TransactionBuilder::buildUnsignedRemoveAllAddressesAction(const Address& address, const std::string& fioName, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { + + string actor = Actor::actor(address); + RemoveAllPubAddressActionData actionData(fioName, fee, walletTpId, actor); + Data serData; + actionData.serialize(serData); + + Action action; + action.account = ContractAddress; + action.name = gRemoveAllPubAddresses; + action.actionDataSer = serData; + action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); + + Transaction tx; + expirySetDefaultIfNeeded(expiryTime); + tx.set(expiryTime, chainParams); + tx.actions.push_back(action); + return tx; +} + +Transaction TransactionBuilder::buildUnsignedAddBundledTransactions(const Address& address, const std::string& fioName, + uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) { + + string actor = Actor::actor(address); + AddBundledTransactionsActionData actionData(fioName, bundleSets, fee, walletTpId, actor); + Data serData; + actionData.serialize(serData); + + Action action; + action.account = ContractAddress; + action.name = gAddBundledTransactions; + action.actionDataSer = serData; + action.auth.authArray.push_back(Authorization{actor, AuthrizationActive}); + + Transaction tx; + expirySetDefaultIfNeeded(expiryTime); + tx.set(expiryTime, chainParams); + tx.actions.push_back(action); + return tx; +} + } // namespace TW::FIO diff --git a/src/FIO/TransactionBuilder.h b/src/FIO/TransactionBuilder.h index 735bf38d834..844a58850b1 100644 --- a/src/FIO/TransactionBuilder.h +++ b/src/FIO/TransactionBuilder.h @@ -81,6 +81,18 @@ class TransactionBuilder { const std::vector>& pubAddresses, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Create a signed `remalladdr` transaction, returned as json string (double quote delimited), suitable for remove_all_pub_address RPC call + /// @address The owners' FIO address + /// @privateKey The private key matching the address, needed for signing. + /// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust" + /// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls. + /// @fee Max fee to spend, can be obtained using get_fee API. + /// @walletTpId The FIO name of the originating wallet (project-wide constant) + /// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry + /// Note: fee is usually 0 for remove_all_pub_address. + static std::string createRemoveAllPubAddresses(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Create a signed TransferTokens transaction, returned as json string (double quote delimited), suitable for transfer_tokens_pub_key RPC call /// @address The owners' FIO address /// @privateKey The private key matching the address, needed for signing. @@ -130,6 +142,18 @@ class TransactionBuilder { const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime, const Data& iv); + /// Create a signed `addbundles` transaction, returned as json string (double quote delimited), suitable for add_bundled_transactions RPC call + /// @address The owners' FIO address + /// @privateKey The private key matching the address, needed for signing. + /// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust" + /// @bundleSets Number of bundled sets. One set is 100 bundled transactions. + /// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls. + /// @fee Max fee to spend, can be obtained using get_fee API. + /// @walletTpId The FIO name of the originating wallet (project-wide constant) + /// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry + static std::string createAddBundledTransactions(const Address& address, const PrivateKey& privateKey, const std::string& fioName, + uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + /// Used internally. Creates signatures and json with transaction. static std::string signAndBuildTx(const Data& chainId, const Data& packedTx, const PrivateKey& privateKey); @@ -166,6 +190,12 @@ class TransactionBuilder { static Transaction buildUnsignedRenewFioAddress(const Address& address, const std::string& fioName, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + + static Transaction buildUnsignedRemoveAllAddressesAction(const Address& address, const std::string& fioName, + const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); + + static Transaction buildUnsignedAddBundledTransactions(const Address& address, const std::string& fioName, + uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime); }; } // namespace TW::FIO diff --git a/src/proto/FIO.proto b/src/proto/FIO.proto index 9fdb61b4c70..664c041e5f0 100644 --- a/src/proto/FIO.proto +++ b/src/proto/FIO.proto @@ -77,6 +77,16 @@ message Action { uint64 fee = 3; } + // Action for removing public chain addresses from a FIO name; remove_pub_address + // Note: actor is not needed, computed from private key + message RemoveAllPubAddress { + // The FIO name already registered to the owner. Ex.: "alice@trust" + string fio_address = 1; + + // Max fee to spend, can be obtained using get_fee API. + uint64 fee = 3; + } + // Action for transferring FIO coins; transfer_tokens_pub_key // Note: actor is not needed, computed from private key message Transfer { @@ -122,6 +132,18 @@ message Action { uint64 fee = 5; } + // Action for adding `100 * bundle_sets` bundled transactions to the supplied FIO Handle. When bundles are purchased one or more sets of bundled transactions are added to the existing count. + message AddBundledTransactions { + // The FIO name already registered to the owner. Ex.: "alice@trust" + string fio_address = 1; + + // Number of bundled sets. One set is 100 bundled transactions. + uint64 bundle_sets = 2; + + // Max fee to spend, can be obtained using get_fee API. + uint64 fee = 3; + } + // Payload message oneof message_oneof { RegisterFioAddress register_fio_address_message = 1; @@ -130,6 +152,8 @@ message Action { RenewFioAddress renew_fio_address_message = 4; NewFundsRequest new_funds_request_message = 5; RemovePubAddress remove_pub_address_message = 6; + RemoveAllPubAddress remove_all_pub_addresses_message = 7; + AddBundledTransactions add_bundled_transactions_message = 8; } } diff --git a/tests/chains/FIO/TWFIOTests.cpp b/tests/chains/FIO/TWFIOTests.cpp index 6cf1fd3c7d7..1c4f35285b3 100644 --- a/tests/chains/FIO/TWFIOTests.cpp +++ b/tests/chains/FIO/TWFIOTests.cpp @@ -116,6 +116,28 @@ TEST(TWFIO, RemovePubAddress) { EXPECT_EQ(output.action_name(), "remaddress"); } +TEST(TWFIO, RemoveAllPubAddresses) { + auto privateKey = parse_hex("93083dc4d9e8f613a57e3a862a1fa5d665c5e90141a8428990c945d1c2b56491"); + + Proto::SigningInput input; + input.set_expiry(1713458993); + input.mutable_chain_params()->set_chain_id(string(gChainIdMainnet.begin(), gChainIdMainnet.end())); + input.mutable_chain_params()->set_head_block_number(256432311); + input.mutable_chain_params()->set_ref_block_prefix(2287536876); + input.set_private_key(string(privateKey.begin(), privateKey.end())); + input.set_tpid("trust@fiomembers"); + auto action = input.mutable_action()->mutable_remove_all_pub_addresses_message(); + action->set_fio_address("sergeitrust@wallet"); + action->set_fee(0); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeFIO); + EXPECT_EQ(Common::Proto::OK, output.error()); + // Successfully broadcasted: https://fio.bloks.io/transaction/f2facdebfcba1981377537424a6d7b7e7ebd8222c87ba4d25a480d1b968704b2 + EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"314f2166b7d8ec0a59880000000001003056372503a85b00c04dc9c468a4ba01b038b9d6c13372f700000000a8ed3232341273657267656974727573744077616c6c65740000000000000000b038b9d6c13372f71074727573744066696f6d656d6265727300","signatures":["SIG_K1_KXXtpz7NWhzCms7Dj54nSwwtCw6w4zLCyTLxs3tqqgLscrz91cMjcbN4yxcySvZ7t4MER8HPteeJZUnR16uLyDa1gFGzrx"]})", output.json()); + EXPECT_EQ(output.action_name(), "remalladdr"); +} + TEST(TWFIO, Transfer) { Proto::SigningInput input; input.set_expiry(1579790000); @@ -183,4 +205,27 @@ TEST(TWFIO, NewFundsRequest) { EXPECT_EQ(output.action_name(), "newfundsreq"); } +TEST(TWFIO, AddBundledTransactions) { + auto privateKey = parse_hex("93083dc4d9e8f613a57e3a862a1fa5d665c5e90141a8428990c945d1c2b56491"); + + Proto::SigningInput input; + input.set_expiry(1713458594); + input.mutable_chain_params()->set_chain_id(string(gChainIdMainnet.begin(), gChainIdMainnet.end())); + input.mutable_chain_params()->set_head_block_number(256431437); + input.mutable_chain_params()->set_ref_block_prefix(791306279); + input.set_private_key(string(privateKey.begin(), privateKey.end())); + input.set_tpid("trust@fiomembers"); + auto action = input.mutable_action()->mutable_add_bundled_transactions_message(); + action->set_fio_address("sergeitrust@wallet"); + action->set_bundle_sets(1); + action->set_fee(100000000000); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeFIO); + EXPECT_EQ(Common::Proto::OK, output.error()); + // Successfully broadcasted: https://fio.bloks.io/transaction/2c00f2051ca3738c4fe03ceddb82c48fefd9c534d8bb793dc7dce5d12f4f4f9c + EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"a24d21664dd527602a2f0000000001003056372503a85b000056314d7d523201b038b9d6c13372f700000000a8ed32323c1273657267656974727573744077616c6c6574010000000000000000e87648170000001074727573744066696f6d656d62657273b038b9d6c13372f700","signatures":["SIG_K1_KjWGZ4Yd48VJcTAgox3HYVQhXeLhpRCgz2WqiF5WHRFSnbHouKxPgLQmymoABHC8EX51G1jU4ocWg2RKU17UYm4L5kTXP6"]})", output.json()); + EXPECT_EQ(output.action_name(), "addbundles"); +} + } // namespace TW::FIO::TWFIOTests From cd212c9cb3a186f10ff2cc98fad01f6b9b514d33 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:21:25 +0200 Subject: [PATCH 3/3] feat(bouncebit): Add support for `BounceBit` EVM (#3807) --- .../blockchains/CoinAddressDerivationTests.kt | 2 +- docs/registry.md | 1 + include/TrustWalletCore/TWCoinType.h | 1 + .../core/test/CoinAddressDerivationTests.kt | 2 +- registry.json | 30 +++++++++++++++++++ .../tests/coin_address_derivation_test.rs | 1 + swift/Tests/CoinAddressDerivationTests.swift | 3 +- tests/chains/BounceBit/TWCoinTypeTests.cpp | 29 ++++++++++++++++++ tests/common/CoinAddressDerivationTests.cpp | 1 + 9 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/chains/BounceBit/TWCoinTypeTests.cpp diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 07108b4c9d6..3fa855cc07b 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -47,7 +47,7 @@ class CoinAddressDerivationTests { FANTOM, CELO, CRONOSCHAIN, SMARTBITCOINCASH, KUCOINCOMMUNITYCHAIN, BOBA, METIS, AURORA, EVMOS, MOONRIVER, MOONBEAM, KAVAEVM, KLAYTN, METER, OKXCHAIN, POLYGONZKEVM, SCROLL, CONFLUXESPACE, ACALAEVM, OPBNB, NEON, BASE, LINEA, GREENFIELD, MANTLE, ZENEON, MANTAPACIFIC, - ZETAEVM, MERLIN, LIGHTLINK, BLAST, + ZETAEVM, MERLIN, LIGHTLINK, BLAST, BOUNCEBIT, -> assertEquals("0x8f348F300873Fd5DA36950B2aC75a26584584feE", address) RONIN -> assertEquals("ronin:8f348F300873Fd5DA36950B2aC75a26584584feE", address) diff --git a/docs/registry.md b/docs/registry.md index 46c7671fa3f..ad4c51171a7 100644 --- a/docs/registry.md +++ b/docs/registry.md @@ -91,6 +91,7 @@ This list is generated from [./registry.json](../registry.json) | 4200 | Merlin | BTC | | | | 5000 | Mantle | MNT | | | | 5600 | BNB Greenfield | BNB | | | +| 6001 | BounceBit | BB | | | | 6060 | GoChain | GO | | | | 7332 | Zen EON | ZEN | | | | 8453 | Base | ETH | | | diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 3af38bb8a6c..ce3f2aa0d20 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -184,6 +184,7 @@ enum TWCoinType { TWCoinTypeMerlin = 4200, TWCoinTypeLightlink = 1890, TWCoinTypeBlast = 81457, + TWCoinTypeBounceBit = 6001, // end_of_tw_coin_type_marker_do_not_modify }; diff --git a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt index 3c1277ce712..1714a611aba 100644 --- a/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt +++ b/kotlin/wallet-core-kotlin/src/commonTest/kotlin/com/trustwallet/core/test/CoinAddressDerivationTests.kt @@ -40,7 +40,7 @@ class CoinAddressDerivationTests { Fantom, Celo, CronosChain, SmartBitcoinCash, KuCoinCommunityChain, Boba, Metis, Aurora, Evmos, Moonriver, Moonbeam, KavaEvm, Klaytn, Meter, OKXChain, PolygonzkEVM, Scroll, ConfluxeSpace, AcalaEVM, OpBNB, Neon, Base, Linea, Greenfield, Mantle, ZenEON, MantaPacific, - ZetaEVM, Merlin, Lightlink, Blast, + ZetaEVM, Merlin, Lightlink, Blast, BounceBit, -> "0x8f348F300873Fd5DA36950B2aC75a26584584feE" Ronin -> "ronin:8f348F300873Fd5DA36950B2aC75a26584584feE" diff --git a/registry.json b/registry.json index 8044e408aae..0a2f370590a 100644 --- a/registry.json +++ b/registry.json @@ -4719,5 +4719,35 @@ "rpc": "https://rpc.blast.io", "documentation": "https://docs.blast.io" } + }, + { + "id": "bouncebit", + "name": "BounceBit", + "coinId": 6001, + "symbol": "BB", + "decimals": 18, + "blockchain": "Ethereum", + "derivation": [ + { + "path": "m/44'/60'/0'/0/0" + } + ], + "curve": "secp256k1", + "publicKeyType": "secp256k1Extended", + "chainId": "6001", + "addressHasher": "keccak256", + "explorer": { + "url": "https://bbscan.io", + "txPath": "/tx/", + "accountPath": "/address/", + "sampleTx": "0x52558f4143d058d942e3b73414090266ae3ffce1fe8c25fe86896e2c8e5ef932", + "sampleAccount": "0xf4aa7349a9ccca4609943955b5ddc7bd9278c223" + }, + "info": { + "url": "https://bouncebit.io", + "source": "https://github.com/BounceBit-Labs", + "rpc": "https://fullnode-mainnet.bouncebitapi.com", + "documentation": "https://docs.bouncebit.io" + } } ] diff --git a/rust/tw_any_coin/tests/coin_address_derivation_test.rs b/rust/tw_any_coin/tests/coin_address_derivation_test.rs index 4eeb9ed7f01..6be2411064a 100644 --- a/rust/tw_any_coin/tests/coin_address_derivation_test.rs +++ b/rust/tw_any_coin/tests/coin_address_derivation_test.rs @@ -89,6 +89,7 @@ fn test_coin_address_derivation() { | CoinType::Merlin | CoinType::Lightlink | CoinType::Blast + | CoinType::BounceBit // end_of_evm_address_derivation_tests_marker_do_not_modify => "0xAc1ec44E4f0ca7D172B7803f6836De87Fb72b309", CoinType::Bitcoin diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 9c04c72e16d..e6cda80b29a 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -117,7 +117,8 @@ class CoinAddressDerivationTests: XCTestCase { .zetaEVM, .merlin, .lightlink, - .blast: + .blast, + .bounceBit: let expectedResult = "0x8f348F300873Fd5DA36950B2aC75a26584584feE" assertCoinDerivation(coin, expectedResult, derivedAddress, address) case .ronin: diff --git a/tests/chains/BounceBit/TWCoinTypeTests.cpp b/tests/chains/BounceBit/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..7dbc072273e --- /dev/null +++ b/tests/chains/BounceBit/TWCoinTypeTests.cpp @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright © 2017 Trust Wallet. + +#include "TestUtilities.h" +#include +#include + +TEST(TWBounceBitCoinType, TWCoinType) { + const auto coin = TWCoinTypeBounceBit; + const auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(coin)); + const auto id = WRAPS(TWCoinTypeConfigurationGetID(coin)); + const auto name = WRAPS(TWCoinTypeConfigurationGetName(coin)); + const auto txId = WRAPS(TWStringCreateWithUTF8Bytes("0x52558f4143d058d942e3b73414090266ae3ffce1fe8c25fe86896e2c8e5ef932")); + const auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(coin, txId.get())); + const auto accId = WRAPS(TWStringCreateWithUTF8Bytes("0xf4aa7349a9ccca4609943955b5ddc7bd9278c223")); + const auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(coin, accId.get())); + + assertStringsEqual(id, "bouncebit"); + assertStringsEqual(name, "BounceBit"); + assertStringsEqual(symbol, "BB"); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(coin), 18); + ASSERT_EQ(TWCoinTypeBlockchain(coin), TWBlockchainEthereum); + ASSERT_EQ(TWCoinTypeP2pkhPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeP2shPrefix(coin), 0); + ASSERT_EQ(TWCoinTypeStaticPrefix(coin), 0); + assertStringsEqual(txUrl, "https://bbscan.io/tx/0x52558f4143d058d942e3b73414090266ae3ffce1fe8c25fe86896e2c8e5ef932"); + assertStringsEqual(accUrl, "https://bbscan.io/address/0xf4aa7349a9ccca4609943955b5ddc7bd9278c223"); +} diff --git a/tests/common/CoinAddressDerivationTests.cpp b/tests/common/CoinAddressDerivationTests.cpp index 04cb56c4fb4..351b5dd4954 100644 --- a/tests/common/CoinAddressDerivationTests.cpp +++ b/tests/common/CoinAddressDerivationTests.cpp @@ -87,6 +87,7 @@ TEST(Coin, DeriveAddress) { case TWCoinTypeMerlin: case TWCoinTypeLightlink: case TWCoinTypeBlast: + case TWCoinTypeBounceBit: // end_of_evm_address_derivation_tests_marker_do_not_modify EXPECT_EQ(address, "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); break;