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 d369f92cf09..493c4f31b96 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
@@ -78,5 +78,6 @@ class CoinAddressDerivationTests {
ZELCASH -> assertEquals("t1UKbRPzL4WN8Rs8aZ8RNiWoD2ftCMHKGUf", address)
ARK -> assertEquals("Ac49m5pu5YpMMNgEbSYeZUEpRMHcSK3DfV", address)
MONETARYUNIT -> assertEquals("7W3QRu8FttKzmYtRbXNKopeHweAKWuun2q", address)
+ RAVENCOIN -> assertEquals("RHoCwPc2FCQqwToYnSiAb3SrCET4zEHsbS", address)
}
}
diff --git a/coins.json b/coins.json
index 8199716c56a..cedb0b04fe2 100644
--- a/coins.json
+++ b/coins.json
@@ -634,5 +634,20 @@
"xpub": "xpub",
"xprv": "xprv",
"explorer": "https://explorer.monetaryunit.org/tx/"
+ },
+ {
+ "id": "ravencoin",
+ "name": "Ravencoin",
+ "symbol": "RVN",
+ "decimals": 8,
+ "blockchain": "Bitcoin",
+ "derivationPath": "m/44'/175'/0'/0/0",
+ "curve": "secp256k1",
+ "publicKeyType": "secp256k1",
+ "publicKeyHasher": "sha256ripemd",
+ "base58Hasher": "sha256d",
+ "xpub": "xpub",
+ "xprv": "xprv",
+ "explorer": "https://ravencoin.network/tx/"
}
]
diff --git a/docs/coins.md b/docs/coins.md
index e226cd3a257..965a2e0e68f 100644
--- a/docs/coins.md
+++ b/docs/coins.md
@@ -50,4 +50,5 @@
| 31102 | Ethersocial | ESN | | https://ethersocial.org/
| 163 | Ellaism | ELLA | | https://ellaism.org/
| 111 | Ark | ARK | | http://ark.io
-| 20 | DigiByte | DGB | | https://www.digibyte.io
\ No newline at end of file
+| 20 | DigiByte | DGB | | https://www.digibyte.io
+| 175 | Ravencoin | RVN | | https://ravencoin.org
\ No newline at end of file
diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h
index 671d1b26603..7435596e722 100644
--- a/include/TrustWalletCore/TWCoinType.h
+++ b/include/TrustWalletCore/TWCoinType.h
@@ -73,7 +73,7 @@ enum TWCoinType {
TWCoinTypeZelcash = 19167,
TWCoinTypeARK = 111,
TWCoinTypeMonetaryUnit = 31,
-
+ TWCoinTypeRavencoin = 175,
};
/// Returns the blockchain for a coin type.
diff --git a/include/TrustWalletCore/TWP2PKHPrefix.h b/include/TrustWalletCore/TWP2PKHPrefix.h
index 5d42b92d1bf..18bd45fb109 100644
--- a/include/TrustWalletCore/TWP2PKHPrefix.h
+++ b/include/TrustWalletCore/TWP2PKHPrefix.h
@@ -26,6 +26,7 @@ enum TWP2PKHPrefix {
TWP2PKHPrefixZcashT = 0xB8,
TWP2PKHPrefixZcoin = 0x52,
TWP2PKHPrefixMonetaryUnit = 0x10,
+ TWP2PKHPrefixRavencoin = 0x3c,
};
TW_EXTERN_C_END
diff --git a/include/TrustWalletCore/TWP2SHPrefix.h b/include/TrustWalletCore/TWP2SHPrefix.h
index 4ff38d45d24..344bffe4156 100644
--- a/include/TrustWalletCore/TWP2SHPrefix.h
+++ b/include/TrustWalletCore/TWP2SHPrefix.h
@@ -25,6 +25,7 @@ enum TWP2SHPrefix {
TWP2SHPrefixZcoin = 0x07,
TWP2SHPrefixS = 0x3F, // Lux and DigiByte
TWP2SHPrefixMonetaryUnit = 0x4C,
+ TWP2SHPrefixRavencoin = 0x7a,
};
// Do not export TWP2SHPrefixGroestlcoin because it the same to
diff --git a/src/Bitcoin/Script.cpp b/src/Bitcoin/Script.cpp
index 38bd40fe762..a555efe2bc0 100644
--- a/src/Bitcoin/Script.cpp
+++ b/src/Bitcoin/Script.cpp
@@ -253,11 +253,13 @@ void Script::encode(std::vector& data) const {
Script Script::buildForAddress(const std::string& string) {
static const std::vector p2pkhPrefixes = {TWP2PKHPrefixBitcoin, TWP2PKHPrefixIocoin, TWP2PKHPrefixLitecoin,
TWP2PKHPrefixDash, TWP2PKHPrefixZcoin, TWP2PKHPrefixViacoin,
- TWP2PKHPrefixD, TWP2PKHPrefixQtum, TWP2PKHPrefixMonetaryUnit};
+ TWP2PKHPrefixD, TWP2PKHPrefixQtum, TWP2PKHPrefixMonetaryUnit,
+ TWP2PKHPrefixRavencoin};
static const std::vector p2shPrefixes = {TWP2SHPrefixBitcoin, TWP2SHPrefixIocoin, TWP2SHPrefixLitecoin,
TWP2SHPrefixDash, TWP2SHPrefixZcoin, TWP2SHPrefixViacoin,
- TWP2SHPrefixDogecoin, TWP2SHPrefixS, TWP2SHPrefixMonetaryUnit};
+ TWP2SHPrefixDogecoin, TWP2SHPrefixS, TWP2SHPrefixMonetaryUnit,
+ TWP2SHPrefixRavencoin};
if (Address::isValid(string)) {
auto address = Address(string);
auto p2pkh = std::find(p2pkhPrefixes.begin(), p2pkhPrefixes.end(), address.bytes[0]);
diff --git a/src/Coin.cpp b/src/Coin.cpp
index 1de00dc6972..329a3e30c60 100644
--- a/src/Coin.cpp
+++ b/src/Coin.cpp
@@ -177,6 +177,9 @@ bool TW::validateAddress(TWCoinType coin, const std::string& string) {
case TWCoinTypeMonetaryUnit:
return Bitcoin::Address::isValid(string, {{TWP2PKHPrefixMonetaryUnit}, {TWP2SHPrefixMonetaryUnit}});
+
+ case TWCoinTypeRavencoin:
+ return Bitcoin::Address::isValid(string, {{TWP2PKHPrefixRavencoin}, {TWP2SHPrefixRavencoin}});
}
}
@@ -306,6 +309,9 @@ std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey) {
case TWCoinTypeMonetaryUnit:
return Bitcoin::Address(publicKey, TWP2PKHPrefixMonetaryUnit).string();
+
+ case TWCoinTypeRavencoin:
+ return Bitcoin::Address(publicKey, TWP2PKHPrefixRavencoin).string();
}
}
diff --git a/swift/Sources/Addresses/CoinType+Address.swift b/swift/Sources/Addresses/CoinType+Address.swift
index dab5ac7fa60..eceb809e4bc 100644
--- a/swift/Sources/Addresses/CoinType+Address.swift
+++ b/swift/Sources/Addresses/CoinType+Address.swift
@@ -87,6 +87,8 @@ public extension CoinType {
return SemuxAddress(string: string)
case .ark:
return ARKAddress(string: string)
+ case .ravencoin:
+ if let addr = BitcoinAddress(string: string), prefixSet.contains(addr.prefix) { return addr }
}
return .none
}
@@ -120,6 +122,8 @@ public extension CoinType {
return Set([P2SHPrefix.viacoin.rawValue, P2PKHPrefix.viacoin.rawValue])
case .monetaryUnit:
return Set([P2SHPrefix.monetaryUnit.rawValue, P2PKHPrefix.monetaryUnit.rawValue])
+ case .ravencoin:
+ return Set([P2SHPrefix.ravencoin.rawValue, P2PKHPrefix.ravencoin.rawValue])
default:
return Set()
}
diff --git a/swift/Tests/Addresses/BitcoinAddressTests.swift b/swift/Tests/Addresses/BitcoinAddressTests.swift
index 1cac41ff2e1..b00345caa1e 100644
--- a/swift/Tests/Addresses/BitcoinAddressTests.swift
+++ b/swift/Tests/Addresses/BitcoinAddressTests.swift
@@ -110,4 +110,60 @@ class BitcoinAddressTests: XCTestCase {
XCTAssertTrue(SegwitAddress.isValidString(string: addressString3),
"'\(addressString3)' should be a valid DigiByte Bech32 address")
}
+
+ func testInvalidDigiByteAddress() {
+ let addressString = "DTPQ92zp96TwpG2pRuUB3oEA3kWGRZPGhX"
+
+ XCTAssertNil(BitcoinAddress(string: addressString)?.prefix)
+ XCTAssertFalse(BitcoinAddress.isValidString(string: addressString),
+ "'\(addressString)' should be an invalid DigiByte address")
+
+ let addressString2 = "SUngTA1vaC2E62mbnc81Mdos3TcvZHwsVX"
+
+ XCTAssertNil(BitcoinAddress(string: addressString2)?.prefix)
+ XCTAssertFalse(BitcoinAddress.isValidString(string: addressString2),
+ "'\(addressString2)' should be an invalid DigiByte address")
+
+ let addressString3 = "xgb1qtjgmerfqwdffyf8ghcrkgy52cghsqptynmyswu"
+
+ XCTAssertNil(SegwitAddress(string: addressString3)?.hrp)
+ XCTAssertFalse(SegwitAddress.isValidString(string: addressString3),
+ "'\(addressString3)' should be an invalid DigiByte Bech32 address")
+ }
+
+ func testValidRavencoinAddress() {
+ let addressString = "RHoCwPc2FCQqwToYnSiAb3SrCET4zEHsbS"
+
+ XCTAssertEqual(P2PKHPrefix.ravencoin.rawValue, BitcoinAddress(string: addressString)?.prefix)
+ XCTAssertTrue(BitcoinAddress.isValidString(string: addressString),
+ "'\(addressString)' should be a valid Ravencoin address")
+
+ let addressString2 = "rPWwn5h4QFZNaz1XmY39rc73sdYGGDdmq1"
+
+ XCTAssertEqual(P2SHPrefix.ravencoin.rawValue, BitcoinAddress(string: addressString2)?.prefix)
+ XCTAssertTrue(BitcoinAddress.isValidString(string: addressString2),
+ "'\(addressString2)' should be a valid Ravencoin address")
+
+ // testnet address
+ let addressString3 = "mwJAu1BWcRSQhepZ71wiGoSwsD6hnB5B7G"
+
+ XCTAssertTrue(BitcoinAddress.isValidString(string: addressString3),
+ "'\(addressString3)' should be a valid Ravencoin testnet address")
+ }
+
+ func testInvalidRavencoinAddress() {
+ // bad address
+ let addressString = "XHoCwPc2FCQqwToYnSiAb3SrCET4zEHsbS"
+
+ XCTAssertNil(BitcoinAddress(string: addressString)?.prefix)
+ XCTAssertFalse(BitcoinAddress.isValidString(string: addressString),
+ "'\(addressString)' should be an invalid Ravencoin address")
+
+ // testnet address
+ let addressString2 = "mwJAu1BWcRSQhepZ71wiGoSwsD6hnB5B7G"
+
+ XCTAssertFalse(CoinType.ravencoin.validate(address: addressString2),
+ "'\(addressString2)' should be an invalid Ravencoin address")
+
+ }
}
diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift
index 14f525a5acc..a31c8014431 100644
--- a/swift/Tests/CoinAddressDerivationTests.swift
+++ b/swift/Tests/CoinAddressDerivationTests.swift
@@ -175,6 +175,9 @@ class CoinAddressDerivationTests: XCTestCase {
case .monetaryUnit:
let expectedResult = "7W3QRu8FttKzmYtRbXNKopeHweAKWuun2q"
AssetCoinDerivation(coin, expectedResult, derivedAddress, address)
+ case .ravencoin:
+ let expectedResult = "RHoCwPc2FCQqwToYnSiAb3SrCET4zEHsbS"
+ AssetCoinDerivation(coin, expectedResult, derivedAddress, address)
}
}
}
diff --git a/swift/Tests/HDWalletTests.swift b/swift/Tests/HDWalletTests.swift
index 20cf9cba5c5..661178bc2d1 100644
--- a/swift/Tests/HDWalletTests.swift
+++ b/swift/Tests/HDWalletTests.swift
@@ -338,4 +338,13 @@ class HDWalletTests: XCTestCase {
XCTAssertEqual(pubkey.data.hexString, "039fdd3652495d01b6a363f8db8b3adce09f83ea5c43ff872ad0a39192340256b0")
XCTAssertEqual(address.description, "bc1qearv5ezm3xfgy2t98denkzxwst4f35fvz608wa")
}
+
+ func testDeriveRavencoin() {
+ let ravencoin = CoinType.ravencoin
+ let wallet = HDWallet.test
+ let key = wallet.getKeyForCoin(coin: ravencoin)
+ let address = ravencoin.deriveAddress(privateKey: key)
+
+ XCTAssertEqual("RHQmrg7nNFnRUwg2mH7GafhRY3ZaF6FB2x", address)
+ }
}
diff --git a/tests/CoinAddressDerivationTests.cpp b/tests/CoinAddressDerivationTests.cpp
index 4f06a507c4d..128d9b8b8ea 100644
--- a/tests/CoinAddressDerivationTests.cpp
+++ b/tests/CoinAddressDerivationTests.cpp
@@ -65,6 +65,7 @@ TEST(Coin, DeriveAddress) {
EXPECT_EQ(TW::deriveAddress(TWCoinTypeDogecoin, privateKey), "DNRTC6GZ5evmM7BZWwPqF54fyDqUqULMyu");
EXPECT_EQ(TW::deriveAddress(TWCoinTypeSemux, privateKey), "0x1574f7f969f41e030a75677af25bd9373b9c87f1");
EXPECT_EQ(TW::deriveAddress(TWCoinTypeZilliqa, privateKey), "zil1j2cvtd7j9n7fnxfv2r3neucjw8tp4xz9sp07v4");
+ EXPECT_EQ(TW::deriveAddress(TWCoinTypeRavencoin, privateKey), "RSZYjMDCP4q3t7NAFXPPnqEGrMZn971pdB");
}
} // namespace TW
diff --git a/tests/CoinAddressValidationTests.cpp b/tests/CoinAddressValidationTests.cpp
index 7f74893231a..d8d5d5b94d7 100644
--- a/tests/CoinAddressValidationTests.cpp
+++ b/tests/CoinAddressValidationTests.cpp
@@ -181,9 +181,12 @@ TEST(Coin, ValidateAddressDGB){
EXPECT_TRUE(validateAddress(TWCoinTypeDigiByte, "dgb1q3p2nf26ac6qtdrv4czh5nmp2eshfj9wyn9vv3d"));
EXPECT_TRUE(validateAddress(TWCoinTypeDigiByte, "SUngTA1vaC2E62mbnc81Mdos3TcvZHwsVo"));
- EXPECT_FALSE(validateAddress(TWCoinTypeDigiByte, "DBfCffUdSbhqKZhjuvrJ6AgvJofT4E2kpx"));
- EXPECT_FALSE(validateAddress(TWCoinTypeDigiByte, "dgb1q3p2nf26ac6qtdrv4czh5nmp2eshfj9wyn9vv3x"));
- EXPECT_FALSE(validateAddress(TWCoinTypeDigiByte, "SUngTA1vaC2E62mbnc81Mdos3TcvZHwsVx"));
+ // bad address
+ EXPECT_FALSE(validateAddress(TWCoinTypeDigiByte, "XBfCffUdSbhqKZhjuvrJ6AgvJofT4E2kp4"));
+ // bad bech32 address
+ EXPECT_FALSE(validateAddress(TWCoinTypeDigiByte, "xgb1q3p2nf26ac6qtdrv4czh5nmp2eshfj9wyn9vv3d"));
+ // testnet address
+ EXPECT_FALSE(validateAddress(TWCoinTypeDigiByte, "ztijPBZmzdAkF6y79LHYGmqNm2CVfaoLqtz"));
}
TEST(Coin, validateAddressIocoin) {
@@ -237,4 +240,14 @@ TEST(Coin, validateAddressCosmos) {
EXPECT_FALSE(validateAddress(TWCoinTypeCosmos, "xosmos1hkfq3zahaqkkzx5mjnamwjsfpq2jk7z0emlrvp"));
}
+TEST(Coin, validateAddressRavencoin) {
+ EXPECT_TRUE(validateAddress(TWCoinTypeRavencoin, "RHoCwPc2FCQqwToYnSiAb3SrCET4zEHsbS"));
+ EXPECT_TRUE(validateAddress(TWCoinTypeRavencoin, "rPWwn5h4QFZNaz1XmY39rc73sdYGGDdmq1"));
+
+ // bad address
+ EXPECT_FALSE(validateAddress(TWCoinTypeRavencoin, "XHoCwPc2FCQqwToYnSiAb3SrCET4zEHsbS"));
+ // testnet address
+ EXPECT_FALSE(validateAddress(TWCoinTypeRavencoin, "mwJAu1BWcRSQhepZ71wiGoSwsD6hnB5B7G"));
+}
+
} // namespace TW
diff --git a/tests/interface/TWRavencoinTransactionTests.cpp b/tests/interface/TWRavencoinTransactionTests.cpp
new file mode 100644
index 00000000000..2a4fb3815b6
--- /dev/null
+++ b/tests/interface/TWRavencoinTransactionTests.cpp
@@ -0,0 +1,93 @@
+
+// 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 "TWTestUtilities.h"
+
+#include "Bitcoin/OutPoint.h"
+#include "Bitcoin/TransactionBuilder.h"
+#include "Bitcoin/TransactionSigner.h"
+#include "HexCoding.h"
+#include "PublicKey.h"
+
+#include
+#include
+
+#include
+
+using namespace TW;
+using namespace Bitcoin;
+
+TEST(RavencoinTransaction, SignTransaction) {
+ /*
+ https://iancoleman.io/bip39/
+ Mnemonic - shoot island position soft burden budget tooth cruel issue economy destroy above
+ m/44'/175'/0'/0/0 Address - RHoCwPc2FCQqwToYnSiAb3SrCET4zEHsbS
+ m/44'/175'/0'/0/0 Private key in Base58 encoding - L1At2vQpaHCmbiu333N3kD4nbDzJgvb8hxNp5S8bQApocFYuW1rx
+ m/44'/175'/0'/0/0 Private key in bytes - 75e4c520c92b3836e77dfe2715da469b71f7df86fc11ef328870735a700551fa
+ utxo - https://blockbook.ravencoin.org/tx/0c7e82b44eec71d634c013e2db3cb4fa26f87fbc90eb8734da93807d23605544
+ tx - https://blockbook.ravencoin.org/tx/3717b528eb4925461d9de5a596d2eefe175985740b4fda153255e10135f236a6
+ */
+
+ const int64_t utxo_amount = 100000000;
+ const int64_t amount = 50000000;
+ const int64_t fee = 2000000;
+
+ auto input = Bitcoin::Proto::SigningInput();
+ input.set_hash_type(TWSignatureHashTypeAll);
+ input.set_amount(amount);
+ input.set_byte_fee(1);
+ input.set_to_address("RNoSGCX8SPFscj8epDaJjqEpuZa2B5in88");
+ input.set_change_address("RHoCwPc2FCQqwToYnSiAb3SrCET4zEHsbS");
+
+ auto hash0 = DATA("445560237d8093da3487eb90bc7ff826fab43cdbe213c034d671ec4eb4827e0c");
+ auto utxo0 = input.add_utxo();
+ utxo0->mutable_out_point()->set_hash(TWDataBytes(hash0.get()), TWDataSize(hash0.get()));
+ utxo0->mutable_out_point()->set_index(0);
+ utxo0->mutable_out_point()->set_sequence(UINT32_MAX);
+ utxo0->set_amount(utxo_amount);
+ auto script0 = parse_hex("76a9145d6e33f3a108bbcc586cbbe90994d5baf5a9cce488ac");
+ utxo0->set_script(script0.data(), script0.size());
+
+ auto utxoKey0 = DATA("75e4c520c92b3836e77dfe2715da469b71f7df86fc11ef328870735a700551fa");
+ input.add_private_key(TWDataBytes(utxoKey0.get()), TWDataSize(utxoKey0.get()));
+
+ auto plan = Bitcoin::TransactionBuilder::plan(input);
+ plan.amount = amount;
+ plan.fee = fee;
+ plan.change = utxo_amount - amount - fee;
+
+ // Sign
+ auto signer = TW::Bitcoin::TransactionSigner(std::move(input), plan);
+ auto result = signer.sign();
+ auto signedTx = result.payload();
+
+ ASSERT_TRUE(result);
+ ASSERT_EQ(fee, signer.plan.fee);
+
+ Data serialized;
+ signedTx.encode(false, serialized);
+ ASSERT_EQ(
+ hex(serialized),
+ "0100000001445560237d8093da3487eb90bc7ff826fab43cdbe213c034d671ec4eb4827e0c000000006b483045022100d790bdaa3c44eb5e3a422365ca5fc009c4512625222e3378f2f16e7e6ef1732a0220688c1bb995b7ff2f12729e101d7c24b6314430317e7717911fdc35c0d84f2f0d012102138724e702d25b0fdce73372ccea9734f9349442d5a9681a5f4d831036cd9429ffffffff0280f0fa02000000001976a9149451f4546e09fc2e49ef9b5303924712ec2b038e88ac006cdc02000000001976a9145d6e33f3a108bbcc586cbbe90994d5baf5a9cce488ac00000000"
+ );
+}
+
+TEST(RavencoinTransaction, LockScripts) {
+ // P2PKH
+ // https://blockbook.ravencoin.org/tx/3717b528eb4925461d9de5a596d2eefe175985740b4fda153255e10135f236a6
+
+ auto script = WRAP(TWBitcoinScript, TWBitcoinScriptBuildForAddress(STRING("RNoSGCX8SPFscj8epDaJjqEpuZa2B5in88").get()));
+ auto scriptData = WRAPD(TWBitcoinScriptData(script.get()));
+ assertHexEqual(scriptData, "76a9149451f4546e09fc2e49ef9b5303924712ec2b038e88ac");
+
+ // P2SH
+ // https://ravencoin.network/api/tx/f600d07814677d1f60545c8f7f71260238595c4928d6fb87caa0f9dd732e9bb5
+
+ auto script2 = WRAP(TWBitcoinScript, TWBitcoinScriptBuildForAddress(STRING("rPWwn5h4QFZNaz1XmY39rc73sdYGGDdmq1").get()));
+ auto scriptData2 = WRAPD(TWBitcoinScriptData(script2.get()));
+ assertHexEqual(scriptData2, "a914bd92088bb7e82d611a9b94fbb74a0908152b784f87");
+}
\ No newline at end of file