Skip to content

Commit

Permalink
feat(FIO): Add support for "remaddress" action type (#3795)
Browse files Browse the repository at this point in the history
* [FIO]: Add support for "remaddress" action

* [FIO]: Add `SigningOutput::action_name` parameter
  • Loading branch information
satoshiotomakan committed Apr 17, 2024
1 parent 49a36fc commit 7a74dcb
Show file tree
Hide file tree
Showing 10 changed files with 165 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/FIO/Action.cpp
Expand Up @@ -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);
Expand Down
9 changes: 6 additions & 3 deletions src/FIO/Action.h
Expand Up @@ -81,16 +81,19 @@ 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;
uint64_t fee;
std::string tpid;
std::string actor;

AddPubAddressData(const std::string& fioAddress, const std::vector<PublicAddress>& addresses,
PubAddressActionData(const std::string& fioAddress, const std::vector<PublicAddress>& addresses,
uint64_t fee, const std::string& tpid, const std::string& actor) :
fioAddress(fioAddress), addresses(addresses),
fee(fee), tpid(tpid), actor(actor) {}
Expand Down
4 changes: 3 additions & 1 deletion src/FIO/Signer.cpp
Expand Up @@ -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);
}
Expand Down
90 changes: 70 additions & 20 deletions src/FIO/TransactionBuilder.cpp
Expand Up @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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<std::pair<std::string, std::string>> 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;
}
Expand All @@ -96,7 +131,19 @@ string TransactionBuilder::createAddPubAddress(const Address& address, const Pri
const vector<pair<string, string>>& 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<pair<string, string>>& 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);
Expand Down Expand Up @@ -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
Expand All @@ -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});

Expand Down Expand Up @@ -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();
Expand All @@ -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<std::pair<std::string, std::string>> 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);
Expand All @@ -239,20 +293,19 @@ 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;
raData.serialize(serData);

Action action;
action.account = ContractAddress;
action.name = apiName;
action.name = gRegisterFioAddress;
action.actionDataSer = serData;
action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});

Expand All @@ -263,19 +316,20 @@ Transaction TransactionBuilder::buildUnsignedRegisterFioAddress(const Address& a
return tx;
}

Transaction TransactionBuilder::buildUnsignedAddPubAddress(const Address& address, const std::string& fioName, const std::vector<std::pair<std::string, std::string>>& 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<std::pair<std::string, std::string>>& 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
vector<PublicAddress> pubAddresses2;
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;
Expand All @@ -290,16 +344,14 @@ 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;
ttData.serialize(serData);

Action action;
action.account = ContractToken;
action.name = apiName;
action.name = gTransferFIOPubkey;
action.actionDataSer = serData;
action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});

Expand All @@ -311,16 +363,14 @@ 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;
raData.serialize(serData);

Action action;
action.account = ContractAddress;
action.name = apiName;
action.name = gRenewFIOAddress;
action.actionDataSer = serData;
action.auth.authArray.push_back(Authorization{actor, AuthrizationActive});

Expand Down
30 changes: 28 additions & 2 deletions src/FIO/TransactionBuilder.h
Expand Up @@ -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.
Expand All @@ -64,6 +67,20 @@ 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 `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<std::pair<std::string, std::string>>& 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.
Expand Down Expand Up @@ -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<std::pair<std::string, std::string>>& 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<std::pair<std::string, std::string>>& 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,
Expand Down
17 changes: 17 additions & 0 deletions src/proto/FIO.proto
Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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;
}
1 change: 1 addition & 0 deletions tests/chains/FIO/SignerTests.cpp
Expand Up @@ -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

0 comments on commit 7a74dcb

Please sign in to comment.