Skip to content

Commit

Permalink
implement zilliqa bech32 address (#445)
Browse files Browse the repository at this point in the history
* implement zilliqa bech32 address

* improve code coverage

* make cocodacy happy

* Fix tests

* test keyhash

* Add a test case from zil js lib
  • Loading branch information
hewigovens authored May 21, 2019
1 parent 32310c0 commit bdfb93b
Show file tree
Hide file tree
Showing 23 changed files with 126 additions and 118 deletions.
4 changes: 4 additions & 0 deletions .codacy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
exclude_paths:
- 'codegen/**'
- 'tools/**'
- 'trezor-crypto/**'
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Trust Wallet Core is a cross-platform library that implements low-level cryptogr
[![Linux status](https://dev.azure.com/TrustWallet/Trust%20Wallet%20Core/_apis/build/status/Wallet%20Core%20Linux)](https://dev.azure.com/TrustWallet/Trust%20Wallet%20Core/_build/latest?definitionId=24)
[![JavaScript status](https://dev.azure.com/TrustWallet/Trust%20Wallet%20Core/_apis/build/status/Wallet%20Core%20JavaScript)](https://dev.azure.com/TrustWallet/Trust%20Wallet%20Core/_build?definitionId=29)


[![Codacy Badge](https://api.codacy.com/project/badge/Grade/82e76f6ea4ba4f0d9029e8846c04c093)](https://www.codacy.com/app/hewigovens/wallet-core?utm_source=github.com&utm_medium=referral&utm_content=TrustWallet/wallet-core&utm_campaign=Badge_Grade)
![Codecov](https://codecov.io/gh/TrustWallet/wallet-core/branch/master/graph/badge.svg)
![GitHub](https://img.shields.io/github/license/TrustWallet/wallet-core.svg)
![Maven Central](https://img.shields.io/maven-central/v/com.trustwallet.walletcore/walletcore.svg)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class CoinAddressDerivationTests {
STEEM -> assertEquals("STM6WSusVTidc1e7TbLjhqQPYctbsndTRwXHpi82gMuJyKEkJVLvg", address)
EOS -> assertEquals("EOS6hs8sRvGSzuQtq223zwJipMzqTJpXUVjyvHPvPwBSZWWrJTJkg", address)
IOTEX -> assertEquals("io1qw9cccecw09q7p5kzyqtuhfhvah2mhfrc84jfk", address)
ZILLIQA -> assertEquals("0xDdb41006F7B6FA8e5FBF06A71c01F789FeBC66e8", address)
ZILLIQA -> assertEquals("zil1mk6pqphhkmaguhalq6n3cq0h38ltcehg0rfmv6", address)
SEMUX -> assertEquals("0xfe604170382452f77bc922bc19eb4b53504b09c2", address)
DEXON -> assertEquals("0x6F3E6a6dDf2C2B4B32B8Bb452eA3F36B2BB489BF", address)
ZELCASH -> assertEquals("t1UKbRPzL4WN8Rs8aZ8RNiWoD2ftCMHKGUf", address)
Expand Down
2 changes: 2 additions & 0 deletions include/TrustWalletCore/TWHRP.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ enum TWHRP {
TWHRPCosmos /* "cosmos" */,
TWHRPGroestlcoin /* "grs" */,
TWHRPQtum /* "qtum" */,
TWHRPZilliqa /* "zilliqa" */,
};

static const char *_Nonnull HRP_BINANCE = "bnb";
Expand All @@ -33,6 +34,7 @@ static const char *_Nonnull HRP_COSMOS = "cosmos";
static const char *_Nonnull HRP_GROESTLCOIN = "grs";
static const char *_Nonnull HRP_VIACOIN = "via";
static const char *_Nonnull HRP_QTUM = "qc";
static const char *_Nonnull HRP_ZILLIQA = "zil";

const char *_Nullable stringForHRP(enum TWHRP hrp);
enum TWHRP hrpForString(const char *_Nonnull string);
Expand Down
4 changes: 4 additions & 0 deletions include/TrustWalletCore/TWZilliqaAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,8 @@ void TWZilliqaAddressDelete(struct TWZilliqaAddress *_Nonnull address);
TW_EXPORT_PROPERTY
TWString *_Nonnull TWZilliqaAddressDescription(struct TWZilliqaAddress *_Nonnull address);

/// Returns the key hash.
TW_EXPORT_PROPERTY
TWData *_Nonnull TWZilliqaAddressKeyHash(struct TWZilliqaAddress *_Nonnull address);

TW_EXTERN_C_END
2 changes: 1 addition & 1 deletion js/tests/utils/CoinAddressDerivation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('CoinAddressDerivation', () => {
case CoinType.LUX: expect('LYL6SZG8S6dyXRFT8Bw4FHUoVef3cWCoPi').to.equal(address); break;
case CoinType.QTUM: expect('QhceuaTdeCZtcxmVc6yyEDEJ7Riu5gWFoF').to.equal(address); break;
case CoinType.NULS: expect('NsdtNvsfmPerWk4BhcapHTB3LptF8Sbe').to.equal(address); break;
case CoinType.ZILLIQA: expect('0xDdb41006F7B6FA8e5FBF06A71c01F789FeBC66e8').to.equal(address); break;
case CoinType.ZILLIQA: expect('zil1mk6pqphhkmaguhalq6n3cq0h38ltcehg0rfmv6').to.equal(address); break;
case CoinType.SEMUX: expect('0xfe604170382452f77bc922bc19eb4b53504b09c2').to.equal(address); break;
}
};
Expand Down
2 changes: 1 addition & 1 deletion src/Coin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ bool TW::validateAddress(TWCoinType coin, const std::string& string) {
return Zcash::TAddress::isValid(string, {{Zcash::TAddress::staticPrefix, TWP2PKHPrefixZcashT}, {Zcash::TAddress::staticPrefix, TWP2SHPrefixZcashT}});

case TWCoinTypeZilliqa:
return Zilliqa::Address::isValid(string);
return Zilliqa::isValidAddress(string);

case TWCoinTypeNano:
return Nano::Address::isValid(string);
Expand Down
47 changes: 6 additions & 41 deletions src/Zilliqa/Address.cpp
Original file line number Diff line number Diff line change
@@ -1,42 +1,7 @@
#include "Address.h"
#include "AddressChecksum.h"
#include "stdint.h"
#include "../HexCoding.h"

using namespace TW::Zilliqa;

bool Address::isValid(const std::string& string) {
if (string.size() != 42 || string[0] != '0' || string[1] != 'x') {
return false;
}
const auto data = parse_hex(string);
return Address::isValid(data);
}

Address::Address(const std::string& string) {
if (!isValid(string)) {
throw std::invalid_argument("Invalid address data");
}
const auto data = parse_hex(string);
std::copy(data.begin(), data.end(), bytes.begin());
}
// Copyright © 2017-2019 Trust.
//
// 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.

Address::Address(const PublicKey& publicKey) {
if (publicKey.type != TWPublicKeyTypeSECP256k1) {
throw std::invalid_argument("Ziliqa::Address needs SECP256k1 public key");
}
const auto data =
publicKey.hash({}, static_cast<Data (*)(const byte*, const byte*)>(Hash::sha256), false);
std::copy(data.end() - Address::size, data.end(), bytes.begin());
}

Address::Address(const Data& data) {
if (!isValid(data)) {
throw std::invalid_argument("Invalid address data");
}
std::copy(data.begin(), data.end(), bytes.begin());
}

std::string Address::string() const {
return checksumed(*this);
}
#include "Address.h"
49 changes: 21 additions & 28 deletions src/Zilliqa/Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,38 @@
#pragma once

#include "../PublicKey.h"
#include "../Cosmos/Address.h"

#include <string>

namespace TW::Zilliqa {

class Address {
public:
/// Number of bytes in an address.
static const size_t size = 20;

/// Address data followed by the public key
std::array<uint8_t, size> bytes;
#include <TrustWalletCore/TWHRP.h>

/// Determines whether a collection of bytes makes a valid address.
static bool isValid(const Data& data) { return data.size() == size; }
#include <string>

/// Determines whether a string makes a valid address.
static bool isValid(const std::string& string);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-function"

/// Initializes an address from a string representation.
explicit Address(const std::string& string);
namespace TW::Zilliqa {

/// Initializes an address with a collection of bytes.
explicit Address(const Data& data);
static bool isValidAddress(const std::string& address) {
return Cosmos::Address::isValid(address, HRP_ZILLIQA);
}

/// Initializes an address from a public key.
explicit Address(const PublicKey& publicKey);
static Cosmos::Address Address(const PublicKey& publicKey) {
const auto hashed = Hash::sha256(publicKey.bytes);
auto keyHash = Data(20);
std::copy(hashed.end() - 20, hashed.end(), keyHash.begin());

/// Returns a string representation of the address. 20-bytes hex-encoded
std::string string() const;
};
return Cosmos::Address(HRP_ZILLIQA, keyHash);
}

inline bool operator==(const Address& lhs, const Address& rhs) {
return lhs.bytes == rhs.bytes;
}
static Cosmos::Address Address(const Data& keyHash) {
return Cosmos::Address(HRP_ZILLIQA, keyHash);
}

} // namespace TW::Zilliqa

#pragma clang diagnostic pop

/// Wrapper for C interface.
struct TWZilliqaAddress {
TW::Zilliqa::Address impl;
TW::Cosmos::Address impl;
};
6 changes: 3 additions & 3 deletions src/Zilliqa/AddressChecksum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ using namespace TW;
using namespace TW::Zilliqa;

/// see https://github.com/Zilliqa/Zilliqa/blob/1c53b792c7ae44f7b77366536a7e2f73a3eade6a/src/libServer/AddressChecksum.h
std::string Zilliqa::checksumed(const Address& address) {
const auto addressString = hex(address.bytes);
const auto hash = hex(Hash::sha256(address.bytes));
std::string Zilliqa::checksumed(const Data& bytes) {
const auto addressString = hex(bytes);
const auto hash = hex(Hash::sha256(bytes));

uint256_t temp_1 = 1;
uint256_t v("0x" + hash);
Expand Down
6 changes: 3 additions & 3 deletions src/Zilliqa/AddressChecksum.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

#pragma once

#include "Address.h"
#include "../Data.h"
#include <string>

namespace TW::Zilliqa {

std::string checksumed(const Address& address);
std::string checksumed(const Data& address);

} // namespace TW::Ethereum
} // namespace TW::Zilliqa
5 changes: 2 additions & 3 deletions src/Zilliqa/Signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,12 @@ Data Signer::getPreImage(const Proto::SigningInput& input) noexcept {
auto internal = ZilliqaMessage::ProtoTransactionCoreInfo();

const auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end()));
const auto address = Address(input.to_address());

const auto address = Cosmos::Address::decode(input.to_address());
const auto pubKey = key.getPublicKey(TWPublicKeyTypeSECP256k1);

internal.set_version(input.version());
internal.set_nonce(input.nonce());
internal.set_toaddr(address.bytes.data(), address.bytes.size());
internal.set_toaddr(address.first.keyHash.data(), address.first.keyHash.size());

auto sender = new ZilliqaMessage::ByteArray();
sender->set_data(pubKey.bytes.data(), pubKey.bytes.size());
Expand Down
3 changes: 3 additions & 0 deletions src/interface/TWHRP.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const char* stringForHRP(enum TWHRP hrp) {
case TWHRPCosmos: return HRP_COSMOS;
case TWHRPGroestlcoin: return HRP_GROESTLCOIN;
case TWHRPQtum: return HRP_QTUM;
case TWHRPZilliqa: return HRP_ZILLIQA;
default: return nullptr;
}
}
Expand All @@ -39,6 +40,8 @@ enum TWHRP hrpForString(const char *_Nonnull string) {
return TWHRPGroestlcoin;
} else if (std::strcmp(string, HRP_QTUM) == 0) {
return TWHRPQtum;
} else if (std::strcmp(string, HRP_ZILLIQA) == 0) {
return TWHRPZilliqa;
} else {
return TWHRPUnknown;
}
Expand Down
12 changes: 9 additions & 3 deletions src/interface/TWZilliqaAddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ bool TWZilliqaAddressEqual(struct TWZilliqaAddress *_Nonnull lhs, struct TWZilli

bool TWZilliqaAddressIsValidString(TWString *_Nonnull string) {
auto s = reinterpret_cast<const std::string*>(string);
return Address::isValid(*s);
return Zilliqa::isValidAddress(*s);
}

struct TWZilliqaAddress *_Nullable TWZilliqaAddressCreateWithString(TWString *_Nonnull string) {
auto s = reinterpret_cast<const std::string*>(string);
if (!Address::isValid(*s)) {
auto dec = Cosmos::Address::decode(*s);
if (!dec.second || dec.first.hrp != HRP_ZILLIQA) {
return nullptr;
}
return new TWZilliqaAddress{ Address(*s) };

return new TWZilliqaAddress{ Address(dec.first.keyHash) };
}

struct TWZilliqaAddress *_Nonnull TWZilliqaAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey) {
Expand All @@ -47,3 +49,7 @@ TWString *_Nonnull TWZilliqaAddressDescription(struct TWZilliqaAddress *_Nonnull
const auto string = address->impl.string();
return TWStringCreateWithUTF8Bytes(string.c_str());
}

TWData *_Nonnull TWZilliqaAddressKeyHash(struct TWZilliqaAddress *_Nonnull address) {
return TWDataCreateWithBytes(address->impl.keyHash.data(), address->impl.keyHash.size());
}
13 changes: 12 additions & 1 deletion swift/Tests/Blockchains/ZilliqaTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ import XCTest
import TrustWalletCore

class ZilliqaTests: XCTestCase {

func testAddress() {
let data = Data(hexString: "029d25b68a18442590e113132a34bb524695c4291d2c49abf2e4cdd7d98db862c3")!
let pubKey = PublicKey(data: data, type: .secp256k1)!
let address = ZilliqaAddress(publicKey: pubKey)
let address2 = ZilliqaAddress(string: "zil10lx2eurx5hexaca0lshdr75czr025cevqu83uz")!

XCTAssertEqual(address.keyHash.hexString, "7FCcaCf066a5F26Ee3AFfc2ED1FA9810Deaa632C".lowercased())
XCTAssertEqual(address.description, address2.description)
}

func testSigner() {

let privateKey = PrivateKey(data: Data(hexString: "0x68ffa8ec149ce50da647166036555f73d57f662eb420e154621e5f24f6cf9748")!)!
Expand All @@ -15,7 +26,7 @@ class ZilliqaTests: XCTestCase {
let input = ZilliqaSigningInput.with {
$0.version = TWZilliqaTxVersion
$0.nonce = 2
$0.toAddress = "0x7FCcaCf066a5F26Ee3AFfc2ED1FA9810Deaa632C"
$0.toAddress = "zil10lx2eurx5hexaca0lshdr75czr025cevqu83uz"
$0.amount = Data(hexString: "e8d4a51000")!
$0.gasPrice = Data(hexString: "3b9aca00")!
$0.gasLimit = 1
Expand Down
2 changes: 1 addition & 1 deletion swift/Tests/CoinAddressDerivationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class CoinAddressDerivationTests: XCTestCase {
let expectedResult = "io1qw9cccecw09q7p5kzyqtuhfhvah2mhfrc84jfk"
AssetCoinDerivation(coin, expectedResult, derivedAddress, address)
case .zilliqa:
let expectedResult = "0xDdb41006F7B6FA8e5FBF06A71c01F789FeBC66e8"
let expectedResult = "zil1mk6pqphhkmaguhalq6n3cq0h38ltcehg0rfmv6"
AssetCoinDerivation(coin, expectedResult, derivedAddress, address)
case .semux:
let expectedResult = "0xfe604170382452f77bc922bc19eb4b53504b09c2"
Expand Down
2 changes: 1 addition & 1 deletion swift/Tests/HDWalletTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class HDWalletTests: XCTestCase {
let address = zil.deriveAddress(privateKey: key)

XCTAssertEqual(key.data.hexString, "b49a9fb16cd2b46ee538be807f712073009ea528e407a25a4bf91a63c3e49f99")
XCTAssertEqual(address.description, "0x65E1a9341E08d10E6f3C00930CE879c5bF980319")
XCTAssertEqual(address.description, "zil1vhs6jdq7prgsumeuqzfse6recklesqcesfe685")
}

func testSignHash() {
Expand Down
3 changes: 2 additions & 1 deletion tests/CoinTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
namespace TW {

TEST(Coin, ValidateAddressZilliqa) {
EXPECT_TRUE(validateAddress(TWCoinTypeZilliqa, "0x91cddcebe846ce4d47712287eee53cf17c2cfb77"));
EXPECT_TRUE(validateAddress(TWCoinTypeZilliqa, "zil1j8xae6lggm8y63m3y2r7aefu797ze7mhzulnqg"));
EXPECT_FALSE(validateAddress(TWCoinTypeZilliqa, "0x91cddcebe846ce4d47712287eee53cf17c2cfb77"));
EXPECT_FALSE(validateAddress(TWCoinTypeZilliqa, "91cddcebe846ce4d47712287eee53cf17c2cfb77"));
EXPECT_FALSE(validateAddress(TWCoinTypeZilliqa, "0x"));
EXPECT_FALSE(validateAddress(TWCoinTypeZilliqa, ""));
Expand Down
Loading

0 comments on commit bdfb93b

Please sign in to comment.