From 893e2f7bfa9c123229fa254eb1baf21de3a4bd25 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Mon, 9 Sep 2019 21:22:01 +0800 Subject: [PATCH] R4R: support htlt transactions for binance chain (#627) * support htlt transactions for binance chain * Add tests for htlt transactions * clang format test file * fix failed tests * refactor naming and resolve comment * change serialization methods to accommodate changes on chain * refactor indent --- include/TrustWalletCore/TWBinanceProto.h | 4 + src/Binance/Serialization.cpp | 23 ++++ src/Binance/Signer.cpp | 16 +++ src/proto/Binance.proto | 38 +++++++ tests/Binance/SignerTests.cpp | 135 ++++++++++++++++++++++- 5 files changed, 214 insertions(+), 2 deletions(-) diff --git a/include/TrustWalletCore/TWBinanceProto.h b/include/TrustWalletCore/TWBinanceProto.h index 255cda77500..7eeb2ed3ad2 100644 --- a/include/TrustWalletCore/TWBinanceProto.h +++ b/include/TrustWalletCore/TWBinanceProto.h @@ -15,5 +15,9 @@ typedef TWData *_Nonnull TW_Binance_Proto_CancelTradeOrder; typedef TWData *_Nonnull TW_Binance_Proto_SendOrder; typedef TWData *_Nonnull TW_Binance_Proto_TokenFreezeOrder; typedef TWData *_Nonnull TW_Binance_Proto_TokenUnfreezeOrder; +typedef TWData *_Nonnull TW_Binance_Proto_HTLTOrder; +typedef TWData *_Nonnull TW_Binance_Proto_DepositHTLTOrder; +typedef TWData *_Nonnull TW_Binance_Proto_ClaimHTLOrder; +typedef TWData *_Nonnull TW_Binance_Proto_RefundHTLTOrder; typedef TWData *_Nonnull TW_Binance_Proto_SigningInput; typedef TWData *_Nonnull TW_Binance_Proto_SigningOutput; diff --git a/src/Binance/Serialization.cpp b/src/Binance/Serialization.cpp index df3b8a8eca4..bb99a4f173d 100644 --- a/src/Binance/Serialization.cpp +++ b/src/Binance/Serialization.cpp @@ -8,6 +8,7 @@ #include "../Cosmos/Address.h" #include +#include "../HexCoding.h" using namespace TW; @@ -57,6 +58,28 @@ json Binance::orderJSON(const Binance::Proto::SigningInput& input) { j["from"] = addressString(input.unfreeze_order().from()); j["symbol"] = input.unfreeze_order().symbol(); j["amount"] = input.unfreeze_order().amount(); + } else if (input.has_htlt_order()) { + j["from"] = addressString(input.htlt_order().from()); + j["to"] = addressString(input.htlt_order().to()); + j["recipient_other_chain"] = input.htlt_order().recipient_other_chain(); + j["sender_other_chain"] = input.htlt_order().sender_other_chain(); + j["random_number_hash"] = hex(input.htlt_order().random_number_hash()); + j["timestamp"] = input.htlt_order().timestamp(); + j["amount"] = tokensJSON(input.htlt_order().amount()); + j["expected_income"] = input.htlt_order().expected_income(); + j["height_span"] = input.htlt_order().height_span(); + j["cross_chain"] = input.htlt_order().cross_chain(); + } else if (input.has_deposithtlt_order()) { + j["from"] = addressString(input.deposithtlt_order().from()); + j["swap_id"] = hex(input.deposithtlt_order().swap_id()); + j["amount"] = tokensJSON(input.deposithtlt_order().amount()); + } else if (input.has_claimhtlt_order()) { + j["from"] = addressString(input.claimhtlt_order().from()); + j["swap_id"] = hex(input.claimhtlt_order().swap_id()); + j["random_number"] = hex(input.claimhtlt_order().random_number()); + } else if (input.has_refundhtlt_order()) { + j["from"] = addressString(input.refundhtlt_order().from()); + j["swap_id"] = hex(input.refundhtlt_order().swap_id()); } return j; } diff --git a/src/Binance/Signer.cpp b/src/Binance/Signer.cpp index 55c1f53a5d5..514deb81e84 100644 --- a/src/Binance/Signer.cpp +++ b/src/Binance/Signer.cpp @@ -23,6 +23,10 @@ static const auto tradeOrderPrefix = std::vector{0xCE, 0x6D, 0xC0, 0x43 static const auto cancelTradeOrderPrefix = std::vector{0x16, 0x6E, 0x68, 0x1B}; static const auto tokenFreezeOrderPrefix = std::vector{0xE7, 0x74, 0xB3, 0x2D}; static const auto tokenUnfreezeOrderPrefix = std::vector{0x65, 0x15, 0xFF, 0x0D}; +static const auto HTLTOrderPrefix = std::vector{0xB3, 0x3F, 0x9A, 0x24}; +static const auto depositHTLTOrderPrefix = std::vector{0x63, 0x98, 0x64, 0x96}; +static const auto claimHTLTOrderPrefix = std::vector{0xC1, 0x66, 0x53, 0x00}; +static const auto refundHTLTOrderPrefix = std::vector{0x34, 0x54, 0xA2, 0x7C}; static const auto pubKeyPrefix = std::vector{0xEB, 0x5A, 0xE9, 0x87}; static const auto transactionPrefix = std::vector{0xF0, 0x62, 0x5D, 0xEE}; @@ -73,6 +77,18 @@ std::vector Signer::encodeOrder() const { } else if (input.has_unfreeze_order()) { data = input.unfreeze_order().SerializeAsString(); prefix = tokenUnfreezeOrderPrefix; + } else if (input.has_htlt_order()) { + data = input.htlt_order().SerializeAsString(); + prefix = HTLTOrderPrefix; + } else if (input.has_deposithtlt_order()) { + data = input.deposithtlt_order().SerializeAsString(); + prefix = depositHTLTOrderPrefix; + } else if (input.has_claimhtlt_order()) { + data = input.claimhtlt_order().SerializeAsString(); + prefix = claimHTLTOrderPrefix; + } else if (input.has_refundhtlt_order()) { + data = input.refundhtlt_order().SerializeAsString(); + prefix = refundHTLTOrderPrefix; } else { return {}; } diff --git a/src/proto/Binance.proto b/src/proto/Binance.proto index e4b429961a6..209a6144ea5 100644 --- a/src/proto/Binance.proto +++ b/src/proto/Binance.proto @@ -75,6 +75,40 @@ message TokenUnfreezeOrder { int64 amount = 3; // amount of token to unfreeze } +message HTLTOrder { + // 0xB33F9A24 // prefix + bytes from = 1; // signer address + bytes to = 2; // recipient address + string recipient_other_chain = 3; + string sender_other_chain = 4; + bytes random_number_hash = 5; //hash of a random number and timestamp, based on SHA256 + int64 timestamp = 6; + repeated SendOrder.Token amount = 7; + string expected_income = 8; // expected gained token on the other chain + int64 height_span = 9; + bool cross_chain = 10; +} + +message DepositHTLTOrder { + // 0xB33F9A24 // prefix + bytes from = 1; // signer address + repeated SendOrder.Token amount = 2; + bytes swap_id = 3; +} + +message ClaimHTLOrder { + // 0xC1665300 // prefix + bytes from = 1; // signer address + bytes swap_id = 2; + bytes random_number = 3; +} + +message RefundHTLTOrder { + // 0x3454A27C // prefix + bytes from = 1; // signer address + bytes swap_id = 2; +} + // Input data necessary to create a signed order. message SigningInput { string chain_id = 1; @@ -90,6 +124,10 @@ message SigningInput { SendOrder send_order = 10; TokenFreezeOrder freeze_order = 11; TokenUnfreezeOrder unfreeze_order = 12; + HTLTOrder htlt_order = 13; + DepositHTLTOrder depositHTLT_order = 14; + ClaimHTLOrder claimHTLT_order = 15; + RefundHTLTOrder refundHTLT_order = 16; } } diff --git a/tests/Binance/SignerTests.cpp b/tests/Binance/SignerTests.cpp index b07fab9a538..28582c86738 100644 --- a/tests/Binance/SignerTests.cpp +++ b/tests/Binance/SignerTests.cpp @@ -40,7 +40,7 @@ TEST(BinanceSigner, Sign) { order.set_price(136350000); order.set_quantity(100000000); order.set_timeinforce(1); - + auto signer = Binance::Signer(std::move(input)); auto signature = signer.sign(); @@ -67,7 +67,7 @@ TEST(BinanceSigner, Build) { order.set_price(100000000); order.set_quantity(1200000000); order.set_timeinforce(1); - + auto signer = Binance::Signer(std::move(input)); auto result = signer.build(); @@ -202,4 +202,135 @@ TEST(BinanceSigner, BuildSend2) { ); } +TEST(BinanceSigner, BuildHTLT) { + const auto fromPrivateKey = PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); + const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + auto fromAddr = Cosmos::Address(HRP_BINANCE, fromPublicKey).keyHash; + + const auto toPrivateKey = PrivateKey(parse_hex("851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a")); + const auto toPublicKey = PublicKey(toPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + auto toAddr = Cosmos::Address(HRP_BINANCE, toPublicKey).keyHash; + + auto signingInput = Proto::SigningInput(); + signingInput.set_chain_id("test-chain"); + signingInput.set_account_number(15); + signingInput.set_sequence(0); + signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); + + auto token = Proto::SendOrder::Token(); + token.set_denom("BNB"); + token.set_amount(100000000); + + auto randomNumberHash = + parse_hex("e8eae926261ab77d018202434791a335249b470246a7b02e28c3b2fb6ffad8f3"); + + auto &htltOrder = *signingInput.mutable_htlt_order(); + htltOrder.set_from(fromAddr.data(), fromAddr.size()); + htltOrder.set_to(toAddr.data(), toAddr.size()); + htltOrder.set_recipient_other_chain(""); + htltOrder.set_sender_other_chain(""); + *htltOrder.add_amount() = token; + htltOrder.set_height_span(400); + htltOrder.set_expected_income("100000000:BTC-1DC"); + htltOrder.set_timestamp(1567746273); + htltOrder.set_random_number_hash(randomNumberHash.data(), randomNumberHash.size()); + htltOrder.set_cross_chain(false); + + const auto data = Binance::Signer(std::move(signingInput)).build(); + ASSERT_EQ(hex(data.begin(), data.end()), + "ee01f0625dee0a7ab33f9a240a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e112140153f11d6db7" + "e69c7d51e771c697378018fb6c242a20e8eae926261ab77d018202434791a335249b470246a7b02e28c3" + "b2fb6ffad8f330e1d1c7eb053a0a0a03424e421080c2d72f42113130303030303030303a4254432d3144" + "43489003126c0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561aac993dbe0f" + "6b6a8fc7124051439de2da19fe9fd22137c903cfc5dc87553bf05dca0bb202c0e07c47f9b51269efa272" + "43eb7b55888f5384a84ac1eac6d325c830d1be0ed042838e2dc0f6a9180f"); +} + +TEST(BinanceSigner, BuildDepositHTLT) { + const auto fromPrivateKey = PrivateKey(parse_hex("851fab89c14f4bbec0cc06f5e445ec065efc641068d78b308c67217d9bd5c88a")); + const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + auto fromAddr = Cosmos::Address(HRP_BINANCE, fromPublicKey).keyHash; + + auto signingInput = Proto::SigningInput(); + signingInput.set_chain_id("test-chain"); + signingInput.set_account_number(16); + signingInput.set_sequence(0); + signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); + + auto token = Proto::SendOrder::Token(); + token.set_denom("BTC-1DC"); + token.set_amount(100000000); + + auto swapID = parse_hex("dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5"); + + auto &depositHTLTOrder = *signingInput.mutable_deposithtlt_order(); + depositHTLTOrder.set_from(fromAddr.data(), fromAddr.size()); + depositHTLTOrder.set_swap_id(swapID.data(), swapID.size()); + *depositHTLTOrder.add_amount() = token; + + const auto data = Binance::Signer(std::move(signingInput)).build(); + ASSERT_EQ(hex(data.begin(), data.end()), + "c001f0625dee0a4c639864960a140153f11d6db7e69c7d51e771c697378018fb6c24120e0a074254432d" + "3144431080c2d72f1a20dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5" + "126c0a26eb5ae98721038df6960084e20b2d07d50e1422f94105c6241d9f1482a4eb79ce8bfd460f19e4" + "12400ca4144c6818e2836d09b4faf3161781d85f9adfc00badb2eaa0953174610a233b81413dafcf8471" + "6abad48a4ed3aeb9884d90eb8416eec5d5c0c6930ab60bd01810"); +} + +TEST(BinanceSigner, BuildClaimHTLT) { + const auto fromPrivateKey = PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); + const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + auto fromAddr = Cosmos::Address(HRP_BINANCE, fromPublicKey).keyHash; + + auto signingInput = Proto::SigningInput(); + signingInput.set_chain_id("test-chain"); + signingInput.set_account_number(15); + signingInput.set_sequence(1); + signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); + + auto randomNumber = + parse_hex("bda6933c7757d0ca428aa01fb9d0935a231f87bf2deeb9b409cea3f2d580a2cc"); + auto swapID = parse_hex("dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5"); + + auto &claimHTLTOrder = *signingInput.mutable_claimhtlt_order(); + claimHTLTOrder.set_from(fromAddr.data(), fromAddr.size()); + claimHTLTOrder.set_swap_id(swapID.data(), swapID.size()); + claimHTLTOrder.set_random_number(randomNumber.data(), randomNumber.size()); + + const auto data = Binance::Signer(std::move(signingInput)).build(); + ASSERT_EQ( + hex(data.begin(), data.end()), + "d401f0625dee0a5ec16653000a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11220dd8fd4719741844d35" + "eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e51a20bda6933c7757d0ca428aa01fb9d0935a231f87bf" + "2deeb9b409cea3f2d580a2cc126e0a26eb5ae9872103a9a55c040c8eb8120f3d1b32193250841c08af44ea561a" + "ac993dbe0f6b6a8fc71240fa30ba50111aa31d8329dacb6d044c1c7d54f1cb782bc9aa2a50c3fabce02a4579d7" + "5b76ca69a9fab11b676d9da66b5af7aa4c9ad3d18e24fffeb16433be39fb180f2001"); +} + +TEST(BinanceSigner, BuildRefundHTLT) { + const auto fromPrivateKey = PrivateKey(parse_hex("eeba3f6f2db26ced519a3d4c43afff101db957a21d54d25dc7fd235c404d7a5d")); + const auto fromPublicKey = PublicKey(fromPrivateKey.getPublicKey(TWPublicKeyTypeSECP256k1)); + auto fromAddr = Cosmos::Address(HRP_BINANCE, fromPublicKey).keyHash; + + auto signingInput = Proto::SigningInput(); + signingInput.set_chain_id("test-chain"); + signingInput.set_account_number(15); + signingInput.set_sequence(1); + signingInput.set_private_key(fromPrivateKey.bytes.data(), fromPrivateKey.bytes.size()); + + auto swapID = parse_hex("dd8fd4719741844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5"); + + auto &refundHTLTOrder = *signingInput.mutable_refundhtlt_order(); + refundHTLTOrder.set_from(fromAddr.data(), fromAddr.size()); + refundHTLTOrder.set_swap_id(swapID.data(), swapID.size()); + + const auto data = Binance::Signer(std::move(signingInput)).build(); + ASSERT_EQ(hex(data.begin(), data.end()), + "b201f0625dee0a3c3454a27c0a1408c7c918f6b72c3c0c21b7d08eb6fc66509998e11220dd8fd4719741" + "844d35eb35ddbeca9531d5493a8e4667689c55e73c77503dd9e5126e0a26eb5ae9872103a9a55c040c8e" + "b8120f3d1b32193250841c08af44ea561aac993dbe0f6b6a8fc71240c9f36142534d16ec8ce656f8eb73" + "70b32206a2d15198b7165acf1e2a18952c9e4570b0f862e1ab7bb868c30781a42c9e3ec0ae08982e8d6c" + "91c55b83c71b7b1e180f2001"); +} + } // namespace TW::Binance