From 76595fd63066576363d6e18f2f07ebcfa9dca1f6 Mon Sep 17 00:00:00 2001 From: satoshiotomakan <127754187+satoshiotomakan@users.noreply.github.com> Date: Mon, 28 Aug 2023 17:36:42 +0200 Subject: [PATCH] [Greenfield]: Add a `Greenfield -> BSC` bridge transaction (#3398) --- .../greenfield/TestGreenfieldSigner.kt | 51 +++- .../Protobuf/greenfield_ethsecp256k1.proto} | 0 src/Cosmos/Protobuf/greenfield_tx.proto | 15 ++ src/Greenfield/Constants.h | 1 + src/Greenfield/Protobuf/.gitignore | 3 - src/Greenfield/ProtobufSerialization.cpp | 41 ++-- src/Greenfield/SignerEip712.cpp | 76 +++++- src/Greenfield/SignerEip712.h | 3 + src/proto/Greenfield.proto | 13 + swift/Tests/Blockchains/GreenfieldTests.swift | 46 +++- tests/chains/Greenfield/SignerTests.cpp | 226 +++++++++++++++++- tools/generate-files | 1 - 12 files changed, 452 insertions(+), 24 deletions(-) rename src/{Greenfield/Protobuf/ethsecp256k1.proto => Cosmos/Protobuf/greenfield_ethsecp256k1.proto} (100%) create mode 100644 src/Cosmos/Protobuf/greenfield_tx.proto delete mode 100644 src/Greenfield/Protobuf/.gitignore diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/greenfield/TestGreenfieldSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/greenfield/TestGreenfieldSigner.kt index 55193bb6edd..757bdaea895 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/greenfield/TestGreenfieldSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/greenfield/TestGreenfieldSigner.kt @@ -22,7 +22,7 @@ class TestGreenfieldSigner { } @Test - fun GreenfieldTransactionSigning() { + fun GreenfieldTransactionSigningSend() { // Successfully broadcasted: https://greenfieldscan.com/tx/ED8508F3C174C4430B8EE718A6D6F0B02A8C516357BE72B1336CF74356529D19 val key = @@ -69,4 +69,53 @@ class TestGreenfieldSigner { ) assertEquals(output.errorMessage, "") } + + @Test + fun GreenfieldTransactionSigningTransferOut() { + // Successfully broadcasted Greenfield: https://greenfieldscan.com/tx/38C29C530A74946CFD22EE07DA642F5EF9399BC9AEA59EC56A9B5BE16DE16CE7 + // BSC (parent transaction): https://testnet.bscscan.com/tx/0x7f73c8a362e14e58cb5e0ec17616afc50eff7aa398db472383a6d017c8a5861a + + val key = + PrivateKey("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0".toHexByteArray()) + + val msgTransferOut = Greenfield.Message.BridgeTransferOut.newBuilder().apply { + fromAddress = "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1" + toAddress = "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1" + amount = Greenfield.Amount.newBuilder().apply { + amount = "5670000000000000" + denom = "BNB" + }.build() + }.build() + + val greenfieldFee = Greenfield.Fee.newBuilder().apply { + gas = 1200 + addAmounts(Greenfield.Amount.newBuilder().apply { + amount = "6000000000000" + denom = "BNB" + }) + }.build() + + val signingInput = Greenfield.SigningInput.newBuilder().apply { + signingMode = Greenfield.SigningMode.Eip712 + encodingMode = Greenfield.EncodingMode.Protobuf + accountNumber = 15560 + ethChainId = "5600" + cosmosChainId = "greenfield_5600-1" + sequence = 7 + fee = greenfieldFee + mode = Greenfield.BroadcastMode.SYNC + privateKey = ByteString.copyFrom(key.data()) + addMessages(Greenfield.Message.newBuilder().apply { + bridgeTransferOut = msgTransferOut + }) + }.build() + + val output = AnySigner.sign(signingInput, CoinType.GREENFIELD, Greenfield.SigningOutput.parser()) + + assertEquals( + output.serialized, + "{\"mode\":\"BROADCAST_MODE_SYNC\",\"tx_bytes\":\"CpkBCpYBCiEvZ3JlZW5maWVsZC5icmlkZ2UuTXNnVHJhbnNmZXJPdXQScQoqMHg5ZDFkOTdhREZjZDMyNEJiZDYwM0QzODcyQkQ3OGUwNDA5ODUxMGIxEioweDlkMWQ5N2FERmNkMzI0QmJkNjAzRDM4NzJCRDc4ZTA0MDk4NTEwYjEaFwoDQk5CEhA1NjcwMDAwMDAwMDAwMDAwEnUKWApNCiYvY29zbW9zLmNyeXB0by5ldGguZXRoc2VjcDI1NmsxLlB1YktleRIjCiECee80Bk2hDbBGPHBIBha6AgcD7DpFAm3ve+vSCC9db8gSBQoDCMgFGAcSGQoUCgNCTkISDTYwMDAwMDAwMDAwMDAQsAkaQc4DDByhu80Uy/M3sQePvAmhmbFWZeGq359rugtskEiXKfCzSB86XmBi+l+Q5ETDS2folMxbufHSE8gM4WBCHzgc\"}" + ) + assertEquals(output.errorMessage, "") + } } diff --git a/src/Greenfield/Protobuf/ethsecp256k1.proto b/src/Cosmos/Protobuf/greenfield_ethsecp256k1.proto similarity index 100% rename from src/Greenfield/Protobuf/ethsecp256k1.proto rename to src/Cosmos/Protobuf/greenfield_ethsecp256k1.proto diff --git a/src/Cosmos/Protobuf/greenfield_tx.proto b/src/Cosmos/Protobuf/greenfield_tx.proto new file mode 100644 index 00000000000..28f78238cd7 --- /dev/null +++ b/src/Cosmos/Protobuf/greenfield_tx.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package greenfield.bridge; + +import "coin.proto"; + +// MsgTransferOut is the Msg/TransferOut request type. +// Original: https://github.com/bnb-chain/greenfield/blob/f1183e57caeb1ba0d28836b4ed2e64d693d2364d/proto/greenfield/bridge/tx.proto +message MsgTransferOut { + // from address + string from = 1; + // to address + string to = 2; + // transfer token amount + cosmos.base.v1beta1.Coin amount = 3; +} \ No newline at end of file diff --git a/src/Greenfield/Constants.h b/src/Greenfield/Constants.h index 0e3bad48a06..f2af62674dd 100644 --- a/src/Greenfield/Constants.h +++ b/src/Greenfield/Constants.h @@ -14,5 +14,6 @@ static constexpr uint64_t TIMEOUT_HEIGHT = 0; static constexpr auto* TIMEOUT_HEIGHT_STR = "0"; static constexpr auto* FEE_GRANTER = ""; static constexpr auto* MSG_SEND_TYPE = "/cosmos.bank.v1beta1.MsgSend"; +static constexpr auto* MSG_TRANSFER_OUT_TYPE = "/greenfield.bridge.MsgTransferOut"; } // namespace TW::Greenfield diff --git a/src/Greenfield/Protobuf/.gitignore b/src/Greenfield/Protobuf/.gitignore deleted file mode 100644 index c96d61208c0..00000000000 --- a/src/Greenfield/Protobuf/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.cc -*.h - diff --git a/src/Greenfield/ProtobufSerialization.cpp b/src/Greenfield/ProtobufSerialization.cpp index a438cf50f00..f9adc290d29 100644 --- a/src/Greenfield/ProtobufSerialization.cpp +++ b/src/Greenfield/ProtobufSerialization.cpp @@ -9,9 +9,10 @@ #include "Base64.h" #include "Constants.h" #include "Cosmos/Protobuf/bank_tx.pb.h" +#include "Cosmos/Protobuf/greenfield_ethsecp256k1.pb.h" +#include "Cosmos/Protobuf/greenfield_tx.pb.h" #include "Cosmos/Protobuf/tx.pb.h" #include "PrivateKey.h" -#include "Protobuf/ethsecp256k1.pb.h" namespace TW::Greenfield { @@ -43,22 +44,34 @@ static cosmos::base::v1beta1::Coin convertCoin(const Proto::Amount& amount) { } static SigningResult convertMessage(const Proto::Message& msg) { - // At this moment, we support `MsgSend` only. - if (!msg.has_send_coins_message()) { - return SigningResult::failure(Common::Proto::SigningError::Error_invalid_params); - } - Any any; - const auto& send = msg.send_coins_message(); - - auto msgSend = cosmos::bank::v1beta1::MsgSend(); - msgSend.set_from_address(send.from_address()); - msgSend.set_to_address(send.to_address()); - for (auto i = 0; i < send.amounts_size(); ++i) { - *msgSend.add_amount() = convertCoin(send.amounts(i)); + switch (msg.message_oneof_case()) { + case Proto::Message::kSendCoinsMessage: { + const auto& send = msg.send_coins_message(); + + auto msgSend = cosmos::bank::v1beta1::MsgSend(); + msgSend.set_from_address(send.from_address()); + msgSend.set_to_address(send.to_address()); + + for (auto i = 0; i < send.amounts_size(); ++i) { + *msgSend.add_amount() = convertCoin(send.amounts(i)); + } + any.PackFrom(msgSend, ProtobufAnyNamespacePrefix); + break; + } + case Proto::Message::kBridgeTransferOut: { + const auto& transferOut = msg.bridge_transfer_out(); + + auto msgTransferOut = greenfield::bridge::MsgTransferOut(); + msgTransferOut.set_from(transferOut.from_address()); + msgTransferOut.set_to(transferOut.to_address()); + *msgTransferOut.mutable_amount() = convertCoin(transferOut.amount()); + + any.PackFrom(msgTransferOut, ProtobufAnyNamespacePrefix); + break; + } } - any.PackFrom(msgSend, ProtobufAnyNamespacePrefix); return SigningResult::success(std::move(any)); } diff --git a/src/Greenfield/SignerEip712.cpp b/src/Greenfield/SignerEip712.cpp index 469e47d4a64..e4a5e91c2c2 100644 --- a/src/Greenfield/SignerEip712.cpp +++ b/src/Greenfield/SignerEip712.cpp @@ -87,6 +87,39 @@ json msgSendTypes() { return makeEip712Types(msgTypes); } +// `TypeMsg1Amount` and `Msg1` type names are chosen automatically at the function: +// https://github.com/bnb-chain/greenfield-cosmos-sdk/blob/master/x/auth/tx/eip712.go#L90 +// Please note that all parameters repeat the same scheme as `greenfield.bridge.MsgTransferOut`. +// +// Use `https://dcellar.io/` with MetaMask to get proper names of types and +json msgTransferOutTypes() { + // `MsgSend` specific types. + TypesMap msgTypes = { + // `TypeMsg1Amount` type represents `cosmos.bank.v1beta1.MsgSend.amount`. + {"TypeMsg1Amount", json::array({ + namedParam("amount", "string"), + namedParam("denom", "string"), + })}, + {"Msg1", json::array({ + namedParam("amount", "TypeMsg1Amount"), + namedParam("from", "string"), + namedParam("to", "string"), + namedParam("type", "string") + })}, + {"Tx", json::array({ + namedParam("account_number", "uint256"), + namedParam("chain_id", "uint256"), + namedParam("fee", "Fee"), + namedParam("memo", "string"), + namedParam("msg1", "Msg1"), + namedParam("sequence", "uint256"), + namedParam("timeout_height", "uint256") + })} + }; + + return makeEip712Types(msgTypes); +} + } // namespace internal::types json feeToJsonData(const Proto::SigningInput& input, const std::string& feePayer) { @@ -128,6 +161,11 @@ json SignerEip712::wrapMsgSendToTypedData(const Proto::SigningInput& input, cons }); } + std::string typePrefix = MSG_SEND_TYPE; + if (!msgSendProto.type_prefix().empty()) { + typePrefix = msgSendProto.type_prefix(); + } + return json{ {"types", internal::types::msgSendTypes()}, {"primaryType", "Tx"}, @@ -149,16 +187,52 @@ json SignerEip712::wrapMsgSendToTypedData(const Proto::SigningInput& input, cons }; } +// Returns a JSON data of the `EIP712Domain` type using `MsgSend` transaction. +json SignerEip712::wrapMsgTransferOutToTypedData(const Proto::SigningInput& input, const Proto::Message_BridgeTransferOut& msgTransferOut) { + std::string typePrefix = MSG_TRANSFER_OUT_TYPE; + if (!msgTransferOut.type_prefix().empty()) { + typePrefix = msgTransferOut.type_prefix(); + } + + return json{ + {"types", internal::types::msgTransferOutTypes()}, + {"primaryType", "Tx"}, + {"domain", domainDataJson(input.eth_chain_id())}, + {"message", json{ + {"account_number", std::to_string(input.account_number())}, + {"chain_id", input.eth_chain_id()}, + {"fee", feeToJsonData(input, msgTransferOut.from_address())}, + {"memo", input.memo()}, + {"msg1", json{ + {"amount", json{ + {"amount", msgTransferOut.amount().amount()}, + {"denom", msgTransferOut.amount().denom()} + }}, + {"from", msgTransferOut.from_address()}, + {"to", msgTransferOut.to_address()}, + {"type", typePrefix} + }}, + {"sequence", std::to_string(input.sequence())}, + {"timeout_height", TIMEOUT_HEIGHT_STR} + }} + }; +} + SigningResult SignerEip712::wrapTxToTypedData(const Proto::SigningInput& input) { if (input.messages_size() != 1) { return SigningResult::failure(Common::Proto::SigningError::Error_invalid_params); } switch(input.messages(0).message_oneof_case()) { + case Proto::Message::kBridgeTransferOut: { + const auto &msgTransferOut = input.messages(0).bridge_transfer_out(); + return SigningResult::success(wrapMsgTransferOutToTypedData(input, msgTransferOut)); + } case Proto::Message::kSendCoinsMessage: - default: + default: { const auto& msgSendProto = input.messages(0).send_coins_message(); return SigningResult::success(wrapMsgSendToTypedData(input, msgSendProto)); + } } } diff --git a/src/Greenfield/SignerEip712.h b/src/Greenfield/SignerEip712.h index 03f3656fac4..f36444b3aa2 100644 --- a/src/Greenfield/SignerEip712.h +++ b/src/Greenfield/SignerEip712.h @@ -39,6 +39,9 @@ struct SignerEip712 { /// Packs the `MsgSend` Tx input in a EIP712 object. static json wrapMsgSendToTypedData(const Proto::SigningInput& input, const Proto::Message_Send& msgSendProto); + /// Packs the `MsgTransferOut` Tx input in a EIP712 object. + static json wrapMsgTransferOutToTypedData(const Proto::SigningInput& input, const Proto::Message_BridgeTransferOut& msgTransferOut); + /// Prepares the given `signature` to make it Ethereum compatible. static void prepareSignature(Data& signature); }; diff --git a/src/proto/Greenfield.proto b/src/proto/Greenfield.proto index 369aa50b43a..20e87548615 100644 --- a/src/proto/Greenfield.proto +++ b/src/proto/Greenfield.proto @@ -36,12 +36,25 @@ message Message { string from_address = 1; string to_address = 2; repeated Amount amounts = 3; + // Optional. Default `cosmos.bank.v1beta1.MsgSend`. + string type_prefix = 4; + } + + // greenfield/MsgTransferOut + // Used to transfer BNB Greenfield to BSC blockchain. + message BridgeTransferOut { + // In most cases, `from_address` and `to_address` are equal. + string from_address = 1; + string to_address = 2; + Amount amount = 3; + // Optional. Default `greenfield.bridge.MsgTransferOut`. string type_prefix = 4; } // The payload message oneof message_oneof { Send send_coins_message = 1; + BridgeTransferOut bridge_transfer_out = 2; } } diff --git a/swift/Tests/Blockchains/GreenfieldTests.swift b/swift/Tests/Blockchains/GreenfieldTests.swift index 0fed77f9a75..a04babe5b52 100644 --- a/swift/Tests/Blockchains/GreenfieldTests.swift +++ b/swift/Tests/Blockchains/GreenfieldTests.swift @@ -8,7 +8,7 @@ import WalletCore import XCTest class GreenfieldTests: XCTestCase { - func testSign() { + func testSignSend() { // Successfully broadcasted: https://greenfieldscan.com/tx/ED8508F3C174C4430B8EE718A6D6F0B02A8C516357BE72B1336CF74356529D19 let sendCoinsMessage = GreenfieldMessage.Send.with { @@ -51,4 +51,48 @@ class GreenfieldTests: XCTestCase { XCTAssertJSONEqual(output.serialized, "{\"tx_bytes\": \"CqwBCpEBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnEKKjB4QTgxNWFlMGIwNmRDODAzMTgxMjE3NDVCRTQwZTdGOGM2NjU0ZTlmMxIqMHg4ZGJENmM3RWRlOTA2NDZhNjFCYmM2NDk4MzFiN2MyOThCRmQzN0EwGhcKA0JOQhIQMTIzNDUwMDAwMDAwMDAwMBIWVHJ1c3QgV2FsbGV0IHRlc3QgbWVtbxJzClYKTQomL2Nvc21vcy5jcnlwdG8uZXRoLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohAhm/mQgs8vzaqBLW66HrqQNv86PYTBgXyElU1OiuKD/sEgUKAwjIBRIZChQKA0JOQhINNjAwMDAwMDAwMDAwMBCwCRpBwbRb1qEAWwaqVfmp1Mn7iMi7wwV/oPi2J2eW9NBIdNoky+ZL+uegS/kY+funCOrqVZ+Kbol9/djAV+bQaNUB0xw=\", \"mode\": \"BROADCAST_MODE_SYNC\"}") XCTAssertEqual(output.errorMessage, "") } + + func testSignTransferOut() { + // Successfully broadcasted Greenfield: https://greenfieldscan.com/tx/38C29C530A74946CFD22EE07DA642F5EF9399BC9AEA59EC56A9B5BE16DE16CE7 + // BSC (parent transaction): https://testnet.bscscan.com/tx/0x7f73c8a362e14e58cb5e0ec17616afc50eff7aa398db472383a6d017c8a5861a + + let transferOutMessage = GreenfieldMessage.BridgeTransferOut.with { + $0.fromAddress = "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1" + $0.toAddress = "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1" + $0.amount = GreenfieldAmount.with { + $0.amount = "5670000000000000" + $0.denom = "BNB" + } + } + + let message = GreenfieldMessage.with { + $0.bridgeTransferOut = transferOutMessage + } + + let fee = GreenfieldFee.with { + $0.gas = 1200 + $0.amounts = [GreenfieldAmount.with { + $0.amount = "6000000000000" + $0.denom = "BNB" + }] + } + + let input = GreenfieldSigningInput.with { + $0.signingMode = .eip712; + $0.encodingMode = .protobuf + $0.mode = .sync + $0.accountNumber = 15560 + $0.ethChainID = "5600" + $0.cosmosChainID = "greenfield_5600-1" + $0.sequence = 7 + $0.messages = [message] + $0.fee = fee + $0.privateKey = Data(hexString: "9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0")! + } + + let output: GreenfieldSigningOutput = AnySigner.sign(input: input, coin: .greenfield) + + XCTAssertJSONEqual(output.serialized, "{\"mode\":\"BROADCAST_MODE_SYNC\",\"tx_bytes\":\"CpkBCpYBCiEvZ3JlZW5maWVsZC5icmlkZ2UuTXNnVHJhbnNmZXJPdXQScQoqMHg5ZDFkOTdhREZjZDMyNEJiZDYwM0QzODcyQkQ3OGUwNDA5ODUxMGIxEioweDlkMWQ5N2FERmNkMzI0QmJkNjAzRDM4NzJCRDc4ZTA0MDk4NTEwYjEaFwoDQk5CEhA1NjcwMDAwMDAwMDAwMDAwEnUKWApNCiYvY29zbW9zLmNyeXB0by5ldGguZXRoc2VjcDI1NmsxLlB1YktleRIjCiECee80Bk2hDbBGPHBIBha6AgcD7DpFAm3ve+vSCC9db8gSBQoDCMgFGAcSGQoUCgNCTkISDTYwMDAwMDAwMDAwMDAQsAkaQc4DDByhu80Uy/M3sQePvAmhmbFWZeGq359rugtskEiXKfCzSB86XmBi+l+Q5ETDS2folMxbufHSE8gM4WBCHzgc\"}") + XCTAssertEqual(output.errorMessage, "") + } } diff --git a/tests/chains/Greenfield/SignerTests.cpp b/tests/chains/Greenfield/SignerTests.cpp index 51a85879290..0cf63636e34 100644 --- a/tests/chains/Greenfield/SignerTests.cpp +++ b/tests/chains/Greenfield/SignerTests.cpp @@ -14,7 +14,7 @@ namespace TW::Greenfield::tests { -TEST(GreenfieldSigner, SignerEip712) { +TEST(GreenfieldSigner, SignerEip712Send) { Proto::SigningInput input; input.set_signing_mode(Proto::Eip712); input.set_account_number(15560); @@ -202,7 +202,192 @@ TEST(GreenfieldSigner, SignerEip712) { EXPECT_EQ(hex(signature), expectedSignature); } -TEST(GreenfieldSigner, Sign9F895C) { +TEST(GreenfieldSigner, SignerEip712TransferOut) { + Proto::SigningInput input; + input.set_signing_mode(Proto::Eip712); + input.set_account_number(15560); + input.set_cosmos_chain_id("greenfield_5600-1"); + input.set_eth_chain_id("5600"); + input.set_sequence(2); + + auto& msg = *input.add_messages(); + auto& msgSend = *msg.mutable_bridge_transfer_out(); + msgSend.set_from_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); + msgSend.set_to_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); + msgSend.mutable_amount()->set_denom("BNB"); + msgSend.mutable_amount()->set_amount("1000000000000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(200000); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("BNB"); + amountOfFee->set_amount("2000000000000000"); + + auto typedData = SignerEip712::wrapTxToTypedData(input).payload(); + auto expectedJson = json::parse(R"( +{ + "types": { + "Coin": [ + { + "name": "amount", + "type": "uint256" + }, + { + "name": "denom", + "type": "string" + } + ], + "EIP712Domain": [ + { + "name": "chainId", + "type": "uint256" + }, + { + "name": "name", + "type": "string" + }, + { + "name": "salt", + "type": "string" + }, + { + "name": "verifyingContract", + "type": "string" + }, + { + "name": "version", + "type": "string" + } + ], + "Fee": [ + { + "name": "amount", + "type": "Coin[]" + }, + { + "name": "gas_limit", + "type": "uint256" + }, + { + "name": "granter", + "type": "string" + }, + { + "name": "payer", + "type": "string" + } + ], + "Msg1": [ + { + "name": "amount", + "type": "TypeMsg1Amount" + }, + { + "name": "from", + "type": "string" + }, + { + "name": "to", + "type": "string" + }, + { + "name": "type", + "type": "string" + } + ], + "Tx": [ + { + "name": "account_number", + "type": "uint256" + }, + { + "name": "chain_id", + "type": "uint256" + }, + { + "name": "fee", + "type": "Fee" + }, + { + "name": "memo", + "type": "string" + }, + { + "name": "msg1", + "type": "Msg1" + }, + { + "name": "sequence", + "type": "uint256" + }, + { + "name": "timeout_height", + "type": "uint256" + } + ], + "TypeMsg1Amount": [ + { + "name": "amount", + "type": "string" + }, + { + "name": "denom", + "type": "string" + } + ] + }, + "primaryType": "Tx", + "domain": { + "name": "Greenfield Tx", + "version": "1.0.0", + "chainId": "5600", + "verifyingContract": "greenfield", + "salt": "0" + }, + "message": { + "account_number": "15560", + "chain_id": "5600", + "fee": { + "amount": [ + { + "amount": "2000000000000000", + "denom": "BNB" + } + ], + "gas_limit": "200000", + "granter": "", + "payer": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1" + }, + "memo": "", + "msg1": { + "amount": { + "amount": "1000000000000000", + "denom": "BNB" + }, + "from": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", + "to": "0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1", + "type": "/greenfield.bridge.MsgTransferOut" + }, + "sequence": "2", + "timeout_height": "0" + } +} + )"); + EXPECT_EQ(typedData, expectedJson); + + auto expectedPreHash = "ea7731461041f5f652ab424bb767c670e484cfe1f4a85179deba8a6596873af4"; + auto preHash = SignerEip712::preImageHash(input).payload(); + EXPECT_EQ(hex(preHash.typedDataHash), expectedPreHash); + + auto privateKey = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto signature = SignerEip712::sign(input).payload(); + auto expectedSignature = "c345fe0deb4fd93da5e808f6bd8aac3fb9de70fea2774e4657c37b02143135e37a02d53e8696edaede4a3e2e624eebd3261f43e02972812c11b356e236c834141c"; + EXPECT_EQ(hex(signature), expectedSignature); +} + +TEST(GreenfieldSigner, SignMsgSend9F895C) { // Successfully broadcasted https://greenfieldscan.com/tx/0x9f895cf2dd64fb1f428cefcf2a6585a813c3540fc9fe1ef42db1da2cb1df55ab Proto::SigningInput input; @@ -236,7 +421,7 @@ TEST(GreenfieldSigner, Sign9F895C) { EXPECT_EQ(output.serialized(), R"({"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CpQBCpEBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnEKKjB4OWQxZDk3YURGY2QzMjRCYmQ2MDNEMzg3MkJENzhlMDQwOTg1MTBiMRIqMHgyODBiMjdmMzY3NmRiMUM0NDc1RUUxMEY3NUQ1MTBFYjUyN2ZkMTU1GhcKA0JOQhIQMTAwMDAwMDAwMDAwMDAwMBJ5ClgKTQomL2Nvc21vcy5jcnlwdG8uZXRoLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohAnnvNAZNoQ2wRjxwSAYWugIHA+w6RQJt73vr0ggvXW/IEgUKAwjIBRgCEh0KFwoDQk5CEhAyMDAwMDAwMDAwMDAwMDAwEMCaDBpByzpGhKmRAUo4egSoW1kifrt5VnwgJa3cspa0yoVun4ENO1JvKg0PrWrRsSazuVFvizvgIKfMqcA8489H9BmbbRs="})"); } -TEST(GreenfieldSigner, SignB798AB) { +TEST(GreenfieldSigner, SignMsgSendB798AB) { // Successfully broadcasted https://greenfieldscan.com/tx/B798AB548B74B9B410F9464CA2B29C6AFEC3B4F45050338FC34F9DFC057C4D2A Proto::SigningInput input; @@ -270,6 +455,41 @@ TEST(GreenfieldSigner, SignB798AB) { EXPECT_EQ(output.serialized(), R"({"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CpQBCpEBChwvY29zbW9zLmJhbmsudjFiZXRhMS5Nc2dTZW5kEnEKKjB4OWQxZDk3YURGY2QzMjRCYmQ2MDNEMzg3MkJENzhlMDQwOTg1MTBiMRIqMHgyODBiMjdmMzY3NmRiMUM0NDc1RUUxMEY3NUQ1MTBFYjUyN2ZkMTU1GhcKA0JOQhIQNTAwMDAwMDAwMDAwMDAwMBJ1ClgKTQomL2Nvc21vcy5jcnlwdG8uZXRoLmV0aHNlY3AyNTZrMS5QdWJLZXkSIwohAnnvNAZNoQ2wRjxwSAYWugIHA+w6RQJt73vr0ggvXW/IEgUKAwjIBRgDEhkKFAoDQk5CEg02MDAwMDAwMDAwMDAwELAJGkE3vCCOdcsWQX9TvsTmCS9C2pWsoiQT7U2pr0G2T9Wf7As12hflSjHO7aCD4Rhe8j342YBeiIa5uJL85XBYe1qVGw=="})"); } +TEST(GreenfieldSigner, SignMsgTransferOut) { + // Successfully broadcasted Greenfield: https://greenfieldscan.com/tx/38C29C530A74946CFD22EE07DA642F5EF9399BC9AEA59EC56A9B5BE16DE16CE7 + // BSC (parent transaction): https://testnet.bscscan.com/tx/0x7f73c8a362e14e58cb5e0ec17616afc50eff7aa398db472383a6d017c8a5861a + + Proto::SigningInput input; + input.set_signing_mode(Proto::Eip712); + input.set_account_number(15560); + input.set_cosmos_chain_id("greenfield_5600-1"); + input.set_eth_chain_id("5600"); + input.set_sequence(7); + input.set_mode(Proto::BroadcastMode::SYNC); + + auto& msg = *input.add_messages(); + auto& msgTransferOut = *msg.mutable_bridge_transfer_out(); + msgTransferOut.set_from_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); + msgTransferOut.set_to_address("0x9d1d97aDFcd324Bbd603D3872BD78e04098510b1"); + auto amountOfTx = msgTransferOut.mutable_amount(); + amountOfTx->set_denom("BNB"); + amountOfTx->set_amount("5670000000000000"); + + auto& fee = *input.mutable_fee(); + fee.set_gas(1200); + auto amountOfFee = fee.add_amounts(); + amountOfFee->set_denom("BNB"); + amountOfFee->set_amount("6000000000000"); + + auto privateKey = parse_hex("9066aa168c379a403becb235c15e7129c133c244e56a757ab07bc369288bcab0"); + input.set_private_key(privateKey.data(), privateKey.size()); + + auto output = Signer::sign(input); + EXPECT_EQ(output.error(), Common::Proto::SigningError::OK); + EXPECT_EQ(hex(output.signature()), "ce030c1ca1bbcd14cbf337b1078fbc09a199b15665e1aadf9f6bba0b6c90489729f0b3481f3a5e6062fa5f90e444c34b67e894cc5bb9f1d213c80ce160421f381c"); + EXPECT_EQ(output.serialized(), R"({"mode":"BROADCAST_MODE_SYNC","tx_bytes":"CpkBCpYBCiEvZ3JlZW5maWVsZC5icmlkZ2UuTXNnVHJhbnNmZXJPdXQScQoqMHg5ZDFkOTdhREZjZDMyNEJiZDYwM0QzODcyQkQ3OGUwNDA5ODUxMGIxEioweDlkMWQ5N2FERmNkMzI0QmJkNjAzRDM4NzJCRDc4ZTA0MDk4NTEwYjEaFwoDQk5CEhA1NjcwMDAwMDAwMDAwMDAwEnUKWApNCiYvY29zbW9zLmNyeXB0by5ldGguZXRoc2VjcDI1NmsxLlB1YktleRIjCiECee80Bk2hDbBGPHBIBha6AgcD7DpFAm3ve+vSCC9db8gSBQoDCMgFGAcSGQoUCgNCTkISDTYwMDAwMDAwMDAwMDAQsAkaQc4DDByhu80Uy/M3sQePvAmhmbFWZeGq359rugtskEiXKfCzSB86XmBi+l+Q5ETDS2folMxbufHSE8gM4WBCHzgc"})"); +} + TEST(GreenfieldSigner, SignNoMessages) { Proto::SigningInput input; input.set_signing_mode(Proto::Eip712); diff --git a/tools/generate-files b/tools/generate-files index 1f4c114c87d..35616f51fe7 100755 --- a/tools/generate-files +++ b/tools/generate-files @@ -72,7 +72,6 @@ fi "$PROTOC" -I=$PREFIX/include -I=src/Zilliqa/Protobuf --cpp_out=src/Zilliqa/Protobuf src/Zilliqa/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=src/Cosmos/Protobuf --cpp_out=src/Cosmos/Protobuf src/Cosmos/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=src/Hedera/Protobuf --cpp_out=src/Hedera/Protobuf src/Hedera/Protobuf/*.proto -"$PROTOC" -I=$PREFIX/include -I=src/Greenfield/Protobuf --cpp_out=src/Greenfield/Protobuf src/Greenfield/Protobuf/*.proto "$PROTOC" -I=$PREFIX/include -I=tests/chains/Cosmos/Protobuf --cpp_out=tests/chains/Cosmos/Protobuf tests/chains/Cosmos/Protobuf/*.proto # Generate Proto interface file