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

IOS-2948 tangem integration research #1

Merged
merged 20 commits into from
Mar 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
32637ab
IOS-2948 Added external signer protocol / callbacks and an implementa…
megakoko Feb 1, 2023
30338ff
IOS-2948 Added external signer to tron tests
megakoko Feb 1, 2023
525fc1c
IOS-2948 Temporarily reduced the amount to build
megakoko Feb 1, 2023
d0e722e
IOS-2948 Added error to the signer protocol
megakoko Feb 6, 2023
72fffdc
IOS-2948 Refactoring, cleanup
megakoko Feb 7, 2023
1381e74
IOS-2948 Initial changes for TON
megakoko Feb 7, 2023
dcd1a04
IOS-2948 Passing external signer's publicKey to the signing function
megakoko Feb 7, 2023
15f5a11
IOS-2948 Making ExternalSigner provide public key
megakoko Feb 7, 2023
ea9dc47
IOS-2948 Made external signer tests co-exist with standard ones
megakoko Feb 7, 2023
45375aa
IOS-2948 Refactoring
megakoko Feb 7, 2023
4c81b57
IOS-2948 Refactoring
megakoko Feb 7, 2023
8aa0910
IOS-2948 Added private key signer clarification
megakoko Feb 7, 2023
5396fe0
IOS-2948 Resolving name conflicts: made Data(hexString:) and Data.hex…
megakoko Feb 8, 2023
5cce893
IOS-2948 Revert "IOS-2948 Temporarily reduced the amount to build"
megakoko Feb 8, 2023
4c07c61
IOS-2948 Added script to build JUST xcframeworks, without docs
megakoko Feb 8, 2023
a6927ab
IOS-2948 Added tangem tags in the source code
megakoko Feb 8, 2023
e75db93
IOS-2948 Added podspec for TrustWalletCore
megakoko Feb 9, 2023
c6e5a4b
IOS-2948 Disable the mac/catalyst build for now
megakoko Mar 3, 2023
f9f5f98
IOS-2948 Useless code
megakoko Mar 3, 2023
f01afbb
IOS-2948 Useless script
megakoko Mar 3, 2023
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
3 changes: 3 additions & 0 deletions include/TrustWalletCore/TWAnySigner.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ struct TWAnySigner;
/// \return The serialized data of a `SigningOutput` proto object. (e.g. TW.Bitcoin.Proto.SigningOutput).
extern TWData *_Nonnull TWAnySignerSign(TWData *_Nonnull input, enum TWCoinType coin);

// TANGEM
extern TWData *_Nonnull TWAnySignerSignExternally(TWData *_Nonnull input, enum TWCoinType coin, TWData *_Nonnull publicKey, TWData *_Nonnull (*_Nonnull externalSigner)(TWData *_Nonnull));

/// Signs a transaction specified by the JSON representation of signing input, coin type and a private key, returning the JSON representation of the signing output.
///
/// \param json JSON representation of a signing input
Expand Down
7 changes: 7 additions & 0 deletions src/Coin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ void TW::anyCoinSign(TWCoinType coinType, const Data& dataIn, Data& dataOut) {
dispatcher->sign(coinType, dataIn, dataOut);
}

// TANGEM
void TW::anyCoinSignExternally(TWCoinType coinType, const Data& dataIn, Data& dataOut, const Data& publicKey, std::function<Data(Data)> externalSigner) {
auto* dispatcher = coinDispatcher(coinType);
assert(dispatcher != nullptr);
dispatcher->signExternally(coinType, dataIn, dataOut, publicKey, externalSigner);
}

std::string TW::anySignJSON(TWCoinType coinType, const std::string& json, const Data& key) {
auto* dispatcher = coinDispatcher(coinType);
assert(dispatcher != nullptr);
Expand Down
3 changes: 3 additions & 0 deletions src/Coin.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ const char* chainId(TWCoinType coin);
// Note: use output parameter to avoid unneeded copies
void anyCoinSign(TWCoinType coinType, const Data& dataIn, Data& dataOut);

// TANGEM
void anyCoinSignExternally(TWCoinType coinType, const Data& dataIn, Data& dataOut, const Data& publicKey, const std::function<Data(Data)> externalSigner);

uint32_t slip44Id(TWCoinType coin);

std::string anySignJSON(TWCoinType coinType, const std::string& json, const Data& key);
Expand Down
11 changes: 11 additions & 0 deletions src/CoinEntry.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class CoinEntry {
virtual Data addressToData([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const std::string& address) const { return {}; }
// Signing
virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const = 0;
// TANGEM
virtual void signExternally(TWCoinType coin, const Data& dataIn, Data& dataOut, const Data& publicKey, std::function<Data(Data)> externalSigner) const { }
virtual bool supportsJSONSigning() const { return false; }
// It is optional, Signing JSON input with private key
virtual std::string signJSON([[maybe_unused]] TWCoinType coin, [[maybe_unused]] const std::string& json, [[maybe_unused]] const Data& key) const { return ""; }
Expand Down Expand Up @@ -79,6 +81,15 @@ void signTemplate(const Data& dataIn, Data& dataOut) {
dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end());
}

// TANGEM
template <typename Signer, typename Input>
void signTemplateExternally(const Data& dataIn, Data& dataOut, const Data& publicKey, const std::function<Data(Data)> externalSigner) {
auto input = Input();
input.ParseFromArray(dataIn.data(), (int)dataIn.size());
auto serializedOut = Signer::sign(input, publicKey, externalSigner).SerializeAsString();
dataOut.insert(dataOut.end(), serializedOut.begin(), serializedOut.end());
}

// Note: use output parameter to avoid unneeded copies
template <typename Planner, typename Input>
void planTemplate(const Data& dataIn, Data& dataOut) {
Expand Down
5 changes: 5 additions & 0 deletions src/TheOpenNetwork/Entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@ void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::D
signTemplate<Signer, Proto::SigningInput>(dataIn, dataOut);
}

// TANGEM
void Entry::signExternally([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut, const Data& publicKey, const std::function<Data(Data)> externalSigner) const {
signTemplateExternally<Signer, Proto::SigningInput>(dataIn, dataOut, publicKey, externalSigner);
}

} // namespace TW::TheOpenNetwork
2 changes: 2 additions & 0 deletions src/TheOpenNetwork/Entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class Entry final : public CoinEntry {
std::string normalizeAddress(TWCoinType coin, const std::string& address) const;
std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const;
void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const;
// TANGEM
void signExternally(TWCoinType coin, const Data& dataIn, Data& dataOut, const Data& publicKey, const std::function<Data(Data)> externalSigner) const;
};

} // namespace TW::TheOpenNetwork
15 changes: 14 additions & 1 deletion src/TheOpenNetwork/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@
namespace TW::TheOpenNetwork {

Data Signer::createTransferMessage(std::shared_ptr<Wallet> wallet, const PrivateKey& privateKey, const Proto::Transfer& transfer) {
return createTransferMessage(wallet, privateKey, transfer, nullptr);
}

// TANGEM
Data Signer::createTransferMessage(std::shared_ptr<Wallet> wallet, const PrivateKey& privateKey, const Proto::Transfer& transfer, const std::function<Data(Data)> externalSigner) {
const auto msg = wallet->createTransferMessage(
privateKey,
externalSigner,
Address(transfer.dest()),
transfer.amount(),
transfer.sequence_number(),
Expand All @@ -32,7 +38,14 @@ Data Signer::createTransferMessage(std::shared_ptr<Wallet> wallet, const Private
Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept {
const auto& privateKey = PrivateKey(input.private_key());
const auto& publicKey = privateKey.getPublicKey(TWPublicKeyTypeED25519);
return Signer::sign(input, publicKey.bytes, nullptr);
}

// TANGEM
Proto::SigningOutput Signer::sign(const Proto::SigningInput& input, const Data& publicKeyData, const std::function<Data(Data)> externalSigner) noexcept {
const auto& privateKey = PrivateKey(input.private_key());
const PublicKey publicKey(publicKeyData, TWPublicKeyTypeED25519);

auto protoOutput = Proto::SigningOutput();

switch (input.action_oneof_case()) {
Expand All @@ -44,7 +57,7 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept {
case Proto::WalletVersion::WALLET_V4_R2: {
const int8_t workchainId = WorkchainType::Basechain;
auto wallet = std::make_shared<WalletV4R2>(publicKey, workchainId);
const auto& transferMessage = Signer::createTransferMessage(wallet, privateKey, transfer);
const auto& transferMessage = Signer::createTransferMessage(wallet, privateKey, transfer, externalSigner);
protoOutput.set_encoded(TW::Base64::encode(transferMessage));
break;
}
Expand Down
6 changes: 6 additions & 0 deletions src/TheOpenNetwork/Signer.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,15 @@ class Signer {

/// Creates a signed transfer message
static Data createTransferMessage(std::shared_ptr<Wallet> wallet, const PrivateKey& privateKey, const Proto::Transfer& transfer);

// TANGEM
static Data createTransferMessage(std::shared_ptr<Wallet> wallet, const PrivateKey& privateKey, const Proto::Transfer& transfer, const std::function<Data(Data)> externalSigner);

/// Signs a Proto::SigningInput transaction
static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept;

// TANGEM
static Proto::SigningOutput sign(const Proto::SigningInput& input, const Data& publicKey, const std::function<Data(Data)> externalSigner) noexcept;
};

} // namespace TW::TheOpenNetwork
35 changes: 34 additions & 1 deletion src/TheOpenNetwork/wallet/Wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// terms governing use, modification, and redistribution, is contained in the
// file LICENSE at the root of the source code distribution tree.

#include <future>

#include "Wallet.h"

#include "HexCoding.h"
Expand Down Expand Up @@ -68,6 +70,29 @@ Cell::Ref Wallet::createTransferMessage(
uint8_t mode,
uint32_t expireAt,
const std::string& comment
) const {
return createTransferMessage(
privateKey,
nullptr,
dest,
amount,
sequence_number,
mode,
expireAt,
comment
);
}

// TANGEM
Cell::Ref Wallet::createTransferMessage(
const PrivateKey& privateKey,
const std::function<Data(Data)> externalSigner,
const Address& dest,
uint64_t amount,
uint32_t sequence_number,
uint8_t mode,
uint32_t expireAt,
const std::string& comment
) const {
const auto transferMessageHeader = std::make_shared<CommonTON::ExternalInboundMessageHeader>(this->getAddress().addressData);
Message transferMessage = Message(MessageData(transferMessageHeader));
Expand All @@ -80,7 +105,15 @@ Cell::Ref Wallet::createTransferMessage(
CellBuilder bodyBuilder;
const Cell::Ref signingMessage = this->createSigningMessage(dest, amount, sequence_number, mode, expireAt, comment);
Data data(signingMessage->hash.begin(), signingMessage->hash.end());
const auto signature = privateKey.sign(data, TWCurveED25519);

// TANGEM
auto signature = Data();
if(externalSigner) {
std::future<Data> signedDataFuture = std::async(externalSigner, data);
signature = signedDataFuture.get();
} else {
signature = privateKey.sign(data, TWCurveED25519);
}

bodyBuilder.appendRaw(signature, static_cast<uint16_t>(signature.size()) * 8);
bodyBuilder.appendCellSlice(CellSlice(signingMessage.get()));
Expand Down
11 changes: 11 additions & 0 deletions src/TheOpenNetwork/wallet/Wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ class Wallet {
uint32_t expireAt = 0,
const std::string& comment = ""
) const;
// TANGEM
[[nodiscard]] Cell::Ref createTransferMessage(
const PrivateKey& privateKey,
const std::function<Data(Data)> externalSigner,
const Address& dest,
uint64_t amount,
uint32_t sequence_number,
uint8_t mode,
uint32_t expireAt = 0,
const std::string& comment = ""
) const;

protected:
[[nodiscard]] virtual Cell::Ref createDataCell() const = 0;
Expand Down
5 changes: 5 additions & 0 deletions src/Tron/Entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,9 @@ void Entry::sign([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::D
signTemplate<Signer, Proto::SigningInput>(dataIn, dataOut);
}

// TANGEM
void Entry::signExternally([[maybe_unused]] TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut, const Data& publicKey, const std::function<Data(Data)> externalSigner) const {
signTemplateExternally<Signer, Proto::SigningInput>(dataIn, dataOut, publicKey, externalSigner);
}

} // namespace TW::Tron
2 changes: 2 additions & 0 deletions src/Tron/Entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class Entry final : public CoinEntry {
bool validateAddress(TWCoinType coin, const std::string& address, const PrefixVariant& addressPrefix) const;
std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TWDerivation derivation, const PrefixVariant& addressPrefix) const;
void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const;
// TANGEM
void signExternally(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut, const Data& publicKey, const std::function<Data(Data)> externalSigner) const;
};

} // namespace TW::Tron
16 changes: 15 additions & 1 deletion src/Tron/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <chrono>
#include <cassert>
#include <future>

namespace TW::Tron {

Expand Down Expand Up @@ -202,6 +203,11 @@ void setBlockReference(const Proto::Transaction& transaction, protocol::Transact
}

Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept {
return Signer::sign(input, Data(), nullptr);
}

// TANGEM
Proto::SigningOutput Signer::sign(const Proto::SigningInput& input, const Data& publicKey, const std::function<Data(Data)> externalSigner) noexcept {
auto internal = protocol::Transaction();
auto output = Proto::SigningOutput();

Expand Down Expand Up @@ -310,7 +316,15 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input) noexcept {
const auto hash = Hash::sha256(Data(serialized.begin(), serialized.end()));

const auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end()));
const auto signature = key.sign(hash, TWCurveSECP256k1);
auto signature = Data();

// TANGEM
if(externalSigner) {
std::future<Data> signedDataFuture = std::async(externalSigner, hash);
signature = signedDataFuture.get();
} else {
signature = key.sign(hash, TWCurveSECP256k1);
}

const auto json = transactionJSON(internal, hash, signature).dump();

Expand Down
3 changes: 3 additions & 0 deletions src/Tron/Signer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class Signer {

/// Signs the given transaction.
static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept;

// TANGEM
static Proto::SigningOutput sign(const Proto::SigningInput& input, const Data& publicKey, const std::function<Data(Data)> externalSigner) noexcept;
};

} // namespace TW::Tron
19 changes: 19 additions & 0 deletions src/interface/TWAnySigner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,25 @@ TWData* _Nonnull TWAnySignerSign(TWData* _Nonnull data, enum TWCoinType coin) {
return TWDataCreateWithBytes(dataOut.data(), dataOut.size());
}

// TANGEM
TWData* _Nonnull TWAnySignerSignExternally(TWData* _Nonnull data, enum TWCoinType coin, TWData *_Nonnull publicKey, TWData* (*externalSigner)(TWData*)) {
// Just a conversion between TWData and TW::Data
auto dataExternalSigner = [externalSigner](Data dataToSign) -> Data {
const TWData* twDataToSign = TWDataCreateWithBytes(dataToSign.data(), dataToSign.size());
const TWData* twDataSigned = externalSigner(twDataToSign);

const Data& dataSigned = *(reinterpret_cast<const Data*>(twDataSigned));
return dataSigned;
};

const Data& publicKeyData = *(reinterpret_cast<const Data*>(publicKey));

const Data& dataIn = *(reinterpret_cast<const Data*>(data));
Data dataOut;
TW::anyCoinSignExternally(coin, dataIn, dataOut, publicKeyData, dataExternalSigner);
return TWDataCreateWithBytes(dataOut.data(), dataOut.size());
}

TWString *_Nonnull TWAnySignerSignJSON(TWString *_Nonnull json, TWData *_Nonnull key, enum TWCoinType coin) {
const Data& keyData = *(reinterpret_cast<const Data*>(key));
const std::string& jsonString = *(reinterpret_cast<const std::string*>(json));
Expand Down
77 changes: 77 additions & 0 deletions swift/Sources/AnySigner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,46 @@ import SwiftProtobuf
public typealias SigningInput = Message
public typealias SigningOutput = Message

// TANGEM
public protocol Signer {
var error: Error? { get }

var publicKey: Data { get }

func sign(_ data: Data) -> Data
func sign(_ data: [Data]) -> [Data]
}

// TANGEM
// For auto tests only
public struct PrivateKeySigner: Signer {
public var error: Error?

public var publicKey: Data {
privateKey.getPublicKey(coinType: coin).data
}

private let privateKey: PrivateKey
private let coin: CoinType

public init(privateKey: PrivateKey, coin: CoinType) {
self.privateKey = privateKey
self.coin = coin
}

public func sign(_ data: Data) -> Data {
return privateKey.sign(digest: data, curve: coin.curve)!
}

public func sign(_ data: [Data]) -> [Data] {
return data.map { privateKey.sign(digest: $0, curve: coin.curve)! }
}
}

// TANGEM
// We can't capture a local variable in a closure we're passing to std::function. Global variables are fine.
var externalSigner: Signer? = nil

/// Represents a signer to sign transactions for any blockchain.
public final class AnySigner {

Expand All @@ -27,6 +67,22 @@ public final class AnySigner {
fatalError(error.localizedDescription)
}
}

// TANGEM
public static func signExternally<SigningOutput: Message>(input: SigningInput, coin: CoinType, signer: Signer) -> SigningOutput {
defer {
externalSigner = nil
}

externalSigner = signer

do {
let outputData = nativeSignExternally(data: try input.serializedData(), coin: coin, publicKey: signer.publicKey)
return try SigningOutput(serializedData: outputData)
} catch let error {
fatalError(error.localizedDescription)
}
}

/// Signs a transaction by serialized data of a SigningInput and coin type
///
Expand All @@ -41,6 +97,27 @@ public final class AnySigner {
}
return TWDataNSData(TWAnySignerSign(inputData, TWCoinType(rawValue: coin.rawValue)))
}

// TANGEM
public static func nativeSignExternally(data: Data, coin: CoinType, publicKey: Data) -> Data {
let inputData = TWDataCreateWithNSData(data)
let publicKeyData = TWDataCreateWithNSData(publicKey)

defer {
TWDataDelete(inputData)
TWDataDelete(publicKeyData)
}

return TWDataNSData(TWAnySignerSignExternally(inputData, TWCoinType(rawValue: coin.rawValue), publicKeyData, { twDataToSign in
guard let externalSigner = externalSigner else {
fatalError("You must set external signer to sign asynchronously")
}

let dataToSign = TWDataNSData(twDataToSign)
let dataSigned = externalSigner.sign(dataToSign)
return TWDataCreateWithNSData(dataSigned)
}))
}

/// Check if AnySigner supports signing JSON representation of SigningInput for a given coin.
public static func supportsJSON(coin: CoinType) -> Bool {
Expand Down
Loading