diff --git a/samples/kmp/shared/build.gradle.kts b/samples/kmp/shared/build.gradle.kts index a86cc47b222..3c96d189b58 100644 --- a/samples/kmp/shared/build.gradle.kts +++ b/samples/kmp/shared/build.gradle.kts @@ -35,7 +35,7 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation("com.trustwallet:wallet-core-kotlin:3.2.13") + implementation("com.trustwallet:wallet-core-kotlin:3.2.17") } } val commonTest by getting { diff --git a/src/Cosmos/ProtobufSerialization.cpp b/src/Cosmos/ProtobufSerialization.cpp index 4ebb11e781c..657fe49cbca 100644 --- a/src/Cosmos/ProtobufSerialization.cpp +++ b/src/Cosmos/ProtobufSerialization.cpp @@ -29,12 +29,21 @@ #include "Base64.h" #include "uint256.h" -#include - using namespace TW; namespace TW::Cosmos::Protobuf { +namespace internal { + +// Some of the Cosmos blockchains use different public key types for address deriving and transaction signing. +// `registry.json` contains the public key required to derive an address, +// while this function prepares the given public key to use it for transaction signing/compiling. +inline PublicKey preparePublicKey(const PublicKey& publicKey, TWCoinType coin) { + return coin == TWCoinTypeNativeEvmos ? publicKey.compressed() : publicKey; +} + +} // namespace internal + using json = nlohmann::json; using string = std::string; const auto ProtobufAnyNamespacePrefix = ""; // to override default 'type.googleapis.com' @@ -407,6 +416,8 @@ std::string buildAuthInfo(const Proto::SigningInput& input, TWCoinType coin) { } std::string buildAuthInfo(const Proto::SigningInput& input, const PublicKey& publicKey, TWCoinType coin) { + const auto pbk = internal::preparePublicKey(publicKey, coin); + if (input.messages_size() >= 1 && input.messages(0).has_sign_direct_message()) { return input.messages(0).sign_direct_message().auth_info_bytes(); } @@ -419,19 +430,19 @@ std::string buildAuthInfo(const Proto::SigningInput& input, const PublicKey& pub switch(coin) { case TWCoinTypeNativeEvmos: { auto pubKey = ethermint::crypto::v1::ethsecp256k1::PubKey(); - pubKey.set_key(publicKey.bytes.data(), publicKey.bytes.size()); + pubKey.set_key(pbk.bytes.data(), pbk.bytes.size()); signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); break; } case TWCoinTypeNativeInjective: { auto pubKey = injective::crypto::v1beta1::ethsecp256k1::PubKey(); - pubKey.set_key(publicKey.bytes.data(), publicKey.bytes.size()); + pubKey.set_key(pbk.bytes.data(), pbk.bytes.size()); signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); break; } default: { auto pubKey = cosmos::crypto::secp256k1::PubKey(); - pubKey.set_key(publicKey.bytes.data(), publicKey.bytes.size()); + pubKey.set_key(pbk.bytes.data(), pbk.bytes.size()); signerInfo->mutable_public_key()->PackFrom(pubKey, ProtobufAnyNamespacePrefix); } } diff --git a/src/Cosmos/Signer.cpp b/src/Cosmos/Signer.cpp index 5913e401752..bceb5b1bd45 100644 --- a/src/Cosmos/Signer.cpp +++ b/src/Cosmos/Signer.cpp @@ -26,16 +26,13 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput& input, TWCoinType c } std::string Signer::signaturePreimage(const Proto::SigningInput& input, const Data& publicKey, TWCoinType coin) const { - auto isEvmCosmosChain = [coin]() { - return coin == TWCoinTypeNativeInjective || coin == TWCoinTypeNativeEvmos || coin == TWCoinTypeNativeCanto; - }; switch (input.signing_mode()) { case Proto::JSON: return Json::signaturePreimageJSON(input).dump(); case Proto::Protobuf: default: - auto pbk = isEvmCosmosChain() ? PublicKey(publicKey, TWPublicKeyTypeSECP256k1Extended) : PublicKey(publicKey, TWPublicKeyTypeSECP256k1); + auto pbk = PublicKey(publicKey, TWCoinTypePublicKeyType(coin)); return Protobuf::signaturePreimageProto(input, pbk, coin); } } diff --git a/src/Greenfield/ProtobufSerialization.cpp b/src/Greenfield/ProtobufSerialization.cpp index f9adc290d29..82da978ccdf 100644 --- a/src/Greenfield/ProtobufSerialization.cpp +++ b/src/Greenfield/ProtobufSerialization.cpp @@ -71,6 +71,9 @@ static SigningResult convertMessage(const Proto::Message& msg) { any.PackFrom(msgTransferOut, ProtobufAnyNamespacePrefix); break; } + default: { + return SigningResult::failure(Common::Proto::SigningError::Error_invalid_params); + } } return SigningResult::success(std::move(any)); diff --git a/swift/Tests/Blockchains/EvmosTests.swift b/swift/Tests/Blockchains/EvmosTests.swift index 89cd9f44577..1809a9c40ca 100644 --- a/swift/Tests/Blockchains/EvmosTests.swift +++ b/swift/Tests/Blockchains/EvmosTests.swift @@ -68,7 +68,7 @@ class EvmosTests: XCTestCase { let output: CosmosSigningOutput = AnySigner.sign(input: input, coin: .nativeEvmos) // https://www.mintscan.io/evmos/txs/B05D2047086B158665EC552879270AEF40AEAAFEE7D275B63E9674E3CC4C4E55 let expected = """ - {"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpoBCpcBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEncKLGV2bW9zMXJrMzlkazN3ZmY1bnBzN2VtdWh2M250a24zbnN6NnoyZXJxZnIwEixldm1vczEwazlscnJydWFwOW51OTZteHd3eWUyZjZhNXdhemVoMzNrcTY3ehoZCgZhZXZtb3MSDzIwMDAwMDAwMDAwMDAwMBKbAQp3Cm8KKC9ldGhlcm1pbnQuY3J5cHRvLnYxLmV0aHNlY3AyNTZrMS5QdWJLZXkSQwpBBJR1yfoj7Gk2Z7qnbE2mm0nMz98FjE3LJ7pnz7yQgtntkHR4ZWCqaYsZu5cpUmscdZNPPUp4975xnkOGt0mzYxASBAoCCAESIAoaCgZhZXZtb3MSEDE0MDAwMDAwMDAwMDAwMDAQ4MUIGkDBSFkYlIx1/6ZvRtEHyq6r0EEFDb/5T2Cb9zVYWtYZE1e7YteG2GuYMX546PvBQ7CYAFZEkr+rU6okC0nAT0UK"} + {"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpoBCpcBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEncKLGV2bW9zMXJrMzlkazN3ZmY1bnBzN2VtdWh2M250a24zbnN6NnoyZXJxZnIwEixldm1vczEwazlscnJydWFwOW51OTZteHd3eWUyZjZhNXdhemVoMzNrcTY3ehoZCgZhZXZtb3MSDzIwMDAwMDAwMDAwMDAwMBJ7ClcKTwooL2V0aGVybWludC5jcnlwdG8udjEuZXRoc2VjcDI1NmsxLlB1YktleRIjCiEClHXJ+iPsaTZnuqdsTaabSczP3wWMTcsnumfPvJCC2e0SBAoCCAESIAoaCgZhZXZtb3MSEDE0MDAwMDAwMDAwMDAwMDAQ4MUIGkAz9vh1EutbLrLZmRA4eK72bA6bhfMX0YnhtRl5jeaL3AYmk0qdrwG9XzzleBsZ++IokJIk47cgOOyvEjl92Jhj"} """ XCTAssertJSONEqual(output.serialized, expected) XCTAssertEqual(output.errorMessage, "") diff --git a/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp b/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp new file mode 100644 index 00000000000..a7591f1b786 --- /dev/null +++ b/tests/chains/Cosmos/NativeInjective/TransactionCompilerTests.cpp @@ -0,0 +1,103 @@ +// Copyright © 2017-2023 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 "Base64.h" +#include "Cosmos/Signer.h" +#include "HexCoding.h" +#include "proto/Cosmos.pb.h" +#include "proto/TransactionCompiler.pb.h" +#include "TrustWalletCore/TWAnySigner.h" +#include "TestUtilities.h" +#include "TransactionCompiler.h" + +namespace TW::Cosmos::nativeInjective::tests { + +TEST(NativeInjectiveCompiler, CompileWithSignatures) { + // Successfully broadcasted: https://www.mintscan.io/injective/transactions/B77D61590353C4AEDEAE2BBFF9E406DCF90E8D3A1A723BF22860F1E0A2617058 + + const auto coin = TWCoinTypeNativeInjective; + TW::Cosmos::Proto::SigningInput input; + + PrivateKey privateKey = + PrivateKey(parse_hex("727513ec3c54eb6fae24f2ff756bbc4c89b82945c6538bbd173613ae3de719d3")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + /// Step 1: Prepare transaction input (protobuf) + input.set_account_number(88701); + input.set_chain_id("injective-1"); + input.set_memo(""); + input.set_sequence(0); + + PublicKey publicKey = privateKey.getPublicKey(TWCoinTypePublicKeyType(coin)); + const auto pubKeyBz = publicKey.bytes; + ASSERT_EQ(hex(pubKeyBz), "04088ac2919987d927368cb2be2ade44cd0ed3616745a9699cae264b3fc5a7c3607d99f441b8340990ee990cb3eaf560f1f0bafe600c7e94a4be8392166984f728"); + input.set_public_key(pubKeyBz.data(), pubKeyBz.size()); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address("inj1d0jkrsd09c7pule43y3ylrul43lwwcqaky8w8c"); + message.set_to_address("inj1xmpkmxr4as00em23tc2zgmuyy2gr4h3wgcl6vd"); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("inj"); + amountOfTx->set_amount("10000000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(110000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("inj"); + amountOfFee->set_amount("100000000000000"); + + /// Step 2: Obtain protobuf preimage hash + input.set_signing_mode(TW::Cosmos::Proto::Protobuf); + auto protoInputString = input.SerializeAsString(); + auto protoInputData = TW::Data(protoInputString.begin(), protoInputString.end()); + + const auto preImageHashData = TransactionCompiler::preImageHashes(coin, protoInputData); + auto preSigningOutput = TW::TxCompiler::Proto::PreSigningOutput(); + ASSERT_TRUE( + preSigningOutput.ParseFromArray(preImageHashData.data(), (int)preImageHashData.size())); + ASSERT_EQ(preSigningOutput.error(), Common::Proto::OK); + auto preImage = preSigningOutput.data(); + auto preImageHash = preSigningOutput.data_hash(); + + EXPECT_EQ( + hex(preImage), + "0a8f010a8c010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e64126c0a2a696e6a3164306a6b7273643039633770756c6534337933796c72756c34336c77776371616b7938773863122a696e6a31786d706b6d78723461733030656d32337463327a676d7579793267723468337767636c3676641a120a03696e6a120b3130303030303030303030129c010a7c0a740a2d2f696e6a6563746976652e63727970746f2e763162657461312e657468736563703235366b312e5075624b657912430a4104088ac2919987d927368cb2be2ade44cd0ed3616745a9699cae264b3fc5a7c3607d99f441b8340990ee990cb3eaf560f1f0bafe600c7e94a4be8392166984f72812040a020801121c0a160a03696e6a120f31303030303030303030303030303010b0db061a0b696e6a6563746976652d3120fdb405"); + EXPECT_EQ(hex(preImageHash), + "57dc10c3d1893ff16e1f5a47fa4b2e96f37b9c57d98a42d88c971debb4947ec9"); + + + auto expectedTx = R"({"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"Co8BCowBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEmwKKmluajFkMGprcnNkMDljN3B1bGU0M3kzeWxydWw0M2x3d2NxYWt5OHc4YxIqaW5qMXhtcGtteHI0YXMwMGVtMjN0YzJ6Z211eXkyZ3I0aDN3Z2NsNnZkGhIKA2luahILMTAwMDAwMDAwMDASnAEKfAp0Ci0vaW5qZWN0aXZlLmNyeXB0by52MWJldGExLmV0aHNlY3AyNTZrMS5QdWJLZXkSQwpBBAiKwpGZh9knNoyyvireRM0O02FnRalpnK4mSz/Fp8NgfZn0Qbg0CZDumQyz6vVg8fC6/mAMfpSkvoOSFmmE9ygSBAoCCAESHAoWCgNpbmoSDzEwMDAwMDAwMDAwMDAwMBCw2wYaQPep7ApSEXC7VWbKlz08c6G2mxYtmc4CIFkYmZHsRAY3MzOU/xyedfrYTrEUOTlp8gmJsDbx3+0olJ6QbcAHdCE="})"; + Data signature; + + { + TW::Cosmos::Proto::SigningOutput output; + ANY_SIGN(input, coin); + assertJSONEqual( + output.serialized(), + expectedTx); + + signature = data(output.signature()); + EXPECT_EQ(hex(signature), + "f7a9ec0a521170bb5566ca973d3c73a1b69b162d99ce022059189991ec440637333394ff1c9e75fad84eb114393969f20989b036f1dfed28949e906dc0077421"); + + ASSERT_TRUE(publicKey.verify(signature, data(preImageHash.data()))); + } + + { + const Data outputData = TransactionCompiler::compileWithSignatures( + coin, protoInputData, {signature}, {publicKey.bytes}); + Cosmos::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + + EXPECT_EQ(output.error(), Common::Proto::OK); + EXPECT_EQ(output.serialized(), expectedTx); + EXPECT_EQ(output.signature(), ""); + EXPECT_EQ(hex(output.signature()), ""); + } +} + +} diff --git a/tests/chains/Evmos/SignerTests.cpp b/tests/chains/Evmos/SignerTests.cpp index d835b0f904d..02ae7c9b45e 100644 --- a/tests/chains/Evmos/SignerTests.cpp +++ b/tests/chains/Evmos/SignerTests.cpp @@ -118,7 +118,7 @@ TEST(EvmosSigner, CompoundingAuthz) { auto output = Signer::sign(input, TWCoinTypeNativeEvmos); auto expected = R"( { - "mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CvUBCvIBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQSzwEKLGV2bW9zMTJtOWdyZ2FzNjB5azBrdWx0MDc2dnhuc3Jxejh4cGp5OXJwZjNlEixldm1vczE4ZnpxNG5hYzI4Z2ZtYTZncWZ2a3B3cmdwbTVjdGFyMno5bXhmMxpxCmcKKi9jb3Ntb3Muc3Rha2luZy52MWJldGExLlN0YWtlQXV0aG9yaXphdGlvbhI5EjUKM2V2bW9zdmFsb3BlcjF1bWs0MDdlZWQ3YWY2YW52dXQ2bGxnMnpldm5mMGRuMGZlcXFueSABEgYI4LD6pgYSnQEKeQpvCigvZXRoZXJtaW50LmNyeXB0by52MS5ldGhzZWNwMjU2azEuUHViS2V5EkMKQQSAdlh24+rB/xlhO8/2FuT0Z12BRmhkQKL+4GWSNhb9p0uO6cUkpw8dpkYb5Wx+YsQgPvyUNSmiaO75siYg72ylEgQKAggBGAMSIAoaCgZhZXZtb3MSEDQ1MjE0NzUwMDAwMDAwMDAQ+4QLGkArFy2yWnZbP9aqxyx9KN8xlTVyWT5jolnrY5fbMtXeijVOGZIrFCtEg+xgjv6XpMTKK9A3cMMMcBAcuv2S+nN8" + "mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CvUBCvIBCh4vY29zbW9zLmF1dGh6LnYxYmV0YTEuTXNnR3JhbnQSzwEKLGV2bW9zMTJtOWdyZ2FzNjB5azBrdWx0MDc2dnhuc3Jxejh4cGp5OXJwZjNlEixldm1vczE4ZnpxNG5hYzI4Z2ZtYTZncWZ2a3B3cmdwbTVjdGFyMno5bXhmMxpxCmcKKi9jb3Ntb3Muc3Rha2luZy52MWJldGExLlN0YWtlQXV0aG9yaXphdGlvbhI5EjUKM2V2bW9zdmFsb3BlcjF1bWs0MDdlZWQ3YWY2YW52dXQ2bGxnMnpldm5mMGRuMGZlcXFueSABEgYI4LD6pgYSfQpZCk8KKC9ldGhlcm1pbnQuY3J5cHRvLnYxLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohA4B2WHbj6sH/GWE7z/YW5PRnXYFGaGRAov7gZZI2Fv2nEgQKAggBGAMSIAoaCgZhZXZtb3MSEDQ1MjE0NzUwMDAwMDAwMDAQ+4QLGkAm17CZgB7m+CPVlITnrHosklMTL9zrUeGRs8FL8N0GcRami9zdJ+e3xuXOtJmwP7G6QNh85CRYjFj8a8lpmmJM" })"; assertJSONEqual(output.serialized(), expected); } diff --git a/tests/chains/Evmos/TransactionCompilerTests.cpp b/tests/chains/Evmos/TransactionCompilerTests.cpp new file mode 100644 index 00000000000..72d8ab82606 --- /dev/null +++ b/tests/chains/Evmos/TransactionCompilerTests.cpp @@ -0,0 +1,103 @@ +// Copyright © 2017-2023 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 "Base64.h" +#include "Cosmos/Signer.h" +#include "HexCoding.h" +#include "proto/Cosmos.pb.h" +#include "proto/TransactionCompiler.pb.h" +#include "TrustWalletCore/TWAnySigner.h" +#include "TestUtilities.h" +#include "TransactionCompiler.h" + +namespace TW::Cosmos::evmos::tests { + +TEST(EvmosCompiler, CompileWithSignatures) { + // Successfully broadcasted: https://www.mintscan.io/evmos/transactions/02105B186FCA473C9F467B2D3BF487F6CE5DB26EE54BCD1667DDB7A2DA0E2489 + + const auto coin = TWCoinTypeNativeEvmos; + TW::Cosmos::Proto::SigningInput input; + + PrivateKey privateKey = + PrivateKey(parse_hex("727513ec3c54eb6fae24f2ff756bbc4c89b82945c6538bbd173613ae3de719d3")); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + /// Step 1: Prepare transaction input (protobuf) + input.set_account_number(106619981); + input.set_chain_id("evmos_9001-2"); + input.set_memo(""); + input.set_sequence(0); + + PublicKey publicKey = privateKey.getPublicKey(TWCoinTypePublicKeyType(coin)); + const auto pubKeyBz = publicKey.bytes; + ASSERT_EQ(hex(pubKeyBz), "04088ac2919987d927368cb2be2ade44cd0ed3616745a9699cae264b3fc5a7c3607d99f441b8340990ee990cb3eaf560f1f0bafe600c7e94a4be8392166984f728"); + input.set_public_key(pubKeyBz.data(), pubKeyBz.size()); + + auto msg = input.add_messages(); + auto& message = *msg->mutable_send_coins_message(); + message.set_from_address("evmos1d0jkrsd09c7pule43y3ylrul43lwwcqa7vpy0g"); + message.set_to_address("evmos17dh3frt0m6kdd3m9lr6e6sr5zz0rz8cvxd7u5t"); + auto amountOfTx = message.add_amounts(); + amountOfTx->set_denom("aevmos"); + amountOfTx->set_amount("10000000000000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(137840); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("aevmos"); + amountOfFee->set_amount("5513600000000000"); + + /// Step 2: Obtain protobuf preimage hash + input.set_signing_mode(TW::Cosmos::Proto::Protobuf); + auto protoInputString = input.SerializeAsString(); + auto protoInputData = TW::Data(protoInputString.begin(), protoInputString.end()); + + const auto preImageHashData = TransactionCompiler::preImageHashes(coin, protoInputData); + auto preSigningOutput = TW::TxCompiler::Proto::PreSigningOutput(); + ASSERT_TRUE( + preSigningOutput.ParseFromArray(preImageHashData.data(), (int)preImageHashData.size())); + ASSERT_EQ(preSigningOutput.error(), Common::Proto::OK); + auto preImage = preSigningOutput.data(); + auto preImageHash = preSigningOutput.data_hash(); + + EXPECT_EQ( + hex(preImage), + "0a9c010a99010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412790a2c65766d6f733164306a6b7273643039633770756c6534337933796c72756c34336c7777637161377670793067122c65766d6f733137646833667274306d366b6464336d396c723665367372357a7a30727a3863767864377535741a1b0a066165766d6f7312113130303030303030303030303030303030127b0a570a4f0a282f65746865726d696e742e63727970746f2e76312e657468736563703235366b312e5075624b657912230a2102088ac2919987d927368cb2be2ade44cd0ed3616745a9699cae264b3fc5a7c36012040a02080112200a1a0a066165766d6f7312103535313336303030303030303030303010f0b4081a0c65766d6f735f393030312d3220cdc8eb32"); + EXPECT_EQ(hex(preImageHash), + "9912eb629e215027b8d587939b1af72a9f70ae326bcaf48dfe77a729fc4ac632"); + + + auto expectedTx = R"({"mode":"BROADCAST_MODE_BLOCK","tx_bytes":"CpwBCpkBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnkKLGV2bW9zMWQwamtyc2QwOWM3cHVsZTQzeTN5bHJ1bDQzbHd3Y3FhN3ZweTBnEixldm1vczE3ZGgzZnJ0MG02a2RkM205bHI2ZTZzcjV6ejByejhjdnhkN3U1dBobCgZhZXZtb3MSETEwMDAwMDAwMDAwMDAwMDAwEnsKVwpPCigvZXRoZXJtaW50LmNyeXB0by52MS5ldGhzZWNwMjU2azEuUHViS2V5EiMKIQIIisKRmYfZJzaMsr4q3kTNDtNhZ0WpaZyuJks/xafDYBIECgIIARIgChoKBmFldm1vcxIQNTUxMzYwMDAwMDAwMDAwMBDwtAgaQKrmMaaSKnohf3ahyCOYdRJKBKJjr4WkkA/cbn6FRdF0Gd6FHSzBP8S4v4VNiy3KC47TD0C+sUBO413gCzjo8/U="})"; + Data signature; + + { + TW::Cosmos::Proto::SigningOutput output; + ANY_SIGN(input, coin); + assertJSONEqual( + output.serialized(), + expectedTx); + + signature = data(output.signature()); + EXPECT_EQ(hex(signature), + "aae631a6922a7a217f76a1c8239875124a04a263af85a4900fdc6e7e8545d17419de851d2cc13fc4b8bf854d8b2dca0b8ed30f40beb1404ee35de00b38e8f3f5"); + + ASSERT_TRUE(publicKey.verify(signature, data(preImageHash.data()))); + } + + { + const Data outputData = TransactionCompiler::compileWithSignatures( + coin, protoInputData, {signature}, {publicKey.bytes}); + Cosmos::Proto::SigningOutput output; + ASSERT_TRUE(output.ParseFromArray(outputData.data(), (int)outputData.size())); + + EXPECT_EQ(output.error(), Common::Proto::OK); + EXPECT_EQ(output.serialized(), expectedTx); + EXPECT_EQ(output.signature(), ""); + EXPECT_EQ(hex(output.signature()), ""); + } +} + +}