Skip to content

Commit 51917c0

Browse files
feat(fio): Add support for addbundles and remalladdr actions (#3802)
1 parent 7a74dcb commit 51917c0

File tree

6 files changed

+230
-1
lines changed

6 files changed

+230
-1
lines changed

src/FIO/Action.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ void PubAddressActionData::serialize(Data& out) const {
4949
encodeString(tpid, out);
5050
}
5151

52+
void RemoveAllPubAddressActionData::serialize(Data& out) const {
53+
encodeString(fioAddress, out);
54+
encode64LE(fee, out);
55+
EOS::Name(actor).serialize(out);
56+
encodeString(tpid, out);
57+
}
58+
5259
void RegisterFioAddressData::serialize(Data& out) const {
5360
encodeString(fioAddress, out);
5461
encodeString(ownerPublicKey, out);
@@ -81,4 +88,12 @@ void NewFundsRequestData::serialize(Data& out) const {
8188
encodeString(tpid, out);
8289
}
8390

91+
void AddBundledTransactionsActionData::serialize(Data& out) const {
92+
encodeString(fioAddress, out);
93+
encode64LE(bundledSets, out);
94+
encode64LE(fee, out);
95+
encodeString(tpid, out);
96+
EOS::Name(actor).serialize(out);
97+
}
98+
8499
} // namespace TW::FIO

src/FIO/Action.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class Action {
8282
};
8383

8484
/// A public address action data part.
85-
/// Can be used for `addaddress`, `remaddress` actions.
85+
/// Can be used for `addaddress`, `remaddress`, `remalladdr` (addresses must be empty) actions.
8686
/// https://dev.fio.net/reference/add_pub_address
8787
/// https://dev.fio.net/reference/remove_pub_address
8888
class PubAddressActionData {
@@ -100,6 +100,20 @@ class PubAddressActionData {
100100
void serialize(Data& out) const;
101101
};
102102

103+
/// RemoveAllPubAddress action data part.
104+
/// https://dev.fio.net/reference/remove_all_pub_address
105+
class RemoveAllPubAddressActionData {
106+
public:
107+
std::string fioAddress;
108+
uint64_t fee;
109+
std::string tpid;
110+
std::string actor;
111+
112+
RemoveAllPubAddressActionData(const std::string& fioAddress, uint64_t fee, const std::string& tpid, const std::string& actor) :
113+
fioAddress(fioAddress), fee(fee), tpid(tpid), actor(actor) {}
114+
void serialize(Data& out) const;
115+
};
116+
103117
/// RegisterFioAddress action data part.
104118
class RegisterFioAddressData {
105119
public:
@@ -158,4 +172,19 @@ class NewFundsRequestData {
158172
void serialize(Data& out) const;
159173
};
160174

175+
/// AddBundledTransactions action data part.
176+
/// https://dev.fio.net/reference/add_bundled_transactions
177+
class AddBundledTransactionsActionData {
178+
public:
179+
std::string fioAddress;
180+
uint64_t bundledSets;
181+
uint64_t fee;
182+
std::string tpid;
183+
std::string actor;
184+
185+
AddBundledTransactionsActionData(const std::string& fioAddress, uint64_t bundledSets, uint64_t fee, const std::string& tpid, const std::string& actor) :
186+
fioAddress(fioAddress), bundledSets(bundledSets), fee(fee), tpid(tpid), actor(actor) {}
187+
void serialize(Data& out) const;
188+
};
189+
161190
} // namespace TW::FIO

src/FIO/TransactionBuilder.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ using json = nlohmann::json;
2424
static constexpr auto gRegisterFioAddress = "regaddress";
2525
static constexpr auto gAddPubAddress = "addaddress";
2626
static constexpr auto gRemoveAddress = "remaddress";
27+
static constexpr auto gRemoveAllPubAddresses = "remalladdr";
2728
static constexpr auto gTransferFIOPubkey = "trnsfiopubky";
2829
static constexpr auto gRenewFIOAddress = "renewaddress";
2930
static constexpr auto gNewFundsRequest = "newfundsreq";
31+
static constexpr auto gAddBundledTransactions = "addbundles";
3032

3133
/// Internal helper
3234
ChainParams getChainParams(const Proto::SigningInput& input) {
@@ -58,6 +60,10 @@ string TransactionBuilder::actionName(const Proto::SigningInput& input) {
5860
return gNewFundsRequest;
5961
case Proto::Action::MessageOneofCase::kRemovePubAddressMessage:
6062
return gRemoveAddress;
63+
case Proto::Action::MessageOneofCase::kRemoveAllPubAddressesMessage:
64+
return gRemoveAllPubAddresses;
65+
case Proto::Action::MessageOneofCase::kAddBundledTransactionsMessage:
66+
return gAddBundledTransactions;
6167
default:
6268
return {};
6369
}
@@ -111,6 +117,14 @@ string TransactionBuilder::sign(Proto::SigningInput in) {
111117
json = TransactionBuilder::createRemovePubAddress(owner, privateKey,
112118
action.fio_address(), addresses,
113119
getChainParams(in), action.fee(), in.tpid(), in.expiry());
120+
} else if (in.action().has_remove_all_pub_addresses_message()) {
121+
const auto action = in.action().remove_all_pub_addresses_message();
122+
json = TransactionBuilder::createRemoveAllPubAddresses(owner, privateKey,
123+
action.fio_address(), getChainParams(in), action.fee(), in.tpid(), in.expiry());
124+
} else if (in.action().has_add_bundled_transactions_message()) {
125+
const auto action = in.action().add_bundled_transactions_message();
126+
json = TransactionBuilder::createAddBundledTransactions(owner, privateKey, action.fio_address(),
127+
action.bundle_sets(), getChainParams(in), action.fee(), in.tpid(), in.expiry());
114128
}
115129
return json;
116130
}
@@ -151,6 +165,28 @@ string TransactionBuilder::createRemovePubAddress(const Address& address, const
151165
return signAndBuildTx(chainParams.chainId, serTx, privateKey);
152166
}
153167

168+
std::string TransactionBuilder::createRemoveAllPubAddresses(const Address& address, const PrivateKey& privateKey, const std::string& fioName,
169+
const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) {
170+
171+
Transaction transaction = TransactionBuilder::buildUnsignedRemoveAllAddressesAction(address, fioName, chainParams, fee, walletTpId, expiryTime);
172+
173+
Data serTx;
174+
transaction.serialize(serTx);
175+
176+
return signAndBuildTx(chainParams.chainId, serTx, privateKey);
177+
}
178+
179+
std::string TransactionBuilder::createAddBundledTransactions(const Address& address, const PrivateKey& privateKey, const std::string& fioName,
180+
uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) {
181+
182+
Transaction transaction = TransactionBuilder::buildUnsignedAddBundledTransactions(address, fioName, bundleSets, chainParams, fee, walletTpId, expiryTime);
183+
184+
Data serTx;
185+
transaction.serialize(serTx);
186+
187+
return signAndBuildTx(chainParams.chainId, serTx, privateKey);
188+
}
189+
154190
string TransactionBuilder::createTransfer(const Address& address, const PrivateKey& privateKey,
155191
const string& payeePublicKey, uint64_t amount,
156192
const ChainParams& chainParams, uint64_t fee, const string& walletTpId, uint32_t expiryTime) {
@@ -271,6 +307,14 @@ Data TransactionBuilder::buildUnsignedTxBytes(const Proto::SigningInput& in) {
271307
}
272308
transaction = TransactionBuilder::buildUnsignedPubAddressAction(gRemoveAddress, owner, action.fio_address(), addresses,
273309
getChainParams(in), action.fee(), in.tpid(), in.expiry());
310+
} else if (in.action().has_remove_all_pub_addresses_message()) {
311+
const auto action = in.action().remove_all_pub_addresses_message();
312+
transaction = TransactionBuilder::buildUnsignedRemoveAllAddressesAction(owner, action.fio_address(),
313+
getChainParams(in), action.fee(), in.tpid(), in.expiry());
314+
} else if (in.action().has_add_bundled_transactions_message()) {
315+
const auto action = in.action().add_bundled_transactions_message();
316+
transaction = TransactionBuilder::buildUnsignedAddBundledTransactions(owner, action.fio_address(), action.bundle_sets(),
317+
getChainParams(in), action.fee(), in.tpid(), in.expiry());
274318
}
275319

276320
Data serTx;
@@ -381,4 +425,46 @@ Transaction TransactionBuilder::buildUnsignedRenewFioAddress(const Address& addr
381425
return tx;
382426
}
383427

428+
Transaction TransactionBuilder::buildUnsignedRemoveAllAddressesAction(const Address& address, const std::string& fioName,
429+
const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) {
430+
431+
string actor = Actor::actor(address);
432+
RemoveAllPubAddressActionData actionData(fioName, fee, walletTpId, actor);
433+
Data serData;
434+
actionData.serialize(serData);
435+
436+
Action action;
437+
action.account = ContractAddress;
438+
action.name = gRemoveAllPubAddresses;
439+
action.actionDataSer = serData;
440+
action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});
441+
442+
Transaction tx;
443+
expirySetDefaultIfNeeded(expiryTime);
444+
tx.set(expiryTime, chainParams);
445+
tx.actions.push_back(action);
446+
return tx;
447+
}
448+
449+
Transaction TransactionBuilder::buildUnsignedAddBundledTransactions(const Address& address, const std::string& fioName,
450+
uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime) {
451+
452+
string actor = Actor::actor(address);
453+
AddBundledTransactionsActionData actionData(fioName, bundleSets, fee, walletTpId, actor);
454+
Data serData;
455+
actionData.serialize(serData);
456+
457+
Action action;
458+
action.account = ContractAddress;
459+
action.name = gAddBundledTransactions;
460+
action.actionDataSer = serData;
461+
action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});
462+
463+
Transaction tx;
464+
expirySetDefaultIfNeeded(expiryTime);
465+
tx.set(expiryTime, chainParams);
466+
tx.actions.push_back(action);
467+
return tx;
468+
}
469+
384470
} // namespace TW::FIO

src/FIO/TransactionBuilder.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ class TransactionBuilder {
8181
const std::vector<std::pair<std::string, std::string>>& pubAddresses,
8282
const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime);
8383

84+
/// Create a signed `remalladdr` transaction, returned as json string (double quote delimited), suitable for remove_all_pub_address RPC call
85+
/// @address The owners' FIO address
86+
/// @privateKey The private key matching the address, needed for signing.
87+
/// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust"
88+
/// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls.
89+
/// @fee Max fee to spend, can be obtained using get_fee API.
90+
/// @walletTpId The FIO name of the originating wallet (project-wide constant)
91+
/// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry
92+
/// Note: fee is usually 0 for remove_all_pub_address.
93+
static std::string createRemoveAllPubAddresses(const Address& address, const PrivateKey& privateKey, const std::string& fioName,
94+
const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime);
95+
8496
/// Create a signed TransferTokens transaction, returned as json string (double quote delimited), suitable for transfer_tokens_pub_key RPC call
8597
/// @address The owners' FIO address
8698
/// @privateKey The private key matching the address, needed for signing.
@@ -130,6 +142,18 @@ class TransactionBuilder {
130142
const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime,
131143
const Data& iv);
132144

145+
/// Create a signed `addbundles` transaction, returned as json string (double quote delimited), suitable for add_bundled_transactions RPC call
146+
/// @address The owners' FIO address
147+
/// @privateKey The private key matching the address, needed for signing.
148+
/// @fioName The FIO name already registered to the owner. Ex.: "dmitry@trust"
149+
/// @bundleSets Number of bundled sets. One set is 100 bundled transactions.
150+
/// @chainParams Current parameters from the FIO chain, must be obtained recently using get_info and get_block calls.
151+
/// @fee Max fee to spend, can be obtained using get_fee API.
152+
/// @walletTpId The FIO name of the originating wallet (project-wide constant)
153+
/// @expiryTime Expiry for this message, can be 0, then it is taken from current time with default expiry
154+
static std::string createAddBundledTransactions(const Address& address, const PrivateKey& privateKey, const std::string& fioName,
155+
uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime);
156+
133157
/// Used internally. Creates signatures and json with transaction.
134158
static std::string signAndBuildTx(const Data& chainId, const Data& packedTx, const PrivateKey& privateKey);
135159

@@ -166,6 +190,12 @@ class TransactionBuilder {
166190

167191
static Transaction buildUnsignedRenewFioAddress(const Address& address, const std::string& fioName,
168192
const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime);
193+
194+
static Transaction buildUnsignedRemoveAllAddressesAction(const Address& address, const std::string& fioName,
195+
const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime);
196+
197+
static Transaction buildUnsignedAddBundledTransactions(const Address& address, const std::string& fioName,
198+
uint64_t bundleSets, const ChainParams& chainParams, uint64_t fee, const std::string& walletTpId, uint32_t expiryTime);
169199
};
170200

171201
} // namespace TW::FIO

src/proto/FIO.proto

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,16 @@ message Action {
7777
uint64 fee = 3;
7878
}
7979

80+
// Action for removing public chain addresses from a FIO name; remove_pub_address
81+
// Note: actor is not needed, computed from private key
82+
message RemoveAllPubAddress {
83+
// The FIO name already registered to the owner. Ex.: "alice@trust"
84+
string fio_address = 1;
85+
86+
// Max fee to spend, can be obtained using get_fee API.
87+
uint64 fee = 3;
88+
}
89+
8090
// Action for transferring FIO coins; transfer_tokens_pub_key
8191
// Note: actor is not needed, computed from private key
8292
message Transfer {
@@ -122,6 +132,18 @@ message Action {
122132
uint64 fee = 5;
123133
}
124134

135+
// 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.
136+
message AddBundledTransactions {
137+
// The FIO name already registered to the owner. Ex.: "alice@trust"
138+
string fio_address = 1;
139+
140+
// Number of bundled sets. One set is 100 bundled transactions.
141+
uint64 bundle_sets = 2;
142+
143+
// Max fee to spend, can be obtained using get_fee API.
144+
uint64 fee = 3;
145+
}
146+
125147
// Payload message
126148
oneof message_oneof {
127149
RegisterFioAddress register_fio_address_message = 1;
@@ -130,6 +152,8 @@ message Action {
130152
RenewFioAddress renew_fio_address_message = 4;
131153
NewFundsRequest new_funds_request_message = 5;
132154
RemovePubAddress remove_pub_address_message = 6;
155+
RemoveAllPubAddress remove_all_pub_addresses_message = 7;
156+
AddBundledTransactions add_bundled_transactions_message = 8;
133157
}
134158
}
135159

tests/chains/FIO/TWFIOTests.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,28 @@ TEST(TWFIO, RemovePubAddress) {
116116
EXPECT_EQ(output.action_name(), "remaddress");
117117
}
118118

119+
TEST(TWFIO, RemoveAllPubAddresses) {
120+
auto privateKey = parse_hex("93083dc4d9e8f613a57e3a862a1fa5d665c5e90141a8428990c945d1c2b56491");
121+
122+
Proto::SigningInput input;
123+
input.set_expiry(1713458993);
124+
input.mutable_chain_params()->set_chain_id(string(gChainIdMainnet.begin(), gChainIdMainnet.end()));
125+
input.mutable_chain_params()->set_head_block_number(256432311);
126+
input.mutable_chain_params()->set_ref_block_prefix(2287536876);
127+
input.set_private_key(string(privateKey.begin(), privateKey.end()));
128+
input.set_tpid("trust@fiomembers");
129+
auto action = input.mutable_action()->mutable_remove_all_pub_addresses_message();
130+
action->set_fio_address("sergeitrust@wallet");
131+
action->set_fee(0);
132+
133+
Proto::SigningOutput output;
134+
ANY_SIGN(input, TWCoinTypeFIO);
135+
EXPECT_EQ(Common::Proto::OK, output.error());
136+
// Successfully broadcasted: https://fio.bloks.io/transaction/f2facdebfcba1981377537424a6d7b7e7ebd8222c87ba4d25a480d1b968704b2
137+
EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"314f2166b7d8ec0a59880000000001003056372503a85b00c04dc9c468a4ba01b038b9d6c13372f700000000a8ed3232341273657267656974727573744077616c6c65740000000000000000b038b9d6c13372f71074727573744066696f6d656d6265727300","signatures":["SIG_K1_KXXtpz7NWhzCms7Dj54nSwwtCw6w4zLCyTLxs3tqqgLscrz91cMjcbN4yxcySvZ7t4MER8HPteeJZUnR16uLyDa1gFGzrx"]})", output.json());
138+
EXPECT_EQ(output.action_name(), "remalladdr");
139+
}
140+
119141
TEST(TWFIO, Transfer) {
120142
Proto::SigningInput input;
121143
input.set_expiry(1579790000);
@@ -183,4 +205,27 @@ TEST(TWFIO, NewFundsRequest) {
183205
EXPECT_EQ(output.action_name(), "newfundsreq");
184206
}
185207

208+
TEST(TWFIO, AddBundledTransactions) {
209+
auto privateKey = parse_hex("93083dc4d9e8f613a57e3a862a1fa5d665c5e90141a8428990c945d1c2b56491");
210+
211+
Proto::SigningInput input;
212+
input.set_expiry(1713458594);
213+
input.mutable_chain_params()->set_chain_id(string(gChainIdMainnet.begin(), gChainIdMainnet.end()));
214+
input.mutable_chain_params()->set_head_block_number(256431437);
215+
input.mutable_chain_params()->set_ref_block_prefix(791306279);
216+
input.set_private_key(string(privateKey.begin(), privateKey.end()));
217+
input.set_tpid("trust@fiomembers");
218+
auto action = input.mutable_action()->mutable_add_bundled_transactions_message();
219+
action->set_fio_address("sergeitrust@wallet");
220+
action->set_bundle_sets(1);
221+
action->set_fee(100000000000);
222+
223+
Proto::SigningOutput output;
224+
ANY_SIGN(input, TWCoinTypeFIO);
225+
EXPECT_EQ(Common::Proto::OK, output.error());
226+
// Successfully broadcasted: https://fio.bloks.io/transaction/2c00f2051ca3738c4fe03ceddb82c48fefd9c534d8bb793dc7dce5d12f4f4f9c
227+
EXPECT_EQ(R"({"compression":"none","packed_context_free_data":"","packed_trx":"a24d21664dd527602a2f0000000001003056372503a85b000056314d7d523201b038b9d6c13372f700000000a8ed32323c1273657267656974727573744077616c6c6574010000000000000000e87648170000001074727573744066696f6d656d62657273b038b9d6c13372f700","signatures":["SIG_K1_KjWGZ4Yd48VJcTAgox3HYVQhXeLhpRCgz2WqiF5WHRFSnbHouKxPgLQmymoABHC8EX51G1jU4ocWg2RKU17UYm4L5kTXP6"]})", output.json());
228+
EXPECT_EQ(output.action_name(), "addbundles");
229+
}
230+
186231
} // namespace TW::FIO::TWFIOTests

0 commit comments

Comments
 (0)