Skip to content

Commit

Permalink
feat(fio): Add support for addbundles and remalladdr actions (#3802)
Browse files Browse the repository at this point in the history
  • Loading branch information
satoshiotomakan committed Apr 18, 2024
1 parent 7a74dcb commit 51917c0
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 1 deletion.
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

0 comments on commit 51917c0

Please sign in to comment.