From 358522497a7267cfeb66a0ebb1ce7d0aaa6d9620 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Thu, 23 Apr 2020 11:46:02 +0000 Subject: [PATCH 01/12] Registers new coin. --- coins.json | 22 +++++++++++++++++ docs/coins.md | 1 + include/TrustWalletCore/TWBlockchain.h | 1 + include/TrustWalletCore/TWCoinType.h | 1 + src/Coin.cpp | 2 ++ tests/Elrond/TWCoinTypeTests.cpp | 34 ++++++++++++++++++++++++++ tests/interface/TWHRPTests.cpp | 3 +++ 7 files changed, 64 insertions(+) create mode 100644 tests/Elrond/TWCoinTypeTests.cpp diff --git a/coins.json b/coins.json index 18047f66535..9c309f1f57d 100644 --- a/coins.json +++ b/coins.json @@ -1330,5 +1330,27 @@ "clientPublic": "", "clientDocs": "https://docs.lotu.sh" } + }, + { + "id": "elrond", + "name": "Elrond", + "symbol": "ERD", + "decimals": 18, + "blockchain": "ElrondNetwork", + "derivationPath": "m/44'/508'/0'", + "curve": "ed25519", + "publicKeyType": "ed25519", + "hrp": "erd", + "explorer": { + "url": "https://explorer.elrond.com", + "txPath": "/transactions/", + "accountPath": "/address/" + }, + "info": { + "url": "https://elrond.com/", + "client": "https://github.com/ElrondNetwork/elrond-go", + "clientPublic": "https://api.elrond.com", + "clientDocs": "https://docs.elrond.com" + } } ] diff --git a/docs/coins.md b/docs/coins.md index 2fae8caf2e8..c99e9d92157 100644 --- a/docs/coins.md +++ b/docs/coins.md @@ -43,6 +43,7 @@ This list is generated from [./coins.json](../coins.json) | 461 | Filecoin | FIL | | | | 500 | Theta | THETA | | | | 501 | Solana | SOL | | | +| 508 | Elrond | ERD | | | | 714 | Binance | BNB | | | | 818 | VeChain | VET | | | | 820 | Callisto | CLO | | | diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index 8b1b82d6028..6a174d8af7c 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -45,6 +45,7 @@ enum TWBlockchain { TWBlockchainCardano = 30, TWBlockchainNEO = 31, TWBlockchainFilecoin = 32, + TWBlockchainElrondNetwork = 33, }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 0c2a152cec0..bf70c377ee9 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -79,6 +79,7 @@ enum TWCoinType { TWCoinTypeKusama = 434, TWCoinTypePolkadot = 354, TWCoinTypeFilecoin = 461, + TWCoinTypeElrond = 508, }; /// Returns the blockchain for a coin type. diff --git a/src/Coin.cpp b/src/Coin.cpp index c6135ddbec0..d0a0454d2ec 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -51,6 +51,7 @@ #include "Waves/Entry.h" #include "Zcash/Entry.h" #include "Zilliqa/Entry.h" +#include "Elrond/Entry.h" // end_of_coin_includes_marker_do_not_modify using namespace TW; @@ -100,6 +101,7 @@ void setupDispatchers() { new Waves::Entry(), new Zcash::Entry(), new Zilliqa::Entry(), + new Elrond::Entry(), }; // end_of_coin_entries_marker_do_not_modify dispatchMap.clear(); diff --git a/tests/Elrond/TWCoinTypeTests.cpp b/tests/Elrond/TWCoinTypeTests.cpp new file mode 100644 index 00000000000..038ed3f9fc6 --- /dev/null +++ b/tests/Elrond/TWCoinTypeTests.cpp @@ -0,0 +1,34 @@ +// 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. +// +// This is a GENERATED FILE, changes made here MAY BE LOST. +// Generated one-time (codegen/bin/cointests) +// + +#include "../interface/TWTestUtilities.h" +#include +#include + + +TEST(TWElrondCoinType, TWCoinType) { + auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeElrond)); + auto txId = TWStringCreateWithUTF8Bytes("t123"); + auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeElrond, txId)); + auto accId = TWStringCreateWithUTF8Bytes("a12"); + auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeElrond, accId)); + auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeElrond)); + auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeElrond)); + + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeElrond), 18); + ASSERT_EQ(TWBlockchainElrondNetwork, TWCoinTypeBlockchain(TWCoinTypeElrond)); + ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeElrond)); + ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeElrond)); + assertStringsEqual(symbol, "ERD"); + assertStringsEqual(txUrl, "https://explorer.elrond.com/transactions/t123"); + assertStringsEqual(accUrl, "https://explorer.elrond.com/address/a12"); + assertStringsEqual(id, "elrond"); + assertStringsEqual(name, "Elrond"); +} diff --git a/tests/interface/TWHRPTests.cpp b/tests/interface/TWHRPTests.cpp index 11af1a19e50..3b04ad4f8c3 100644 --- a/tests/interface/TWHRPTests.cpp +++ b/tests/interface/TWHRPTests.cpp @@ -28,6 +28,7 @@ TEST(TWHRP, StringForHRP) { ASSERT_STREQ(stringForHRP(TWHRPMonacoin), "mona"); ASSERT_STREQ(stringForHRP(TWHRPKava), "kava"); ASSERT_STREQ(stringForHRP(TWHRPCardano), "addr"); + ASSERT_STREQ(stringForHRP(TWHRPElrond), "erd"); } TEST(TWHRP, HRPForString) { @@ -47,6 +48,7 @@ TEST(TWHRP, HRPForString) { ASSERT_EQ(hrpForString("mona"), TWHRPMonacoin); ASSERT_EQ(hrpForString("kava"), TWHRPKava); ASSERT_EQ(hrpForString("addr"), TWHRPCardano); + ASSERT_EQ(hrpForString("erd"), TWHRPElrond); } TEST(TWHPR, HPRByCoinType) { @@ -65,6 +67,7 @@ TEST(TWHPR, HPRByCoinType) { ASSERT_EQ(TWHRPMonacoin, TWCoinTypeHRP(TWCoinTypeMonacoin)); ASSERT_EQ(TWHRPKava, TWCoinTypeHRP(TWCoinTypeKava)); ASSERT_EQ(TWHRPCardano, TWCoinTypeHRP(TWCoinTypeCardano)); + ASSERT_EQ(TWHRPElrond, TWCoinTypeHRP(TWCoinTypeElrond)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeAion)); ASSERT_EQ(TWHRPUnknown, TWCoinTypeHRP(TWCoinTypeCallisto)); From 066c0d8ebe210c9607cfc2af026d62a2b29f84f2 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Thu, 23 Apr 2020 11:48:23 +0000 Subject: [PATCH 02/12] Define protobuf models --- src/proto/Elrond.proto | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/proto/Elrond.proto diff --git a/src/proto/Elrond.proto b/src/proto/Elrond.proto new file mode 100644 index 00000000000..a823a4aca27 --- /dev/null +++ b/src/proto/Elrond.proto @@ -0,0 +1,36 @@ +// 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. + +syntax = "proto3"; + +package TW.Elrond.Proto; +option java_package = "wallet.core.jni.proto"; + +// A transaction, typical balance transfer +message TransactionMessage { + uint64 nonce = 1; + string value = 2; + string receiver = 3; + string sender = 4; + uint64 gas_price = 5; + uint64 gas_limit = 6; + string data = 7; +} + +// Input data necessary to create a signed transaction. +message SigningInput { + bytes private_key = 1; + + oneof message_oneof { + TransactionMessage transaction = 2; + } +} + +// Transaction signing output. +message SigningOutput { + string signed_transaction = 1; + string signature = 2; +} From a7b57838b036d2062242a0be69d5a632927f55cc Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Thu, 23 Apr 2020 11:50:13 +0000 Subject: [PATCH 03/12] Implement entry, address and add tests --- src/Elrond/Address.cpp | 17 ++++++++ src/Elrond/Address.h | 43 +++++++++++++++++++ src/Elrond/Entry.cpp | 31 ++++++++++++++ src/Elrond/Entry.h | 25 +++++++++++ tests/Elrond/AddressTests.cpp | 79 +++++++++++++++++++++++++++++++++++ tests/Elrond/TestAccounts.h | 11 +++++ 6 files changed, 206 insertions(+) create mode 100644 src/Elrond/Address.cpp create mode 100644 src/Elrond/Address.h create mode 100644 src/Elrond/Entry.cpp create mode 100644 src/Elrond/Entry.h create mode 100644 tests/Elrond/AddressTests.cpp create mode 100644 tests/Elrond/TestAccounts.h diff --git a/src/Elrond/Address.cpp b/src/Elrond/Address.cpp new file mode 100644 index 00000000000..1af84ace200 --- /dev/null +++ b/src/Elrond/Address.cpp @@ -0,0 +1,17 @@ +// 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 + +#include "Address.h" + +using namespace TW::Elrond; + +const std::string Address::hrp = HRP_ELROND; + +bool Address::isValid(const std::string& string) { + return Bech32Address::isValid(string, hrp); +} diff --git a/src/Elrond/Address.h b/src/Elrond/Address.h new file mode 100644 index 00000000000..d56854cbc90 --- /dev/null +++ b/src/Elrond/Address.h @@ -0,0 +1,43 @@ +// 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 "../Data.h" +#include "../PublicKey.h" +#include "../Bech32Address.h" + +#include + +namespace TW::Elrond { + +class Address : public Bech32Address { + public: + // The human-readable part of the address, as defined in "coins.json" + static const std::string hrp; // HRP_ELROND + + /// Determines whether a string makes a valid address. + static bool isValid(const std::string& string); + + Address() : Bech32Address(hrp) {} + + /// Initializes an address with a key hash. + Address(Data keyHash) : Bech32Address(hrp, keyHash) {} + + /// Initializes an address with a public key. + Address(const PublicKey& publicKey) : Bech32Address(hrp, publicKey.bytes) {} + + static bool decode(const std::string& addr, Address& obj_out) { + return Bech32Address::decode(addr, obj_out, hrp); + } +}; + +} // namespace TW::Elrond + +/// Wrapper for C interface. +struct TWElrondAddress { + TW::Elrond::Address impl; +}; diff --git a/src/Elrond/Entry.cpp b/src/Elrond/Entry.cpp new file mode 100644 index 00000000000..76b806ff128 --- /dev/null +++ b/src/Elrond/Entry.cpp @@ -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. + +#include "Entry.h" + +#include "Address.h" +#include "Signer.h" + +using namespace TW::Elrond; +using namespace std; + +// Note: avoid business logic from here, rather just call into classes like Address, Signer, etc. + +bool Entry::validateAddress(TWCoinType coin, const string& address, TW::byte, TW::byte, const char*) const { + return Address::isValid(address); +} + +string Entry::deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte, const char*) const { + return Address(publicKey).string(); +} + +void Entry::sign(TWCoinType coin, const TW::Data& dataIn, TW::Data& dataOut) const { + signTemplate(dataIn, dataOut); +} + +string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const { + return Signer::signJSON(json, key); +} diff --git a/src/Elrond/Entry.h b/src/Elrond/Entry.h new file mode 100644 index 00000000000..78a5f60a39f --- /dev/null +++ b/src/Elrond/Entry.h @@ -0,0 +1,25 @@ +// 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 "../CoinEntry.h" + +namespace TW::Elrond { + +/// Entry point for implementation of Elrond coin. +/// Note: do not put the implementation here (no matter how simple), to avoid having coin-specific includes in this file +class Entry: public CoinEntry { +public: + virtual std::vector coinTypes() const { return {TWCoinTypeElrond}; } + virtual bool validateAddress(TWCoinType coin, const std::string& address, TW::byte p2pkh, TW::byte p2sh, const char* hrp) const; + virtual std::string deriveAddress(TWCoinType coin, const PublicKey& publicKey, TW::byte p2pkh, const char* hrp) const; + virtual void sign(TWCoinType coin, const Data& dataIn, Data& dataOut) const; + virtual bool supportsJSONSigning() const { return true; } + virtual std::string signJSON(TWCoinType coin, const std::string& json, const Data& key) const; +}; + +} // namespace TW::Elrond diff --git a/tests/Elrond/AddressTests.cpp b/tests/Elrond/AddressTests.cpp new file mode 100644 index 00000000000..3dd1fb6f166 --- /dev/null +++ b/tests/Elrond/AddressTests.cpp @@ -0,0 +1,79 @@ +// 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 +#include + +#include "HexCoding.h" +#include "PublicKey.h" +#include "PrivateKey.h" +#include "Elrond/Address.h" +#include "TestAccounts.h" + +using namespace TW; +using namespace TW::Elrond; + + +TEST(ElrondAddress, Valid) { + ASSERT_TRUE(Address::isValid(ALICE_BECH32)); + ASSERT_TRUE(Address::isValid(BOB_BECH32)); + ASSERT_TRUE(Address::isValid(CAROL_BECH32)); +} + +TEST(ElrondAddress, Invalid) { + ASSERT_FALSE(Address::isValid("")); + ASSERT_FALSE(Address::isValid("foo")); + ASSERT_FALSE(Address::isValid("10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); + ASSERT_FALSE(Address::isValid("xerd10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); + ASSERT_FALSE(Address::isValid("foo10z9xdugayn528ksaesdwlhf006fw5sg2qmmm0h52fvxczwgesyvq5pwemr")); + ASSERT_FALSE(Address::isValid(ALICE_PUBKEY_HEX)); +} + +TEST(ElrondAddress, FromString) { + Address alice, bob, carol; + ASSERT_TRUE(Address::decode(ALICE_BECH32, alice)); + ASSERT_TRUE(Address::decode(BOB_BECH32, bob)); + ASSERT_TRUE(Address::decode(CAROL_BECH32, carol)); + + ASSERT_EQ(ALICE_PUBKEY_HEX, hex(alice.getKeyHash())); + ASSERT_EQ(BOB_PUBKEY_HEX, hex(bob.getKeyHash())); + ASSERT_EQ(CAROL_PUBKEY_HEX, hex(carol.getKeyHash())); +} + +TEST(ElrondAddress, FromData) { + const auto alice = Address(parse_hex(ALICE_PUBKEY_HEX)); + const auto bob = Address(parse_hex(BOB_PUBKEY_HEX)); + const auto carol = Address(parse_hex(CAROL_PUBKEY_HEX)); + + ASSERT_EQ(ALICE_BECH32, alice.string()); + ASSERT_EQ(BOB_BECH32, bob.string()); + ASSERT_EQ(CAROL_BECH32, carol.string()); +} + +TEST(ElrondAddress, FromPrivateKey) { + auto aliceKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + auto alice = Address(aliceKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(ALICE_BECH32, alice.string()); + + auto bobKey = PrivateKey(parse_hex(BOB_SEED_HEX)); + auto bob = Address(bobKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(BOB_BECH32, bob.string()); + + auto carolKey = PrivateKey(parse_hex(CAROL_SEED_HEX)); + auto carol = Address(carolKey.getPublicKey(TWPublicKeyTypeED25519)); + ASSERT_EQ(CAROL_BECH32, carol.string()); +} + +TEST(ElrondAddress, FromPublicKey) { + auto alice = PublicKey(parse_hex(ALICE_PUBKEY_HEX), TWPublicKeyTypeED25519); + ASSERT_EQ(ALICE_BECH32, Address(alice).string()); + + auto bob = PublicKey(parse_hex(BOB_PUBKEY_HEX), TWPublicKeyTypeED25519); + ASSERT_EQ(BOB_BECH32, Address(bob).string()); + + auto carol = PublicKey(parse_hex(CAROL_PUBKEY_HEX), TWPublicKeyTypeED25519); + ASSERT_EQ(CAROL_BECH32, Address(carol).string()); +} diff --git a/tests/Elrond/TestAccounts.h b/tests/Elrond/TestAccounts.h new file mode 100644 index 00000000000..501f60cfbec --- /dev/null +++ b/tests/Elrond/TestAccounts.h @@ -0,0 +1,11 @@ +#pragma once + +const auto ALICE_BECH32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"; +const auto ALICE_SEED_HEX = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf"; +const auto ALICE_PUBKEY_HEX = "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293"; +const auto BOB_BECH32 = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r"; +const auto BOB_SEED_HEX = "e3a3a3d1ac40d42d8fd4c569a9749b65a1250dd3d10b6f4e438297662ea4850e"; +const auto BOB_PUBKEY_HEX = "c70cf50b238372fffaf7b7c5723b06b57859d424a2da621bcc1b2f317543aa36"; +const auto CAROL_BECH32 = "erd19nu5t7hszckwah5nlcadmk5rlchtugzplznskffpwecygcu0520s9tnyy0"; +const auto CAROL_SEED_HEX = "a926316cc3daf8ff25ba3e417797e6dfd51f62ae735ab07148234732f7314052"; +const auto CAROL_PUBKEY_HEX = "2cf945faf0162ceede93fe3addda83fe2ebe2041f8a70b2521767044638fa29f"; From 85eae4f2f88218517bdda7d4cc092c40ddc24e46 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Thu, 23 Apr 2020 11:51:39 +0000 Subject: [PATCH 04/12] Fix AnyAddress, add further address tests --- src/interface/TWAnyAddress.cpp | 11 +++++++++++ tests/Bech32AddressTests.cpp | 8 ++++++++ tests/CoinAddressValidationTests.cpp | 6 ++++++ tests/interface/TWAnyAddressTests.cpp | 7 +++++++ 4 files changed, 32 insertions(+) diff --git a/src/interface/TWAnyAddress.cpp b/src/interface/TWAnyAddress.cpp index fcd2587ddf2..8fab7633d30 100644 --- a/src/interface/TWAnyAddress.cpp +++ b/src/interface/TWAnyAddress.cpp @@ -20,6 +20,7 @@ #include "../Cardano/AddressV3.h" #include "../NEO/Address.h" #include "../Nano/Address.h" +#include "../Elrond/Address.h" #include "../Coin.h" #include "../HexCoding.h" @@ -184,6 +185,16 @@ TWData* _Nonnull TWAnyAddressData(struct TWAnyAddress* _Nonnull address) { data = Data(addr.bytes.begin(), addr.bytes.end()); break; } + + case TWCoinTypeElrond: { + Elrond::Address addr; + if (Elrond::Address::decode(string, addr)) { + data = addr.getKeyHash(); + } + + break; + } + default: break; } return TWDataCreateWithBytes(data.data(), data.size()); diff --git a/tests/Bech32AddressTests.cpp b/tests/Bech32AddressTests.cpp index ff425cb7c4f..4b6024ecd5e 100644 --- a/tests/Bech32AddressTests.cpp +++ b/tests/Bech32AddressTests.cpp @@ -27,6 +27,10 @@ TEST(Bech32Address, Valid) { ASSERT_TRUE(Bech32Address::isValid("io187wzp08vnhjjpkydnr97qlh8kh0dpkkytfam8j", "io")); ASSERT_TRUE(Bech32Address::isValid("zil1fwh4ltdguhde9s7nysnp33d5wye6uqpugufkz7", "zil")); + + ASSERT_TRUE(Bech32Address::isValid("erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz", "erd")); + ASSERT_TRUE(Bech32Address::isValid("erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r", "erd")); + ASSERT_TRUE(Bech32Address::isValid("erd19nu5t7hszckwah5nlcadmk5rlchtugzplznskffpwecygcu0520s9tnyy0", "erd")); } TEST(Bech32Address, Invalid) { @@ -48,6 +52,10 @@ TEST(Bech32Address, Invalid) { ASSERT_FALSE(Bech32Address::isValid("")); ASSERT_FALSE(Bech32Address::isValid("0x")); ASSERT_FALSE(Bech32Address::isValid("91cddcebe846ce4d47712287eee53cf17c2cfb7")); + + ASSERT_FALSE(Bech32Address::isValid("", "erd")); + ASSERT_FALSE(Bech32Address::isValid("erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35!", "erd")); + ASSERT_FALSE(Bech32Address::isValid("xerd19nu5t7hszckwah5nlcadmk5rlchtugzplznskffpwecygcu0520s9tnyy0", "erd")); } TEST(Bech32Address, InvalidWrongPrefix) { diff --git a/tests/CoinAddressValidationTests.cpp b/tests/CoinAddressValidationTests.cpp index 7b08cf9f61b..86a0d7f309a 100644 --- a/tests/CoinAddressValidationTests.cpp +++ b/tests/CoinAddressValidationTests.cpp @@ -373,4 +373,10 @@ TEST(Coin, ValidateAddressVeChain) { EXPECT_EQ(normalizeAddress(TWCoinTypeVeChain, "0x9d8a62f656a8d1615c1294fd71e9cfb3e4855a4f"), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); } +TEST(Coin, ValidateAddressElrond) { + EXPECT_TRUE(validateAddress(TWCoinTypeElrond, "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz")); + EXPECT_FALSE(validateAddress(TWCoinTypeElrond, "xerd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz")); +} + + } // namespace TW diff --git a/tests/interface/TWAnyAddressTests.cpp b/tests/interface/TWAnyAddressTests.cpp index c35b6935a79..dbc77477d5d 100644 --- a/tests/interface/TWAnyAddressTests.cpp +++ b/tests/interface/TWAnyAddressTests.cpp @@ -119,4 +119,11 @@ TEST(AnyAddress, Data) { auto keyHash = WRAPD(TWAnyAddressData(addr.get())); assertHexEqual(keyHash, "172bdf43265c0adfe105a1a8c45b3f406a38362f24"); } + // elrond + { + auto string = STRING("erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"); + auto addr = WRAP(TWAnyAddress, TWAnyAddressCreateWithString(string.get(), TWCoinTypeElrond)); + auto pubkey = WRAPD(TWAnyAddressData(addr.get())); + assertHexEqual(pubkey, "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293"); + } } From 96e94651fc12c0f20e5326a29f5fa026107a58c8 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Thu, 23 Apr 2020 11:52:30 +0000 Subject: [PATCH 05/12] Implement serialization and signing, add tests --- src/Elrond/Serialization.cpp | 67 ++++++++++++++++++++++ src/Elrond/Serialization.h | 21 +++++++ src/Elrond/Signer.cpp | 39 +++++++++++++ src/Elrond/Signer.h | 33 +++++++++++ tests/Elrond/SerializationTests.cpp | 54 +++++++++++++++++ tests/Elrond/SignerTests.cpp | 89 +++++++++++++++++++++++++++++ tests/Elrond/TWAnySignerTests.cpp | 55 ++++++++++++++++++ 7 files changed, 358 insertions(+) create mode 100644 src/Elrond/Serialization.cpp create mode 100644 src/Elrond/Serialization.h create mode 100644 src/Elrond/Signer.cpp create mode 100644 src/Elrond/Signer.h create mode 100644 tests/Elrond/SerializationTests.cpp create mode 100644 tests/Elrond/SignerTests.cpp create mode 100644 tests/Elrond/TWAnySignerTests.cpp diff --git a/src/Elrond/Serialization.cpp b/src/Elrond/Serialization.cpp new file mode 100644 index 00000000000..7e190dad1fa --- /dev/null +++ b/src/Elrond/Serialization.cpp @@ -0,0 +1,67 @@ +// 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 "Serialization.h" + +#include "../Elrond/Address.h" +#include "../proto/Elrond.pb.h" +#include "Base64.h" +#include "PrivateKey.h" + +using namespace TW::Elrond; + +std::map fields_order { + {"nonce", 1}, + {"value", 2}, + {"receiver", 3}, + {"sender", 4}, + {"gasPrice", 5}, + {"gasLimit", 6}, + {"data", 7}, + {"signature", 8} +}; + +struct FieldsSorter { + bool operator() (const string& lhs, const string& rhs) const { + return fields_order[lhs] < fields_order[rhs]; + } +}; + +template +using sorted_map = std::map; +using sorted_json = nlohmann::basic_json; + +string TW::Elrond::serializeTransactionToSignableString(const Proto::TransactionMessage& message) { + sorted_json payload { + {"nonce", json(message.nonce())}, + {"value", json(message.value())}, + {"receiver", json(message.receiver())}, + {"sender", json(message.sender())}, + {"gasPrice", json(message.gas_price())}, + {"gasLimit", json(message.gas_limit())}, + }; + + if (!message.data().empty()) { + payload["data"] = json(TW::Base64::encode(TW::data(message.data()))); + } + + return payload.dump(); +} + +string TW::Elrond::serializeSignedTransaction(const Proto::TransactionMessage& message, string signature) { + sorted_json payload { + {"nonce", json(message.nonce())}, + {"value", json(message.value())}, + {"receiver", json(message.receiver())}, + {"sender", json(message.sender())}, + {"gasPrice", json(message.gas_price())}, + {"gasLimit", json(message.gas_limit())}, + {"data", json(message.data())}, + {"signature", json(signature)}, + }; + + return payload.dump(); +} diff --git a/src/Elrond/Serialization.h b/src/Elrond/Serialization.h new file mode 100644 index 00000000000..8ae39d6c11b --- /dev/null +++ b/src/Elrond/Serialization.h @@ -0,0 +1,21 @@ +// 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 "../proto/Elrond.pb.h" +#include "Data.h" +#include + +using string = std::string; +using json = nlohmann::json; + +namespace TW::Elrond { + +string serializeTransactionToSignableString(const Proto::TransactionMessage& message); +string serializeSignedTransaction(const Proto::TransactionMessage& message, string encodedSignature); + +} // namespace diff --git a/src/Elrond/Signer.cpp b/src/Elrond/Signer.cpp new file mode 100644 index 00000000000..1e4257a5bd6 --- /dev/null +++ b/src/Elrond/Signer.cpp @@ -0,0 +1,39 @@ +// 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 "Signer.h" +#include "Address.h" +#include "Serialization.h" +#include "../PublicKey.h" +#include "HexCoding.h" + +#include + +using namespace TW; +using namespace TW::Elrond; + + +Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { + auto privateKey = PrivateKey(input.private_key()); + auto signableAsString = serializeTransactionToSignableString(input.transaction()); + auto signableAsData = TW::data(signableAsString); + auto signature = privateKey.sign(signableAsData, TWCurveED25519); + auto encodedSignature = hex(signature); + auto serializedTransaction = serializeSignedTransaction(input.transaction(), encodedSignature); + + auto protoOutput = Proto::SigningOutput(); + protoOutput.set_signature(encodedSignature); + protoOutput.set_signed_transaction(serializedTransaction); + return protoOutput; +} + +std::string Signer::signJSON(const std::string& json, const Data& key) { + auto input = Proto::SigningInput(); + google::protobuf::util::JsonStringToMessage(json, &input); + input.set_private_key(key.data(), key.size()); + auto output = sign(input); + return output.signed_transaction(); +} diff --git a/src/Elrond/Signer.h b/src/Elrond/Signer.h new file mode 100644 index 00000000000..3eda71ca9d0 --- /dev/null +++ b/src/Elrond/Signer.h @@ -0,0 +1,33 @@ +// 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 "../Data.h" +#include "../PrivateKey.h" +#include "../proto/Elrond.pb.h" + +namespace TW::Elrond { + +/// Helper class that performs Elrond transaction signing. +class Signer { +public: + /// Hide default constructor + Signer() = delete; + + /// Signs a Proto::SigningInput transaction + static Proto::SigningOutput sign(const Proto::SigningInput& input) noexcept; + + /// Signs a json Proto::SigningInput with private key + static std::string signJSON(const std::string& json, const Data& key); +}; + +} // namespace TW::Elrond + +/// Wrapper for C interface. +struct TWElrondSigner { + TW::Elrond::Signer impl; +}; diff --git a/tests/Elrond/SerializationTests.cpp b/tests/Elrond/SerializationTests.cpp new file mode 100644 index 00000000000..617601c24f5 --- /dev/null +++ b/tests/Elrond/SerializationTests.cpp @@ -0,0 +1,54 @@ +// 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 +#include +#include "boost/format.hpp" + +#include "HexCoding.h" +#include "Elrond/Serialization.h" +#include "TestAccounts.h" + +using namespace TW; +using namespace TW::Elrond; + +TEST(ElrondSerialization, SignableString) { + Proto::TransactionMessage message; + message.set_nonce(42); + message.set_value("43"); + message.set_sender("alice"); + message.set_receiver("bob"); + message.set_data("foobar"); + + string jsonString = serializeTransactionToSignableString(message); + ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"bob","sender":"alice","gasPrice":0,"gasLimit":0,"data":"Zm9vYmFy"})", jsonString); +} + +TEST(ElrondSerialization, SignableStringWithRealData) { + Proto::TransactionMessage message; + message.set_nonce(15); + message.set_value("100"); + message.set_sender(ALICE_BECH32); + message.set_receiver(BOB_BECH32); + message.set_gas_price(200000000000000); + message.set_gas_limit(500000000); + message.set_data("for dinner"); + + string expected = (boost::format(R"({"nonce":15,"value":"100","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"Zm9yIGRpbm5lcg=="})") % BOB_BECH32 % ALICE_BECH32).str(); + string actual = serializeTransactionToSignableString(message); + ASSERT_EQ(expected, actual); +} + +TEST(ElrondSerialization, SignableStringWithoutData) { + Proto::TransactionMessage message; + message.set_nonce(42); + message.set_value("43"); + message.set_sender("feed"); + message.set_receiver("abba"); + + string jsonString = serializeTransactionToSignableString(message); + ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"abba","sender":"feed","gasPrice":0,"gasLimit":0})", jsonString); +} diff --git a/tests/Elrond/SignerTests.cpp b/tests/Elrond/SignerTests.cpp new file mode 100644 index 00000000000..b74e30ac9f1 --- /dev/null +++ b/tests/Elrond/SignerTests.cpp @@ -0,0 +1,89 @@ +// 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 +#include "boost/format.hpp" + +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "Elrond/Signer.h" +#include "Elrond/Address.h" +#include "TestAccounts.h" + +using namespace TW; +using namespace TW::Elrond; + + +TEST(ElrondSigner, Sign) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_transaction()->set_nonce(0); + input.mutable_transaction()->set_value("0"); + input.mutable_transaction()->set_sender(ALICE_BECH32); + input.mutable_transaction()->set_receiver(BOB_BECH32); + input.mutable_transaction()->set_gas_price(200000000000000); + input.mutable_transaction()->set_gas_limit(500000000); + input.mutable_transaction()->set_data("foo"); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto signedTransaction = output.signed_transaction(); + auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; + auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedSignedTransaction, signedTransaction); +} + +TEST(ElrondSigner, SignJSON) { + // Shuffle some fields, assume arbitrary order in the input + auto input = (boost::format(R"({"transaction" : {"data":"foo","value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000}})") % BOB_BECH32 % ALICE_BECH32).str(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + + auto signedTransaction = Signer::signJSON(input, privateKey.bytes); + auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; + auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignedTransaction, signedTransaction); +} + +TEST(ElrondSigner, SignWithoutData) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_transaction()->set_nonce(0); + input.mutable_transaction()->set_value("0"); + input.mutable_transaction()->set_sender(ALICE_BECH32); + input.mutable_transaction()->set_receiver(BOB_BECH32); + input.mutable_transaction()->set_gas_price(200000000000000); + input.mutable_transaction()->set_gas_limit(500000000); + input.mutable_transaction()->set_data(""); + + auto output = Signer::sign(input); + auto signature = output.signature(); + auto signedTransaction = output.signed_transaction(); + auto expectedSignature = "39ab0e18bfce04bf53c9610faa3b9e7cecfca919510a7631e529e9086279b70a4832df32a5d1b8fdceb4e9082f2995da97f9195532c8d611ee749bc312cbf90c"; + auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedSignedTransaction, signedTransaction); +} + +TEST(ElrondSigner, SignJSONWithoutData) { + // Shuffle some fields, assume arbitrary order in the input + auto input = (boost::format(R"({"transaction" : {"value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000}})") % BOB_BECH32 % ALICE_BECH32).str(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + + auto signedTransaction = Signer::signJSON(input, privateKey.bytes); + auto expectedSignature = "39ab0e18bfce04bf53c9610faa3b9e7cecfca919510a7631e529e9086279b70a4832df32a5d1b8fdceb4e9082f2995da97f9195532c8d611ee749bc312cbf90c"; + auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignedTransaction, signedTransaction); +} diff --git a/tests/Elrond/TWAnySignerTests.cpp b/tests/Elrond/TWAnySignerTests.cpp new file mode 100644 index 00000000000..0caa6df7970 --- /dev/null +++ b/tests/Elrond/TWAnySignerTests.cpp @@ -0,0 +1,55 @@ +// 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 +#include "boost/format.hpp" + +#include +#include "HexCoding.h" +#include "../interface/TWTestUtilities.h" +#include "Elrond/Signer.h" +#include "TestAccounts.h" + +using namespace TW; +using namespace TW::Elrond; + + +TEST(TWAnySignerElrond, Sign) { + auto input = Proto::SigningInput(); + auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); + input.set_private_key(privateKey.bytes.data(), privateKey.bytes.size()); + + input.mutable_transaction()->set_nonce(0); + input.mutable_transaction()->set_value("0"); + input.mutable_transaction()->set_sender(ALICE_BECH32); + input.mutable_transaction()->set_receiver(BOB_BECH32); + input.mutable_transaction()->set_gas_price(200000000000000); + input.mutable_transaction()->set_gas_limit(500000000); + input.mutable_transaction()->set_data("foo"); + + Proto::SigningOutput output; + ANY_SIGN(input, TWCoinTypeElrond); + + auto signature = output.signature(); + auto signedTransaction = output.signed_transaction(); + auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; + auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_EQ(expectedSignature, signature); + ASSERT_EQ(expectedSignedTransaction, signedTransaction); +} + +TEST(TWAnySignerElrond, SignJSON) { + // Shuffle some fields, assume arbitrary order in the input + auto input = STRING((boost::format(R"({"transaction" : {"data":"foo","value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000}})") % BOB_BECH32 % ALICE_BECH32).str().c_str()); + auto privateKey = DATA(ALICE_SEED_HEX); + auto signedTransaction = WRAPS(TWAnySignerSignJSON(input.get(), privateKey.get(), TWCoinTypeElrond)); + auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; + auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + + ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeElrond)); + assertStringsEqual(signedTransaction, expectedSignedTransaction.c_str()); +} From c44400d8f5d295c7076a03b0d31a2f0dbdf0a044 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Thu, 23 Apr 2020 11:52:48 +0000 Subject: [PATCH 06/12] Add key derivation tests --- tests/CoinAddressDerivationTests.cpp | 1 + tests/interface/TWHDWalletTests.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/tests/CoinAddressDerivationTests.cpp b/tests/CoinAddressDerivationTests.cpp index 16ac09114df..1bf597c6d88 100644 --- a/tests/CoinAddressDerivationTests.cpp +++ b/tests/CoinAddressDerivationTests.cpp @@ -71,6 +71,7 @@ TEST(Coin, DeriveAddress) { EXPECT_EQ(TW::deriveAddress(TWCoinTypeFilecoin, privateKey), "f1qsx7qwiojh5duxbxhbqgnlyx5hmpcf7mcz5oxsy"); EXPECT_EQ(TW::deriveAddress(TWCoinTypeNEAR, privateKey), "NEAR2p5878zbFhxnrm7meL7TmqwtvBaqcBddyp5eGzZbovZ5HYrdGj"); EXPECT_EQ(TW::deriveAddress(TWCoinTypeSolana, privateKey), "H4JcMPicKkHcxxDjkyyrLoQj7Kcibd9t815ak4UvTr9M"); + EXPECT_EQ(TW::deriveAddress(TWCoinTypeElrond, privateKey), "erd1a6f6fan035ttsxdmn04ellxdlnwpgyhg0lhx5vjv92v6rc8xw9yq83344f"); } } // namespace TW diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index 62f67695f0e..df8deb258cd 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -231,6 +231,16 @@ TEST(HDWallet, DeriveAlgorand) { assertHexEqual(privateKeyData, "ce0b7ac644e2b7d9d14d3928b11643f43e48c33d3e328d059fef8add7f070e82"); } +TEST(HDWallet, DeriveElrond) { + auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), passphrase.get())); + auto privateKey = WRAP(TWPrivateKey, TWHDWalletGetKeyForCoin(wallet.get(), TWCoinTypeElrond)); + auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); + auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeElrond, privateKey.get())); + + assertHexEqual(privateKeyData, "1e6a08a0dc7cfcafdeea72949c3f6c1f8828536727e4b3d64232609c41cb8b3e"); + assertStringsEqual(address, "erd17vh5dwqrvnlxpmgwujewkuqk0rs9wkl2sgmnxgcp8ahjap5suwusqkuc5r"); +} + TEST(HDWallet, ExtendedKeys) { auto words = STRING("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"); auto wallet = WRAP(TWHDWallet, TWHDWalletCreateWithMnemonic(words.get(), STRING("").get())); From b26a49e4d99505ab29186431725c0108cde32b38 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Thu, 23 Apr 2020 11:53:01 +0000 Subject: [PATCH 07/12] Add tests for Android --- .../blockchains/CoinAddressDerivationTests.kt | 1 + .../blockchains/elrond/TestElrondAddress.kt | 49 ++++++++++++++++ .../blockchains/elrond/TestElrondSigner.kt | 57 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt create mode 100644 android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 8bfe4dd5268..810d8e30fc9 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -83,5 +83,6 @@ class CoinAddressDerivationTests { CARDANO -> assertEquals("addr1snpa4z7ntyfszv7ckquprdw75w4qjqh0qmya9jtkpxxlzxghlqyvv7l0yjamh8fxraw06p3ua8sj2g2gv98v4849s43t9g2999kquuu5egnprk", address) NEO -> assertEquals("AT6w7PJvwPcSqHvtbNBY2aHPDv12eW5Uuf", address) FILECOIN -> assertEquals("f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori", address) + ELROND -> assertEquals("erd1k3hzqq8yffrsr86238r3avwq6w0kfgtwrjwhf72n07kxyk4nzwxqvm47d2", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt new file mode 100644 index 00000000000..05c17837ddc --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondAddress.kt @@ -0,0 +1,49 @@ +// 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.elrond + +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 TestElrondAddress { + + init { + System.loadLibrary("TrustWalletCore") + } + + private val aliceBech32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" + private var aliceSeedHex = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf" + private var alicePubKeyHex = "0xfd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293" + + @Test + fun testAddressFromPrivateKey() { + val key = PrivateKey(aliceSeedHex.toHexByteArray()) + val pubKey = key.publicKeyEd25519 + val address = AnyAddress(pubKey, CoinType.ELROND) + + assertEquals(alicePubKeyHex, pubKey.data().toHex()) + assertEquals(aliceBech32, address.description()) + } + + @Test + fun testAddressFromPublicKey() { + val pubKey = PublicKey(alicePubKeyHex.toHexByteArray(), PublicKeyType.ED25519) + val address = AnyAddress(pubKey, CoinType.ELROND) + + assertEquals(aliceBech32, address.description()) + } + + @Test + fun testAddressFromString() { + val address = AnyAddress(aliceBech32, CoinType.ELROND) + + assertEquals(aliceBech32, address.description()) + } +} diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt new file mode 100644 index 00000000000..1fd627122ca --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt @@ -0,0 +1,57 @@ +// 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.elrond + +import com.google.protobuf.ByteString +import com.trustwallet.core.app.utils.toHexByteArray +import junit.framework.Assert.assertEquals +import org.junit.Test +import wallet.core.java.AnySigner +import wallet.core.jni.CoinType +import wallet.core.jni.PrivateKey +import wallet.core.jni.proto.Elrond + +class TestElrondSigner { + + init { + System.loadLibrary("TrustWalletCore") + } + + val aliceBech32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" + var aliceSeedHex = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf" + var alicePubKeyHex = "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293" + + val bobBech32 = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" + var bobSeedHex = "e3a3a3d1ac40d42d8fd4c569a9749b65a1250dd3d10b6f4e438297662ea4850e" + var bobPubKeyHex = "c70cf50b238372fffaf7b7c5723b06b57859d424a2da621bcc1b2f317543aa36" + + @Test + fun signTransaction() { + val transaction = Elrond.TransactionMessage.newBuilder() + .setNonce(0) + .setValue("0") + .setSender(aliceBech32) + .setReceiver(bobBech32) + .setGasPrice(200000000000000) + .setGasLimit(500000000) + .setData("foo") + .build() + + val privateKey = ByteString.copyFrom(PrivateKey(aliceSeedHex.toHexByteArray()).data()) + + val signingInput = Elrond.SigningInput.newBuilder() + .setPrivateKey(privateKey) + .setTransaction(transaction) + .build() + + val output = AnySigner.sign(signingInput, CoinType.ELROND, Elrond.SigningOutput.parser()) + val expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308" + + assertEquals(expectedSignature, output.signature) + assertEquals("""{"nonce":0,"value":"0","receiver":"$bobBech32","sender":"$aliceBech32","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"$expectedSignature"}""", output.signedTransaction) + } +} From a76ecaa77d3575e39043bf8c2719ed8470ba5faf Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Thu, 23 Apr 2020 11:53:16 +0000 Subject: [PATCH 08/12] Add tests for iOS --- swift/Tests/Blockchains/ElrondTests.swift | 49 ++++++++++++++++++++ swift/Tests/CoinAddressDerivationTests.swift | 3 ++ 2 files changed, 52 insertions(+) create mode 100644 swift/Tests/Blockchains/ElrondTests.swift diff --git a/swift/Tests/Blockchains/ElrondTests.swift b/swift/Tests/Blockchains/ElrondTests.swift new file mode 100644 index 00000000000..c051c32facd --- /dev/null +++ b/swift/Tests/Blockchains/ElrondTests.swift @@ -0,0 +1,49 @@ +// 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. + +import TrustWalletCore +import XCTest + +class ElrondTests: XCTestCase { + + let aliceBech32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz" + let aliceSeedHex = "1a927e2af5306a9bb2ea777f73e06ecc0ac9aaa72fb4ea3fecf659451394cccf" + let alicePubKeyHex = "fd691bb5e85d102687d81079dffce842d4dc328276d2d4c60d8fd1c3433c3293" + let bobBech32 = "erd1cux02zersde0l7hhklzhywcxk4u9n4py5tdxyx7vrvhnza2r4gmq4vw35r" + + func testAddress() { + let key = PrivateKey(data: Data(hexString: aliceSeedHex)!)! + let pubkey = key.getPublicKeyEd25519() + let address = AnyAddress(publicKey: pubkey, coin: .elrond) + let addressFromString = AnyAddress(string: aliceBech32, coin: .elrond)! + + XCTAssertEqual(pubkey.data.hexString, alicePubKeyHex) + XCTAssertEqual(address.description, addressFromString.description) + } + + func testSign() { + let privateKey = PrivateKey(data: Data(hexString: aliceSeedHex)!)! + + let input = ElrondSigningInput.with { + $0.transaction = ElrondTransactionMessage.with { + $0.nonce = 0 + $0.value = "0" + $0.sender = aliceBech32 + $0.receiver = bobBech32 + $0.gasPrice = 200000000000000 + $0.gasLimit = 500000000 + $0.data = "foo" + } + + $0.privateKey = privateKey.data + } + + let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) + let expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308" + + XCTAssertEqual(output.signature, expectedSignature) + } +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 81b55b548ed..b7dbcdaddbc 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -190,6 +190,9 @@ class CoinAddressDerivationTests: XCTestCase { case .filecoin: let expectedResult = "f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori" AssetCoinDerivation(coin, expectedResult, derivedAddress, address) + case .elrond: + let expectedResult = "erd1k3hzqq8yffrsr86238r3avwq6w0kfgtwrjwhf72n07kxyk4nzwxqvm47d2" + AssetCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() } From ae0a05635a2995e4f01e4910fd56430ae194defb Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Tue, 28 Apr 2020 12:10:52 +0000 Subject: [PATCH 09/12] Fix derivation path and tests --- .../core/app/blockchains/CoinAddressDerivationTests.kt | 2 +- coins.json | 2 +- swift/Tests/CoinAddressDerivationTests.swift | 2 +- tests/interface/TWHDWalletTests.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt index 810d8e30fc9..18ab0a98320 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/CoinAddressDerivationTests.kt @@ -83,6 +83,6 @@ class CoinAddressDerivationTests { CARDANO -> assertEquals("addr1snpa4z7ntyfszv7ckquprdw75w4qjqh0qmya9jtkpxxlzxghlqyvv7l0yjamh8fxraw06p3ua8sj2g2gv98v4849s43t9g2999kquuu5egnprk", address) NEO -> assertEquals("AT6w7PJvwPcSqHvtbNBY2aHPDv12eW5Uuf", address) FILECOIN -> assertEquals("f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori", address) - ELROND -> assertEquals("erd1k3hzqq8yffrsr86238r3avwq6w0kfgtwrjwhf72n07kxyk4nzwxqvm47d2", address) + ELROND -> assertEquals("erd1jfcy8aeru6vlx4fe6h3pc3vlpe2cnnur5zetxdhp879yagq7vqvs8na4f8", address) } } diff --git a/coins.json b/coins.json index 9c309f1f57d..175ef086de3 100644 --- a/coins.json +++ b/coins.json @@ -1337,7 +1337,7 @@ "symbol": "ERD", "decimals": 18, "blockchain": "ElrondNetwork", - "derivationPath": "m/44'/508'/0'", + "derivationPath": "m/44'/508'/0'/0'/0'", "curve": "ed25519", "publicKeyType": "ed25519", "hrp": "erd", diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index b7dbcdaddbc..2a09ac50169 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -191,7 +191,7 @@ class CoinAddressDerivationTests: XCTestCase { let expectedResult = "f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori" AssetCoinDerivation(coin, expectedResult, derivedAddress, address) case .elrond: - let expectedResult = "erd1k3hzqq8yffrsr86238r3avwq6w0kfgtwrjwhf72n07kxyk4nzwxqvm47d2" + let expectedResult = "erd1jfcy8aeru6vlx4fe6h3pc3vlpe2cnnur5zetxdhp879yagq7vqvs8na4f8" AssetCoinDerivation(coin, expectedResult, derivedAddress, address) @unknown default: fatalError() diff --git a/tests/interface/TWHDWalletTests.cpp b/tests/interface/TWHDWalletTests.cpp index df8deb258cd..6b286eee3cb 100644 --- a/tests/interface/TWHDWalletTests.cpp +++ b/tests/interface/TWHDWalletTests.cpp @@ -237,8 +237,8 @@ TEST(HDWallet, DeriveElrond) { auto privateKeyData = WRAPD(TWPrivateKeyData(privateKey.get())); auto address = WRAPS(TWCoinTypeDeriveAddress(TWCoinTypeElrond, privateKey.get())); - assertHexEqual(privateKeyData, "1e6a08a0dc7cfcafdeea72949c3f6c1f8828536727e4b3d64232609c41cb8b3e"); - assertStringsEqual(address, "erd17vh5dwqrvnlxpmgwujewkuqk0rs9wkl2sgmnxgcp8ahjap5suwusqkuc5r"); + assertHexEqual(privateKeyData, "0eb593141de897d60a0883320793eb49e63d556ccdf783a87ec014f150d50453"); + assertStringsEqual(address, "erd1a6l7q9cfvrgr80xuzm37tapdr4zm3mwrtl6vt8f45df45x7eadfs8ds5vv"); } TEST(HDWallet, ExtendedKeys) { From 09fef52cf53c3f3e360d2a87c75019ef345e1c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrei=20B=C4=83ncioiu?= Date: Tue, 28 Apr 2020 18:02:45 +0300 Subject: [PATCH 10/12] Apply suggestions from code review Co-Authored-By: hewig <360470+hewigovens@users.noreply.github.com> --- src/Elrond/Address.h | 5 ----- src/Elrond/Serialization.cpp | 6 +++--- src/Elrond/Signer.cpp | 1 - src/Elrond/Signer.h | 5 ----- src/proto/Elrond.proto | 2 +- 5 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/Elrond/Address.h b/src/Elrond/Address.h index d56854cbc90..1f96edc6700 100644 --- a/src/Elrond/Address.h +++ b/src/Elrond/Address.h @@ -36,8 +36,3 @@ class Address : public Bech32Address { }; } // namespace TW::Elrond - -/// Wrapper for C interface. -struct TWElrondAddress { - TW::Elrond::Address impl; -}; diff --git a/src/Elrond/Serialization.cpp b/src/Elrond/Serialization.cpp index 7e190dad1fa..62e0ffb0e4a 100644 --- a/src/Elrond/Serialization.cpp +++ b/src/Elrond/Serialization.cpp @@ -34,7 +34,7 @@ template using sorted_map = std::map; using sorted_json = nlohmann::basic_json; -string TW::Elrond::serializeTransactionToSignableString(const Proto::TransactionMessage& message) { +string serializeTransaction(const Proto::TransactionMessage& message) { sorted_json payload { {"nonce", json(message.nonce())}, {"value", json(message.value())}, @@ -45,13 +45,13 @@ string TW::Elrond::serializeTransactionToSignableString(const Proto::Transaction }; if (!message.data().empty()) { - payload["data"] = json(TW::Base64::encode(TW::data(message.data()))); + payload["data"] = json(Base64::encode(TW::data(message.data()))); } return payload.dump(); } -string TW::Elrond::serializeSignedTransaction(const Proto::TransactionMessage& message, string signature) { +string serializeSignedTransaction(const Proto::TransactionMessage& message, string signature) { sorted_json payload { {"nonce", json(message.nonce())}, {"value", json(message.value())}, diff --git a/src/Elrond/Signer.cpp b/src/Elrond/Signer.cpp index 1e4257a5bd6..f1a30132619 100644 --- a/src/Elrond/Signer.cpp +++ b/src/Elrond/Signer.cpp @@ -15,7 +15,6 @@ using namespace TW; using namespace TW::Elrond; - Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { auto privateKey = PrivateKey(input.private_key()); auto signableAsString = serializeTransactionToSignableString(input.transaction()); diff --git a/src/Elrond/Signer.h b/src/Elrond/Signer.h index 3eda71ca9d0..046f5e52d67 100644 --- a/src/Elrond/Signer.h +++ b/src/Elrond/Signer.h @@ -26,8 +26,3 @@ class Signer { }; } // namespace TW::Elrond - -/// Wrapper for C interface. -struct TWElrondSigner { - TW::Elrond::Signer impl; -}; diff --git a/src/proto/Elrond.proto b/src/proto/Elrond.proto index a823a4aca27..41b4a0efc06 100644 --- a/src/proto/Elrond.proto +++ b/src/proto/Elrond.proto @@ -31,6 +31,6 @@ message SigningInput { // Transaction signing output. message SigningOutput { - string signed_transaction = 1; + string encoded = 1; string signature = 2; } From 7ba30371a39ea2e658bf5259414c855ad1049d9c Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Tue, 28 Apr 2020 16:18:07 +0000 Subject: [PATCH 11/12] Fix after review --- src/Elrond/Serialization.cpp | 8 ++++---- src/Elrond/Serialization.h | 2 +- src/Elrond/Signer.cpp | 2 +- tests/Elrond/SerializationTests.cpp | 6 +++--- tests/Elrond/TWCoinTypeTests.cpp | 8 ++++---- tests/Elrond/TestAccounts.h | 6 ++++++ 6 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/Elrond/Serialization.cpp b/src/Elrond/Serialization.cpp index 62e0ffb0e4a..193daf5d210 100644 --- a/src/Elrond/Serialization.cpp +++ b/src/Elrond/Serialization.cpp @@ -11,7 +11,7 @@ #include "Base64.h" #include "PrivateKey.h" -using namespace TW::Elrond; +using namespace TW; std::map fields_order { {"nonce", 1}, @@ -34,7 +34,7 @@ template using sorted_map = std::map; using sorted_json = nlohmann::basic_json; -string serializeTransaction(const Proto::TransactionMessage& message) { +string Elrond::serializeTransaction(const Proto::TransactionMessage& message) { sorted_json payload { {"nonce", json(message.nonce())}, {"value", json(message.value())}, @@ -45,13 +45,13 @@ string serializeTransaction(const Proto::TransactionMessage& message) { }; if (!message.data().empty()) { - payload["data"] = json(Base64::encode(TW::data(message.data()))); + payload["data"] = json(TW::Base64::encode(TW::data(message.data()))); } return payload.dump(); } -string serializeSignedTransaction(const Proto::TransactionMessage& message, string signature) { +string Elrond::serializeSignedTransaction(const Proto::TransactionMessage& message, string signature) { sorted_json payload { {"nonce", json(message.nonce())}, {"value", json(message.value())}, diff --git a/src/Elrond/Serialization.h b/src/Elrond/Serialization.h index 8ae39d6c11b..e56d8a5bf87 100644 --- a/src/Elrond/Serialization.h +++ b/src/Elrond/Serialization.h @@ -15,7 +15,7 @@ using json = nlohmann::json; namespace TW::Elrond { -string serializeTransactionToSignableString(const Proto::TransactionMessage& message); +string serializeTransaction(const Proto::TransactionMessage& message); string serializeSignedTransaction(const Proto::TransactionMessage& message, string encodedSignature); } // namespace diff --git a/src/Elrond/Signer.cpp b/src/Elrond/Signer.cpp index f1a30132619..4300229efbb 100644 --- a/src/Elrond/Signer.cpp +++ b/src/Elrond/Signer.cpp @@ -17,7 +17,7 @@ using namespace TW::Elrond; Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { auto privateKey = PrivateKey(input.private_key()); - auto signableAsString = serializeTransactionToSignableString(input.transaction()); + auto signableAsString = serializeTransaction(input.transaction()); auto signableAsData = TW::data(signableAsString); auto signature = privateKey.sign(signableAsData, TWCurveED25519); auto encodedSignature = hex(signature); diff --git a/tests/Elrond/SerializationTests.cpp b/tests/Elrond/SerializationTests.cpp index 617601c24f5..80c048bb9da 100644 --- a/tests/Elrond/SerializationTests.cpp +++ b/tests/Elrond/SerializationTests.cpp @@ -23,7 +23,7 @@ TEST(ElrondSerialization, SignableString) { message.set_receiver("bob"); message.set_data("foobar"); - string jsonString = serializeTransactionToSignableString(message); + string jsonString = serializeTransaction(message); ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"bob","sender":"alice","gasPrice":0,"gasLimit":0,"data":"Zm9vYmFy"})", jsonString); } @@ -38,7 +38,7 @@ TEST(ElrondSerialization, SignableStringWithRealData) { message.set_data("for dinner"); string expected = (boost::format(R"({"nonce":15,"value":"100","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"Zm9yIGRpbm5lcg=="})") % BOB_BECH32 % ALICE_BECH32).str(); - string actual = serializeTransactionToSignableString(message); + string actual = serializeTransaction(message); ASSERT_EQ(expected, actual); } @@ -49,6 +49,6 @@ TEST(ElrondSerialization, SignableStringWithoutData) { message.set_sender("feed"); message.set_receiver("abba"); - string jsonString = serializeTransactionToSignableString(message); + string jsonString = serializeTransaction(message); ASSERT_EQ(R"({"nonce":42,"value":"43","receiver":"abba","sender":"feed","gasPrice":0,"gasLimit":0})", jsonString); } diff --git a/tests/Elrond/TWCoinTypeTests.cpp b/tests/Elrond/TWCoinTypeTests.cpp index 038ed3f9fc6..9e73e615721 100644 --- a/tests/Elrond/TWCoinTypeTests.cpp +++ b/tests/Elrond/TWCoinTypeTests.cpp @@ -15,9 +15,9 @@ TEST(TWElrondCoinType, TWCoinType) { auto symbol = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeElrond)); - auto txId = TWStringCreateWithUTF8Bytes("t123"); + auto txId = TWStringCreateWithUTF8Bytes("1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8"); auto txUrl = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeElrond, txId)); - auto accId = TWStringCreateWithUTF8Bytes("a12"); + auto accId = TWStringCreateWithUTF8Bytes("erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj"); auto accUrl = WRAPS(TWCoinTypeConfigurationGetAccountURL(TWCoinTypeElrond, accId)); auto id = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeElrond)); auto name = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeElrond)); @@ -27,8 +27,8 @@ TEST(TWElrondCoinType, TWCoinType) { ASSERT_EQ(0x0, TWCoinTypeP2shPrefix(TWCoinTypeElrond)); ASSERT_EQ(0x0, TWCoinTypeStaticPrefix(TWCoinTypeElrond)); assertStringsEqual(symbol, "ERD"); - assertStringsEqual(txUrl, "https://explorer.elrond.com/transactions/t123"); - assertStringsEqual(accUrl, "https://explorer.elrond.com/address/a12"); + assertStringsEqual(txUrl, "https://explorer.elrond.com/transactions/1fc9785cb8bea0129a16cf7bddc97630c176a556ea566f0e72923c882b5cb3c8"); + assertStringsEqual(accUrl, "https://explorer.elrond.com/address/erd12yne790km8ezwetkz7m3hmqy9utdc6vdkgsunfzpwguec6v04p2qtk9uqj"); assertStringsEqual(id, "elrond"); assertStringsEqual(name, "Elrond"); } diff --git a/tests/Elrond/TestAccounts.h b/tests/Elrond/TestAccounts.h index 501f60cfbec..55060002abd 100644 --- a/tests/Elrond/TestAccounts.h +++ b/tests/Elrond/TestAccounts.h @@ -1,3 +1,9 @@ +// 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 const auto ALICE_BECH32 = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"; From 71b502341149e33b590232671cd809e0230771a4 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Tue, 28 Apr 2020 19:06:25 +0000 Subject: [PATCH 12/12] Fix after review (including build issues) --- .../blockchains/elrond/TestElrondSigner.kt | 2 +- src/Elrond/Signer.cpp | 6 ++--- swift/Tests/Blockchains/ElrondTests.swift | 2 ++ tests/Elrond/SignerTests.cpp | 24 +++++++++---------- tests/Elrond/TWAnySignerTests.cpp | 12 +++++----- 5 files changed, 24 insertions(+), 22 deletions(-) diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt index 1fd627122ca..160dd9d7765 100644 --- a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/elrond/TestElrondSigner.kt @@ -52,6 +52,6 @@ class TestElrondSigner { val expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308" assertEquals(expectedSignature, output.signature) - assertEquals("""{"nonce":0,"value":"0","receiver":"$bobBech32","sender":"$aliceBech32","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"$expectedSignature"}""", output.signedTransaction) + assertEquals("""{"nonce":0,"value":"0","receiver":"$bobBech32","sender":"$aliceBech32","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"$expectedSignature"}""", output.encoded) } } diff --git a/src/Elrond/Signer.cpp b/src/Elrond/Signer.cpp index 4300229efbb..20519c8b80a 100644 --- a/src/Elrond/Signer.cpp +++ b/src/Elrond/Signer.cpp @@ -21,11 +21,11 @@ Proto::SigningOutput Signer::sign(const Proto::SigningInput &input) noexcept { auto signableAsData = TW::data(signableAsString); auto signature = privateKey.sign(signableAsData, TWCurveED25519); auto encodedSignature = hex(signature); - auto serializedTransaction = serializeSignedTransaction(input.transaction(), encodedSignature); + auto encoded = serializeSignedTransaction(input.transaction(), encodedSignature); auto protoOutput = Proto::SigningOutput(); protoOutput.set_signature(encodedSignature); - protoOutput.set_signed_transaction(serializedTransaction); + protoOutput.set_encoded(encoded); return protoOutput; } @@ -34,5 +34,5 @@ std::string Signer::signJSON(const std::string& json, const Data& key) { google::protobuf::util::JsonStringToMessage(json, &input); input.set_private_key(key.data(), key.size()); auto output = sign(input); - return output.signed_transaction(); + return output.encoded(); } diff --git a/swift/Tests/Blockchains/ElrondTests.swift b/swift/Tests/Blockchains/ElrondTests.swift index c051c32facd..c3e3e5b4b8c 100644 --- a/swift/Tests/Blockchains/ElrondTests.swift +++ b/swift/Tests/Blockchains/ElrondTests.swift @@ -43,7 +43,9 @@ class ElrondTests: XCTestCase { let output: ElrondSigningOutput = AnySigner.sign(input: input, coin: .elrond) let expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308" + let expectedEncoded = #"{"nonce":0,"value":"0","receiver":"\#(bobBech32)","sender":"\#(aliceBech32)","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"\#(expectedSignature)"}"# XCTAssertEqual(output.signature, expectedSignature) + XCTAssertEqual(output.encoded, expectedEncoded) } } diff --git a/tests/Elrond/SignerTests.cpp b/tests/Elrond/SignerTests.cpp index b74e30ac9f1..adda6c83a4d 100644 --- a/tests/Elrond/SignerTests.cpp +++ b/tests/Elrond/SignerTests.cpp @@ -33,12 +33,12 @@ TEST(ElrondSigner, Sign) { auto output = Signer::sign(input); auto signature = output.signature(); - auto signedTransaction = output.signed_transaction(); + auto encoded = output.encoded(); auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; - auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); ASSERT_EQ(expectedSignature, signature); - ASSERT_EQ(expectedSignedTransaction, signedTransaction); + ASSERT_EQ(expectedEncoded, encoded); } TEST(ElrondSigner, SignJSON) { @@ -46,11 +46,11 @@ TEST(ElrondSigner, SignJSON) { auto input = (boost::format(R"({"transaction" : {"data":"foo","value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000}})") % BOB_BECH32 % ALICE_BECH32).str(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); - auto signedTransaction = Signer::signJSON(input, privateKey.bytes); + auto encoded = Signer::signJSON(input, privateKey.bytes); auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; - auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); - ASSERT_EQ(expectedSignedTransaction, signedTransaction); + ASSERT_EQ(expectedEncoded, encoded); } TEST(ElrondSigner, SignWithoutData) { @@ -68,12 +68,12 @@ TEST(ElrondSigner, SignWithoutData) { auto output = Signer::sign(input); auto signature = output.signature(); - auto signedTransaction = output.signed_transaction(); + auto encoded = output.encoded(); auto expectedSignature = "39ab0e18bfce04bf53c9610faa3b9e7cecfca919510a7631e529e9086279b70a4832df32a5d1b8fdceb4e9082f2995da97f9195532c8d611ee749bc312cbf90c"; - auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); ASSERT_EQ(expectedSignature, signature); - ASSERT_EQ(expectedSignedTransaction, signedTransaction); + ASSERT_EQ(expectedEncoded, encoded); } TEST(ElrondSigner, SignJSONWithoutData) { @@ -81,9 +81,9 @@ TEST(ElrondSigner, SignJSONWithoutData) { auto input = (boost::format(R"({"transaction" : {"value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000}})") % BOB_BECH32 % ALICE_BECH32).str(); auto privateKey = PrivateKey(parse_hex(ALICE_SEED_HEX)); - auto signedTransaction = Signer::signJSON(input, privateKey.bytes); + auto encoded = Signer::signJSON(input, privateKey.bytes); auto expectedSignature = "39ab0e18bfce04bf53c9610faa3b9e7cecfca919510a7631e529e9086279b70a4832df32a5d1b8fdceb4e9082f2995da97f9195532c8d611ee749bc312cbf90c"; - auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); - ASSERT_EQ(expectedSignedTransaction, signedTransaction); + ASSERT_EQ(expectedEncoded, encoded); } diff --git a/tests/Elrond/TWAnySignerTests.cpp b/tests/Elrond/TWAnySignerTests.cpp index 0caa6df7970..f9366ab52ba 100644 --- a/tests/Elrond/TWAnySignerTests.cpp +++ b/tests/Elrond/TWAnySignerTests.cpp @@ -34,22 +34,22 @@ TEST(TWAnySignerElrond, Sign) { ANY_SIGN(input, TWCoinTypeElrond); auto signature = output.signature(); - auto signedTransaction = output.signed_transaction(); + auto encoded = output.encoded(); auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; - auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); ASSERT_EQ(expectedSignature, signature); - ASSERT_EQ(expectedSignedTransaction, signedTransaction); + ASSERT_EQ(expectedEncoded, encoded); } TEST(TWAnySignerElrond, SignJSON) { // Shuffle some fields, assume arbitrary order in the input auto input = STRING((boost::format(R"({"transaction" : {"data":"foo","value":"0","nonce":0,"receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000}})") % BOB_BECH32 % ALICE_BECH32).str().c_str()); auto privateKey = DATA(ALICE_SEED_HEX); - auto signedTransaction = WRAPS(TWAnySignerSignJSON(input.get(), privateKey.get(), TWCoinTypeElrond)); + auto encoded = WRAPS(TWAnySignerSignJSON(input.get(), privateKey.get(), TWCoinTypeElrond)); auto expectedSignature = "b88ad2fe98a7316ea432a0a76c18cd87200fe75f27a8f053ea6532b40317dbec5136c5463aef132ae951b7e60d45d921caaa5903e70821dcda98f237d4ec4308"; - auto expectedSignedTransaction = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); + auto expectedEncoded = (boost::format(R"({"nonce":0,"value":"0","receiver":"%1%","sender":"%2%","gasPrice":200000000000000,"gasLimit":500000000,"data":"foo","signature":"%3%"})") % BOB_BECH32 % ALICE_BECH32 % expectedSignature).str(); ASSERT_TRUE(TWAnySignerSupportsJSON(TWCoinTypeElrond)); - assertStringsEqual(signedTransaction, expectedSignedTransaction.c_str()); + assertStringsEqual(encoded, expectedEncoded.c_str()); }