Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fio): Add support for addbundles and remalladdr actions #3802

Merged
merged 1 commit into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/FIO/Action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
31 changes: 30 additions & 1 deletion src/FIO/Action.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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:
Expand Down Expand Up @@ -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
86 changes: 86 additions & 0 deletions src/FIO/TransactionBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {};
}
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
30 changes: 30 additions & 0 deletions src/FIO/TransactionBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ class TransactionBuilder {
const std::vector<std::pair<std::string, std::string>>& 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.
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
24 changes: 24 additions & 0 deletions src/proto/FIO.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
}

Expand Down
45 changes: 45 additions & 0 deletions tests/chains/FIO/TWFIOTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Loading