Skip to content

Commit

Permalink
Support XRP X-address (#730)
Browse files Browse the repository at this point in the history
* 1. Add XRP X-address
2. Add Sourcetrail project
3. Other minor fixes

* Fix codacy warning
  • Loading branch information
hewigovens authored Nov 21, 2019
1 parent b48a437 commit 25ef9f8
Show file tree
Hide file tree
Showing 23 changed files with 377 additions and 64 deletions.
15 changes: 10 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
# Misc
.DS_Store
*~
*.orig

# Build directories
.build/
build/
xcode/
cmake-build-debug/
.cquery_cache/
.cxx/

# Dependencies
node_modules
lib/json
lib/protobuf

# Editor
*~
*.orig
.idea/
.vscode/

Expand All @@ -26,6 +31,6 @@ include/TrustWalletCore/TWHRP.h
coverage.info
coverage/

cmake-build-debug/
.cquery_cache/
.cxx/
# Sourcetrail
*.srctrldb
*.srctrlbm
5 changes: 4 additions & 1 deletion TrustWalletCore.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,15 @@ Pod::Spec.new do |s|
"#{protobuf_dir}/src/google/protobuf/port_def.inc",
"#{protobuf_dir}/src/google/protobuf/port_undef.inc",
s.exclude_files =
'trezor-crypto/src/rand.c'
'trezor-crypto/src/rand.c',
'trezor-crypto/include/TrezorCrypto/**/base58.h'
s.public_header_files =
'include/**/*.h',
'swift/Sources/*.h'

s.preserve_paths =
'trezor-crypto/src/*.{table}',
'trezor-crypto/include/TrezorCrypto/**/base58.h',
"#{protobuf_dir}/src/**/*.h",
"#{include_dir}/nlohmann/**/*.hpp",
'src/proto/*.proto'
Expand All @@ -156,6 +158,7 @@ Pod::Spec.new do |s|
'GCC_WARN_64_TO_32_BIT_CONVERSION' => 'NO',
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++17',
'OTHER_CFLAGS' => '-DHAVE_PTHREAD=1'
'OTHER_LDFLAGS' => '$(inherited) -fprofile-instr-generate'
}
s.pod_target_xcconfig = {
'SYSTEM_HEADER_SEARCH_PATHS' => '$(inherited) /usr/local/include'
Expand Down
4 changes: 0 additions & 4 deletions include/TrustWalletCore/TWRippleAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,4 @@ void TWRippleAddressDelete(struct TWRippleAddress *_Nonnull address);
TW_EXPORT_PROPERTY
TWString *_Nonnull TWRippleAddressDescription(struct TWRippleAddress *_Nonnull address);

/// Returns the key hash.
TW_EXPORT_PROPERTY
TWData *_Nonnull TWRippleAddressKeyHash(struct TWRippleAddress *_Nonnull address);

TW_EXTERN_C_END
49 changes: 49 additions & 0 deletions include/TrustWalletCore/TWRippleXAddress.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright © 2017-2019 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 "TWBase.h"
#include "TWData.h"
#include "TWHRP.h"
#include "TWString.h"

TW_EXTERN_C_BEGIN

struct TWPublicKey;

/// Represents a Ripple X-address.
TW_EXPORT_CLASS
struct TWRippleXAddress;

/// Compares two addresses for equality.
TW_EXPORT_STATIC_METHOD
bool TWRippleXAddressEqual(struct TWRippleXAddress *_Nonnull lhs, struct TWRippleXAddress *_Nonnull rhs);

/// Determines if the string is a valid Ripple address.
TW_EXPORT_STATIC_METHOD
bool TWRippleXAddressIsValidString(TWString *_Nonnull string);

/// Creates an address from a string representaion.
TW_EXPORT_STATIC_METHOD
struct TWRippleXAddress *_Nullable TWRippleXAddressCreateWithString(TWString *_Nonnull string);

/// Creates an address from a public key and destination tag.
TW_EXPORT_STATIC_METHOD
struct TWRippleXAddress *_Nonnull TWRippleXAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey, uint32_t tag);

TW_EXPORT_METHOD
void TWRippleXAddressDelete(struct TWRippleXAddress *_Nonnull address);

/// Returns the address string representation.
TW_EXPORT_PROPERTY
TWString *_Nonnull TWRippleXAddressDescription(struct TWRippleXAddress *_Nonnull address);

/// Returns the destination tag.
TW_EXPORT_PROPERTY
uint32_t TWRippleXAddressTag(struct TWRippleXAddress *_Nonnull address);

TW_EXTERN_C_END
2 changes: 1 addition & 1 deletion src/Aeternity/Transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ TW::Data Transaction::buildTag(const std::string &address) {
auto payload = address.substr(Identifiers::prefixTransaction.size(), address.size());

auto data = Data();
append(data, {Identifiers::iDTagAccount});
append(data, Identifiers::iDTagAccount);
append(data, Base58::bitcoin.decodeCheck(payload));

return data;
Expand Down
4 changes: 3 additions & 1 deletion src/Coin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "NULS/Address.h"
#include "Ontology/Address.h"
#include "Ripple/Address.h"
#include "Ripple/XAddress.h"
#include "Solana/Address.h"
#include "Stellar/Address.h"
#include "TON/Address.h"
Expand Down Expand Up @@ -126,7 +127,8 @@ bool TW::validateAddress(TWCoinType coin, const std::string &string) {
return Nimiq::Address::isValid(string);

case TWCoinTypeXRP:
return Ripple::Address::isValid(string);
return Ripple::Address::isValid(string) ||
Ripple::XAddress::isValid(string);

case TWCoinTypeStellar:
case TWCoinTypeKin:
Expand Down
5 changes: 5 additions & 0 deletions src/Data.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <cstdint>
#include <vector>
#include <string>
#include <array>

namespace TW {

Expand All @@ -31,6 +32,10 @@ inline void append(Data& data, const Data& suffix) {
data.insert(data.end(), suffix.begin(), suffix.end());
}

inline void append(Data& data, const byte suffix) {
data.push_back(suffix);
}

/// Determines if a byte array has a specific prefix.
template <typename T>
inline bool has_prefix(const Data& data, T& prefix) {
Expand Down
4 changes: 2 additions & 2 deletions src/Harmony/Staking.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ class CreateValidator {
, commissionRates(move(commissionRates))
, minSelfDelegation(move(minSelfDelegation))
, maxTotalDelegation(move(maxTotalDelegation))
, slotPubKeys(move(slotPubKeys))
, amount(move(amount)) {}
, amount(move(amount))
, slotPubKeys(move(slotPubKeys)) {}
};

class EditValidator {
Expand Down
13 changes: 0 additions & 13 deletions src/Ripple/Address.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,16 @@
// file LICENSE at the root of the source code distribution tree.

#include "Address.h"

#include "../Base58.h"
#include "../HexCoding.h"

#include <TrezorCrypto/ecdsa.h>

#include <cassert>

using namespace TW::Ripple;

bool Address::isValid(const std::string& string) {
const auto decoded = Base58::ripple.decodeCheck(string);
if (decoded.size() != Address::size) {
return false;
}

return true;
}

Expand All @@ -32,13 +26,6 @@ Address::Address(const std::string& string) {
std::copy(decoded.begin(), decoded.end(), bytes.begin());
}

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

Address::Address(const PublicKey& publicKey) {
/// see type prefix: https://developers.ripple.com/base58-encodings.html
bytes[0] = 0x00;
Expand Down
14 changes: 2 additions & 12 deletions src/Ripple/Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,15 @@ class Address {
/// Number of bytes in an address.
static const size_t size = 21;

/// Address data consisting of a prefix byte followed by the public key
/// hash.
/// Address data consisting of a prefix byte followed by the public key hash
std::array<byte, size> bytes;

/// Determines whether a collection of bytes makes a valid Ripple address.
template <typename T>
static bool isValid(const T& data) {
return data.size() == size;
}

/// Determines whether a string makes a valid address.
/// Determines whether a string makes a valid address.
static bool isValid(const std::string& string);

/// Initializes a Ripple address with a string representation.
explicit Address(const std::string& string);

/// Initializes a Ripple address with a collection of bytes.
explicit Address(const std::vector<uint8_t>& data);

/// Initializes a Ripple address with a public key.
explicit Address(const PublicKey& publicKey);

Expand Down
4 changes: 2 additions & 2 deletions src/Ripple/Transaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Data Transaction::serialize() const {
encodeType(FieldType::int32, 4, data);
encode32BE(sequence, data);
/// "destinationTag"
if (destination_tag > 0) {
if (encode_tag) {
encodeType(FieldType::int32, 14, data);
encode32BE(static_cast<uint32_t>(destination_tag), data);
}
Expand Down Expand Up @@ -57,7 +57,7 @@ Data Transaction::serialize() const {
encodeBytes(serializeAddress(account), data);
/// "destination"
encodeType(FieldType::account, 3, data);
encodeBytes(serializeAddress(destination), data);
encodeBytes(destination, data);
return data;
}

Expand Down
22 changes: 17 additions & 5 deletions src/Ripple/Transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#pragma once

#include "Address.h"
#include "XAddress.h"
#include "../Data.h"
#include "../proto/Ripple.pb.h"

Expand All @@ -33,22 +34,33 @@ class Transaction {
int32_t sequence;
int32_t last_ledger_sequence;
Address account;
Address destination;
Data destination;
bool encode_tag;
int64_t destination_tag;
Data pub_key;
Data signature;

Transaction(int64_t amount, int64_t fee, int64_t flags, int32_t sequence,
int32_t last_ledger_sequence, Address account, Address destination,
int32_t last_ledger_sequence, Address account, const std::string& destination,
int64_t destination_tag)
: amount(amount)
, fee(fee)
, flags(flags)
, sequence(sequence)
, last_ledger_sequence(last_ledger_sequence)
, account(account)
, destination(destination)
, destination_tag(destination_tag) {}
, account(account) {
try {
auto address = Address(destination);
encode_tag = destination_tag > 0;
this->destination_tag = destination_tag;
this->destination = Data(address.bytes.begin() + 1, address.bytes.end());
} catch(const std::exception& e) {
auto xAddress = XAddress(destination);
encode_tag = xAddress.flag != TagFlag::none;
this->destination_tag = xAddress.tag;
this->destination = Data(xAddress.bytes.begin(), xAddress.bytes.end());
}
}

public:
/// simplified serialization format tailored for Payment transaction type
Expand Down
62 changes: 62 additions & 0 deletions src/Ripple/XAddress.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright © 2017-2019 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 "XAddress.h"

#include "../Base58.h"
#include "../BinaryCoding.h"
#include "../Data.h"
#include <TrezorCrypto/ecdsa.h>

using namespace TW;
using namespace TW::Ripple;

const Data prefixMainnet = {0x05, 0x44};

bool XAddress::isValid(const std::string& string) {
const auto decoded = Base58::ripple.decodeCheck(string);
if (decoded.size() != XAddress::size) {
return false;
}
if(!std::equal(decoded.begin(), decoded.begin() + 2, prefixMainnet.begin())) {
return false;
}
if (!(decoded[22] == byte(TagFlag::none) || decoded[22] == byte(TagFlag::classic))) {
return false;
}
return true;
}

XAddress::XAddress(const std::string& string) {
if (!XAddress::isValid(string)) {
throw std::invalid_argument("Invalid address string");
}
const auto decoded = Base58::ripple.decodeCheck(string);
std::copy(decoded.begin() + prefixMainnet.size(), decoded.begin() + prefixMainnet.size() + XAddress::keyHashSize, bytes.begin());
if (decoded[22] == byte(TagFlag::classic)) {
tag = decode32LE(Data(decoded.end() - 8, decoded.end() - 4).data());
} else if (decoded[22] == byte(TagFlag::none)) {
flag = TagFlag::none;
} else {
throw std::invalid_argument("Invalid flag");
}
}

XAddress::XAddress(const PublicKey& publicKey, const uint32_t destination): tag(destination) {
ecdsa_get_pubkeyhash(publicKey.bytes.data(), HASHER_SHA2_RIPEMD, bytes.data());
}

std::string XAddress::string() const {
/// see https://github.com/ripple/ripple-address-codec/blob/master/src/index.ts
/// base58check(2 bytes prefix + 20 bytes keyhash + 1 byte flag + 4 bytes + 32bit tag + 4 bytes reserved)
Data result;
append(result, prefixMainnet);
append(result, Data{bytes.begin(), bytes.end()});
append(result, byte(flag));
encode32LE(tag, result);
append(result, Data{0x00, 0x00, 0x00, 0x00});
return Base58::ripple.encodeCheck(result);
}
Loading

0 comments on commit 25ef9f8

Please sign in to comment.