Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Original, draft] Add Elrond coin #1

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from 23 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,6 @@ class CoinAddressDerivationTests {
CARDANO -> assertEquals("addr1snpa4z7ntyfszv7ckquprdw75w4qjqh0qmya9jtkpxxlzxghlqyvv7l0yjamh8fxraw06p3ua8sj2g2gv98v4849s43t9g2999kquuu5egnprk", address)
NEO -> assertEquals("AT6w7PJvwPcSqHvtbNBY2aHPDv12eW5Uuf", address)
FILECOIN -> assertEquals("f1zzykebxldfcakj5wdb5n3n7priul522fnmjzori", address)
ELROND -> assertEquals("erd1hwnmlzs8kvtsf248m7j7zp9hklncesemq8awv530y42r8s9y27rse5hfw0", address)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// 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)
val expected = AnyAddress(aliceBech32, CoinType.ELROND)

assertEquals(alicePubKeyHex, pubKey.data().toHex())
assertEquals(expected.description(), address.description())
AdoAdoAdo marked this conversation as resolved.
Show resolved Hide resolved
}

@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())
}
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
22 changes: 22 additions & 0 deletions coins.json
Original file line number Diff line number Diff line change
Expand Up @@ -1330,5 +1330,27 @@
"clientPublic": "",
"clientDocs": "https://docs.lotu.sh"
}
},
{
"id": "elrond",
"name": "Elrond",
"symbol": "ERD",
"decimals": 18,
"blockchain": "ElrondNetwork",
"derivationPath": "m/44'/2003'/0'",
AdoAdoAdo marked this conversation as resolved.
Show resolved Hide resolved
"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://wallet-api.elrond.com",
"clientDocs": "https://docs.elrond.com"
}
}
]
1 change: 1 addition & 0 deletions docs/coins.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ This list is generated from [./coins.json](../coins.json)
| 1024 | Ontology | ONT | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ontology/info/logo.png" width="32" /> | <https://ont.io> |
| 1729 | Tezos | XTZ | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/tezos/info/logo.png" width="32" /> | <https://tezos.com> |
| 1815 | Cardano | ADA | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/cardano/info/logo.png" width="32" /> | <https://www.cardano.org> |
| 2003 | Elrond | ERD | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/elrond/info/logo.png" width="32" /> | <https://elrond.com/> |
| 2017 | Kin | KIN | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/kin/info/logo.png" width="32" /> | <https://www.kin.org> |
| 2301 | Qtum | QTUM | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/qtum/info/logo.png" width="32" /> | <https://qtum.org> |
| 2718 | Nebulas | NAS | <img src="https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/nebulas/info/logo.png" width="32" /> | <https://nebulas.io> |
Expand Down
1 change: 1 addition & 0 deletions include/TrustWalletCore/TWBlockchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum TWBlockchain {
TWBlockchainCardano = 30,
TWBlockchainNEO = 31,
TWBlockchainFilecoin = 32,
TWBlockchainElrondNetwork = 33,
};

TW_EXTERN_C_END
1 change: 1 addition & 0 deletions include/TrustWalletCore/TWCoinType.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ enum TWCoinType {
TWCoinTypeKusama = 434,
TWCoinTypePolkadot = 354,
TWCoinTypeFilecoin = 461,
TWCoinTypeElrond = 2003,
AdoAdoAdo marked this conversation as resolved.
Show resolved Hide resolved
};

/// Returns the blockchain for a coin type.
Expand Down
2 changes: 2 additions & 0 deletions src/Coin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
17 changes: 17 additions & 0 deletions src/Elrond/Address.cpp
Original file line number Diff line number Diff line change
@@ -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 <TrustWalletCore/TWHRP.h>

#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);
}
43 changes: 43 additions & 0 deletions src/Elrond/Address.h
Original file line number Diff line number Diff line change
@@ -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 <string>

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) {}
AdoAdoAdo marked this conversation as resolved.
Show resolved Hide resolved

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;
};
31 changes: 31 additions & 0 deletions src/Elrond/Entry.cpp
Original file line number Diff line number Diff line change
@@ -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<Signer, Proto::SigningInput>(dataIn, dataOut);
}

string Entry::signJSON(TWCoinType coin, const std::string& json, const Data& key) const {
return Signer::signJSON(json, key);
}
25 changes: 25 additions & 0 deletions src/Elrond/Entry.h
Original file line number Diff line number Diff line change
@@ -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<TWCoinType> 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
67 changes: 67 additions & 0 deletions src/Elrond/Serialization.cpp
Original file line number Diff line number Diff line change
@@ -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<string, int> 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<class Key, class T, class Compare, class Allocator>
using sorted_map = std::map<Key, T, FieldsSorter, Allocator>;
using sorted_json = nlohmann::basic_json<sorted_map>;

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();
}
21 changes: 21 additions & 0 deletions src/Elrond/Serialization.h
Original file line number Diff line number Diff line change
@@ -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 <nlohmann/json.hpp>

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
Loading