Skip to content

Commit

Permalink
FIO Support (#832)
Browse files Browse the repository at this point in the history
* FIO Signer: Actor derivation.
* FIO Signer: Actor test -- use test from fiojs.
* FIO Signer and TransationBuilder, add_pub_address.
* FIO TransactionBuilder: Expose maxFee for add_pub_address.
* FIO TransactionBuilder, add register_fio_address.
* Review comments.
* FIO Signer:  Add proto and C interfaces, tests.
* FIO: Compile fix for iOS compile, method returning string and no parameters.
* FIO Signer: Add swift tests.
* Minor iOS codacy fix.
* FIO Signer: Add Kotlin tests.
* Android test fix.
* Remove DefaultTpid.
* Some API renames (review comment).
* Use Json library for json building (review comments).
* FIO Signer: add transfer_tokens_pub_key.
* Some renames (review comments).
  • Loading branch information
optout21 authored Feb 2, 2020
1 parent b823223 commit 5b979d4
Show file tree
Hide file tree
Showing 20 changed files with 1,411 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright © 2017-2020 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

package com.trustwallet.core.app.blockchains.fio

import com.trustwallet.core.app.utils.toHex
import com.trustwallet.core.app.utils.toHexByteArray
import org.junit.Assert.assertEquals
import org.junit.Test
import wallet.core.jni.*

class TestFIOAddress {

init {
System.loadLibrary("TrustWalletCore")
}

@Test
fun testAddress() {
val addressFromString = AnyAddress("FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o", CoinType.FIO)
assertEquals(addressFromString.description(), "FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o")

val key = PrivateKey("ea8eb60b7e5868e218f248e032769020b4fea5dcfd02f2992861eaf4fb534854".toHexByteArray())
val pubkey = key.getPublicKeySecp256k1(false)
val addressFromKey = AnyAddress(pubkey, CoinType.FIO)
assertEquals(addressFromKey.description(), "FIO5kJKNHwctcfUM5XZyiWSqSTM5HTzznJP9F3ZdbhaQAHEVq575o")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright © 2017-2020 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

package com.trustwallet.core.app.blockchains.fio

import com.google.protobuf.ByteString
import com.trustwallet.core.app.utils.toHex
import com.trustwallet.core.app.utils.toHexBytes
import com.trustwallet.core.app.utils.toHexByteArray
import org.junit.Assert.assertEquals
import org.junit.Test
import wallet.core.jni.FIOSigner
import wallet.core.jni.AnyAddress
import wallet.core.jni.CoinType
import wallet.core.jni.PrivateKey
import wallet.core.jni.proto.FIO

class TestFIOSigner {

init {
System.loadLibrary("TrustWalletCore")
}

@Test
fun testRegisterFioAddress() {
val chainId = ByteString.copyFrom("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd77".toHexBytes())
val privateKey = PrivateKey("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035".toHexByteArray())
val publicKey = privateKey.getPublicKeySecp256k1(false)
val address = AnyAddress(publicKey, CoinType.FIO)

val chainParams = FIO.ChainParams.newBuilder()
.setChainId(chainId)
.setHeadBlockNumber(39881)
.setRefBlockPrefix(4279583376.toInt())
val regAddrAction = FIO.Action.RegisterFioAddress.newBuilder()
.setFioAddress("adam@fiotestnet")
.setOwnerFioPublicKey(address.description())
.setFee(5000000000)
.setTpid("rewards@wallet")
val action = FIO.Action.newBuilder()
.setRegisterFioAddressMessage(regAddrAction)
val input = FIO.SigningInput.newBuilder()
.setExpiry(1579784511)
.setChainParams(chainParams)
.setPrivateKey(ByteString.copyFrom(privateKey.data()))
.setAction(action)

val out = FIOSigner.sign(input.build())
assertEquals(out.error, "")
val expectedJson =
"""{"compression":"none","packed_context_free_data":"","packed_trx":"3f99295ec99b904215ff0000000001003056372503a85b0000c6eaa66498ba01102b2f46fca756b200000000a8ed3232650f6164616d4066696f746573746e65743546494f366d31664d645470526b52426e6564765973685843784c4669433573755255384b44667838787874587032686e7478706e6600f2052a01000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K19ugLriG3ApYgjJCRDsy21p9xgsjbDtqBuZrmAEix9XYzndR1kNbJ6fXCngMJMAhxUHfwHAsPnh58otXiJZkazaM1EkS5"]}"""
assertEquals(expectedJson, out.json)
}

@Test
fun testAddPubAddress() {
val chainId = ByteString.copyFrom("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd77".toHexBytes())
val privateKey = PrivateKey("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035".toHexByteArray())
val publicKey = privateKey.getPublicKeySecp256k1(false)
val address = AnyAddress(publicKey, CoinType.FIO)

val chainParams = FIO.ChainParams.newBuilder()
.setChainId(chainId)
.setHeadBlockNumber(11565)
.setRefBlockPrefix(4281229859.toInt())
val addAddrAction = FIO.Action.AddPubAddress.newBuilder()
.setFioAddress("adam@fiotestnet")
.addPublicAddresses(FIO.PublicAddress.newBuilder().setTokenCode("BTC").setAddress("bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"))
.addPublicAddresses(FIO.PublicAddress.newBuilder().setTokenCode("ETH").setAddress("0xce5cB6c92Da37bbBa91Bd40D4C9D4D724A3a8F51"))
.addPublicAddresses(FIO.PublicAddress.newBuilder().setTokenCode("BNB").setAddress("bnb1ts3dg54apwlvr9hupv2n0j6e46q54znnusjk9s"))
.setFee(0)
.setTpid("rewards@wallet")
val action = FIO.Action.newBuilder()
.setAddPubAddressMessage(addAddrAction)
val input = FIO.SigningInput.newBuilder()
.setExpiry(1579729429)
.setChainParams(chainParams)
.setPrivateKey(ByteString.copyFrom(privateKey.data()))
.setAction(action)

val out = FIOSigner.sign(input.build())
assertEquals(out.error, "")
val expectedJson =
"""{"compression":"none","packed_context_free_data":"","packed_trx":"15c2285e2d2d23622eff0000000001003056372503a85b0000c6eaa664523201102b2f46fca756b200000000a8ed3232bd010f6164616d4066696f746573746e657403034254432a626331717679343037347267676b647232707a773576706e6e3632656730736d7a6c7877703730643776034554482a30786365356342366339324461333762624261393142643430443443394434443732344133613846353103424e422a626e6231747333646735346170776c76723968757076326e306a366534367135347a6e6e75736a6b39730000000000000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K85BxXzJwvjPs3mFeKatWSjBHuMXTw634RRtf6ZMytpzLCdpHcJ7CQWPeXJvwm7aoz7XJJKapmoT4jzCLoVBv2cxP149Bx"]}"""
assertEquals(expectedJson, out.json)
}

@Test
fun testTransfer() {
val chainId = ByteString.copyFrom("4e46572250454b796d7296eec9e8896327ea82dd40f2cd74cf1b1d8ba90bcd77".toHexBytes())
val privateKey = PrivateKey("ba0828d5734b65e3bcc2c51c93dfc26dd71bd666cc0273adee77d73d9a322035".toHexByteArray())
val publicKey = privateKey.getPublicKeySecp256k1(false)
val address = AnyAddress(publicKey, CoinType.FIO)

val chainParams = FIO.ChainParams.newBuilder()
.setChainId(chainId)
.setHeadBlockNumber(50000)
.setRefBlockPrefix(4000123456.toInt())
val transferAction = FIO.Action.Transfer.newBuilder()
.setPayeePublicKey("FIO7uMZoeei5HtXAD24C4yCkpWWbf24bjYtrRNjWdmGCXHZccwuiE")
.setAmount(1000000000)
.setFee(250000000)
.setTpid("rewards@wallet")
val action = FIO.Action.newBuilder()
.setTransferMessage(transferAction)
val input = FIO.SigningInput.newBuilder()
.setExpiry(1579790000)
.setChainParams(chainParams)
.setPrivateKey(ByteString.copyFrom(privateKey.data()))
.setAction(action)

val out = FIOSigner.sign(input.build())
assertEquals(out.error, "")
val expectedJson =
"""{"compression":"none","packed_context_free_data":"","packed_trx":"b0ae295e50c3400a6dee00000000010000980ad20ca85be0e1d195ba85e7cd01102b2f46fca756b200000000a8ed32325d3546494f37754d5a6f6565693548745841443234433479436b70575762663234626a597472524e6a57646d474358485a63637775694500ca9a3b0000000080b2e60e00000000102b2f46fca756b20e726577617264734077616c6c657400","signatures":["SIG_K1_K9VRCnvaTYN7vgcoVKVXgyJTdKUGV8hLXgFLoEbvqAcFxy7DXQ1rSnAfEuabi4ATkgmvnpaSBdVFN7TBtM1wrbZYqeJQw9"]}"""
assertEquals(expectedJson, out.json)
}
}
3 changes: 1 addition & 2 deletions codegen/lib/code_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ def should_return_data(method)
end

def should_return_string(method)
return false if method.parameters.empty?

# Note: method with no parameters can also return string
method.return_type.name == :string
end

Expand Down
24 changes: 24 additions & 0 deletions include/TrustWalletCore/TWFIOSigner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright © 2017-2020 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

#pragma once

#include "TWBase.h"

#include "TWFIOProto.h"
#include "TWString.h"

TW_EXTERN_C_BEGIN

/// Represents a FIO Signer.
TW_EXPORT_CLASS
struct TWFIOSigner;

/// Build and sign a FIO message
TW_EXPORT_STATIC_METHOD
TW_FIO_Proto_SigningOutput TWFIOSignerSign(TW_FIO_Proto_SigningInput input);

TW_EXTERN_C_END
8 changes: 8 additions & 0 deletions src/BinaryCoding.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <cstddef>
#include <cstdint>
#include <vector>
#include <string>

namespace TW {

Expand Down Expand Up @@ -175,4 +176,11 @@ inline uint64_t decode64BE(const uint8_t* _Nonnull src) {
// clang-format on
}

/// Encodes an ASCII string prefixed by the length (varInt)
inline void encodeString(const std::string& str, std::vector<uint8_t>& data) {
size_t size = str.size();
encodeVarInt(size, data);
data.insert(data.end(), str.data(), str.data() + size);
}

} // namespace TW
47 changes: 47 additions & 0 deletions src/FIO/Action.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright © 2017-2020 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

#include "Action.h"

namespace TW::FIO {

void Action::serialize(Data& out) const {
EOS::Name(account).serialize(out);
EOS::Name(name).serialize(out);
auth.serialize(out);
encodeVarInt(actionDataSer.size(), out);
if (includeExtra01BeforeData) {
append(out, 1); // 01
}
append(out, actionDataSer);
append(out, 0); // 00
}

void AddPubAddressData::serialize(Data& out) const {
encodeString(fioAddress, out);
addresses.serialize(out);
encode64LE(fee, out);
EOS::Name(actor).serialize(out);
encodeString(tpid, out);
}

void RegisterFioAddressData::serialize(Data& out) const {
encodeString(fioAddress, out);
encodeString(ownerPublicKey, out);
encode64LE(fee, out);
EOS::Name(actor).serialize(out);
encodeString(tpid, out);
}

void TransferData::serialize(Data& out) const {
encodeString(payeePublicKey, out);
encode64LE(amount, out);
encode64LE(fee, out);
EOS::Name(actor).serialize(out);
encodeString(tpid, out);
}

} // namespace TW::FIO
127 changes: 127 additions & 0 deletions src/FIO/Action.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright © 2017-2020 Trust Wallet.
//
// This file is part of Trust. The full Trust copyright notice, including
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

#pragma once

#include "EOS/Name.h" // Name is reused
#include "../Data.h"
#include "../BinaryCoding.h"

#include <string>
#include <vector>

namespace TW::FIO {

/// Represents an authorization record (actor/permission pair)
class Authorization {
public:
std::string actor;
std::string permission;
void serialize(Data& out) const {
EOS::Name(actor).serialize(out);
EOS::Name(permission).serialize(out);
}
};

/// Represents an array of authorizations
class AuthorizationArray {
public:
std::vector<Authorization> authArray;
void serialize(Data& out) const {
encodeVarInt(authArray.size(), out);
for (const auto& item : authArray) {
item.serialize(out);
}
}
};

/// Represents a tokentype:address pair, such as {"BTC", "bc1qvy4074rggkdr2pzw5vpnn62eg0smzlxwp70d7v"}
class PublicAddress {
public:
std::string tokenCode;
std::string address;
void serialize(Data& out) const {
encodeString(tokenCode, out);
encodeString(address, out);
}
};

/// An array of public addresses
class PublicAddresses {
public:
std::vector<PublicAddress> addressList;
PublicAddresses(const std::vector<std::pair<std::string, std::string>>& addresses) {
for (const auto& item : addresses) {
addressList.push_back(PublicAddress{item.first, item.second});
}
}
void serialize(Data& out) const {
encodeVarInt(addressList.size(), out);
for (const auto& item : addressList) {
item.serialize(out);
}
}
};

/// Represents an Action. Some common fields, and an action-specific data field, stored serialized.
class Action {
public:
std::string account;
std::string name;
AuthorizationArray auth;
bool includeExtra01BeforeData = false;
Data actionDataSer;

void serialize(Data& out) const;
};

/// AddPubAddress action data part.
class AddPubAddressData {
public:
std::string fioAddress;
PublicAddresses addresses;
uint64_t fee;
std::string tpid;
std::string actor;

AddPubAddressData(const std::string& fioAddress, std::vector<std::pair<std::string, std::string>> addresses,
uint64_t fee, const std::string& tpid, const std::string& actor) :
fioAddress(fioAddress), addresses(addresses),
fee(fee), tpid(tpid), actor(actor) {}
void serialize(Data& out) const;
};

/// RegisterFioAddress action data part.
class RegisterFioAddressData {
public:
std::string fioAddress;
std::string ownerPublicKey;
uint64_t fee;
std::string tpid;
std::string actor;

RegisterFioAddressData(const std::string& fioAddress, const std::string& ownerPublicKey,
uint64_t fee, const std::string& tpid, const std::string& actor) :
fioAddress(fioAddress), ownerPublicKey(ownerPublicKey),
fee(fee), tpid(tpid), actor(actor) {}
void serialize(Data& out) const;
};

/// TransferTokens action data part.
class TransferData {
public:
std::string payeePublicKey;
uint64_t amount;
uint64_t fee;
std::string tpid;
std::string actor;

TransferData(const std::string& payeePublicKey, uint64_t amount, uint64_t fee, const std::string& tpid, const std::string& actor) :
payeePublicKey(payeePublicKey), amount(amount), fee(fee), tpid(tpid), actor(actor) {}
void serialize(Data& out) const;
};

} // namespace TW::FIO
Loading

0 comments on commit 5b979d4

Please sign in to comment.