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 5fa7b9d07f9..5d17220fab1 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 @@ -75,5 +75,6 @@ class CoinAddressDerivationTests { SEMUX -> assertEquals("0xfe604170382452f77bc922bc19eb4b53504b09c2", address) DEXON -> assertEquals("0x6F3E6a6dDf2C2B4B32B8Bb452eA3F36B2BB489BF", address) ZELCASH -> assertEquals("t1UKbRPzL4WN8Rs8aZ8RNiWoD2ftCMHKGUf", address) + ARK -> assertEquals("Ac49m5pu5YpMMNgEbSYeZUEpRMHcSK3DfV", address) } } diff --git a/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ark/TestArkTransactionSigning.kt b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ark/TestArkTransactionSigning.kt new file mode 100644 index 00000000000..e45f7dbc9ef --- /dev/null +++ b/android/app/src/androidTest/java/com/trustwallet/core/app/blockchains/ark/TestArkTransactionSigning.kt @@ -0,0 +1,32 @@ +package com.trustwallet.core.app.blockchains.ark + +import com.trustwallet.core.app.utils.toHexBytesInByteString +import junit.framework.Assert.assertEquals +import org.junit.Test +import wallet.core.jni.ARKSigner +import wallet.core.jni.proto.ARK + +class TestArkTransactionSigning{ + init { + System.loadLibrary("TrustWalletCore") + } + @Test + fun arkTransactionSigning(){ + val signingInput = ARK.SigningInput.newBuilder() + .setType(ARK.TransactionType.Transfer) + .setAmount(123123123) + .setPrivateKey("d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712".toHexBytesInByteString()) + .setTimestamp(67447770) + .setToAddress("ARkMaRcjcwRgr6vmDtAWo7bFqUgy9wG3NU") + .setFee(10000000) + .build() + + val output = ARKSigner.sign(signingInput) + + assertEquals(output.signature, "304402205e6365f4c3b49c28f03afd89d308736dca56671ea707dd3dd5af42272a0cc8ed02207fa7fc015fba7ae527d22a058cc4ebd8e9867c563ace7effc2dbaad2af8976c3".toHexBytesInByteString()) + assertEquals(output.encoded, "{\"amount\":123123123,\"asset\":{},\"fee\":10000000,\"id\":\"219b1cc99ec804df02230a9e913ccb45edb7819f22328e3cd15030174a8c4167\",\"recipientId\":\"ARkMaRcjcwRgr6vmDtAWo7bFqUgy9wG3NU\",\"senderPublicKey\":\"034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192\",\"signature\":\"304402205e6365f4c3b49c28f03afd89d308736dca56671ea707dd3dd5af42272a0cc8ed02207fa7fc015fba7ae527d22a058cc4ebd8e9867c563ace7effc2dbaad2af8976c3\",\"timestamp\":67447770,\"type\":0}") + + } + + class Asset +} diff --git a/coins.json b/coins.json index 94f96758bcf..cd76a5e0c41 100644 --- a/coins.json +++ b/coins.json @@ -593,5 +593,16 @@ "xpub": "xpub", "xprv": "xprv", "explorer": "https://explorer.zel.cash/tx/" + }, + { + "id": "ark", + "name": "ARK", + "symbol": "ARK", + "decimals": 8, + "blockchain": "Ark", + "derivationPath": "m/44'/111'/0'/0/0", + "curve": "secp256k1", + "publicKeyType": "secp256k1", + "explorer": "https://explorer.ark.io/transaction/" } ] diff --git a/include/TrustWalletCore/TWARKAddress.h b/include/TrustWalletCore/TWARKAddress.h new file mode 100644 index 00000000000..5c5bc35d5cf --- /dev/null +++ b/include/TrustWalletCore/TWARKAddress.h @@ -0,0 +1,42 @@ +// 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 "TWString.h" + +TW_EXTERN_C_BEGIN + +struct TWPublicKey; + +/// Represents an ARK address. +TW_EXPORT_CLASS +struct TWARKAddress; + +/// Compares two addresses for equality. +TW_EXPORT_STATIC_METHOD +bool TWARKAddressEqual(struct TWARKAddress *_Nonnull lhs, struct TWARKAddress *_Nonnull rhs); + +/// Determines if the string is a valid address. +TW_EXPORT_STATIC_METHOD +bool TWARKAddressIsValidString(TWString *_Nonnull string); + +/// Creates an address from a string representaion. +TW_EXPORT_STATIC_METHOD +struct TWARKAddress *_Nullable TWARKAddressCreateWithString(TWString *_Nonnull string); + +/// Creates an address from a public key. +TW_EXPORT_STATIC_METHOD +struct TWARKAddress *_Nonnull TWARKAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey); + +TW_EXPORT_METHOD +void TWARKAddressDelete(struct TWARKAddress *_Nonnull address); + +/// Returns the address string representation. +TW_EXPORT_PROPERTY +TWString *_Nonnull TWARKAddressDescription(struct TWARKAddress *_Nonnull address); + +TW_EXTERN_C_END \ No newline at end of file diff --git a/include/TrustWalletCore/TWARKProto.h b/include/TrustWalletCore/TWARKProto.h new file mode 100644 index 00000000000..de23427ca08 --- /dev/null +++ b/include/TrustWalletCore/TWARKProto.h @@ -0,0 +1,12 @@ +// 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 "TWData.h" + +typedef TWData *_Nonnull TW_ARK_Proto_SigningInput; +typedef TWData *_Nonnull TW_ARK_Proto_SigningOutput; diff --git a/include/TrustWalletCore/TWARKSigner.h b/include/TrustWalletCore/TWARKSigner.h new file mode 100644 index 00000000000..6a1da97b34a --- /dev/null +++ b/include/TrustWalletCore/TWARKSigner.h @@ -0,0 +1,21 @@ +// 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 "TWARKProto.h" + +TW_EXTERN_C_BEGIN + +TW_EXPORT_CLASS +struct TWARKSigner; + +/// Signs a transaction. +TW_EXPORT_STATIC_METHOD +TW_ARK_Proto_SigningOutput TWARKSignerSign(TW_ARK_Proto_SigningInput input); + +TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWBlockchain.h b/include/TrustWalletCore/TWBlockchain.h index 520621c2794..977d75e0a86 100644 --- a/include/TrustWalletCore/TWBlockchain.h +++ b/include/TrustWalletCore/TWBlockchain.h @@ -29,6 +29,7 @@ enum TWBlockchain { TWBlockchainOntology = 14, TWBlockchainZilliqa = 15, TWBlockchainIoTeX = 16, + TWBlockchainArk = 17 }; TW_EXTERN_C_END diff --git a/include/TrustWalletCore/TWCoinType.h b/include/TrustWalletCore/TWCoinType.h index 5ae5e1a4499..1f04bf5b55d 100644 --- a/include/TrustWalletCore/TWCoinType.h +++ b/include/TrustWalletCore/TWCoinType.h @@ -70,6 +70,7 @@ enum TWCoinType { TWCoinTypeSemux = 7562605, TWCoinTypeDEXON = 237, TWCoinTypeZelcash = 19167, + TWCoinTypeARK = 111, }; /// Returns the blockchain for a coin type. diff --git a/js/tests/blockchain/ark/ARKSigner.test.ts b/js/tests/blockchain/ark/ARKSigner.test.ts new file mode 100644 index 00000000000..e79969248ea --- /dev/null +++ b/js/tests/blockchain/ark/ARKSigner.test.ts @@ -0,0 +1,29 @@ +import { expect } from 'chai'; +import 'mocha'; + +import * as Long from 'long'; + +import { fromHexString, bufToHex } from '../../Utils'; +import { TW, ARKSigner } from '../../../lib'; + +describe('ARKSigner', () => { + + it('test sign ARKSigner', () => { + let privateKey = fromHexString("d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712"); + + let input = TW.ARK.Proto.SigningInput.create({ + type: TW.ARK.Proto.TransactionType.Transfer, + privateKey: privateKey, + amount: Long.fromNumber(123123123), + fee: Long.fromNumber(10000000), + timestamp: 67447770, + toAddress: "ARkMaRcjcwRgr6vmDtAWo7bFqUgy9wG3NU" + }); + + let output = ARKSigner.sign(input); + + expect(bufToHex(output.signature)).to.equal("0x304402205e6365f4c3b49c28f03afd89d308736dca56671ea707dd3dd5af42272a0cc8ed02207fa7fc015fba7ae527d22a058cc4ebd8e9867c563ace7effc2dbaad2af8976c3"); + expect(output.encoded).to.equal("{\"amount\":123123123,\"asset\":{},\"fee\":10000000,\"id\":\"219b1cc99ec804df02230a9e913ccb45edb7819f22328e3cd15030174a8c4167\",\"recipientId\":\"ARkMaRcjcwRgr6vmDtAWo7bFqUgy9wG3NU\",\"senderPublicKey\":\"034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192\",\"signature\":\"304402205e6365f4c3b49c28f03afd89d308736dca56671ea707dd3dd5af42272a0cc8ed02207fa7fc015fba7ae527d22a058cc4ebd8e9867c563ace7effc2dbaad2af8976c3\",\"timestamp\":67447770,\"type\":0}"); + }); + +}); diff --git a/src/ARK/Address.cpp b/src/ARK/Address.cpp new file mode 100644 index 00000000000..c2ffc3622db --- /dev/null +++ b/src/ARK/Address.cpp @@ -0,0 +1,21 @@ +// 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 "Address.h" +#include "../Hash.h" +#include "../PublicKey.h" + +using namespace TW; +using namespace TW::ARK; + +Address::Address(const PublicKey &publicKey) { + if (publicKey.type != TWPublicKeyTypeSECP256k1) { + throw std::invalid_argument("Ark::Address needs a compressed SECP256k1 public key."); + } + const auto data = + publicKey.hash({Address::prefix}, static_cast(Hash::ripemd), false); + std::copy(data.begin(), data.end(), bytes.begin()); +} diff --git a/src/ARK/Address.h b/src/ARK/Address.h new file mode 100644 index 00000000000..41530bd013c --- /dev/null +++ b/src/ARK/Address.h @@ -0,0 +1,35 @@ +// 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 "../Data.h" +#include "../Base58Address.h" +#include "../PublicKey.h" + +namespace TW::ARK { + +class Address : public TW::Base58Address<21> { + public: + /// mainnet address prefix + static const byte prefix = 0x17; + + /// Initializes an address with a string representation. + explicit Address(const std::string& string) : TW::Base58Address<21>(string) {} + + /// Initializes an address with a collection of bytes. + explicit Address(const Data& data) : TW::Base58Address<21>(data) {} + + /// Initializes an address with a public key and a prefix. + Address(const PublicKey& publicKey); +}; + +} // namespace TW::ARK + +// Wrapper for C interface. +struct TWARKAddress { + TW::ARK::Address impl; +}; diff --git a/src/ARK/Signer.cpp b/src/ARK/Signer.cpp new file mode 100644 index 00000000000..0e4ff685cdf --- /dev/null +++ b/src/ARK/Signer.cpp @@ -0,0 +1,24 @@ +// 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 "Signer.h" +#include "../Hash.h" +#include "../PublicKey.h" + +#include + +using namespace TW::ARK; + +Proto::SigningOutput Signer::sign(PrivateKey &privateKey, Transaction &tx) { + Data hash = Hash::sha256(tx.encoded()); + tx.signature = privateKey.signAsDER(hash, TWCurveSECP256k1); + + Proto::SigningOutput output; + output.set_signature(tx.signature.data(), tx.signature.size()); + output.set_encoded(tx.encodedJson()); + + return output; +} diff --git a/src/ARK/Signer.h b/src/ARK/Signer.h new file mode 100644 index 00000000000..85b05b6b887 --- /dev/null +++ b/src/ARK/Signer.h @@ -0,0 +1,24 @@ +// 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 "Transaction.h" +#include "../PrivateKey.h" +#include "../proto/ARK.pb.h" + +namespace TW::ARK { +class Signer { + public: + static Proto::SigningOutput sign(PrivateKey &privateKey, Transaction &tx); +}; + +}; // namespace TW::ARK + +// Wrapper for C interface. +struct TWARKSigner { + TW::ARK::Signer impl; +}; diff --git a/src/ARK/Transaction.cpp b/src/ARK/Transaction.cpp new file mode 100644 index 00000000000..07be6627a9b --- /dev/null +++ b/src/ARK/Transaction.cpp @@ -0,0 +1,54 @@ +// 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 "Transaction.h" +#include "../Base58.h" +#include "../BinaryCoding.h" +#include "../Data.h" +#include "../Hash.h" +#include "../HexCoding.h" + +#include + +using json = nlohmann::json; + +using namespace TW; +using namespace TW::ARK; + +Data Transaction::encoded() const { + + // transaction type + auto data = Data{type}; + + encode32LE(timestamp, data); + append(data, publicKey); + append(data, Data(to.bytes.begin(), to.bytes.end())); + + // vendor field + Data vendor(64, 0); + append(data, vendor); + + encode64LE(amount, data); + encode64LE(fee, data); + + if (signature.size() > 0) { + append(data, signature); + } + return data; +} + +std::string Transaction::encodedJson() const { + json j = {{"amount", amount}, + {"asset", {}}, + {"fee", fee}, + {"id", hex(Hash::sha256(encoded()))}, + {"recipientId", to.string()}, + {"senderPublicKey", hex(publicKey)}, + {"signature", hex(signature)}, + {"timestamp", timestamp}, + {"type", type}}; + return j.dump(); +} diff --git a/src/ARK/Transaction.h b/src/ARK/Transaction.h new file mode 100644 index 00000000000..cf2d1db524b --- /dev/null +++ b/src/ARK/Transaction.h @@ -0,0 +1,43 @@ +// 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 "Address.h" +#include "../Data.h" + +namespace TW::ARK { + +class Transaction { + public: + byte type; + uint64_t amount; + uint64_t fee; + uint32_t timestamp; + Address to; + Data publicKey; + + /// Transaction signature. + Data signature; + + Transaction(byte type, uint64_t amount, uint64_t fee, uint32_t timestamp, Address to, + Data publicKey) + : type(type) + , amount(amount) + , fee(fee) + , timestamp(timestamp) + , to(to) + , publicKey(publicKey) {} + + public: + /// Encodes the transaction. + Data encoded() const; + + /// Encodes the transaction as json string. + std::string encodedJson() const; +}; + +} // namespace TW::ARK diff --git a/src/Coin.cpp b/src/Coin.cpp index 38011255c87..76cba8a4974 100644 --- a/src/Coin.cpp +++ b/src/Coin.cpp @@ -34,6 +34,7 @@ #include "IoTeX/Address.h" #include "Zilliqa/Address.h" #include "Semux/Address.h" +#include "ARK/Address.h" #include #include @@ -166,6 +167,9 @@ bool TW::validateAddress(TWCoinType coin, const std::string& string) { case TWCoinTypeSemux: return Semux::Address::isValid(string); + + case TWCoinTypeARK: + return ARK::Address::isValid(string); } } @@ -286,6 +290,9 @@ std::string TW::deriveAddress(TWCoinType coin, const PublicKey& publicKey) { case TWCoinTypeSemux: return Semux::Address(publicKey).string(); + + case TWCoinTypeARK: + return ARK::Address(publicKey).string(); } } diff --git a/src/interface/TWARKAddress.cpp b/src/interface/TWARKAddress.cpp new file mode 100644 index 00000000000..e21fc10aa24 --- /dev/null +++ b/src/interface/TWARKAddress.cpp @@ -0,0 +1,48 @@ +// 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. +#include "../ARK/Address.h" + +#include +#include +#include + +using namespace TW; +using namespace TW::ARK; + +/// Compares two addresses for equality. +bool TWARKAddressEqual(struct TWARKAddress *_Nonnull lhs, struct TWARKAddress *_Nonnull rhs){ + return lhs->impl == rhs->impl; +} + +/// Determines if the string is a valid address. +bool TWARKAddressIsValidString(TWString *_Nonnull string){ + auto s = reinterpret_cast(string); + return Address::isValid(*s); +}; + +/// Creates an address from a string representaion. +struct TWARKAddress *_Nullable TWARKAddressCreateWithString(TWString *_Nonnull string){ + auto s = reinterpret_cast(string); + if (!Address::isValid(*s)) { + return nullptr; + } + return new TWARKAddress{ Address(*s) }; +}; + +/// Creates an address from a public key. +struct TWARKAddress *_Nonnull TWARKAddressCreateWithPublicKey(struct TWPublicKey *_Nonnull publicKey){ + return new TWARKAddress{ Address(publicKey->impl) }; +}; + +/// Returns the address string representation. +TWString *_Nonnull TWARKAddressDescription(struct TWARKAddress *_Nonnull address){ + const auto string = address->impl.string(); + return TWStringCreateWithUTF8Bytes(string.c_str()); +} + +void TWARKAddressDelete(struct TWARKAddress *_Nonnull address) { + delete address; +} diff --git a/src/interface/TWARKSigner.cpp b/src/interface/TWARKSigner.cpp new file mode 100644 index 00000000000..ab57e0de57b --- /dev/null +++ b/src/interface/TWARKSigner.cpp @@ -0,0 +1,40 @@ +// 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. + +#include "../ARK/Address.h" +#include "../ARK/Signer.h" +#include "../ARK/Transaction.h" +#include "../proto/ARK.pb.h" +#include "../uint256.h" +#include +#include + +using namespace TW; +using namespace TW::ARK; + +TW_ARK_Proto_SigningOutput TWARKSignerSign(TW_ARK_Proto_SigningInput data) { + Proto::SigningInput input; + input.ParseFromArray(TWDataBytes(data), static_cast(TWDataSize(data))); + + auto key = PrivateKey(Data(input.private_key().begin(), input.private_key().end())); + auto pubKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); + auto to = Address(input.to_address()); + + auto tx = Transaction( + /* type */ static_cast(input.type()), + /* amount */ input.amount(), + /* fee */ input.fee(), + /* timestamp */ input.timestamp(), + /* to */ to, + /* publicKey*/ pubKey.bytes + ); + + auto output = Signer::sign(key, tx); + + auto serialized = output.SerializeAsString(); + return TWDataCreateWithBytes(reinterpret_cast(serialized.data()), + serialized.size()); +} diff --git a/src/proto/ARK.proto b/src/proto/ARK.proto new file mode 100644 index 00000000000..abe38dc6185 --- /dev/null +++ b/src/proto/ARK.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +package TW.ARK.Proto; +option java_package = "wallet.core.jni.proto"; + +enum TransactionType { + Transfer = 0; +} + +// Input data necessary to create a signed transaction. +message SigningInput { + // Transaction type + TransactionType type = 1; + + // Amount to send + uint64 amount = 2; + + // Fee + uint64 fee = 3; + + // Recipient's address. + string to_address = 4; + + // Timestamp + uint32 timestamp = 5; + + // Private key. + bytes private_key = 6; +} + +message SigningOutput { + // JSON-encoded transaction parameters. + string encoded = 1; + + // Signature. + bytes signature = 2; +} diff --git a/swift/Sources/Addresses/CoinType+Address.swift b/swift/Sources/Addresses/CoinType+Address.swift index 1f9705cb8d4..e71240a06c6 100644 --- a/swift/Sources/Addresses/CoinType+Address.swift +++ b/swift/Sources/Addresses/CoinType+Address.swift @@ -87,6 +87,8 @@ public extension CoinType { return ZilliqaAddress(string: string) case .semux: return SemuxAddress(string: string) + case .ark: + return ARKAddress(string: string) } return .none } diff --git a/swift/Tests/Blockchains/ARKTests.swift b/swift/Tests/Blockchains/ARKTests.swift new file mode 100644 index 00000000000..cf7344b4ea6 --- /dev/null +++ b/swift/Tests/Blockchains/ARKTests.swift @@ -0,0 +1,38 @@ +// 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. +import TrustWalletCore +import XCTest + +class ARKTests: XCTestCase { + + func testAddress() { + let data = "this is a top secret passphrase".data(using: .utf8)! + let hashed = Hash.sha256(data: data) + let privateKey = PrivateKey(data: hashed)! + let publicKey = privateKey.getPublicKeySecp256k1(compressed: true); + let address = ARKAddress(publicKey: publicKey) + + XCTAssertEqual(hashed.hexString, "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712") + XCTAssertEqual(address.description, "AGeYmgbg2LgGxRW2vNNJvQ88PknEJsYizC") + } + + func testSigner() throws { + let input = ARKSigningInput.with { + $0.type = .transfer + $0.amount = 123123123 + $0.fee = 10000000 + $0.privateKey = Data(hexString: "d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712")! + $0.timestamp = 67447770 + $0.toAddress = "ARkMaRcjcwRgr6vmDtAWo7bFqUgy9wG3NU" + } + + let output = ARKSigner.sign(input: input) + + XCTAssertEqual(output.signature.hexString, "304402205e6365f4c3b49c28f03afd89d308736dca56671ea707dd3dd5af42272a0cc8ed02207fa7fc015fba7ae527d22a058cc4ebd8e9867c563ace7effc2dbaad2af8976c3") + XCTAssertEqual(output.encoded, "{\"amount\":123123123,\"asset\":{},\"fee\":10000000,\"id\":\"219b1cc99ec804df02230a9e913ccb45edb7819f22328e3cd15030174a8c4167\",\"recipientId\":\"ARkMaRcjcwRgr6vmDtAWo7bFqUgy9wG3NU\",\"senderPublicKey\":\"034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192\",\"signature\":\"304402205e6365f4c3b49c28f03afd89d308736dca56671ea707dd3dd5af42272a0cc8ed02207fa7fc015fba7ae527d22a058cc4ebd8e9867c563ace7effc2dbaad2af8976c3\",\"timestamp\":67447770,\"type\":0}") + } + +} diff --git a/swift/Tests/CoinAddressDerivationTests.swift b/swift/Tests/CoinAddressDerivationTests.swift index 9f45ce2ddc4..904ee5a6f95 100644 --- a/swift/Tests/CoinAddressDerivationTests.swift +++ b/swift/Tests/CoinAddressDerivationTests.swift @@ -166,6 +166,9 @@ class CoinAddressDerivationTests: XCTestCase { case .zelcash: let expectedResult = "t1UKbRPzL4WN8Rs8aZ8RNiWoD2ftCMHKGUf" AssetCoinDerivation(coin, expectedResult, derivedAddress, address) + case .ark: + let expectedResult = "Ac49m5pu5YpMMNgEbSYeZUEpRMHcSK3DfV" + AssetCoinDerivation(coin, expectedResult, derivedAddress, address) } } } diff --git a/tests/ARK/AddressTests.cpp b/tests/ARK/AddressTests.cpp new file mode 100644 index 00000000000..44b18dbafa1 --- /dev/null +++ b/tests/ARK/AddressTests.cpp @@ -0,0 +1,37 @@ +// 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. + +#include "ARK/Address.h" +#include "Base58.h" +#include "Data.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include "HexCoding.h" +#include + +using namespace TW; +using namespace TW::ARK; + +TEST(ARKAddress, fromPrivateKey) { + auto key = PrivateKey(parse_hex("d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712")); + auto pubKey = key.getPublicKey(TWPublicKeyTypeSECP256k1); + auto address = Address(pubKey); + + ASSERT_EQ(address.string(), "AGeYmgbg2LgGxRW2vNNJvQ88PknEJsYizC"); +} + +TEST(ARKAddress, fromString) { + auto address = Address("AewxfHQobSc49a4radHp74JZCGP8LRe4xA"); + ASSERT_EQ(address.string(), "AewxfHQobSc49a4radHp74JZCGP8LRe4xA"); +} + +TEST(ARKAddress, validation) { + auto prefixes = std::vector(); + prefixes.insert(prefixes.end(), Data({Address::prefix})); + + ASSERT_TRUE(Address::isValid(std::string("ARjdsayeC5q6xRo432Ru3F9Zcb73A5dfME"), prefixes)); + ASSERT_FALSE(Address::isValid(std::string("D61mfSggzbvQgTUe6JhYKH2doHaqJ3Dyib"), prefixes)); +} diff --git a/tests/ARK/SignerTests.cpp b/tests/ARK/SignerTests.cpp new file mode 100644 index 00000000000..4030e33681e --- /dev/null +++ b/tests/ARK/SignerTests.cpp @@ -0,0 +1,65 @@ +// 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. + +#include "ARK/Address.h" +#include "ARK/Signer.h" +#include "ARK/Transaction.h" +#include "Data.h" +#include "HexCoding.h" +#include "PrivateKey.h" +#include "PublicKey.h" +#include + +using namespace TW; +using namespace TW::ARK; + +TEST(ARKSigner, sign) { + + auto key = PrivateKey(parse_hex("d8839c2432bfd0a67ef10a804ba991eabba19f154a3d707917681d45822a5712")); + auto tx = Transaction( + /* type */ 0, + /* amount */ 123123123, + /* fee */ 10000000, + /* timestamp */ 67447770, // unix epoch - 1490101200 + /* */ Address("ARkMaRcjcwRgr6vmDtAWo7bFqUgy9wG3NU"), + /* */ key.getPublicKey(TWPublicKeyTypeSECP256k1).bytes); + + auto output = Signer::sign(key, tx); + + ASSERT_EQ(hex(tx.publicKey), + "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192"); + + ASSERT_EQ(hex(tx.signature), + "304402205e6365f4c3b49c28f03afd89d308736dca56671ea707dd3dd5af42272a0cc8ed02207fa7fc01" + "5fba7ae527d22a058cc4ebd8e9867c563ace7effc2dbaad2af8976c3"); + + ASSERT_EQ(hex(tx.encoded()), + "00" + "da2b0504" + "034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192" + "176d67bd926e417336456be869bc16a268b3705cbe" + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000" + "b3b5560700000000" + "8096980000000000" + "304402205e6365f4c3b49c28f03afd89d308736dca56671ea707dd3dd5af42272a0cc8ed02207fa7fc01" + "5fba7ae527d22a058cc4ebd8e9867c563ace7effc2dbaad2af8976c3"); + + ASSERT_EQ(tx.encodedJson(), + "{\"amount\":123123123," + "\"asset\":{}," + "\"fee\":10000000," + "\"id\":\"219b1cc99ec804df02230a9e913ccb45edb7819f22328e3cd15030174a8c4167\"," + "\"recipientId\":\"ARkMaRcjcwRgr6vmDtAWo7bFqUgy9wG3NU\"," + "\"senderPublicKey\":" + "\"034151a3ec46b5670a682b0a63394f863587d1bc97483b1b6c70eb58e7f0aed192\"," + "\"signature\":" + "\"304402205e6365f4c3b49c28f03afd89d308736dca56671ea707dd3dd5af42272a0cc8ed02207fa7fc" + "015fba7ae527d22a058cc4ebd8e9867c563ace7effc2dbaad2af8976c3\"," + "\"timestamp\":67447770," + "\"type\":0" + "}"); +} diff --git a/tests/CoinTests.cpp b/tests/CoinTests.cpp index 581f621c922..fea7abbdbfa 100644 --- a/tests/CoinTests.cpp +++ b/tests/CoinTests.cpp @@ -160,6 +160,14 @@ TEST(Coin, ValidateAddressSemux) { EXPECT_FALSE(validateAddress(TWCoinTypeSemux, "")); } +TEST(Coin, ValidateAddressARK){ + EXPECT_TRUE(validateAddress(TWCoinTypeARK, "AewxfHQobSc49a4radHp74JZCGP8LRe4xA")); + EXPECT_TRUE(validateAddress(TWCoinTypeARK, "AdZWxCcQVG871gpb3Qd6EP2PEKAKKRJ1pY")); + EXPECT_FALSE(validateAddress(TWCoinTypeARK, "DdZWxCcQVG871gpb3Qd6EP2PEKAKKRJ1pY")); + EXPECT_FALSE(validateAddress(TWCoinTypeARK, "1GUGZxXMeoiikynbq8XoMy57RtUciiHrP1")); + EXPECT_FALSE(validateAddress(TWCoinTypeARK, "Aqweqweqwe")); +} + TEST(Coin, DeriveAddress) { const auto privateKey = PrivateKey(parse_hex("0x4646464646464646464646464646464646464646464646464646464646464646")); EXPECT_EQ(TW::deriveAddress(TWCoinTypeAion, privateKey), "0xa0010b0ea04ba4d76ca6e5e9900bacf19bc4402eaec7e36ea7ddd8eed48f60f3"); @@ -206,6 +214,7 @@ TEST(Coin, DeriveAddress) { EXPECT_EQ(TW::deriveAddress(TWCoinTypeIoTeX, privateKey), "io1nk9x9ajk4rgkzhqjjn7hr6w0k0jg2kj0zgdt6h"); EXPECT_EQ(TW::deriveAddress(TWCoinTypeEllaism, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); EXPECT_EQ(TW::deriveAddress(TWCoinTypeDEXON, privateKey), "0x9d8A62f656a8d1615C1294fd71e9CFb3E4855A4F"); + EXPECT_EQ(TW::deriveAddress(TWCoinTypeARK, privateKey), "AdZWxCcQVG871gpb3Qd6EP2PEKAKKRJ1pY"); } } // namespace TW diff --git a/tests/interface/TWCoinTypeConfigTests.cpp b/tests/interface/TWCoinTypeConfigTests.cpp index e3390406730..1ee4e3ca28d 100644 --- a/tests/interface/TWCoinTypeConfigTests.cpp +++ b/tests/interface/TWCoinTypeConfigTests.cpp @@ -124,6 +124,9 @@ TEST(TWCoinTypeConfiguration, TWCoinTypeConfigurationGetSymbol) { auto dexon = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeDEXON)); assertStringsEqual(dexon, "DXN"); + + auto ark = WRAPS(TWCoinTypeConfigurationGetSymbol(TWCoinTypeARK)); + assertStringsEqual(ark, "ARK"); } TEST(TWCoinTypeConfiguration, TWCoinTypeConfigurationGetDecimals) { @@ -167,6 +170,7 @@ TEST(TWCoinTypeConfiguration, TWCoinTypeConfigurationGetDecimals) { ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeZilliqa), 12); ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeSemux), 9); ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeDEXON), 18); + ASSERT_EQ(TWCoinTypeConfigurationGetDecimals(TWCoinTypeARK), 8); } TEST(TWCoinTypeConfiguration, TWCoinTypeConfigurationGetTransactionURL) { @@ -294,6 +298,9 @@ TEST(TWCoinTypeConfiguration, TWCoinTypeConfigurationGetTransactionURL) { auto zel = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeZelcash, txId)); assertStringsEqual(zel, "https://explorer.zel.cash/tx/123"); + + auto ark = WRAPS(TWCoinTypeConfigurationGetTransactionURL(TWCoinTypeARK, txId)); + assertStringsEqual(ark, "https://explorer.ark.io/transaction/123"); } TEST(TWCoinTypeConfiguration, TWCoinTypeConfigurationGetID) { @@ -413,6 +420,9 @@ TEST(TWCoinTypeConfiguration, TWCoinTypeConfigurationGetID) { auto zel = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeZelcash)); assertStringsEqual(zel, "zelcash"); + + auto ark = WRAPS(TWCoinTypeConfigurationGetID(TWCoinTypeARK)); + assertStringsEqual(ark, "ark"); } TEST(TWCoinTypeConfiguration, TWCoinTypeConfigurationGetName) { @@ -535,11 +545,15 @@ TEST(TWCoinTypeConfiguration, TWCoinTypeConfigurationGetName) { auto zel = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeZelcash)); assertStringsEqual(zel, "Zelcash"); + + auto ark = WRAPS(TWCoinTypeConfigurationGetName(TWCoinTypeARK)); + assertStringsEqual(ark, "ARK"); } TEST(TWCoinTypeConfiguration, TWCoinTypeBlockchain) { ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeBitcoin)); ASSERT_EQ(TWBlockchainBitcoin, TWCoinTypeBlockchain(TWCoinTypeLitecoin)); ASSERT_EQ(TWBlockchainEthereum, TWCoinTypeBlockchain(TWCoinTypeEthereum)); - ASSERT_EQ(TWBlockchainIoTeX, TWCoinTypeBlockchain(TWCoinTypeIoTeX)); -} \ No newline at end of file + ASSERT_EQ(TWBlockchainIoTeX, TWCoinTypeBlockchain(TWCoinTypeIoTeX)); + ASSERT_EQ(TWBlockchainArk, TWCoinTypeBlockchain(TWCoinTypeARK)); +}